mrd6-0.9.6/0000755000175000017500000000000010746353706011077 5ustar hugohugomrd6-0.9.6/tools/0000755000175000017500000000000010746353706012237 5ustar hugohugomrd6-0.9.6/tools/c/0000755000175000017500000000000010746353706012461 5ustar hugohugomrd6-0.9.6/tools/c/Makefile0000644000175000017500000000027610351363623014115 0ustar hugohugoCFLAGS = -g -O2 -Wall -ansi PREFIX ?= /usr/local -include ../../src/Makefile.options all: mrd6sh install: mrd6sh install -D mrd6sh $(DESTDIR)$(PREFIX)/bin/mrd6sh clean: rm -f mrd6sh mrd6-0.9.6/tools/c/mrd6sh.c0000644000175000017500000000204410314233102014002 0ustar hugohugo#include #include #include #include #include #define MRD_SOCKET "/var/run/mrd6" int main(int argc, char *argv[]) { int i, sock; struct sockaddr_un addr; char buf[256]; int ptr; if (argc < 2) { printf("No command specified.\n"); return -1; } strcpy(buf, argv[1]); ptr = strlen(buf); for (i = 2; i < argc; i++) { if ((ptr + strlen(argv[i])) >= sizeof(buf)) { printf("Command is too long.\n"); return -1; } sprintf(buf + ptr, " %s", argv[i]); ptr += 1 + strlen(argv[i]); } buf[ptr] = 0; sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { perror("socket()"); return -1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, MRD_SOCKET); if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("connect()"); return -1; } if (send(sock, buf, ptr + 1, 0) < 0) { perror("send()"); return -1; } while ((i = recv(sock, buf, sizeof(buf), 0)) > 0) { buf[i] = 0; printf(buf); } return 0; } mrd6-0.9.6/tools/mrd6sh0000755000175000017500000000074410340603000013344 0ustar hugohugo#!/usr/bin/perl -w # by Marco d'Itri use strict; use IO::Socket::UNIX; my $MRD_SOCKET = '/var/run/mrd6'; if (@ARGV == 0) { print "No command specified.\n"; exit 1; } my $command = join(' ', @ARGV) . "\r\n"; my $sock = new IO::Socket::UNIX( Type => SOCK_STREAM, Peer => $MRD_SOCKET, ); if (not defined $sock) { print "Failed to connect to MRD6, is the router daemon running?\n"; exit 1; } print $sock $command or die "write: $!"; while (<$sock>) { print $_; } exit 0; mrd6-0.9.6/Makefile0000644000175000017500000000022510360120100012502 0ustar hugohugoall: @$(MAKE) -C src install: @$(MAKE) -C src install clean: @$(MAKE) -C src clean config: @config/base.pl .PHONY: all config install clean mrd6-0.9.6/README0000644000175000017500000000514210746337106011755 0ustar hugohugoMRD - Multicast Routing Daemon ------------------------------ MRD is a IPv6 Multicast routing Daemon for Linux. MRD is written in C++ and has been primarily developed in Linux 2.6. Send any bugs or issues to Hugo Santos Features -------- - Extensible modular design; - MLDv1 and MLDv2 support (mld); * MLD Proxying (mld); - PIM-SM support (ASM and SSM) (pim); * Bootstrap (BSR) Mechanism support (pim); * Static RP configuration (pim); * Embedded-RP support (RFC 3956); - partial MBGP support (bgp); * Implements basic BGP4 mechanisms (IPv6 Multicast SAFI) to update local MRIB from MBGP info; - Supports both native and virtual (tunnel) interfaces (tested IPv6-IPv4, IPv6-IPv6 and TUN/TAP tunnels); - Abstract Forwarding Interface (MFA) with user-space module; - Remote configuration and online administration (console); Building -------- Read the included INSTALL file. Running ------- Usage `mrd [options]` * Options -D Run MRD in the background -f Use `conf` as the configuration file. The default configuration file is '/etc/mrd.conf' or if it doesn't exist, the 'mrd.conf' file in the running directory. Configuration File ------------------ The configuration file follows a hierarchical definition using `identifier { ... }` blocks. The `interfaces` and `groups` keywords specify both interface and group configuration. * Example pim { enable bsr-candidate; /* we are a BSR candidate */ enable rp-candidate; /* we are a RP candidate */ } groups { ff0e::/16 { pim { enable rp_adv; /* include this group in the RP advertisement */ } } } There are several example configuration files included in the src/confs/ directory. Console ------- The console module (which is enabled by default) allows you to to control/monitor mrd remotely by using the included 'mrd6sh' program. You may also connect remotely directly to mrd using a standard telnet program and connecting to port 44510. telnet -6 ::1 44510 Author and Contact ------------------ Hugo Santos Copyright and Licensing ----------------------- This MRD release is GPL Licensed (Refer to the included COPYING file for details). Copyright (c) 2006-2008 - Hugo Santos Copyright (c) 2004-2006 - Univ. Aveiro, IT Aveiro Thanks to --------- - Sebastien Chaumontet and Mickael Hoerdt for extensive testing and support; - Bernhard Schmidt for testing and supplying IPv6 multicast connectivity; - Anand Kumria for uploading and maintaining the oficial debian package; mrd6-0.9.6/MRD6shQuickRef.txt0000644000175000017500000000422610276757744014351 0ustar hugohugomrd6sh is a tool that along with builtin telnet support allow one to interactively configure and obtain information from MRD6. The following consists of a brief list of the most important commands. Please note that most commands may be called by the least common command prefix, for instance, 'sho gr' may be used instead of 'show group'. $ mrd6sh show group [] Display all group information, including possible MLD and PIM information related to a particular group. A group address may be supplied to obtain information about a single group only. $ mrd6sh show group detail Outputs extra information related to group interfaces, in MLD's case it outputs the full Multicast Filter, Source timers and particular listener interest. $ mrd6sh show mfa counters Displays per-group and per-source state 60 second average forwarding statistics. $ mrd6sh show interface [ [extended]] Displays all interface information, including PIM info such as known neighbours and MLD information (Querier, etc). An interface name may be supplied to obtain information about that interface only. If the parameter 'extended' is used for a particular interface, also outputs extra PIM information such as neighbour upstream Join/Prune info. $ mrd6sh show mrib Outputs the current contents of the Multicast Routing Information base. $ mrd6sh show rpf
Matches MRIB prefix information (possibly including output interface and nexthop address) for the supplied address. This command can be used to check what information is MRD6 using to route towards a particular source. $ mrd6sh log attach default \"mrd.log\" all Changes mrd.log's log level to All. Use this whenever you find odd behaviour as it triggers the logging of all signaling information, state changes, etc. You should use your log file name instead of mrd.log if you use a different one. $ mrd6sh show version Important information that should be supplied while reporting bugs :-) -- In case you are using the BGP module, the following command is also of interest. $ mrd6sh show bgp Outputs current BGP neighbour state, including filter match statistics, current BGP state machine state, uptime, etc. mrd6-0.9.6/INSTALL0000644000175000017500000000102410360050223012101 0ustar hugohugoBuilding MRD6 ------------- * Configuration * MRD6 includes a configuration system that enables you to select which features you which to compile staticaly and as modules. By default, MLD, PIM and the console modules are built staticaly resulting in a single binary. To select the features you want run $ make config * Building * After configuration, just type $ make in the top level directory to build MRD6. To install the binary and modules to the system run # make install mrd6-0.9.6/debian/0000755000175000017500000000000010746353706012321 5ustar hugohugomrd6-0.9.6/debian/changelog0000644000175000017500000001045710746353230014172 0ustar hugohugomrd6 (0.9.6) unstable; urgency=low * New upstream release. * Now really fix FTBFS with GCC 4.3 (Closes: #455280) -- Hugo Santos Fri, 25 Jan 2008 13:29:21 +0100 mrd6 (0.9.5-rev3.dfsg-0.2) unstable; urgency=low * Non-maintainer upload. * Repackage upstream tarball to remove non-free IETF RFC/I-D (Closes: #423063) * Really fix FTBFS with GCC 4.3 (Closes: #417436) * Clean up debian/rules and create debian/{examples,manpages} -- Lior Kaplan Sat, 09 Jun 2007 00:57:01 +0300 mrd6 (0.9.5-rev3-0.1) unstable; urgency=low * Non-maintainer upload. (Closes: #418074) * New maintainer. (Closes: #418084) - Adopt this package by its upstream, which works on it very tightly with Debian. Thank you Hugo for covering for an MIA maintainer. * New upstream release - Fix segfault when unloading module (Closes: #394590) - Fix FTBFS with GCC 4.3 (Closes: #417436) - New upstream address (Closes: #411805) - Change the package's version number inside debian, so it will be more accurate and easy to maintain with future (debian & upstream) versions. * debian/control: - Bump standards version to 3.7.2.2 (no changes needed) - Remove extra build depends, as they are covered by build-essential. - Update package description according to the README file. - Change maintainer to Hugo Santos. * Add watch file. -- Lior Kaplan Mon, 09 Apr 2007 20:28:32 +0300 mrd6 (0.9.5-release-1) unstable; urgency=low * New upstream release * Hack the version number so we can upgrade properly * New features: - update to draft 06 - implements MSNIP and MRDISC - improved user-space forwarding on Linux -- Anand Kumria Sun, 8 Jan 2006 12:28:48 +1100 mrd6 (0.9.5-pre2-1) unstable; urgency=low * New upstream (pre)release -- Anand Kumria Mon, 28 Nov 2005 15:41:11 +1100 mrd6 (0.9.5-pre1-1) unstable; urgency=low * Initial upload to Debian -- Anand Kumria Tue, 15 Nov 2005 11:05:57 +1100 mrd6 (0.9.4-beta2-1) unstable; urgency=low * beta2 -- Hugo Santos Mon, 17 Oct 2005 22:57:45 +0100 mrd6 (0.9.4-beta1-1) unstable; urgency=low * Bumped to 0.9.4-beta1 -- Hugo Santos Wed, 28 Sep 2005 01:11:03 +0100 mrd6 (0.9.2-beta3-1) unstable; urgency=low * beta3 -- Hugo Santos Tue, 23 Aug 2005 23:36:40 +0100 mrd6 (0.9.2-beta2-1) unstable; urgency=low * beta2 -- Hugo Santos Thu, 18 Aug 2005 00:09:07 +0100 mrd6 (0.9.2-beta1-1) unstable; urgency=low * Bumped to 0.9.2. -- Hugo Santos Fri, 12 Aug 2005 00:30:29 +0100 mrd6 (0.9.1-beta2-1) unstable; urgency=low * Version bump -- Hugo Santos Mon, 25 Jul 2005 00:32:34 +0100 mrd6 (0.9.1-beta1-1) unstable; urgency=low * Version bump -- Hugo Santos Thu, 7 Jul 2005 02:34:26 +0100 mrd6 (0.9-beta2) unstable; urgency=low * Beta 2 -- Hugo Santos Thu, 30 Jun 2005 06:19:51 +0100 mrd6 (0.9-beta1) unstable; urgency=low * Bump to 0.9 -- Hugo Santos Sat, 25 Jun 2005 20:07:24 +0100 mrd6 (0.8.5-beta1-1) unstable; urgency=low * Bumped to 0.8.5 -- Hugo Santos Sat, 30 Apr 2005 21:55:09 +0100 mrd6 (0.8-beta2-1) unstable; urgency=low * Yet another bump. 0.8-beta2 -- Hugo Santos Sat, 23 Apr 2005 03:06:53 +0100 mrd6 (0.8-beta1-1) unstable; urgency=low * Bumped version to 0.8-beta1 -- Hugo Santos Tue, 19 Apr 2005 23:34:04 +0100 mrd6 (0.7-beta3-1) unstable; urgency=low * Bumped version to 0.7-beta3 -- Hugo Santos Tue, 5 Apr 2005 02:08:32 +0100 mrd6 (0.7-beta2-1) unstable; urgency=low * Bumped version to 0.7-beta2 -- Hugo Santos Thu, 26 Mar 2005 03:46:26 +0000 mrd6 (0.7-beta1-1) unstable; urgency=low * Bumped version to 0.7-beta1 -- Hugo Santos Thu, 24 Mar 2005 14:52:00 +0000 mrd6 (0.6-beta6-1) unstable; urgency=low * Bumped version to beta6 -- Hugo Santos Sun, 7 Mar 2005 23:13:08 +0000 mrd6 (0.6-beta5-1) unstable; urgency=low * Initial Release. -- Hugo Santos Sun, 27 Feb 2005 03:08:08 +0000 mrd6-0.9.6/debian/control0000644000175000017500000000207010746353230013713 0ustar hugohugoSource: mrd6 Section: net Priority: optional Maintainer: Hugo Santos Build-Depends: debhelper (>= 5.0.0), perl Standards-Version: 3.7.2.2 Package: mrd6 Architecture: any Depends: ${shlibs:Depends}, lsb-base Description: IPv6 Multicast Routing Daemon mrd6 is a modular IPv6 Multicast Routing Daemon which implements: * MLDv1 and MLDv2 support - MLD proxying * PIM-SM (ASM and SSM) - Bootstrap (BSR) Mechanism support - Static RP configuration - Embedded-RP support (RFC 3956) * partial MBGP support - Implements basic BGP4 mechanisms (IPv6 Multicast SAFI) to update local MRIB from MBGP info - Uses IPv6 Multicast SAFI prefixes announced by peers to update local MRIB - Is able to announce local prefixes - Filter support * Supports both native and virtual (tunnel) interfaces (tested IPv6-IPv4, IPv6-IPv6 and TUN/TAP tunnels) * Abstract Forwarding Interface (MFA) with user-space module * Remote configuration and online administration . Homepage: http://fivebits.net/proj/mrd6/ mrd6-0.9.6/debian/dirs0000644000175000017500000000001510746353230013171 0ustar hugohugousr/lib/mrd6 mrd6-0.9.6/debian/watch0000644000175000017500000000036210746353230013343 0ustar hugohugoversion=3 # Using example fron the uscan man page: # This is a variant HTTP format which allows direct specification of # the homepage: # Homepage Pattern [Version [Action]] http://fivebits.net/proj/mrd6 /files/mrd6/mrd6-([\d\.-]*).tar.gz mrd6-0.9.6/debian/copyright0000644000175000017500000000200410547654026014246 0ustar hugohugoIt was downloaded from http://fivebits.net/proj/mrd6/ Copyright Holder: 2006, 2007 - Hugo Santos 2004, 2005, 2006 - Universidade de Aveiro, IT Aveiro Contact: Hugo Santos License: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA A full copy of the GNU General Public Licence can be found at /usr/share/common-licenses/GPL-2 mrd6-0.9.6/debian/docs0000644000175000017500000000005110746353230013160 0ustar hugohugoMRD6shQuickRef.txt Troubleshooter README mrd6-0.9.6/debian/mrd6.init0000755000175000017500000000241510746353230014053 0ustar hugohugo#!/bin/sh ### BEGIN INIT INFO # Provides: mrd6 # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Required-Start: $network # Required-Stop: $network # Short-Description: IPv6 Multicast Routing Daemon # Description: mrd6 is a IPv6 IPv6 Multicast Routing Daemon which # implements: MLDv1, MLDv2, PIM-SM, partial MBGP support, # Native and virtual (tunnel) interfaces support and CLI # support. ### END INIT INFO # Author: Hugo Santos PATH=/usr/sbin:/usr/bin:/sbin:/bin DAEMON=/usr/sbin/mrd6 NAME=mrd6 DESC="Multicast routing daemon" CONF=/etc/mrd6.conf test -x $DAEMON || exit 0 test -r $CONF || exit 0 . /lib/lsb/init-functions case "$1" in start) log_begin_msg "Starting $DESC: $NAME" start-stop-daemon --start --quiet -m --pidfile /var/run/mrd6.pid --exec $DAEMON -- -D log_end_msg $? ;; stop) log_begin_msg "Stopping $DESC: $NAME" start-stop-daemon --stop --quiet --oknodo --exec $DAEMON rm -f /var/run/mrd6.pid log_end_msg $? ;; restart|force-reload) /etc/init.d/$NAME stop /etc/init.d/$NAME start ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart|force-reload}" >&2 exit 1 ;; esac exit 0 mrd6-0.9.6/debian/rules0000755000175000017500000000223510746353230013373 0ustar hugohugo#!/usr/bin/make -f # export DH_VERBOSE=1 TARGET = mrd6 DESTDIR = $(CURDIR)/debian/mrd6 export TARGET DESTDIR configure: configure-stamp configure-stamp: dh_testdir config/base.pl \ --prefix /usr \ --static mld \ --static pim \ --static console \ --external bgp \ --external msnip \ --external mrdisc \ touch configure-stamp build: build-stamp build-stamp: configure dh_testdir $(MAKE) touch build-stamp clean: dh_testdir dh_testroot -$(MAKE) clean rm -rf src/build/ rm -f src/Makefile.options rm -f build-stamp configure-stamp dh_clean install: build dh_testdir dh_testroot dh_clean -k dh_installdirs $(MAKE) install # Build architecture-independent files here. binary-indep: build install # We have nothing to do by default. # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_installchangelogs dh_installdocs dh_installexamples # dh_install dh_installinit dh_installman dh_link dh_strip dh_compress dh_fixperms dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install mrd6-0.9.6/debian/manpages0000644000175000017500000000003210746353230014022 0ustar hugohugodocs/mrd6.8 docs/mrd6sh.1 mrd6-0.9.6/debian/compat0000644000175000017500000000000210746353230013507 0ustar hugohugo5 mrd6-0.9.6/debian/examples0000644000175000017500000000001410746353230014045 0ustar hugohugosrc/confs/* mrd6-0.9.6/docs/0000755000175000017500000000000010746353706012027 5ustar hugohugomrd6-0.9.6/docs/mrd6.80000644000175000017500000000166410547654026012775 0ustar hugohugo.TH MRD6 8 "IPv6 Multicast routing daemon" .SH NAME mrd6 \- an IPv6 Multicast routing daemon and framework .SH SYNOPSIS .B mrd6 [ .B \-D ] [ .B \-f .I config-file ] [ .B \-A ] .SH DESCRIPTION .B mrd6 is a multicast routing daemon that supports PIM-SM, MLDv2, MBGP, and others. .SH OPTIONS Options available for the .B mrd6 command: .TP \fB\-D\fR Forks into the background after init. .TP \fB\-f\fR Specifies the configuration file to be used. If this option is not specified .B mrd6 will try to load \fB\fI/etc/mrd6.conf\fR. .TP \fB\-A\fR Don't auto-load .B mrd6 modules that were built statically. .SH FILES .TP .BI /etc/mrd6.conf The default location of the .B mrd6 config file. .SH DIAGNOSTICS .B mrd6 will log to the stderr file descriptor, to a log file or through syslog to the system logs. For more information on how to configure \fBmrd6\fR check the included documentation. .SH BUGS Report any bugs to .BI http://fivebits.net/proj/mrd6/ mrd6-0.9.6/docs/mrd6sh.10000644000175000017500000000120610547654026013311 0ustar hugohugo.TH MRD6SH 1 "IPv6 Multicast routing daemon" .SH NAME mrd6 \- an IPv6 Multicast routing daemon and framework .SH SYNOPSIS .B mrd6sh .I command .SH DESCRIPTION .B mrd6sh allows you both to either obtain current state information from .B mrd6 as well as to dynamically configure it. .SH OPTIONS Options available for the .B mrd6 command: .TP .I command The full command that will be sent to \fBmrd6\fR. For a list of common useful commands check the included MRD6shQuickRef.txt .SH FILES .TP .BI /usr/share/doc/mrd6/MRD6shQuickRef.txt A quick reference to useful \fBmrd6\fR commands. .SH BUGS Report any bugs to .BI http://fivebits.net/proj/mrd6/ mrd6-0.9.6/AUTHORS0000644000175000017500000000004110547654026012140 0ustar hugohugoHugo Santos mrd6-0.9.6/include/0000755000175000017500000000000010746353706012522 5ustar hugohugomrd6-0.9.6/include/mrdpriv/0000755000175000017500000000000010746353706014205 5ustar hugohugomrd6-0.9.6/include/mrdpriv/linux/0000755000175000017500000000000010746353706015344 5ustar hugohugomrd6-0.9.6/include/mrdpriv/linux/unicast_route.h0000644000175000017500000000511010637602444020371 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * unicast_route.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_linux_unicast_route_h_ #define _mrd_linux_unicast_route_h_ #include #include #include #ifdef __STRICT_ANSI__ #undef __STRICT_ANSI__ #include #include #include #define __STRICT_ANSI__ #else #include #include #include #endif #include #include #include struct netlink_msg; /*! * \brief Implements the `unicast_router' interface in Linux. */ class linux_unicast_router : public rib_def { public: linux_unicast_router(); ~linux_unicast_router(); bool check_startup(); void shutdown(); void check_initial_interfaces(); bool set_property(const char *, const char *); void do_dump(int); private: bool send_nlmsg(const netlink_msg *, netlink_msg *) const; bool lookup_prefix(const in6_addr &, lookup_result &) const; void notify_changes(); void data_available(uint32_t); int process_message(); void dump_request(int); void handle_route_event(bool isnew, nlmsghdr *); void handle_intf_event(bool isnew, nlmsghdr *); void handle_addr_event(bool isnew, nlmsghdr *); void parse_prefix_rec(rtattr *tb[], int, int, lookup_result &) const; bool call_method(int, base_stream &, const std::vector &); bool filter_routes(base_stream &, const std::vector &); bool show_filter_routes(base_stream &, const std::vector &); std::map parse_rt_protos() const; uint8_t *buffer; property_def *bufferlen; int rt_sock; socket0 rt_bcast_sock; bool rt_dumping; uint32_t rt_nlseq; std::set filter_protos; }; #endif mrd6-0.9.6/include/mrdpriv/linux/us_mfa.h0000644000175000017500000001563310550053610016757 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * us_mfa.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_us_mfa_h_ #define _mrd_us_mfa_h_ #include #include #include #include #include #include #include #include class interface; class router; struct ip6_hdr; class us_mfa_group_source; class us_mfa_group; class us_mfa; class us_mfa_group_source : public mfa_group_source { public: us_mfa_group_source(us_mfa_group *, const in6_addr &, uint32_t, action *); const in6_addr &address() const { return m_addr; } void route(int, ip6_hdr *, uint16_t); bool is_iif(int intf) const { return m_iif && m_iif->index() == intf; } bool has_oif(interface *) const; void change_flags(uint32_t, action); void set_iif(interface *); void release_iif(interface *); void add_oif(interface *); void release_oif(interface *); void update_stats(); void output_info(base_stream &, bool, bool) const; void clear_interface_references(const inet6_addr &, interface *); void get_input_counter(uint64_t &bytes) const; void get_forwarding_counter(uint64_t &bytes) const; private: typedef std::vector oifs; us_mfa_group *m_owner; in6_addr m_addr; interface *m_iif; oifs m_oifs; uint32_t m_flags; uint32_t m_interest_flags; enum { stat_forwarded = 0, stat_input, stat_forwarded_size, stat_wrong_iif, stat_count }; mutable uint64_t m_stats[stat_count]; /* enough for 10Gbps+ */ mutable uint32_t m_fw_bag, m_fw_pkt_bag; mutable uint64_t stat_octet_count60s, stat_packet_count60s; }; class us_mfa_group : public mfa_group { public: us_mfa_group(router *, const inet6_addr &); void activate(bool); void route(int, ip6_hdr *, uint16_t); mfa_group_source *create_source_state(const in6_addr &, void *); mfa_group_source *get_source_state(const in6_addr &) const; void release_source_state(mfa_group_source *); void change_default_flags(uint32_t, mfa_group_source::action); void update_stats(); void output_info(base_stream &, bool, bool) const; void clear_interface_references(const inet6_addr &, interface *); private: us_mfa_group_source *match_source(const in6_addr &addr) const; typedef std::map sources; sources m_sources; #define _SOURCE_CACHE_LEN 32 mutable us_mfa_group_source *m_source_cache[_SOURCE_CACHE_LEN]; bool m_useful_cache; void invalidate_source_cache(); enum state { running, pending, denied }; state m_state; uint32_t m_flags; mfa_group_source::action m_actions[mfa_group_source::event_count]; mutable uint32_t m_fw_bag, m_fw_pkt_bag; mutable uint64_t stat_octet_count60s, stat_packet_count60s; friend class us_mfa_group_source; }; #define _SOURCE_CACHE_HASH(x) \ (((x).s6_addr32[2] ^ (x).s6_addr32[3]) & (_SOURCE_CACHE_LEN-1)) inline us_mfa_group_source *us_mfa_group::match_source(const in6_addr &addr) const { if (m_useful_cache) { register us_mfa_group_source *possible = m_source_cache[_SOURCE_CACHE_HASH(addr)]; if (possible && possible->address() == addr) return possible; } sources::const_iterator k = m_sources.find(addr); if (k != m_sources.end()) { if (m_useful_cache) { m_source_cache[_SOURCE_CACHE_HASH(addr)] = k->second; } return k->second; } return 0; } /*! * \brief Implements the User Space Linux Multicast forwarding agent (MFA). * * This MFA implements does multicast forwarding completly in user-space * without any special kernel requirements besides IPv6 support. Internally * the forwarding is done using Raw sockets of type PF_PACKET. */ class us_mfa : public mfa_core { public: us_mfa(); bool pre_startup(); bool check_startup(); void shutdown(); bool supports_stats() const { return true; } void send_icmpv6_toobig(interface *intf, ip6_hdr *, uint16_t) const; void added_interface(interface *); void removed_interface(interface *); bool output_info(base_stream &, bool counters, bool noempty) const; bool output_info(base_stream &, const std::vector &) const; void discovered_source(int, const inet6_addr &, const inet6_addr &); mfa_group *create_group(router *, const inet6_addr &, void *); mfa_group *get_group(const inet6_addr &) const; void release_group(mfa_group *); void route(interface *, ip6_hdr *, uint16_t); void change_group_default_flags(uint32_t, mfa_group_source::action); void forward(interface *, ip6_hdr *, uint16_t) const; uint32_t m_grpflags; mfa_group_source::action m_grpactions[mfa_group_source::event_count]; void clear_interface_references(interface *); private: void data_available(uint32_t); void handle_ipv6(int, uint8_t *, uint16_t); void log_failed_packet(const interface *, int) const; socket0 m_rawsock; data_plane_source_discovery m_sourcedisc; #ifndef LINUX_NO_MMAP void *m_mmaped; uint32_t m_mmapedlen; uint32_t m_framesize; uint8_t *m_mmapbuf; #endif void update_stats(); timer m_stat_timer; us_mfa_group *match_group(const in6_addr &) const; typedef std::map groups; groups m_groups; typedef std::map singles; mutable singles m_singles; #define _GROUP_CACHE_LEN 128 struct grp_cache_entry { in6_addr addr; us_mfa_group *entry; }; mutable grp_cache_entry m_grp_cache[_GROUP_CACHE_LEN]; void invalidate_group_cache(); void invalidate_group_cache(const in6_addr &); }; #define _GROUP_CACHE_HASH(x) \ (((x).s6_addr32[2] ^ (x).s6_addr32[3]) & (_GROUP_CACHE_LEN-1)) inline us_mfa_group *us_mfa::match_group(const in6_addr &addr) const { grp_cache_entry &ent = m_grp_cache[_GROUP_CACHE_HASH(addr)]; if (ent.addr == addr && ent.entry) { return ent.entry; } singles::const_iterator i = m_singles.find(addr); if (i != m_singles.end()) { ent.addr = addr; ent.entry = i->second; return i->second; } for (groups::const_iterator k = m_groups.begin(); k != m_groups.end(); ++k) { if (k->first.matches(addr)) { m_singles[addr] = k->second; return k->second; } } return 0; } inline bool us_mfa_group_source::has_oif(interface *oif) const { return std::find(m_oifs.begin(), m_oifs.end(), oif) != m_oifs.end(); } #endif mrd6-0.9.6/include/mrdpriv/linux/icmp_raw.h0000644000175000017500000000262410550053610017302 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mrdpriv/linux/icmp_raw.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_linux_icmp_raw_h_ #define _mrd_linux_icmp_raw_h_ #include #include class linux_icmp_raw : public icmp_inet6 { public: linux_icmp_raw(); bool check_startup(); void shutdown(); void added_interface(interface *); void removed_interface(interface *); void data_available(uint32_t); void registration_changed(); void internal_require_mgroup(const in6_addr &, bool); mutable socket0 m_rawicmpsock; }; #endif mrd6-0.9.6/include/mrdpriv/dummy/0000755000175000017500000000000010746353706015340 5ustar hugohugomrd6-0.9.6/include/mrdpriv/dummy/rib.h0000644000175000017500000000263410550053610016252 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * dummy/rib.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_dummy_rib_h_ #define _mrd_dummy_rib_h_ #include #include #include #include #include class dummy_rib : public rib_def { public: dummy_rib(); bool check_startup(); void shutdown(); void register_route(rib_watcher_base *, const inet6_addr &); void unregister_route(rib_watcher_base *); void update_route(rib_watcher_base *); interface *path_towards(const inet6_addr &, inet6_addr &, inet6_addr &, inet6_addr &) const; }; #endif mrd6-0.9.6/include/mrdpriv/dummy/mfa.h0000644000175000017500000000464510550053610016245 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * dummy/mfa.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_dummy_mfa_h_ #define _mrd_dummy_mfa_h_ #include #include #include #include #include #include #include #include class interface; class router; struct ip6_hdr; class dummy_mfa_group_source; class dummy_mfa_group; class dummy_mfa_instance; class dummy_mfa; class dummy_mfa_group_source : public mfa_group_source { public: dummy_mfa_group_source(dummy_mfa_group *, const in6_addr &, uint32_t, action *); void change_flags(uint32_t, action); void set_iif(interface *); void release_iif(interface *); void add_oif(interface *); void release_oif(interface *); void forward(ip6_hdr *, uint16_t) const; }; class dummy_mfa_group : public mfa_group { public: dummy_mfa_group(dummy_mfa_instance *, const inet6_addr &); void activate(bool); mfa_group_source *create_source_state(const in6_addr &, void *); void release_source_state(mfa_group_source *); void change_default_flags(uint32_t, mfa_group_source::action); }; class dummy_mfa_instance : public mfa_instance { public: dummy_mfa_instance(dummy_mfa *, router *); mfa_group *create_group(const inet6_addr &, void *); void release_group(mfa_group *); void change_group_default_flags(uint32_t, mfa_group_source::action); }; class dummy_mfa : public mfa_core { public: dummy_mfa(); bool check_startup(); void shutdown(); mfa_instance *alloc_instance(router *); void added_interface(interface *); void removed_interface(interface *); }; #endif mrd6-0.9.6/include/mrdpriv/bsd/0000755000175000017500000000000010746353706014755 5ustar hugohugomrd6-0.9.6/include/mrdpriv/bsd/rib.h0000644000175000017500000000335310550053610015666 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * bsd/rib.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_bsd_rib_h_ #define _mrd_bsd_rib_h_ #include #include #include #include #include struct rt_msghdr; struct if_msghdr; struct ifa_msghdr; struct rt_addrinfo; class bsd_rib : public rib_def { public: bsd_rib(); bool check_startup(); void shutdown(); void check_initial_interfaces(); bool lookup_prefix(const in6_addr &, lookup_result &) const; void process_messages(rt_msghdr *, int len); void process_if_msg(if_msghdr *); void process_ifa_msg(int, ifa_msghdr *, bool); void process_addrinfo(int, rt_addrinfo *, bool); bool fill_lookup_result(lookup_result &, rt_msghdr *) const; void data_pending(uint32_t); void event_pending(rt_msghdr *); rt_msghdr *read_until(unsigned) const; int rtsock; socket0 evsock; mutable uint32_t rtseq; }; #endif mrd6-0.9.6/include/mrdpriv/bsd/mfa.h0000644000175000017500000001161210550053610015652 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * bsd/mfa.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_bsd_mfa_h_ #define _mrd_bsd_mfa_h_ #include #include #include #include #include #include #include #include #include #include #include class interface; class router; struct ip6_hdr; class bsd_mfa_group_source; class bsd_mfa_group; class bsd_mfa; class bsd_mfa_group_source : public mfa_group_source { public: bsd_mfa_group_source(bsd_mfa_group *, const in6_addr &, uint32_t, action *); virtual ~bsd_mfa_group_source(); const in6_addr &address() const { return m_addr.address(); } bool is_iif(interface *intf) const { return intf == m_iif; } bool has_oif(interface *) const; void change_flags(uint32_t, action); void set_iif(interface *); void release_iif(interface *); void add_oif(interface *); void release_oif(interface *); void get_input_counter(uint64_t &) const; void get_forwarding_counter(uint64_t &) const; void output_info(base_stream &) const; private: typedef std::vector oifs; bsd_mfa_group *m_owner; inet6_addr m_addr; interface *m_iif; oifs m_oifs; mf6cctl m_bsd_state; uint32_t m_flags; uint32_t m_interest_flags; friend class bsd_mfa; }; class bsd_mfa_group : public mfa_group { public: bsd_mfa_group(router *, const inet6_addr &); const inet6_addr &addr() const { return m_addr; } void activate(bool); mfa_group_source *create_source_state(const in6_addr &, void *); mfa_group_source *get_source_state(const in6_addr &) const; void release_source_state(mfa_group_source *); void change_default_flags(uint32_t, mfa_group_source::action); void output_info(base_stream &) const; private: bsd_mfa_group_source *match_source(const in6_addr &addr) const; typedef std::map sources; sources m_sources; enum state { running, pending, denied }; state m_state; inet6_addr m_addr; uint32_t m_flags; mfa_group_source::action m_actions[mfa_group_source::event_count]; }; inline bsd_mfa_group_source *bsd_mfa_group::match_source(const in6_addr &addr) const { sources::const_iterator k = m_sources.find(addr); if (k != m_sources.end()) { return k->second; } return 0; } class bsd_mfa : public mfa_core { public: bsd_mfa(); bool pre_startup(); bool check_startup(); void shutdown(); bool supports_stats() const { return true; } void added_interface(interface *); void removed_interface(interface *); bool output_info(base_stream &, const std::vector &) const; void data_available(interface *, int); int vif(interface *iif) const; void commit(mf6cctl *, bool = false); void discovered_source(int, const inet6_addr &, const inet6_addr &); mfa_group *create_group(router *, const inet6_addr &, void *); mfa_group *get_group(const inet6_addr &) const; void release_group(mfa_group *); void change_group_default_flags(uint32_t, mfa_group_source::action); void forward(interface *, ip6_hdr *, uint16_t) const; uint32_t m_grpflags; mfa_group_source::action m_grpactions[mfa_group_source::event_count]; void get_input_counter(const bsd_mfa_group_source *, uint64_t &); void get_forwarding_counter(const bsd_mfa_group_source *, uint64_t &); void get_source_counters(const bsd_mfa_group_source *, sioc_sg_req6 *); private: int m_icmpsock; void kernel_data_pending(uint32_t); socket0 m_sock; std::map vifs; std::map rev_vifs; data_plane_source_discovery data_plane_sourcedisc; bsd_mfa_group *match_group(const in6_addr &) const; typedef std::map groups; groups m_groups; }; inline bsd_mfa_group *bsd_mfa::match_group(const in6_addr &addr) const { for (groups::const_iterator k = m_groups.begin(); k != m_groups.end(); ++k) { if (k->first.matches(addr)) { return k->second; } } return 0; } inline bool bsd_mfa_group_source::has_oif(interface *oif) const { return std::find(m_oifs.begin(), m_oifs.end(), oif) != m_oifs.end(); } #endif mrd6-0.9.6/include/mrdpriv/bgp/0000755000175000017500000000000010746353706014755 5ustar hugohugomrd6-0.9.6/include/mrdpriv/bgp/def.h0000644000175000017500000000553510550053610015654 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * bgp/def.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_bgp_def_h_ #define _mrd_bgp_def_h_ #include #include #include #include class bgp_message { public: bgp_message(); bgp_message(uint8_t); bgp_message(const bgp_message &); virtual ~bgp_message(); virtual bool decode(encoding_buffer &); virtual bool encode(encoding_buffer &) const; virtual uint16_t length() const { return len; } const char *type_name() const; uint16_t len; uint8_t type; }; class bgp_open_message : public bgp_message { public: bgp_open_message(); bgp_open_message(const bgp_message &); virtual uint16_t length() const; bool decode(encoding_buffer &); bool encode(encoding_buffer &) const; uint8_t version; uint16_t as; uint16_t holdtime; uint32_t bgpid; enum { IPV6 = 2 }; enum { UNICAST = 1, MULTICAST, UNICAST_MULTICAST }; typedef std::pair capability; std::vector capabilities; }; class bgp_as_path : public std::vector { public: bgp_as_path &prepend(uint16_t value) { insert(begin(), value); return *this; } }; typedef std::pair bgp_community; typedef std::vector bgp_communities; class bgp_update_message : public bgp_message { public: bgp_update_message(); bgp_update_message(const bgp_message &); virtual uint16_t length() const; bool decode(encoding_buffer &); bool encode(encoding_buffer &) const; enum { IGP = 0, INCOMPLETE = 2 }; uint8_t origin; uint32_t localpref, med; bgp_as_path as_path; bgp_communities communities; std::vector nexthops; std::vector prefixes; std::vector unreach_prefixes; }; class bgp_notification_message : public bgp_message { public: bgp_notification_message(); bgp_notification_message(const bgp_message &); virtual uint16_t length() const; bool decode(encoding_buffer &); bool encode(encoding_buffer &) const; uint8_t errorcode; uint8_t suberrorcode; }; #endif mrd6-0.9.6/include/mrdpriv/mld/0000755000175000017500000000000010746353706014761 5ustar hugohugomrd6-0.9.6/include/mrdpriv/mld/router.h0000644000175000017500000001716310550053610016442 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mld/router.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_mld_router_h_ #define _mrd_mld_router_h_ #include #include #include #include #include #include #include #include #include #include #include #include class interface; class group_interface; class intfconf; struct mldv1; struct mldv2_report; class mld_interface; class mld_group_interface; class mld_group; class mld_intfconf_node : public intfconf_node { public: mld_intfconf_node(intfconf *); bool check_startup(); bool fill_defaults(); bool set_property(const char *, const char *); uint32_t robustness() const; uint32_t query_interval() const; uint32_t query_response_interval() const; uint32_t mali() const; uint32_t other_querier_present_timeout() const; uint32_t startup_query_interval() const; uint32_t startup_query_count() const; uint32_t last_listener_query_interval() const; uint32_t last_listener_query_count() const; uint32_t last_listener_query_time() const; uint32_t unsolicited_report_interval() const; uint32_t older_version_querier_present_timeout() const; uint32_t version() const; bool querier() const; const std::set &signaling_filter() const; bool call_method(int id, base_stream &, const std::vector &); std::set m_signaling_filter; }; class mld_groupconf_node : public groupconf_node { public: mld_groupconf_node(groupconf *); bool fill_defaults(); bool set_property(const char *, const char *); }; class mld_interface : public interface_node { public: mld_interface(); ~mld_interface(); const char *description() const { return "MLD interface information"; } bool check_startup(); void shutdown(); void attached(interface *); bool send_mld_query(const in6_addr &); bool send_mld_query(const in6_addr &, const std::set &); int get_current_version() const; bool is_querier() const; uint32_t get_querier_expiry_time() const; void start_querying(); void change_is_querier(bool); mld_intfconf_node *conf() const; bool output_info(base_stream &, const std::vector &) const; private: friend class mld_router; void event(int, void *); bool send_mldv1_query(const in6_addr &); bool send_mldv2_query(const in6_addr &); void message_available(const in6_addr &, const in6_addr &, icmp6_hdr *, int); void icmp_message_available(const in6_addr &, const in6_addr &, icmp6_hdr *, int); void handle_mldv1_membership_report(const in6_addr &, mldv1 *); void handle_mldv2_membership_report(const in6_addr &, mldv2_report *, int); void handle_mldv1_membership_reduction(const in6_addr &, mldv1 *); void handle_membership_query(const in6_addr &); void handle_mode_change_for_group(int ver, const inet6_addr &reqsrc, const inet6_addr &grpaddr, int mode, const address_set &); void handle_send_query_timeout(); void handle_other_querier_present_timeout(); int mif_mld_version; bool mif_isquerier; uint32_t mif_query_count; inet6_addr mif_querier_addr; typedef timer intf_timer; intf_timer mif_query_timer_id, mif_other_querier_present_timer_id; message_stats_node m_stats; void address_added_or_removed(bool, const inet6_addr &); }; inline int mld_interface::get_current_version() const { return mif_mld_version; } inline bool mld_interface::is_querier() const { return mif_isquerier; } inline uint32_t mld_interface::get_querier_expiry_time() const { return mif_other_querier_present_timer_id.time_left(); } inline mld_intfconf_node *mld_interface::conf() const { return (mld_intfconf_node *)owner()->conf()->get_child("mld"); } class mld_group_interface : public group_interface { public: mld_group_interface(mld_group *, mld_interface *); virtual ~mld_group_interface(); const char *description() const { return "Multicast group local MLD information"; } virtual void refresh(const inet6_addr &, int, const address_set &); uint32_t time_left_to_expiry(bool withft) const; uint32_t uptime() const { return tval::now() - g_creation_time; } const inet6_addr &last_reporter() const { return g_last_reporter; } protected: friend class mld_router; void output_info(base_stream &, bool) const; virtual void output_inner_info(base_stream &, bool) const; uint32_t mali() const; mld_group *g_owner; mld_interface *g_intf; tval g_creation_time; inet6_addr g_last_reporter; void delete_sources(const address_set &); void update_sources_timer(const address_set &, uint32_t = 0); void handle_filter_timer(); void restart_filter_timer(); virtual void send_mld_query(bool general, const address_set & = address_set()); timer g_filter_timer; timer g_last_listener_timer; uint32_t g_last_listener_query_count; void start_fast_leave(); void handle_source_timeout(in6_addr &); void handle_last_listener_query(); typedef timer1 source_timer; std::vector g_sources_timers; address_set g_request_set; }; class mld_group : public group_node { public: mld_group(router *); ~mld_group(); const char *description() const { return "Active multicast group MLD information"; } void attached(group *); void dettached(); void subscriptions_changed(const group_interface *, group_interface::event_type, const address_set &); group_interface *instantiate_group_interface(interface *); bool has_interest_in_group() const; mld_group_interface *local_oif(mld_interface *); bool output_info(base_stream &, const std::vector &) const; private: friend class mld_router; node *m_conf; }; /*! * \brief implements the core MLD protocol. * * `mld_router' implements both MLDv1 and MLDv2. */ class mld_router : public router, public icmp_handler { public: mld_router(); ~mld_router(); const char *description() const; bool check_startup(); void shutdown(); void add_interface(interface *); void remove_interface(interface *); void created_group(group *); void released_group(group *); intfconf_node *create_interface_configuration(intfconf *); groupconf_node *create_group_configuration(groupconf *); mld_interface *get_interface(int) const; mld_group *match(group *grp) const; bool send_icmp(const interface *, const in6_addr &, icmp6_hdr *, uint16_t) const; void icmp_message_available(interface *, const in6_addr &, const in6_addr &, icmp6_hdr *, int len); bool call_method(int, base_stream &, const std::vector &); bool output_info(base_stream &, const std::vector &) const; message_stats_node &stats() { return m_stats; } base_stream &log_router_desc(base_stream &) const; private: virtual mld_group *allocate_group(); message_stats_node m_stats; }; #endif mrd6-0.9.6/include/mrdpriv/mld/def.h0000644000175000017500000000702510600737663015671 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mld/def.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_mdl_def_h_ #define _mrd_mdl_def_h_ #include #include #include #include #include class node; class mld_intfconf_node; #ifndef MLD_LISTENER_QUERY #define MLD_LISTENER_QUERY 130 #endif #ifndef MLD_LISTENER_REPORT #define MLD_LISTENER_REPORT 131 #endif #ifndef MLD_LISTENER_REDUCTION #define MLD_LISTENER_REDUCTION 132 #endif #ifndef MLDv2_LISTENER_REPORT #define MLDv2_LISTENER_REPORT 143 #endif #ifndef MLDv2_LISTENER_REPORT_OLD #define MLDv2_LISTENER_REPORT_OLD 206 #endif extern in6_addr in6addr_linkscope_allnodes; #define MLD_SSM_MODE_INCLUDE 1 #define MLD_SSM_MODE_EXCLUDE 2 #define MLD_SSM_CHANGE_TO_INCLUDE 3 #define MLD_SSM_CHANGE_TO_EXCLUDE 4 #define MLD_SSM_ALLOW_SOURCES 5 #define MLD_SSM_BLOCK_SOURCES 6 class interface; /*! * \brief MLD base header. */ struct mld_base { uint8_t type; uint8_t code; uint16_t checksum; uint16n_t maxdelay; uint16n_t data; } __attribute__ ((packed)); struct mldv1 : mld_base { in6_addr mcaddr; int length() const { return sizeof(mldv1); } void construct(const in6_addr &, int type, mld_intfconf_node *); } __attribute__ ((packed)); struct mldv1_query : mldv1 { void construct(const in6_addr &, mld_intfconf_node *); } __attribute__ ((packed)); /*! * \brief MLDv2 Query header. derives from MLD header. */ struct mldv2_query : mldv1 { #if BYTE_ORDER == LITTLE_ENDIAN uint8_t qrv : 3, suppress : 1, resv2 : 4; #elif BYTE_ORDER == BIG_ENDIAN uint8_t resv2 : 4, suppress : 1, qrv : 3; #else # error "your system endianness isn't supported yet" #endif uint8_t qqic; uint16n_t nsrcs; in6_addr srcs[0]; int length() const { return sizeof(mldv1) + 4 + ntoh(nsrcs) * sizeof(in6_addr); } void construct(const in6_addr &, int type, mld_intfconf_node *); void construct_query(const in6_addr &mca, mld_intfconf_node *conf) { construct(mca, MLD_LISTENER_QUERY, conf); } } __attribute__ ((packed)); /*! * \brief A MLDv2 Multicast record. Contained in MLDv2 Report messages. */ struct mldv2_mrec { uint8_t type; uint8_t auxdatalen; uint16n_t nsrcs; in6_addr mca; in6_addr *sources() { return (in6_addr *)(((uint8_t *)this) + sizeof(*this)); } mldv2_mrec *next() { return (mldv2_mrec *) (((uint8_t *)this) + sizeof(*this) + ntoh(nsrcs) * sizeof(in6_addr) + auxdatalen); } } __attribute__ ((packed)); /*! * \brief A MLDv2 Report header. Derives from MLD base header. */ struct mldv2_report : mld_base { const uint16n_t &nmrecs() const { return data; } mldv2_mrec *mrecs() { return (mldv2_mrec *)(((uint8_t *)this) + sizeof(*this)); } } __attribute__ ((packed)); #endif mrd6-0.9.6/include/mrdpriv/icmp_inet6.h0000644000175000017500000000277610550053610016407 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mrdpriv/icmp_inet6.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_icmp_inet6_h_ #define _mrd_icmp_inet6_h_ #include #include class icmp_inet6 : public icmp_base { public: icmp_inet6(); bool check_startup(); void shutdown(); bool send_icmp(const interface *, const in6_addr &, const in6_addr &, int, icmp6_hdr *, uint16_t) const; void added_interface(interface *); void removed_interface(interface *); void data_available(uint32_t); bool apply_icmp_filter(); void registration_changed(); void internal_require_mgroup(const in6_addr &, bool); mutable socket6 m_icmpsock; }; #endif mrd6-0.9.6/include/mrdpriv/console/0000755000175000017500000000000010746353706015647 5ustar hugohugomrd6-0.9.6/include/mrdpriv/console/console.h0000644000175000017500000001245210550053610017446 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * console.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_console_h_ #define _mrd_console_h_ #include #include #include #include #include #include #include #include #include #include #include #include #include class mrd; class console_connection; class console_module : public mrd_module, public node { public: console_module(mrd *m, void *dlh); ~console_module(); const char *description() const; bool check_startup(); void shutdown(); bool call_method(int id, base_stream &, const std::vector &); bool negate_method(int id, base_stream &, const std::vector &); bool output_info(base_stream &ctx, const std::vector &) const; #ifndef CONSOLE_NO_TELNET void new_client(uint32_t); #endif void new_unix_client(uint32_t); void release_connection(console_connection *); bool password_for(const inet6_addr &, const char *, std::string &) const; #ifndef CONSOLE_NO_TELNET socket0 srvsock; #endif socket0 unix_srvsock; std::list connections; struct auth_desc { std::string username; std::string password; }; typedef std::map, std::greater > allow_local_def; allow_local_def acl; bool allow_addr(const std::vector &); bool deny_addr(const std::vector &); bool allow_local(const std::vector &); bool attach_log(base_stream &, const std::vector &); bool show_history(base_stream &); console_connection *calling_connection(base_stream &) const; }; extern console_module *console; class console_log_node : public tb_log_node { public: console_log_node(console_connection *); void rename(const char *); void log(int, int, const char *, bool newline); console_connection *_conn; std::string _buf; }; class console_connection : public stream_flusher { public: console_connection(mrd *core, int); virtual ~console_connection() {} virtual bool check_startup(); void shutdown(); void doom(); virtual void release(); void data_available(uint32_t); virtual void process_input(int) = 0; virtual bool process_line(const char *); void process_deep_line(parser_context *); enum { CONSISTENCY_ERROR = -3, INPUT_ERROR = -2, END_LINE = -1, OK = 0 }; int transform(parser_context *, node *, node::content_type, node * &) const; int transform(parser_context *, node *, node::content_type, node * &, std::string &) const; void writeclient(const char *); virtual void flushed(const char *, bool) = 0; virtual void dump_history(base_stream &) const; virtual void log(bool end); mrd *m_mrd; bool is_doomed; socket0 sock; mutable base_stream _output; unsigned char buffer[1024]; std::string bufbuffer; int advance_one(parser_context *, node *); int check_termination(parser_context *, node *); void dump_partial(const char *); void dump_partial(node *, parser_context *, bool); bool autoclose; console_log_node *clog; }; class telnet_console_connection : public console_connection { public: telnet_console_connection(mrd *core, int, const inet6_addr &, uint32_t); ~telnet_console_connection(); bool check_startup(); void release(); const inet6_addr &peeraddr() const { return c_peeraddr; } bool authenticate(const char *); void process_input(int); void flushed(const char *, bool); bool process_line(const char *); void dump_history(base_stream &) const; void log(bool end); private: enum { NoState, WaitingPassword, GotAllData, }; bool process_cmd(); bool tabcomplete(); void show_prompt(); void set_prompt(const char *); void clearline(); void redisplay_input(); void cmd(char c, char opt); void release_connection(); void history_up(); void history_down(); timer conn_timer; std::deque ctlbuf; int pmode; bool will_echo; bool should_echo; bool input_is_updated; std::string inputbuf; std::string tmp_inputbuf; std::vector history; int history_pos; int authenticate_state; std::string username; std::string prompt; inet6_addr c_peeraddr; }; class unix_console_connection : public console_connection { public: unix_console_connection(mrd *core, int); void process_input(int); void release(); void flushed(const char *, bool); }; #endif mrd6-0.9.6/include/mrdpriv/pim/0000755000175000017500000000000010746353706014772 5ustar hugohugomrd6-0.9.6/include/mrdpriv/pim/interface.h0000644000175000017500000001204510550053610017065 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim/interface.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_pim_interface_h_ #define _mrd_pim_interface_h_ #include #include #include #include #include #include #include #include class interface; class group; class pim_group_node; class pim_neighbour; class pim_intfconf_node; class pim_interface : public interface_node { public: pim_interface(); ~pim_interface(); const char *description() const { return "PIM interface information"; } bool check_startup(); bool start_timers(); void shutdown(); void attached(interface *); bool am_dr() const { return !elected_dr; } uint32_t effective_propagation_delay() const; uint32_t effective_override_interval() const; bool lan_delay_enabled() const; bool send_local(sockaddr_in6 *dst, pim_message *msg, uint16_t len) const; bool send_all_routers(pim_message *msg, uint16_t len) const; bool send_join_prune(pim_joinprune_message *) const; bool send_assert(pim_assert_message *) const; void data_available(const sockaddr_in6 *src, const sockaddr_in6 *dst); bool call_method(int id, base_stream &, const std::vector &); pim_neighbour *get_neighbour(const in6_addr &) const; pim_neighbour *allocate_neighbour(const in6_addr &); enum state { NOT_READY = 0, LOCAL_READY, READY }; state get_state() const { return intf_state; } const std::list &get_neighbours() const { return neighbours; } bool output_info(base_stream &, const std::vector &) const; bool output_info(base_stream &, bool extended) const; pim_intfconf_node *conf() const; bool suppression_enabled() const; uint32_t suppressed_value() const; private: void found_new_neighbour(pim_neighbour *); void event(int, void *); void handle_hello(const sockaddr_in6 *, pim_hello_message *, uint16_t len); void handle_joinprune(const sockaddr_in6 *, pim_joinprune_message *, uint16_t len); void handle_external_joinprune(const sockaddr_in6 *, pim_joinprune_message *, uint16_t len); void handle_join_wc_rpt(const inet6_addr &, const inet6_addr &, const address_set &, uint16_t, bool); void handle_join_wc_rpt(group *, const inet6_addr &, const address_set &, uint32_t, bool); void handle_join_source(const inet6_addr &, const inet6_addr &, uint32_t, bool); void handle_join_source(group *, const inet6_addr &, uint32_t, bool); void handle_join(const inet6_addr &, const inet6_addr &, uint32_t, bool); void handle_join(pim_group_node *, const inet6_addr &, uint32_t, bool); void handle_assert(const sockaddr_in6 *, pim_assert_message *msg, uint16_t len); void handle_bootstrap(const sockaddr_in6 *, const sockaddr_in6 *, pim_bootstrap_message *, uint16_t len); void handle_register(const sockaddr_in6 *, const sockaddr_in6 *); void handle_register_stop(const sockaddr_in6 *); void handle_candidate_rp_adv(const sockaddr_in6 *, pim_candidate_rp_adv_message *, uint16_t len); void send_hello(); void send_hellox(uint16_t); bool flap_neighbour(base_stream &, const std::vector &, bool remove); void property_changed(node *n, const char *); /* Triggered whenever a Neighbour timer expires. */ void neighbour_timed_out(pim_neighbour * &); /* Removes a Neighbour from this interface, notifying of the * Neighbour loss. If elect is true, after removal the DR * election mechanism is triggered. */ void remove_neighbour(pim_neighbour *, bool elect); /* Implements the Lan-Prune-Delay mechanism. */ void check_lan_delay(); /* Implements the DR election mechanism. */ void elect_subnet_dr(); void update_hello_interval(uint32_t); typedef std::list neighbours_def; message_stats_node m_stats; uint32_t gen_id; timer hello_timer_id; pim_neighbour *elected_dr; uint32_t m_propagation_delay, m_override_interval; bool m_landelay_enabled; neighbours_def neighbours; state intf_state; void address_added_or_removed(bool, const inet6_addr &); friend class pim_neighbour; }; inline pim_intfconf_node *pim_interface::conf() const { return owner() ? (pim_intfconf_node *)owner()->conf()->get_child("pim") : 0; } #endif mrd6-0.9.6/include/mrdpriv/pim/router.h0000644000175000017500000002451010550053610016445 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim/router.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_pim_router_h_ #define _mrd_pim_router_h_ #include #include #include #include #include #include #include #include struct pim_message; struct pim_bootstrap_message; struct pim_candidate_rp_adv_message; class pim_interface; class pim_group_node; class pim_interface; class pim_neighbour; class pim_router; struct pim_source_filter { /* for Reject, value is true */ bool filter_mode; std::set sources; bool accepts(const in6_addr &) const; }; class pim_intfconf_node : public intfconf_node { public: typedef intfconf_node base; pim_intfconf_node(intfconf *); bool check_startup(); bool fill_defaults(); bool set_property(const char *, const char *); bool call_method(int, base_stream &, const std::vector &); uint32_t hello_interval() const; uint32_t holdtime() const; uint32_t joinprune_interval() const; uint32_t joinprune_holdtime() const; uint32_t joinprune_supression_timeout() const; uint32_t data_timeout() const; uint32_t register_supression_timeout() const; uint32_t probe_time() const; uint32_t assert_timeout() const; uint32_t random_delay_join_timeout() const; uint32_t dr_priority() const; uint32_t register_stop_rate_limit() const; uint32_t register_stop_rate_timelen() const; uint32_t propagation_delay() const; uint32_t override_interval() const; bool support_old_cisco_addrlist() const; pim_source_filter neigh_acl; bool neigh_acl_accepts(const in6_addr &) const; }; enum rp_source { rps_static, rps_embedded, rps_rp_set, rps_join }; enum rp_access { rpa_any, rpa_allow, rpa_deny }; enum spt_option { dont_use = 0, always_use }; class pim_groupconf_node : public groupconf_node { public: typedef groupconf_node base; pim_groupconf_node(groupconf *); bool check_startup(); bool fill_defaults(); bool set_property(const char *, const char *); bool call_method(int, base_stream &, const std::vector &); bool increment_property(const char *, const char *); bool rp_for_group(const in6_addr &grpaddr, in6_addr &rpaddr, rp_source &) const; /* RP rejection policy */ enum { RPRejRegisterStop, RPRejSilentIgnore, RPRejLogIgnore }; int rp_rejected_source_policy() const; pim_source_filter rp_source_acl; bool rp_source_acl_accepts(const pim_group_node *, const in6_addr &) const; }; class pim_rp_set : public node { public: pim_rp_set(pim_router *); bool check_startup(); const char *description() const; bool add_entry(const inet6_addr &, const inet6_addr &, uint8_t, uint16_t, bool); bool remove_entry(const inet6_addr &, const inet6_addr &); void update_entries(const inet6_addr &rpaddr, uint8_t prio, uint16_t holdtime, const std::list &grps); void store_from_message(const in6_addr &, pim_bootstrap_message *msg, uint16_t len); void build_message(pim_bootstrap_message *msg, uint16_t &len) const; void clear(); int count_entries() const; void set_hashmask(uint16_t); uint16_t get_hashmask() const { return m_hashmask; } inet6_addr rp_for(const inet6_addr &) const; bool call_method(int id, base_stream &, const std::vector &); bool output_info(base_stream &ctx, const std::vector &) const; private: struct entry; struct group_set : ptree_node { inet6_addr prefix; std::list entries; uint8_t greater_prio() const; bool has_entry(entry *) const; bool release_entry(const inet6_addr &grpaddr, const inet6_addr &, bool verbose = true); std::list::iterator find_entry(entry *); std::list::iterator find(const in6_addr &); void insert_entry(entry *); bool add_entry(pim_rp_set *, const in6_addr &, uint8_t, uint16_t, bool); }; struct entry { entry(pim_rp_set *); void update_holdtime(uint16_t, bool andtimer = true); group_set *owner; uint8_t prio; uint16_t holdtime; in6_addr rpaddr; timer1 timer; }; void handle_entry_timeout(entry * &); typedef ptree db; db m_db; uint16_t m_hashmask; }; class pim_bsr { public: pim_bsr(pim_router *); /* 'Public' interface used by pim_router */ bool check_startup(); void handle_bootstrap_message(pim_interface *intf, const sockaddr_in6 *, const sockaddr_in6 *, pim_bootstrap_message *, uint16_t); void handle_candidate_rp_adv(pim_interface *intf, const sockaddr_in6 *, pim_candidate_rp_adv_message *, uint16_t); in6_addr rp_from_rpset(const inet6_addr &groupid) const; void output_info(base_stream &) const; void acquired_primary_address(); void leaving(); void shutdown(); void found_new_neighbour(pim_neighbour *); /* Internal stuff */ bool is_bsr() const { return m_bsr_state == BSRElected; } void send_bootstrap_message(sockaddr_in6 *) const; void broadcast_rp_set_changed(pim_rp_set *) const; void enable_rp_adv(const inet6_addr &, bool); uint16_t get_default_hashmask() const { return m_p_hashmask->get_integer(); } enum candidate_bsr_state { BSRCandidate, BSRPending, BSRElected }; enum no_cand_bsr_state { NCNoInfo, NCAcceptAny, NCAcceptPreferred }; base_stream &log() const; private: property_def *m_p_enable_bootstrap; property_def *m_p_bsr_candidate; property_def *m_p_bsr_priority; property_def *m_p_bsr_timeout; property_def *m_p_bsr_period; property_def *m_p_sz_timeout; property_def *m_p_rp_candidate; property_def *m_p_rp_cand_prio; property_def *m_p_rp_cand_adv_period; property_def *m_p_rp_cand_holdtime; property_def *m_p_hashmask; void send_leave_bootstrap() const; void send_leave_rp_candidate() const; candidate_bsr_state m_bsr_state; timer m_bsr_timer, m_sz_timer; int m_bsr_preferred_priority; inet6_addr m_bsr_preferred; no_cand_bsr_state m_nc_bsr_state; void handle_bsr_timeout(); void handle_sz_timeout(); bool is_bsr_preferred(const pim_bootstrap_message *) const; bool is_bsr_preferred(const in6_addr &, int prio) const; void to_pending_bsr(); void reset_preferred_bsr(); void im_the_elected_bsr(bool); uint32_t bsr_rand_override() const; void accept_preferred_bsr(const in6_addr *, int prio, pim_bootstrap_message *, uint16_t); void refresh_sz_timer(); void sz_expired(); void has_new_bsr(bool); void switch_bsr_state(candidate_bsr_state); void change_nc_state(no_cand_bsr_state); timer m_rp_adv_timer; void handle_rp_adv_timer(); mutable time_t m_last_sent_bsm; uint32_t m_rp_adv_count; pim_rp_set m_rp_set; }; /*! * \brief core PIM protocol implementation. * * `pim_router' implements all the PIM state management logic: * from group/source creation, to pim interface handling. */ class pim_router : public router { public: pim_router(); ~pim_router(); const char *name() const { return "pim"; } const char *description() const; bool check_startup(); bool router_startup(); void shutdown(); void created_group(group *); void released_group(group *); void release_group(pim_group_node *); void add_interface(interface *); void remove_interface(interface *); intfconf_node *create_interface_configuration(intfconf *); groupconf_node *create_group_configuration(groupconf *); bool output_info(base_stream &ctx, const std::vector &) const; pim_interface *get_interface(interface *) const; pim_interface *get_interface(int) const; virtual pim_group_node *create_group(const inet6_addr &, node *); pim_group_node *get_group(const inet6_addr &) const; void found_new_neighbour(pim_neighbour *) const; void lost_neighbour(pim_neighbour *) const; pim_neighbour *get_neighbour(const inet6_addr &) const; pim_neighbour *get_rpf_neighbour(const in6_addr &) const; std::list all_global_addrs() const; bool sendmsg(const sockaddr_in6 *from, sockaddr_in6 *dst, pim_message *, uint16_t) const; bool send_all(pim_message *, uint16_t len, const sockaddr_in6 * = 0) const; bool send_all_neighbours(pim_message *, uint16_t len, const sockaddr_in6 * = 0) const; bool send_register(const in6_addr &src, const in6_addr &dst, pim_register_message *msg, int payload) const; bool send_register_probe(const in6_addr &src, const in6_addr &dst, pim_register_message *msg, int payload) const; void send_register_stop_to_router(const inet6_addr &, const in6_addr &, const in6_addr &src, const in6_addr &from) const; const inet6_addr &my_address() const { return m_my_address; } void check_my_address(bool force = false); void interface_state_changed(pim_interface *, pim_interface::state oldstate); void dr_changed(pim_interface *, bool); bool mfa_create_state_if_needed() const { return true; } void mfa_notify(mfa_group_source *, const in6_addr &, const in6_addr &, uint32_t, mfa_group_source::action, interface *, ip6_hdr *, uint16_t, uint16_t); #ifndef PIM_NO_BSR pim_bsr &bsr() { return m_bsr; } #endif bool call_method(int id, base_stream &, const std::vector &); base_stream &log_router_desc(base_stream &) const; mutable socket6 pim_sock; private: void data_available(uint32_t); void event(int, void *); void handle_garbage_collector(); void discovered_source(interface *, const inet6_addr &, const inet6_addr &, source_discovery_origin *); bool send_register_generic(const in6_addr &src, const in6_addr &dst, pim_register_message *msg, int payload, int) const; timer m_gc; inet6_addr m_my_address; #ifndef PIM_NO_BSR pim_bsr m_bsr; friend class pim_bsr; #endif }; extern pim_router *pim; #endif mrd6-0.9.6/include/mrdpriv/pim/def.h0000644000175000017500000001633710726010730015674 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim/def.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_pim_h_ #define _mrd_pim_h_ #include #include #include #include class base_stream; enum pim_msg_type { pim_msg_hello = 0, pim_msg_register, pim_msg_register_stop, pim_msg_joinprune, pim_msg_bootstrap, pim_msg_assert, pim_msg_graft, pim_msg_graft_ack, pim_msg_candidate_rp_adv }; /*! * \brief base PIM message header. */ struct pim_message { uint8_t vt; uint8_t resv1; uint16_t checksum; pim_msg_type type() const { return (pim_msg_type)(vt & 0xf); } const char *type_name() const; void construct(pim_msg_type t); void build_checksum(const in6_addr &, const in6_addr &, int); bool has_valid_checksum(const in6_addr &, const in6_addr &, int len); } __attribute__ ((packed)); struct pim_hello_option { enum { holdtime = 1, lan_prune_delay = 2, dr_priority = 19, genid = 20, addrlist = 24, cisco_old_addrlist = 65001, }; uint16n_t type; uint16n_t length; void construct(uint16_t type, uint16_t length); void add_uint16(uint16_t type, uint16_t value); void add_uint16pair(uint16_t type, uint16_t v1, uint16_t v2); void add_uint32(uint16_t type, uint32_t value); void *data(); uint16n_t *data16() { return (uint16n_t *)data(); } uint32n_t *data32() { return (uint32n_t *)data(); } pim_hello_option *next() const; } __attribute__ ((packed)); /*! * \brief PIM Hello message. */ struct pim_hello_message : pim_message { void construct(); pim_hello_option *options(); } __attribute__ ((packed)); /*! * \brief PIM Register message. */ struct pim_register_message : pim_message { uint32n_t nb; void construct(bool border, bool null); ip6_hdr *ip6_header(); bool border() const; bool null() const; } __attribute__ ((packed)); enum pim_addr_type { // we only care about IPv6 pim_addr_reserved = 0, pim_addr_ip6 = 2 }; struct pim_encoded_unicast_address { uint8_t family; uint8_t type; in6_addr addr; void construct(const in6_addr &); } __attribute__ ((packed)); struct pim_encoded_group_address { uint8_t family; uint8_t type; uint8_t zb; uint8_t masklen; in6_addr addr; void construct(const inet6_addr &, bool z, bool b); void construct(const inet6_addr &); inet6_addr address() const { return inet6_addr(addr, masklen); } bool bidir() const; bool zoned() const; } __attribute__ ((packed)); /* from draft-ietf-pim-join-attributes */ struct pim_join_attribute_tlv { uint8_t fs_type; uint8_t length; void construct(bool forward, bool bottom, int type, int length); void *data() const; bool forward() const; bool bottom() const; }; struct pim_encoded_source_address { uint8_t family; uint8_t type; uint8_t flags; uint8_t masklen; in6_addr addr; void construct(const inet6_addr &, bool wc, bool rpt); inet6_addr address() const { return inet6_addr(addr, masklen); } bool sparse() const; bool wc() const ; bool rpt() const; int length() const; /* advances length() bytes to possibly next encoded source address */ pim_encoded_source_address *next() const; } __attribute__ ((packed)); /*! * \brief PIM Register Stop message. */ struct pim_register_stop_message : pim_message { pim_encoded_group_address gaddr; pim_encoded_unicast_address uaddr; void construct(const inet6_addr &, const inet6_addr &); } __attribute__ ((packed)); typedef pim_encoded_source_address *pim_jp_g_iterator; struct pim_joinprune_group { pim_encoded_group_address maddr; uint16n_t njoins; uint16n_t nprunes; void construct(const inet6_addr &addr, uint16_t js, uint16_t ps); uint16_t length() const; pim_jp_g_iterator join_begin() const { return pim_jp_g_iterator(addrs()); } pim_jp_g_iterator join_end() const { return pim_jp_g_iterator(addrs() + join_count()); } pim_jp_g_iterator prune_begin() const { return pim_jp_g_iterator(addrs() + join_count()); } pim_jp_g_iterator prune_end() const { return pim_jp_g_iterator(addrs() + join_count() + prune_count()); } address_set &pruned_addrs(address_set &) const; bool has_prune_addr(const inet6_addr &) const; pim_joinprune_group *next() const; pim_encoded_source_address *addrs() const { return (pim_encoded_source_address *)(((uint8_t *)this) + sizeof(*this)); } private: uint16_t join_count() const { return ntoh(njoins); } uint16_t prune_count() const { return ntoh(nprunes); } } __attribute__ ((packed)); /*! * \brief PIM Join/Prune message. */ struct pim_joinprune_message : pim_message { pim_encoded_unicast_address upstream_neigh; uint8_t resv1; uint8_t ngroups; uint16n_t ht; void construct(const inet6_addr &, uint8_t groups, uint16_t holdtime); /* holdtime in miliseconds */ uint32_t holdtime() const; uint16_t length() const; pim_joinprune_group *groups() const; } __attribute__ ((packed)); struct pim_bootstrap_rp_record { pim_encoded_unicast_address addr; uint16n_t holdtime; uint8_t priority; uint8_t resv; } __attribute__ ((packed)); struct pim_bootstrap_group_def { pim_encoded_group_address grpaddr; uint8_t rpcount, fragrp; uint16_t resv; uint16_t length() const; pim_bootstrap_rp_record *rps() const; pim_bootstrap_group_def *next() const; } __attribute__ ((packed)); /*! * \brief PIM Bootstrap message. */ struct pim_bootstrap_message : pim_message { uint16n_t fragment; uint8_t hash_masklen, bsr_priority; pim_encoded_unicast_address bsr_address; void construct(uint16_t, uint8_t, uint8_t, const in6_addr &); pim_bootstrap_group_def *grps() const; bool no_forward() const; } __attribute__ ((packed)); /*! * \brief PIM Candidate RP Adv message. */ struct pim_candidate_rp_adv_message : pim_message { uint8_t prefixcount; uint8_t priority; uint16n_t holdtime; pim_encoded_unicast_address rp_addr; void construct(uint8_t, uint8_t, uint16_t, const in6_addr &); pim_encoded_group_address *grps() const; uint16_t length() const; } __attribute__ ((packed)); /*! * \brief PIM Assert message. */ struct pim_assert_message : pim_message { pim_encoded_group_address gaddr; pim_encoded_unicast_address saddr; uint32n_t metpref; uint32n_t metric; void construct(const inet6_addr &, const in6_addr &, bool, uint32_t, uint32_t); bool rpt() const; uint32_t metric_pref() const; } __attribute__ ((packed)); void _debug_pim_dump(base_stream &, const pim_joinprune_message &); void _debug_pim_dump(base_stream &, const pim_assert_message &); void _debug_pim_dump(base_stream &, const pim_bootstrap_message &, int); #endif mrd6-0.9.6/include/mrdpriv/pim/group.h0000644000175000017500000004331110550053610016261 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim/group.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_pim_group_h_ #define _mrd_pim_group_h_ #include #include #include #include #include #include #include struct pim_message; struct pim_joinprune_group; struct pim_joinprune_message; class pim_groupconf_node; class pim_interface; class pim_group_node; class pim_source_state_base; class mfa_group; class mfa_group_source; class source_discovery_origin; /*! * \brief Represents PIM output interface. Used in PIM source states. */ class pim_oif { public: pim_oif(pim_source_state_base *owner, interface *intf); virtual ~pim_oif(); interface *intf() const { return m_intf; } pim_interface *pim_intf() const; enum interest { NoInfo, Include, Exclude }; void change_local_membership(interest); void update(bool join, uint32_t hold = 0); interest get_interest() const; interest get_interest(bool includelocal) const; virtual interest get_local_interest() const; interest get_real_local_interest() const { return m_local; } virtual bool has_interest() const; void dr_changed(bool); void output_info(base_stream &) const; enum state { JPNoInfo, Joined, PendingPrune, Pruned }; base_stream &log() const; protected: void release(); void timed_out(); virtual void pp_timed_out() = 0; virtual void inner_update(bool join, uint32_t hold) = 0; virtual interest get_internal_interest(interest local) const; bool needs_supressing() const; uint32_t jp_override_interval() const; bool change_state(state); virtual void changed_state(interest); virtual void output_extra_info(base_stream &) const; pim_source_state_base *m_state; interface *m_intf; timer m_timer, m_pp_timer; interest m_local; state m_jpstate; }; class pim_common_oif : public pim_oif { public: pim_common_oif(pim_source_state_base *owner, interface *intf); bool has_interest() const; enum assert_state { AssertNoInfo, LostAssert, WonAssert }; assert_state current_assert_state() const; pim_neighbour *assert_winner() const; void change_assert_state(assert_state, bool propagate = true); void store_assert_info(pim_neighbour *, uint32_t, uint32_t); void restart_assert_timer(); void restart_assert_timer_minus_override(); void delete_assert_info(); protected: void pp_timed_out(); void assert_timed_out(); void inner_update(bool join, uint32_t hold); interest get_internal_interest(interest local) const; void output_extra_info(base_stream &) const; assert_state m_assert_state; timer m_assert_timer; pim_neighbour *m_assert_winner; uint32_t m_assert_winner_pref, m_assert_winner_metric; }; class pim_sg_rpt_oif : public pim_oif { public: pim_sg_rpt_oif(pim_source_state_base *owner, interface *intf); private: void pp_timed_out(); void inner_update(bool join, uint32_t hold); }; class pim_source_state_base : public refcountable { public: pim_source_state_base(pim_group_node *owner, const inet6_addr &); virtual ~pim_source_state_base(); virtual bool check_startup(); virtual bool output_info(base_stream &) const = 0; pim_group_node *owner() const; const in6_addr &addr() const; virtual interface *iif() const = 0; pim_neighbour::upstream_path *upstream_path() const; bool set_oif(interface *, uint32_t holdtime, bool join = true); bool set_local_oif(interface *, bool join = true); virtual bool remove_oif(interface *); virtual bool release_oif(interface *, bool local); virtual pim_oif *get_oif(interface *) const; int count_oifs() const; virtual bool is_source_local() const { return false; } /* pim machinary */ virtual bool is_wildcard() const = 0; virtual bool is_rpt() const = 0; /* returns the PIM neighbour which is acting as upstream * for this source state */ virtual pim_neighbour *upstream_neighbour() const = 0; /* returns true if we are the upstream neighbour for this state, * i.e. this source is local */ virtual bool am_self_upstream() const = 0; virtual uint32_t path_metric() const = 0; virtual uint32_t path_protocol() const = 0; /* by default returns true if get_downstream_interest != NoInfo */ virtual bool join_desired() const; /* returns the downstream Oif interest */ virtual pim_oif::interest get_downstream_interest() const; pim_oif::interest get_oif_downstream_interest(bool) const; /* by default the join_target() is S from (S,G) */ virtual const in6_addr &join_target() const; /* internal machinery */ /* checks if the state wishes to remain alive, and if not, * removes itself */ bool check_interest(); bool check_interest_and_update_upstream(); /* by default state_desired() has the same value as join_desired() */ virtual bool state_desired() const; virtual void check_upstream_path(); /* called when the upstream neighbour changes and the * upstream path must be constructed */ virtual void build_upstream_state(); /* called to update the upstream path when state interest changes */ void update_upstream(); /* events */ /* called when the RP for G changes */ virtual void rp_changed() = 0; /* called when the DR for interface I changes */ virtual void dr_changed(pim_interface *, bool islocal); /* called when the (*,G) state is created or destroyed */ virtual void wildcard_state_existance_changed(bool created); /* called when one of the Oifs changes state */ virtual void oif_changed_state(pim_oif *, pim_oif::interest previous_int) = 0; /* called when an interface is being removed and any references * to it must be removed */ virtual void clear_interface_references(interface *); /* definitions */ typedef std::list oifs; /* helpers, utils */ int32_t uptime() const { return tval::now() - m_creation_time; } /* logging */ base_stream &log() const; virtual void output_name(base_stream &) const = 0; protected: pim_oif *create_oif(interface *); virtual pim_oif *create_oif(pim_source_state_base *, interface *) const = 0; void output_common_info(base_stream &ctx) const; /* internal semi-events */ virtual void removing_oif(pim_oif *); virtual void upstream_changed(); void destructor(); pim_group_node *m_owner; in6_addr m_addr; tval m_creation_time; oifs m_oifs; pim_neighbour::upstream_path *m_upstream_path; bool m_previous_interest; }; inline pim_group_node *pim_source_state_base::owner() const { return m_owner; } inline const in6_addr &pim_source_state_base::addr() const { return m_addr; } inline pim_neighbour::upstream_path *pim_source_state_base::upstream_path() const { return m_upstream_path; } class pim_source_state_common : public pim_source_state_base, public mrib_watcher_target { public: pim_source_state_common(pim_group_node *grp, const inet6_addr &); bool check_startup(); interface *iif() const; uint32_t path_metric() const; uint32_t path_protocol() const; pim_neighbour *upstream_neighbour() const; bool am_self_upstream() const; virtual const in6_addr &join_destination() const = 0; /* mrib_watcher required methods */ const in6_addr &target_destination() const; const inet6_addr &target_group() const; void check_upstream_path(); /* pim assert machinery */ /* implements PIM's CouldAssert macro */ virtual bool could_assert(interface *) const; /* implements PIM's AssertTrackingDesired macro */ virtual bool assert_tracking_desired(interface *) const; bool check_assert(interface *, const inet6_addr &, bool rpt, uint32_t metric, uint32_t pref) const; /* sends a PIM Assert message for this source state in interface I */ void send_assert(pim_interface *); void send_assert_cancel(pim_interface *); /* events */ void found_new_neighbour(pim_neighbour *); void neighbour_lost(pim_neighbour *); void clear_interface_references(interface *); protected: virtual void neighbour_changed(pim_neighbour_watcher_base *); pim_oif *create_oif(pim_source_state_base *, interface *) const; void assert_wstate_actions1(pim_common_oif *); void assert_lstate_actions2(pim_common_oif *, pim_neighbour *, uint32_t, uint32_t); /* internal semi-events */ virtual void changed_iif(interface *); virtual void removing_iif(interface *); interface *m_iif; pim_neighbour_watcher m_neigh_watcher; }; /*! * \brief Represents a PIM source state (*, G) or (S, G). */ class pim_group_source_state : public pim_source_state_common { public: pim_group_source_state(pim_group_node *grp, const inet6_addr &addr); virtual ~pim_group_source_state(); bool check_startup(); bool output_info(base_stream &) const; bool spt() const { return m_spt; } bool is_rpt() const { return false; } bool is_wildcard() const { return false; } /* join_destination for a (S,G) state is S */ const in6_addr &join_destination() const; void set_spt(bool); bool is_source_local() const { return m_local; } bool has_downstream_interest(bool includelocal) const; void forward_to_rp(interface *, ip6_hdr *, uint16_t); void register_stop(); void trigger_register_stop(const in6_addr *); void send_register_stop_to_router(const in6_addr *) const; void forward(interface *, ip6_hdr *, uint16_t); void trigger_assert(interface *); /* implements lost_assert(S,G,rpt,I) */ bool lost_assert_rpt(pim_common_oif *) const; void inherited_oif_changed_state(pim_oif *, pim_oif::interest); void restart_kat(); pim_oif::interest get_downstream_interest() const; void rp_changed(); /* implements PIM (S,G) CouldAssert macro */ bool could_assert(interface *) const; /* implements PIM's (S,G) AssertTrackingDesired macro */ bool assert_tracking_desired(interface *) const; void handle_assert(interface *, const in6_addr &, bool rpt, uint32_t, uint32_t); pim_oif::interest get_inherited_oif_downstream_interest() const; protected: bool inherited_includes(pim_oif *) const; void update_rpts() const; void build_single_jp_msg(pim_joinprune_message *) const; void send_probe(); void output_name(base_stream &) const; void wildcard_state_existance_changed(bool created); void oif_changed_state(pim_oif *, pim_oif::interest); void merge_inherited_oifs(); virtual bool join_desired() const; virtual bool state_desired() const; void changed_iif(interface *); void removing_iif(interface *); void removing_oif(pim_oif *); void upstream_changed(); void update_fw_counters(); void update_fib(interface *, int change); void check_downstream_activity(); bool m_spt, m_local; mfa_group_source *m_mfa_inst; bool m_downstream_activity; const oifs *m_inherited_oifs; bool m_kat_enabled; tval m_kat_last_update; timer m_register_supression_timer; bool m_sent_probe; uint64_t m_fw_counter; struct register_stop_state { uint32_t count; uint64_t last; }; std::map m_register_stop_router_rates; enum assert_state { noinfo, won_assert, lost_assert }; assert_state m_assert_state; friend class pim_group_node; }; inline void pim_group_source_state::restart_kat() { m_kat_enabled = true; m_kat_last_update.update_to_now(); } class pim_group_source_rpt_state : public pim_source_state_base { public: pim_group_source_rpt_state(pim_group_node *, const inet6_addr &); interface *iif() const; uint32_t path_metric() const; uint32_t path_protocol() const; pim_neighbour *upstream_neighbour() const; bool am_self_upstream() const; pim_oif::interest get_downstream_interest() const; void set_local_interest(pim_oif::interest); bool join_desired() const; bool state_desired() const; void rp_changed(); bool output_info(base_stream &) const; private: bool is_rpt() const { return true; } bool is_wildcard() const { return false; } void oif_changed_state(pim_oif *, pim_oif::interest); void wildcard_state_existance_changed(bool created); pim_oif *create_oif(pim_source_state_base *, interface *) const; void output_name(base_stream &) const; pim_oif::interest m_local_interest; friend class pim_group_node; }; class pim_group_wildcard_state : public pim_source_state_common { public: pim_group_wildcard_state(pim_group_node *); ~pim_group_wildcard_state(); void build_upstream_state(); void rp_changed(); const in6_addr &join_target() const; const in6_addr &join_destination() const; const oifs *get_oifs() const { return &m_oifs; } /* implements PIM (*,G) CouldAssert macro */ bool could_assert(interface *) const; void handle_assert(interface *, const in6_addr &, bool rpt, uint32_t, uint32_t); bool output_info(base_stream &) const; bool state_desired() const; private: bool is_rpt() const { return true; } bool is_wildcard() const { return true; } void removing_oif(pim_oif *); void upstream_changed(); void oif_changed_state(pim_oif *, pim_oif::interest); void output_name(base_stream &) const; }; class pim_group_node : public group_node { public: pim_group_node(router *, const inet6_addr &, pim_groupconf_node *); virtual ~pim_group_node(); const char *description() const { return "PIM active multicast group information"; } static bool calculate_embedded_rp_addr(const in6_addr &, inet6_addr &); void shutdown(); bool output_info(base_stream &, const std::vector &) const; void garbage_collect(); const inet6_addr &id() const { return m_addr; } void set_rp(); void set_rp(const inet6_addr &, rp_source); inet6_addr rp_for_group(rp_source &) const; bool is_embedded() const { return !m_embedded_rpaddr.is_any(); } const inet6_addr &embedded_rp_addr() const { return m_embedded_rpaddr; } void rp_set_changed(); void attached(group *); bool attach(group *, const pim_groupconf_node *); void dettached(); bool check_startup(); bool has_interest_in_group() const; bool has_downstream_interest(const in6_addr &) const; pim_source_state_base *create_state(const inet6_addr &, bool rpt); pim_source_state_base *create_state(const inet6_addr &, bool rpt, interface *oif, bool, uint32_t = 0); pim_source_state_base *get_state(const inet6_addr &, bool rpt) const; pim_group_source_state *get_state(const inet6_addr &addr) const { return (pim_group_source_state *)get_state(addr, false); } pim_group_source_rpt_state *get_rpt_state(const inet6_addr &addr) const { return (pim_group_source_rpt_state *)get_state(addr, true); } address_set source_state_set() const; address_set local_source_state_set() const; void remove_state(pim_source_state_base *); bool create_wildcard(); bool create_wildcard(interface *oif, bool, uint32_t); bool has_wildcard() const; pim_group_wildcard_state *wildcard() const; bool has_rp_path() const { return m_rp_path.valid; } interface *interface_towards_rp() const { return g_mrd->get_interface_by_index(m_rp_path.dev); } const in6_addr &pref_source_towards_rp() const { return m_rp_path.prefsrc; } const in6_addr &rpaddr() const { return m_rpaddr; } bool has_rp() const { return !IN6_IS_ADDR_UNSPECIFIED(&m_rpaddr); } void do_register(const in6_addr *, ip6_hdr *, uint16_t, bool); void register_stop(const inet6_addr &, const inet6_addr &); void clear_interface_references(interface *); void subscriptions_changed(const group_interface *, group_interface::event_type, const address_set &); void found_new_neighbour(pim_neighbour *) const; void lost_neighbour(pim_neighbour *) const; void dr_changed(pim_interface *, bool); void rpt_upstream_changed(); void rpt_update_upstream(); void inherited_oif_changed_state(pim_oif *, pim_oif::interest); bool is_ssm() const { return m_ssm; } bool is_self_rp() const { return m_selfrp; } void forward_to_rp(pim_group_source_state *, interface *, ip6_hdr *, uint16_t); void failed_to_forward_to_rp(const char *); void send_register_stop_to_router(const in6_addr &src, const in6_addr &from) const; pim_groupconf_node *get_conf() const { return m_conf; } mfa_group *mfa() const { return m_mfa_inst; } virtual void discovered_source(interface *, const inet6_addr &, source_discovery_origin *); bool has_interest_on(const in6_addr &) const; private: bool handle_kat_expired(pim_group_source_state *); void rp_path_changed(uint32_t); virtual bool rp_acl_accept(const in6_addr &) const; virtual bool rp_acl_accept_source(const in6_addr &) const; virtual pim_group_source_state *create_source_state(const inet6_addr &); virtual pim_group_source_rpt_state *create_source_rpt_state(const inet6_addr &); virtual pim_group_wildcard_state *create_wildcard_state(); inet6_addr m_addr; pim_groupconf_node *m_conf; int m_refcount; void property_changed(node *, const char *); void report_forward_to_rp_failure(); in6_addr m_rpaddr; rp_source m_rp_source; bool m_selfrp; rib_watcher m_rp_path; bool m_ssm; inet6_addr m_embedded_rpaddr; uint32_t m_rp_failure_count; const char *m_rp_failure_last_msg; timer m_rp_failure_report_timer; mfa_group *m_mfa_inst; pim_group_wildcard_state *m_wildcard; typedef std::pair source_pair; typedef std::map states; states m_states; friend class pim_group_source_state; }; #endif mrd6-0.9.6/include/mrdpriv/pim/neighbour.h0000644000175000017500000001332410550053610017110 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim/neighbour.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_pim_neighbour_h_ #define _mrd_pim_neighbour_h_ #include #include #include #include #include class pim_interface; class pim_source_state_base; class pim_neighbour; struct pim_encoded_unicast_address; class pim_neighbour_watcher_base : public mrib_watcher_base { public: pim_neighbour_watcher_base(mrib_watcher_target *); virtual ~pim_neighbour_watcher_base(); bool check_startup(); bool self_upstream() const; bool recheck_neighbour(); pim_neighbour *neigh() const { return w_neigh; } pim_interface *tentative_interface() const; uint32_t route_protocol() const; uint32_t route_metric() const; virtual void callback() = 0; private: void entry_changed(); pim_neighbour *w_neigh; pim_interface *w_lastintf; }; inline uint32_t pim_neighbour_watcher_base::route_protocol() const { return mrib_watcher_base::prefix_protocol(); } inline uint32_t pim_neighbour_watcher_base::route_metric() const { return mrib_watcher_base::prefix_metric(); } template class pim_neighbour_watcher : public pim_neighbour_watcher_base { public: typedef std::mem_fun1_t watcher_callback; pim_neighbour_watcher(Holder *, watcher_callback c, mrib_watcher_target *); void callback(); private: Holder *_h; watcher_callback _cb; }; template inline pim_neighbour_watcher::pim_neighbour_watcher(H *h, watcher_callback c, mrib_watcher_target *t) : pim_neighbour_watcher_base(t), _h(h), _cb(c) {} template inline void pim_neighbour_watcher::callback() { _cb(_h, this); } class pim_neighbour { public: pim_neighbour(pim_interface *, const inet6_addr &); void shutdown(); void set_present(bool); bool compare_genid(uint32_t) const; const inet6_addr &localaddr() const { return n_addr; } pim_interface *intf() const { return n_intf; } bool has_dr_priority() const { return n_flags & f_has_dr_priority; } uint32_t dr_priority() const { return n_dr_priority; } bool has_genid() const { return n_flags & f_has_genid; } uint32_t genid() const { return n_genid; } bool has_lan_delay() const { return n_flags & f_has_lan_delay; } uint32_t propagation_delay() const { return n_propagation_delay; } uint32_t override_interval() const { return n_override_interval; } bool tracking_support() const { return n_tracking_support; } uint32_t holdtime() const { return n_holdtimer.get_interval(); } const std::set &secundary_addresses() const { return n_secaddrs; } void update_from_hello(pim_encoded_unicast_address *, int, pim_encoded_unicast_address *, int, int); void set_holdtime(uint32_t); void set_dr_priority(uint32_t); void set_genid(uint32_t); void set_lan_delay(uint16_t, uint16_t, bool); bool has_address(const in6_addr &) const; class upstream_path { public: upstream_path(pim_neighbour *, pim_source_state_base *, const inet6_addr &target, bool wc, bool rpt); pim_neighbour *neigh() const { return p_neigh; } pim_source_state_base *state() const { return p_state; } const inet6_addr &target() const { return p_target; } bool wc() const { return p_wc; } bool rpt() const { return p_rpt; } void join(bool permanent); void prune(bool permanent); void remove(bool sendinvsingle = true); void send_single(bool supressholdtime) const; bool is_joined() const { return p_isjoin; } bool is_active() const { return p_active; } void refresh_now() const; void update_last_seen(uint32_t holdtime); bool may_be_overridden() const; void output_info(base_stream &) const; private: pim_neighbour *p_neigh; pim_source_state_base *p_state; inet6_addr p_target; bool p_wc, p_rpt; bool p_isjoin, p_active; tval p_last_seen; uint32_t p_last_seen_holdtime; }; friend class upstream_path; upstream_path *add_path(pim_source_state_base *, const inet6_addr &, bool wc, bool rpt); void remove_path(upstream_path *); typedef timer1 holdtimer; const holdtimer *get_holdtimer() const { return &n_holdtimer; } void output_info(base_stream &, bool extended) const; base_stream &log() const; private: void handle_jp_timer(); bool move_to_joins(upstream_path *); bool move_to_prunes(upstream_path *); pim_interface *n_intf; inet6_addr n_addr; holdtimer n_holdtimer; timer n_jp_timer; bool n_present; uint32_t n_flags; uint32_t n_dr_priority; uint32_t n_genid; uint32_t n_propagation_delay, n_override_interval; bool n_tracking_support; enum { f_has_dr_priority = 1, f_has_genid = 2, f_has_lan_delay = 4 }; std::set n_secaddrs; typedef std::list upstream_jp_state; struct group_state { upstream_jp_state joins, prunes; }; typedef std::map upstream_state; upstream_state n_gstates; int npaths; }; #endif mrd6-0.9.6/include/mrd/0000755000175000017500000000000010746353706013304 5ustar hugohugomrd6-0.9.6/include/mrd/packet_buffer.h0000644000175000017500000000713310550053610016241 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * packet_buffer.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_packet_buffer_h_ #define _mrd_packet_buffer_h_ #include #include #include #include struct ip6_hdr; class interface; class socket_base; /*! * \class std_packet_buffer mrd/packet_buffer.h * \brief Implements a packet buffer with pre-reserve head-space for * fast encapsulation. */ template class std_packet_buffer { public: std_packet_buffer() : source(0), rlength(-1), read_offset(0), send_offset(0) { memset(pb_buf, 0, sizeof(pb_buf)); } template T *header(int offset = 0) { return (T *)(pb_buf + EBS + offset); } void *pheader(int offset = 0) { return pb_buf + EBS + offset; } ip6_hdr *ip6_header() { return (ip6_hdr *)(pb_buf + EBS + read_offset); } int recvfrom(int sock, sockaddr *sa, socklen_t *salen) { rlength = ::recvfrom(sock, pb_buf + EBS, BS - EBS, 0, sa, salen); read_offset = 0; return rlength; } int sendto(int sock, const sockaddr *sa, socklen_t salen) { int res = ::sendto(sock, pb_buf + EBS + send_offset, rlength, 0, sa, salen); send_offset = 0; return res; } void full_resize(int length) { rlength = length; } void set_send_offset(int length) { send_offset -= length; rlength += length; } uint8_t *buffer() { return pb_buf + EBS; } const uint8_t *buffer() const { return pb_buf + EBS; } uint32_t bufferlen() const { return BS - EBS; } interface *source; int rlength; int read_offset, send_offset; int auxhdr_off, auxhdr_len; private: uint8_t pb_buf[BS]; }; typedef std_packet_buffer<4096, 256> packet_buffer; class encoding_buffer { public: encoding_buffer(int); ~encoding_buffer(); bool check_startup(); bool require(int len) const { return (m_head + len) <= m_tail; } bool tail_require(int len) const { return (m_tail + len) <= m_end; } void *eat(int); void *put(int); uint8_t *head() const { return m_head; } uint8_t *tail() const { return m_tail; } uint32_t data_length() const { return m_tail - m_head; } uint32_t available_length() const { return m_end - m_tail; } void advance_head(int); void advance_tail(int); void compact(); void clear(); bool empty() const { return m_head == m_tail; } /* Helpers */ template T &eat() { return *((T *)eat(sizeof(T))); } template T &put() { return *((T *)put(sizeof(T))); } int neatl() { return ntohl(eat()); } uint32_t neatu32() { return ntohl(eat()); } uint8_t neatu8() { return eat(); } uint16_t neatu16() { return ntohs(eat()); } int consume(socket_base &, bool blocking = false); int flush_to(socket_base &, bool wantsread, bool blocking = false); private: uint8_t *m_buffer, *m_end; uint8_t *m_head, *m_tail; }; #endif mrd6-0.9.6/include/mrd/interface.h0000644000175000017500000001137610550053610015405 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * interface.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_interface_h_ #define _mrd_interface_h_ #include #include #include #include #include #include class mrd; class router; class intfconf; class interface; class base_stream; class intfconf_node; class intfconf : public conf_node { public: typedef conf_node base; intfconf(const char *); ~intfconf(); const char *description() const { return "Interface configuration"; } bool check_startup(); void fill_defaults(); void property_changed(node *, const char *); node *create_child(const char *); bool call_method(int, base_stream &, const std::vector &); virtual node *next_similiar_node() const; bool is_enabled() const; bool is_router_enabled(const char *) const; void update_interface_configuration(interface *); private: bool disable_router(const std::vector &); void remove_child_node(node *); std::set disabled_routers; }; class intfconf_node : public conf_node { public: intfconf_node(intfconf *, const char *); virtual bool fill_defaults() { return true; } virtual node *next_similiar_node() const; }; class interface_node : public node { public: interface_node(router *rt); virtual ~interface_node(); virtual void attached(interface *owner); virtual void dettached(); interface *owner() const { return n_owner; } router *owner_router() const { return n_owner_router; } virtual void address_added_or_removed(bool, const inet6_addr &) {} /* logging */ bool should_log(int) const; base_stream &log() const; protected: interface *n_owner; router *n_owner_router; }; /*! * \class interface mrd/interface.h * \brief Represents a system network interface. */ class interface : public node { public: interface(intfconf *, int indx, const char *name, int type, int mtu, int flags); ~interface(); void shutdown(); int index() const { return mif_index; } const char *name() const { return mif_name.c_str(); } const char *description() const { return "Network interface"; } enum { None = 0, Loopback, Ethernet, PPP, Tunnel, TUN, IEEE1394, IEEE802_11, IEEE802_1Q, }; enum kernel_state { Down, NoLink, Up, }; int type() const { return mif_type; } const char *type_str() const; int mtu() const { return mif_mtu; } kernel_state state() const { return mif_state; } bool up(bool ignoremrd = false) const; void change_state(kernel_state); /* returns true if this interface is attached * to a multi-access LAN */ bool is_multiaccess() const; bool attach_node(interface_node *); void dettach_node(interface_node *); interface_node *node_owned_by(const router *) const; const in6_addr *linklocal() const { return mif_linklocal.address_p(); } const std::set &linklocals() const { return mif_linklocals; } const std::set &globals() const { return mif_globals; } const inet6_addr &primary_addr() const; const sockaddr_in6 *localaddr() const { return &mif_localaddr; } bool has_global(const in6_addr &addr) const; bool in_same_subnet(const in6_addr &) const; intfconf *conf() const { return mif_conf; } void address_added_or_removed(bool isnew, const inet6_addr &); bool output_info(base_stream &, const std::vector &) const; base_stream &log() const; private: friend class mrd; friend class intfconf; int mif_index; std::string mif_name; int mif_type; int mif_mtu; int mif_flags; kernel_state mif_state; bool mif_enabled; sockaddr_in6 mif_localaddr; inet6_addr mif_linklocal; std::set mif_linklocals; std::set mif_globals; intfconf *mif_conf; tval mif_creationtime; void add_remove_address(bool isnew, const inet6_addr &); void broadcast_change_state(bool wasdown); void set_enabled(bool); }; uint16_t ipv6_checksum(uint8_t protocol, const in6_addr &src, const in6_addr &dst, const void *data, uint16_t len); #endif mrd6-0.9.6/include/mrd/mrib.h0000644000175000017500000001614210600075275014401 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mrib.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_mrib_h_ #define _mrd_mrib_h_ #include #include #include #include #include #include #include class mrib_origin; class mrib_watcher_base; class mrib_connected_origin; class mrib_static_origin; /*! * Implements the Multicast Routing Information Base interface */ class mrib_def : public node { public: mrib_def(node *); ~mrib_def(); bool check_startup(); void shutdown(); const char *description() const; typedef uint32_t metric_def; struct mrib_node; struct prefix { prefix(mrib_origin *, uint32_t distance = 1000); const inet6_addr &get_prefix() const { return trie_owner->prefix; } bool is_best_entry() const { return trie_owner->head == this; } bool is_valid() const { return intf && intf->up(); } in6_addr nexthop; interface *intf; mrib_origin *owner; time_t creation; enum { NO_EXPORT = 1, }; uint32_t flags; uint32_t distance; metric_def metric; /* private */ uint32_t refcount; mrib_node *trie_owner; struct prefix *next; }; struct mrib_node : ptree_node { uint32_t refcount; inet6_addr prefix; struct prefix *head; mrib_watcher_base *watchhead; }; typedef ptree mrib_trie; mrib_connected_origin &local() { return *m_local; } const prefix *resolve_nexthop(const inet6_addr &, const inet6_addr &, inet6_addr &) const; const prefix *prefix_lookup(const inet6_addr &, const inet6_addr &) const; prefix *get_prefix(const inet6_addr &, mrib_origin *) const; bool install_prefix(const inet6_addr &, prefix *); void update_prefix(prefix *); void remove_prefix(prefix *); void install_listener(mrib_origin *); void origin_lost(mrib_origin *); void invalidate_watcher(mrib_watcher_base *); bool call_method(int id, base_stream &, const std::vector &); bool negate_method(int id, base_stream &, const std::vector &); bool output_info(base_stream &, const std::vector &) const; void removed_interface(interface *); uint32_t registry_prefix_count() const { return m_trie.size(); } struct visitor { const inet6_addr &addr() const; prefix *entry() const; mrib_trie::const_iterator i; prefix *p; bool bestmetric; mrib_origin *owner; }; bool visit_best_metric(visitor &) const; bool visit_origin(visitor &, mrib_origin *) const; bool visit_next(visitor &) const; private: mrib_trie m_trie; objpool m_trie_nodes; void event(int, void *); void insert_prefix_in_node(mrib_node *n, prefix *p); void remove_prefix_from_node(prefix *); mrib_node *prefix_lookup_y(const inet6_addr &, const inet6_addr &) const; mrib_node *prefix_lookup_y(const inet6_addr &) const; void invalidate_node_watchers(mrib_node *); void prefix_lost(const inet6_addr &, metric_def, const prefix &); mrib_node *grab_node(mrib_node *, mrib_watcher_base *, bool); prefix *grab_prefix(prefix *); void dec_node_refcount(mrib_node *); void dec_prefix_refcount(prefix *); bool local(const std::vector &); bool negate_local(const std::vector &); bool confprefix(const std::vector &); bool negate_prefix(const std::vector &); void dump_node_watchers(base_stream &out) const; typedef std::list listeners; listeners m_listeners; mrib_connected_origin *m_local; mrib_static_origin *m_static; friend class mrib_watcher_base; }; /*! * MRIB origin interface. Only modules implementing this interface * may feed new mrib entries */ class mrib_origin { public: virtual ~mrib_origin(); virtual const char *description() const = 0; virtual void output_prefix_info(base_stream &, const mrib_def::prefix &) const; virtual void prefix_added(const inet6_addr &, mrib_def::metric_def, const mrib_def::prefix &); virtual void prefix_lost(const inet6_addr &, mrib_def::metric_def, const mrib_def::prefix &); virtual void return_prefix(mrib_def::prefix *) = 0; }; /*! * provides a mrib origin for directly connected prefixes */ class mrib_connected_origin : public mrib_origin { public: const char *description() const; void register_prefix(const inet6_addr &, interface *); void unregister_prefix(const inet6_addr &, interface *); void return_prefix(mrib_def::prefix *); private: typedef std::pair interface_prefix; typedef std::map regdef; regdef _reg; }; class mrib_static_origin : public mrib_origin { public: const char *description() const; void return_prefix(mrib_def::prefix *); }; class mrib_watcher_target { public: virtual ~mrib_watcher_target() {} virtual const inet6_addr &target_group() const = 0; virtual const in6_addr &target_destination() const = 0; }; /*! * provides a callback based MRIB entry watcher */ class mrib_watcher_base : public event_sink { public: mrib_watcher_base(mrib_watcher_target *); virtual ~mrib_watcher_base(); void release(); const in6_addr &target() const { return _target->target_destination(); } const inet6_addr &group() const { return _target->target_group(); } const in6_addr &nexthop() const { return _nexthop; } void invalidate(); interface *intf() const; uint32_t prefix_protocol() const; uint32_t prefix_metric() const; virtual void entry_changed() = 0; enum { Invalidated = 100 }; void event(int, void *); private: void nexthop_changed(); void invalidated(); mrib_watcher_target *_target; mrib_def::mrib_node *_rec; mrib_def::prefix *_prefix; in6_addr _nexthop; uint32_t _metric, _protocol; interface *_intf; mrib_watcher_base *wnext; /* dont add the invalidate task twice */ bool pending_update; friend class mrib_def; }; /*! * provides a template based mrib watcher */ template class mrib_watcher : public mrib_watcher_base { public: typedef std::mem_fun_t callback_def; mrib_watcher(Holder *, callback_def); void entry_changed(); private: Holder *_h; callback_def _cb; }; template inline mrib_watcher::mrib_watcher(H *h, mrib_watcher::callback_def c) : mrib_watcher_base(h), _h(h), _cb(c) {} template inline void mrib_watcher::entry_changed() { _cb(_h); } #endif mrd6-0.9.6/include/mrd/router.h0000644000175000017500000000572310550053610014764 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * router.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_router_h_ #define _mrd_router_h_ #include #include #include #include #include class mrd; class group; class router; class interface; class intfconf; class intfconf_node; class groupconf; class groupconf_node; class mfa_group; /*! * Each routing protocol (PIM, etc) in `mrd' derives from this * router class which provides an interface with the core router * for event notification, mfa interaction, etc. This class also * follows the configuration protocol through the `node' class. */ class router : public node, public source_discovery_sink { public: router(const char *); virtual ~router(); virtual void attach(mrd *); virtual bool check_startup(); virtual void shutdown(); /* logging */ base_stream &log() const; virtual base_stream &log_router_desc(base_stream &) const; /*! * router implementations should use this method instead of * mrd::get_interface_by_index in order to comply with configured * parameters, including the disabling of this router instance */ interface *get_interface_by_index(int) const; /*! * Event triggered whenever a new group instance is created. * router implementations should attach their own group_node * instances at this point if they wish to react to group * filter changes */ virtual void created_group(group *); virtual void released_group(group *); /*! * This method is called whenever a new interface is added to * the system. If for some reason the router instance wishes * to prevent the interface's inclusion, it should return false, * effectively vetoing the new interface */ virtual void add_interface(interface *); virtual void remove_interface(interface *); virtual intfconf_node *create_interface_configuration(intfconf *); virtual groupconf_node *create_group_configuration(groupconf *); virtual void mfa_notify(mfa_group_source *, const in6_addr &, const in6_addr &, uint32_t flags, mfa_group_source::action, interface *iif, ip6_hdr *, uint16_t alen, uint16_t len); void event(int, void *); }; #endif mrd6-0.9.6/include/mrd/parser.h0000644000175000017500000000467510550053610014745 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * parser.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_parser_h_ #define _mrd_parser_h_ #include #include #include #include #include class parser_context { public: parser_context(); parser_context(const char *input, bool partial = false); parser_context(const parser_context &); parser_context &operator = (const parser_context &); enum token_type { NONE, LCURLY, RCURLY, LPARENT, RPARENT, TERM, EQUAL, PLUSEQUAL, DOT, COMMA, /* identifier, integer, address, etc. */ TOKEN, /* TOKEN directly followed by a '?' */ PARTIAL_TOKEN, /* quoted string */ STRING, }; static const char *token_name(token_type); struct symbol { symbol(); symbol(int, token_type, const std::string &); symbol(const symbol &); int line; token_type sym; std::string value; }; int current_line_number() const { return m_current_line; } std::string current_line() const; const char *current_input() const { return m_input_pointer; } int current_column() const { return m_input_pointer - m_input_line_start; } // -1 parsing error // 0 no more symbols // 1 parsed with success int read(); int eat(); int eat(token_type); int eat(int, ...); const symbol &head() const { return m_current; } private: int read_token(bool, int *, const char ** = 0, bool = true); int parse_one(const char *input, int state, int *sym, int *readnum) const; symbol m_current; int m_current_line; std::list m_symq; const char *m_input; const char *m_input_pointer; const char *m_input_line_start; bool m_partial; }; #endif mrd6-0.9.6/include/mrd/node.h0000644000175000017500000003214310550053610014365 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * node.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_node_h_ #define _mrd_node_h_ #include #include #include #include #include #include #include #include #include class base_stream; class inet6_addr; class node; struct method_info { const char *name; const char *description; int id; bool informational; uint32_t flags; }; /*! * Implements an abstract interface to property values */ struct propval { virtual ~propval(); /*! * Returns a pointer to the internal structure holding the value */ virtual const void *get_value() const = 0; /*! * Parses the string representation specified in the param and sets * the value if valid. If the input has a bad format, returns false. */ virtual bool set_value(const char *) = 0; /*! * Outputs the current value into the specified base_stream */ virtual void output_value(base_stream &) const = 0; }; /*! * Implements an interface to property definitions, including value * instantiation, flags and helper methods. */ class property_def { public: property_def(); ~property_def(); enum { READ_ONLY = 0x01, REMOVABLE = 0x02, DEFAULT_VALUE = 0x04, PROPERTY = 0x08, METHOD = 0x10, CHILD = 0x20, COMPLETE_M = 0x40, NEGATE = 0x80, }; /*! * Internal available value types */ enum valtype { VAL_UNKNOWN, VAL_BOOL, VAL_INTEGER, VAL_UNSIGNED, VAL_TIME_INTERVAL, VAL_STRING, VAL_ADDRESS, }; /*! * Instantiates a property to the specified type with the supplied * default value. Also applies supplied flags. */ bool instantiate(valtype, const void *def, const char *desc, uint32_t flags); /*! * Provides instantiation of alien propvals. The moment the propval * is fed into instantiation, the property_def will be it's owner * and is responsible for freeing as required (including if this * method fails). */ bool instantiate(propval *, const char *desc, uint32_t flags); /*! Instantiates a child node property */ bool instantiate(node *, uint32_t flags); bool instantiate(const method_info *); bool is_readonly() const { return m_flags & READ_ONLY; } bool is_removable() const { return m_flags & REMOVABLE; } bool is_default() const { return m_flags & DEFAULT_VALUE; } bool is_property() const { return m_flags & PROPERTY; } bool is_method() const { return m_flags & METHOD; } bool is_child() const { return m_flags & CHILD; } uint32_t flags() const { return m_flags; } void set_description(const char *); const char *description() const; void set_readonly(); void set_removable(); /*! * Feeds the supplied value into propval::set_value. If rch = true, * removes flag DEFAULT_VALUE. This call will fail if is_property * is not true. */ bool set_value(const char *, bool rch = true); /* helper methods */ bool get_bool() const; int32_t get_integer() const; uint32_t get_unsigned() const; const char *get_string() const; const inet6_addr &get_address() const; node *get_node() const; const method_info *get_method_info() const; /*! * Feeds into propval::output_value, or outputs (null) if * not instantiated */ void output_value(base_stream &) const; private: bool is_instantiated() const; uint32_t m_flags; union { propval *val; node *child; const method_info *method; } u; const char *m_prop_description; }; inline bool property_def::get_bool() const { return *(const bool *)u.val->get_value(); } inline int32_t property_def::get_integer() const { return *(const int32_t *)u.val->get_value(); } inline uint32_t property_def::get_unsigned() const { return *(const uint32_t *)u.val->get_value(); } inline const char *property_def::get_string() const { return (const char *)u.val->get_value(); } inline const inet6_addr &property_def::get_address() const { return *(const inet6_addr *)u.val->get_value(); } inline node *property_def::get_node() const { return u.child; } inline const method_info *property_def::get_method_info() const { return u.method; } class event_sink { public: virtual ~event_sink(); virtual void event(int, void *); }; /*! * Implements base node class, core to the mrd internal hierarchy. nodes * come with property handling by default. */ class node : public event_sink { public: node(node *, const char *); virtual ~node(); virtual bool check_startup(); /*! * Returns this node's parent node. */ node *parent() const { return m_parent; } /*! * Returns this node's name. */ const char *name() const { return m_name.c_str(); } /*! * Returns a textual description of this node. */ virtual const char *description() const { return 0; } /*! * Returns the full name of this node, with each parent separated * by a dot (.) e.g. mrd.interfaces.eth0 */ std::string full_name() const; /*! * Should return a less specific node related to this node. Used for * get_property when the node doesn't have the requested property. */ virtual node *next_similiar_node() const; enum content_type { unknown = 0, property = 1, child = 2, method = 4, info_method = 8 }; /*! * Sets the property specified by key's value. Returns false if the * specified value is not parsable by propval's requirement or the * property doesn't exist. */ virtual bool set_property(const char *key, const char *value); /*! * Removes a REMOVABLE property. Returns false if the property is not * REMOVABLE or doesn't exist. */ virtual bool remove_property(const char *key, bool force = false); /*! * Increments a property propval with value. Follows same rules * as set_property */ virtual bool increment_property(const char *key, const char *value); /*! * Returns a reference to the specified property. If strict=true, * similiar nodes aren't used in case this node doesn't contain * the specified property. */ const property_def *get_property(const char *key, bool strict = false) const; /*! * Returns a modifiable reference to the specified property. */ property_def *get_property(const char *key, bool strict = false); const property_def *get_any_property(const char *name) const; /*! * Same as constant get_property, but returns the property from the * specified child. If the child doesn't exist, returns a null * reference */ const property_def *get_child_property(const char *, const char *, bool strict = false) const; /*! * These methods should only be called when you are sure of the * property's content and existance */ bool get_property_bool(const char *) const; int32_t get_property_integer(const char *) const; uint32_t get_property_unsigned(const char *) const; const char *get_property_string(const char *) const; const inet6_addr &get_property_address(const char *) const; /*! * Returns true if the node holds a property with the specified name */ virtual bool has_property(const char *) const; bool has_child_property(const char *) const; /*! * Returns a reference to the specified child. A null reference will * be returned if the child doesn't exist */ virtual node *get_child(const char *) const; /*! * Returns a reference to an existing specified child, or if it doesn't * exist calls create_child() and returns the new child */ virtual node *get_or_create_child(const char *); virtual node *create_child(const char *); node *add_child(node *chld, bool complete_m = false, const char *name = 0, const char *description = 0); /*! * Removes a child node from the node */ void remove_child(const char *); void clear_childs(); /*! * Returns true if the node has the specified method */ virtual bool has_method(const char *name, uint32_t) const; /*! * Implements the call handler */ virtual bool call_method(int id, base_stream &, const std::vector &); virtual bool negate_method(int, base_stream &, const std::vector &); /*! * Imports a method table into this node's property list. The * table must be terminated by an entry with name = NULL. */ void import_methods(const method_info *); /*! * Adds a single new method to the node's property list. The * method_info object must live while this node exists. */ bool add_method(const method_info *info); /*! * Removes the specified method from the node's children list */ void remove_method(const char *name); /* Logging */ virtual bool should_log(int) const; virtual base_stream &log() const; // Info virtual bool output_info(base_stream &, const std::vector &) const; typedef std::map properties; const properties &get_properties() const { return m_properties; } int match_property(uint32_t, const char *, content_type &, const char * &) const; void broadcast_event(int, void *, bool all = false); protected: node *m_parent; properties m_properties; property_def *instantiate_property(const char *name, property_def::valtype, const char *desc = 0, uint32_t flags = 0); property_def *instantiate_property(const char *name, property_def::valtype, const void *, const char *desc = 0, uint32_t flags = 0); property_def *instantiate_property(const char *name, propval *, const char *desc = 0, uint32_t flags = 0); property_def *instantiate_property_b(const char *name, bool def, const char *desc = 0, uint32_t flags = 0); property_def *instantiate_property_i(const char *name, int32_t def, const char *desc = 0, uint32_t flags = 0); property_def *instantiate_property_u(const char *name, uint32_t def, const char *desc = 0, uint32_t flags = 0); property_def *instantiate_property_t(const char *name, uint32_t def, const char *desc = 0, uint32_t flags = 0); property_def *instantiate_property_s(const char *name, const char *def, const char *desc = 0, uint32_t flags = 0); property_def *instantiate_property_a(const char *name, const inet6_addr &, const char *desc = 0, uint32_t flags = 0); bool set_property_inst(const char *name, property_def::valtype, const char *value); bool enable_several(const std::vector &, bool); bool show(base_stream &, const std::vector &); bool exec_negate(base_stream &, const std::vector &); virtual void propagate_property_changed(node *, const char *); virtual void property_changed(node *, const char *) {} virtual void remove_child_node(node *); friend class conf_node; std::string m_name; }; class conf_node : public node { public: conf_node(node *, const char *); void attach_watcher(node *); void dettach_watcher(node *); void enable(bool); protected: void propagate_property_changed(node *, const char *); void property_changed(node *, const char *); std::vector m_watchers; }; class statistics_node : public node { public: typedef uint64_t counter_type; statistics_node(node *parent, int count, const char **descriptions); ~statistics_node(); bool check_startup(); /* calls check_startup and adds itself to parent */ bool setup(const char *description = 0); counter_type &counter(int index) const; void reset_counters(); bool call_method(int id, base_stream &, const std::vector &); bool output_info(base_stream &, const std::vector &) const; protected: int m_count; counter_type *m_counters; const char **m_descriptions; }; class message_stats_node : public statistics_node { public: message_stats_node(node *parent, int count, const char **descriptions, int typecount = 3, const char **types = 0); statistics_node::counter_type &counter(int index, int type) const; void disable_counter(int index, int type); bool output_info(base_stream &, const std::vector &) const; protected: void print_counter(base_stream &, int index, int type) const; int m_msgcount, m_typecount; const char **m_typedescriptions; std::bitset<64> m_enablecounters; }; struct propval_integer : propval { propval_integer(const int32_t *); const void *get_value() const; bool set_value(const char *); void output_value(base_stream &) const; int32_t value; }; struct propval_enum : propval_integer { struct entry { const char *name; int32_t value; }; propval_enum(entry *); bool set_value(const char *); void output_value(base_stream &) const; entry *entries; }; #endif mrd6-0.9.6/include/mrd/timers.h0000644000175000017500000001744310550053610014751 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * timers.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_timers_h_ #define _mrd_timers_h_ #include #include #include #include #include #include #include class base_stream; struct time_duration { time_duration(int); int32_t value; }; inline time_duration::time_duration(int val) : value(val) {} class tval { private: timeval v; public: tval() { v.tv_sec = 0; v.tv_usec = 0; } tval(time_t secs) { v.tv_sec = secs; v.tv_usec = 0; } tval(time_t secs, suseconds_t usecs) { v.tv_sec = secs; v.tv_usec = usecs; } tval operator + (const tval &) const; int32_t operator - (const tval &) const; // milisecs tval &operator += (const tval &tv) { v.tv_usec += tv.v.tv_usec; if (v.tv_usec > 1000000) { v.tv_sec ++; v.tv_usec -= 1000000; } v.tv_sec += tv.v.tv_sec; return *this; } tval &operator += (uint32_t diff) { v.tv_usec += (diff % 1000) * 1000; v.tv_sec += diff / 1000; if (v.tv_usec > 1000000) { v.tv_usec -= 1000000; v.tv_sec ++; } return *this; } tval &operator = (const tval &tv) { v = tv.v; return *this; } bool operator < (const tval &tv) const { return v.tv_sec < tv.v.tv_sec || (v.tv_sec == tv.v.tv_sec && v.tv_usec < tv.v.tv_usec); } bool operator == (const tval &tv) const { return v.tv_sec == tv.v.tv_sec && v.tv_usec == tv.v.tv_usec; } bool operator <= (const tval &tv) const { return v.tv_sec < tv.v.tv_sec || (v.tv_sec == tv.v.tv_sec && v.tv_usec <= tv.v.tv_usec); } tval diff(const tval &) const; uint32_t round_milisecs() const { return v.tv_sec * 1000 + v.tv_usec / 1000; } uint32_t round_secs() const { return v.tv_sec + (v.tv_usec >= 5000000 ? 1 : 0); } void update_to_now() { gettimeofday(&v, 0); } static tval now() { tval tv; tv.update_to_now(); return tv; } const timeval &as_timeval() const { return v; } time_t secs() const { return v.tv_sec; } suseconds_t usecs() const { return v.tv_usec; } base_stream &print_to(base_stream &) const; }; class timer_base { public: timer_base(const std::string &); timer_base(const std::string &, uint32_t interval, bool repeat, uint32_t perturb = 0); timer_base(const timer_base &); virtual ~timer_base(); std::string name; /*! returns true if the timer is running */ bool is_running() const; /*! returns the time left in miliseconds */ uint32_t time_left() const; uint32_t get_interval() const; time_duration time_left_d() const; bool start(bool immediatly = false); bool start(uint32_t interval, bool repeat, bool immediatly = false, uint32_t perturb = 0); bool stop(); bool restart(bool immediatly = false); void update(uint32_t interval, bool repeat, uint32_t perturb = 0); bool start_or_update(uint32_t interval, bool repeat, bool immediatly = false, uint32_t perturb = 0); virtual void callback() = 0; base_stream &print_to(base_stream &) const; protected: bool _running, _repeat; uint32_t _interval, _perturb; uint32_t _target, _extra; friend class timermgr; }; inline timer_base::timer_base(const std::string &n) : name(n), _running(false), _repeat(false), _interval(0), _perturb(0), _target(0), _extra(0) {} inline timer_base::timer_base(const std::string &n, uint32_t interval, bool repeat, uint32_t perturb) : name(n), _running(false), _repeat(repeat), _interval(interval), _perturb(perturb), _target(0), _extra(0) {} inline bool timer_base::is_running() const { return _running; } inline uint32_t timer_base::get_interval() const { return _interval; } inline time_duration timer_base::time_left_d() const { return time_duration(time_left()); } inline bool timer_base::restart(bool immediatly) { return start_or_update(_interval, _repeat, immediatly); } inline bool timer_base::start_or_update(uint32_t interval, bool repeat, bool immediatly, uint32_t perturb) { if (_running) { update(interval, repeat, perturb); return true; } else { return start(interval, repeat, immediatly, perturb); } } template class timer : public timer_base { public: typedef std::mem_fun_t callback_def; timer(const std::string &, Holder *h, callback_def c); timer(const std::string &, Holder *h, callback_def c, uint32_t interval, bool repeat); timer(const timer &); void callback(); protected: Holder *_h; callback_def _cb; }; template inline timer::timer(const std::string &name, H *h, timer::callback_def c) : timer_base(name), _h(h), _cb(c) {} template inline timer::timer(const std::string &name, H *h, timer::callback_def c, uint32_t interval, bool repeat) : timer_base(name, interval, repeat), _h(h), _cb(c) {} template inline timer::timer(const timer &original) : timer_base(original), _h(original._h), _cb(original._cb) {} template inline void timer::callback() { _cb(_h); } template class timer1 : public timer_base { public: typedef std::mem_fun1_t callback_def; timer1(const std::string &, Holder *h, callback_def c, const Arg &a); timer1(const std::string &, Holder *h, callback_def c, const Arg &a, uint32_t interval, bool repeat); timer1(const timer1 &); void callback(); const Arg &argument() const { return _arg; } protected: Holder *_h; callback_def _cb; Arg _arg; }; template inline timer1::timer1(const std::string &name, H *h, timer1::callback_def c, const A &a) : timer_base(name), _h(h), _cb(c), _arg(a) {} template inline timer1::timer1(const std::string &name, H *h, timer1::callback_def c, const A &a, uint32_t interval, bool repeat) : timer_base(name, interval, repeat), _h(h), _cb(c), _arg(a) {} template inline timer1::timer1(const timer1 &original) : timer_base(original), _h(original._h), _cb(original._cb), _arg(original._arg) {} template inline void timer1::callback() { _cb(_h, _arg); } /*! * \class timermgr mrd/timers.h * \brief Timer manager class. */ class timermgr { public: timermgr(); virtual ~timermgr(); bool check_startup(); void shutdown(); bool time_left(timeval &); bool output_info(base_stream &, bool) const; void start_timer(timer_base *); void stop_timer(timer_base *); void update_timer(timer_base *, uint32_t, bool, uint32_t); bool handle_event(); void timed_out_event(); void start_timer(timer_base *, uint32_t, uint32_t); void clone_position(const timer_base *, timer_base *); uint32_t time_left(const timer_base *) const; private: void handle_timer_event(); void update_taccum(); typedef std::list tq_def; tq_def tq; uint32_t clk_tck; clock_t lastclk; uint32_t taccum; }; static inline const char *stream_type_format_parameter(const time_duration &) { return "{duration}"; } void stream_push_formated_type(base_stream &os, const time_duration &); #endif mrd6-0.9.6/include/mrd/rib.h0000644000175000017500000000701310550053610014212 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * rib.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_unicast_route_h_ #define _mrd_unicast_route_h_ #include #include #include #include class interface; /*! * callback based unicast route watcher */ struct rib_watcher_base { rib_watcher_base(); virtual ~rib_watcher_base(); void set_destination(const inet6_addr &); void release(); void update(); enum { HAS_ROUTE = 1, GATEWAY = 2, PREFSRC = 4, DEV = 8, PROTOCOL = 16, METRIC = 32 }; virtual void route_changed(uint32_t) = 0; template T *oif(R *r) const { return r->get_interface(dev); } bool valid; int dev; in6_addr dst, gateway, prefsrc; uint32_t protocol, metric; }; /*! * template based unicast route watcher */ template struct rib_watcher : rib_watcher_base { typedef std::mem_fun1_t callback_def; rib_watcher(Holder *, callback_def c); void route_changed(uint32_t); private: Holder *_h; callback_def _cb; }; template inline rib_watcher::rib_watcher(H *h, rib_watcher::callback_def c) : rib_watcher_base(), _h(h), _cb(c) {} template inline void rib_watcher::route_changed(uint32_t flags) { _cb(_h, flags); } /*! * provides an interface to get resolve unicast routes from * the operating system's RIB */ class rib_def : public node, public mrib_origin { public: rib_def(); virtual bool check_startup(); virtual void shutdown(); virtual void check_initial_interfaces(); bool call_method(int, base_stream &, const std::vector &); const char *description() const; void return_prefix(mrib_def::prefix *); virtual bool dump_info(base_stream &) const; virtual void register_route(rib_watcher_base *, const inet6_addr &); virtual void unregister_route(rib_watcher_base *); virtual void update_route(rib_watcher_base *); interface *path_towards(const inet6_addr &) const; interface *path_towards(const inet6_addr &, inet6_addr &) const; interface *path_towards(const inet6_addr &, inet6_addr &, inet6_addr &) const; interface *path_towards(const inet6_addr &addr, inet6_addr &prefsrc, inet6_addr &nexthop, inet6_addr &record) const; void transfer_watchers(rib_def *); protected: typedef std::multimap notify_list; notify_list rt_notify_list; struct lookup_result { int dev; inet6_addr dst; in6_addr nexthop, source; uint32_t protocol, metric; }; virtual bool lookup_prefix(const in6_addr &, lookup_result &) const = 0; void update_all(); void prefix_changed(bool, const lookup_result &); property_def *populate_mrib; property_def *base_distance; }; #endif mrd6-0.9.6/include/mrd/source_discovery.h0000644000175000017500000001124610550053610017030 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * source_discovery.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_source_discovery_h_ #define _mrd_source_discovery_h_ #include #include #include #include class group; class group_node; class groupconf; /*! * Source discovery base interface. Source discovery origin's supply * new sources to mrd's core which are then distributed to source sinks * and group nodes, triggering the creation of source states. */ class source_discovery_origin { public: virtual ~source_discovery_origin(); /*! * Returns the origin unique textual description. i.e. `static`, * `data-plane`, etc. */ virtual const char *origin_description() const = 0; /*! * may be called by source_discovery implementations in order to * advertise a new source to all active groups that match the supplied * group mask */ virtual void discovered_source(int ifindex, const inet6_addr &grpmask, const inet6_addr &source); /*! * may be called by source_discovery implementations in order to * advertise that the supplied source was lost to all active groups * that match the supplied group mask */ virtual void lost_source(const inet6_addr &groupmask, const inet6_addr &source); /*! * called by mrd whenever the interest of an active group on this * source discovery origin changes. if include=true, the current * known sources should be advertised via discovered_source */ virtual void group_interest_changed(group_node *, bool include); /*! * called by mrd whenever this source discovery origin instance * is attached to a group conf object. any childs and/or methods * and properties should be instantiated here */ virtual void groupconf_registered(groupconf *, bool include); }; class aggr_source_discovery : public source_discovery_origin { public: bool check_startup(); void discovered_source(int ifindex, const inet6_addr &groupmask, const inet6_addr &source); void lost_source(const inet6_addr &groupmask, const inet6_addr &source); void group_interest_changed(group_node *n, bool include); void dump_cache(base_stream &) const; protected: aggr_source_discovery(int keepalive); virtual void gc(); typedef std::pair sg_pair; typedef std::map cache; int add_to_cache(cache &, int, const inet6_addr &, const inet6_addr &); void run_gc(cache &); void dump_cache(base_stream &, const cache &) const; int m_keepalive; cache m_cache; timer m_gc_timer; }; /*! * Data plane based source discovery implementation. Operating system * modules should instantiate a `data-plane` origin and call discovered_source() * for each non-existant state with active flows */ class data_plane_source_discovery : public aggr_source_discovery { public: data_plane_source_discovery(); const char *origin_description() const { return "data-plane"; } }; /*! * Implements a static source origin. The sources to be advertised are populated * statically via configuration. */ class static_source_discovery : public source_discovery_origin { public: const char *origin_description() const { return "static"; } void group_interest_changed(group_node *n, bool include); void groupconf_registered(groupconf *, bool include); }; /*! * Registered source discovery sinks in mrd receive discovered_source events */ class source_discovery_sink { public: virtual ~source_discovery_sink(); /*! * the default implementation checks if the group exists, and if * so calls discovered_source with the group instance */ virtual void discovered_source(interface *, const inet6_addr &grpaddr, const inet6_addr &sourceaddr, source_discovery_origin *); virtual void discovered_source(interface *, group *, const inet6_addr &source, source_discovery_origin *); }; #endif mrd6-0.9.6/include/mrd/icmp.h0000644000175000017500000000463010575655373014415 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mrd/icmp.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_icmp_h_ #define _mrd_icmp_h_ #include #include #include class interface; class icmp_handler { public: virtual ~icmp_handler() {} virtual void icmp_message_available(interface *, const in6_addr &, const in6_addr &, icmp6_hdr *, int) = 0; }; class icmp_base { public: virtual ~icmp_base() {} virtual bool check_startup() = 0; virtual void shutdown() = 0; bool send_icmp(const in6_addr &dst, icmp6_hdr *, uint16_t) const; bool send_icmp(const interface *intf, const in6_addr &dst, icmp6_hdr *, uint16_t) const; bool send_icmp(const interface *intf, const in6_addr &dst, int rta, icmp6_hdr *, uint16_t) const; bool send_icmp(const interface *, const in6_addr &, const in6_addr &, icmp6_hdr *, uint16_t) const; virtual bool send_icmp(const interface *, const in6_addr &, const in6_addr &, int, icmp6_hdr *, uint16_t) const = 0; bool register_handler(int type, icmp_handler *); void require_mgroup(const in6_addr &, bool); virtual void added_interface(interface *) = 0; virtual void removed_interface(interface *) = 0; protected: void icmp_message_available(interface *, const in6_addr &, const in6_addr &, icmp6_hdr *, int); virtual void registration_changed(); virtual void internal_require_mgroup(const in6_addr &, bool) = 0; typedef std::map handlers; handlers m_handlers; typedef std::map mgroups; mgroups m_mgroups; }; #endif mrd6-0.9.6/include/mrd/mrd.h0000644000175000017500000004065210746067643014250 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mrd.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_mrd_h_ #define _mrd_mrd_h_ #include #include #include #include #include #include #include #include #include #include /* POSIX ucontext_t */ #include #include #include #include #include #include class group; class router; class rib_def; class mfa_core; class confnode; class intfconf; class icmp_base; class groupconf; class interface; class event_sink; class mrd_module; class base_stream; class mfa_instance; class intfconf_node; class groupconf_node; class group_interface; class source_discovery_sink; class source_discovery_origin; /*! * callback based socket wrapper implementation. */ class socket_base { public: socket_base(const char *); virtual ~socket_base(); const char *name() const; enum { Read = 1, Write = 2 }; virtual void callback(uint32_t) = 0; bool register_fd(int fd, uint32_t flags = Read); void unregister(bool close = true); bool monitor(uint32_t flags = Read); int fd() const { return _fd; } uint64_t hits() const { return _hits; } /* these are required in _handle_pending_socks */ std::string _name; int _fd; uint64_t _hits; }; /*! * callback based ipv6 socket wrapper implementation. */ class socket6_base : public socket_base { public: socket6_base(const char *); bool register_fd(int fd, uint32_t flags = Read); int sendto(const void *, uint16_t, const sockaddr_in6 *); int sendto(const void *, uint16_t, const sockaddr_in6 *, const sockaddr_in6 *from, int = 0); int recvfrom(void *, uint16_t, sockaddr_in6 *); int recvfrom(void *, uint16_t); /*! * Joins the supplied multicast group in the supplied interface */ bool join_mc(interface *, const in6_addr &); /*! * Leaves the supplied multicast group in the supplied interface */ bool leave_mc(interface *, const in6_addr &); bool enable_mc_loop(bool); bool set_hoplimit(int); bool set_mcast_hoplimit(int); /*! * Retrieves the last received packet's source address */ const sockaddr_in6 &source_address() const { return _recvfrom; } /*! * Retrives the last received packet's destination * address and receiving interface */ bool destination_address(sockaddr_in6 &, int &); /*! Returns the next cmsghdr, as the first one is always IPV6_PKTINFO */ cmsghdr *next_cmsghdr(int maxlen) const; private: /* in6_pktinfo is 20 bytes length */ /* cmsghdr is 16 bytes length */ /* msghdr is 28 bytes length */ /* reserve a cmsg(20 bytes) for other uses */ /* uint8_t _ctlbuf[CMSG_SPACE(sizeof(in6_pktinfo)) + CMSG_SPACE(20)]; */ uint8_t _ctlbuf[96]; msghdr _h; sockaddr_in6 _recvfrom; }; inline const char *socket_base::name() const { return _name.c_str(); } /*! * template based socket wrapper implementation. */ template class socket0 : public socket_base { public: typedef std::mem_fun1_t callback_def; socket0(const char *, Holder *h, callback_def c); void callback(uint32_t); private: Holder *_h; callback_def _cb; }; template inline socket0::socket0(const char *name, H *h, callback_def c) : socket_base(name), _h(h), _cb(c) {} template inline void socket0::callback(uint32_t flags) { _cb(_h, flags); } /*! * template based ipv6 socket wrapper implementation. */ template class socket6 : public socket6_base { public: typedef std::mem_fun1_t callback_def; socket6(const char *, Holder *h, callback_def c); void callback(uint32_t); private: Holder *_h; callback_def _cb; }; template inline socket6::socket6(const char *name, H *h, callback_def c) : socket6_base(name), _h(h), _cb(c) {} template inline void socket6::callback(uint32_t flags) { _cb(_h, flags); } class group_request_interface { public: virtual ~group_request_interface() {} virtual bool request_group(interface *, const inet6_addr &, const inet6_addr &, router *) const = 0; }; /*! * base core class. Provides interface, group, router, etc management. */ class mrd : public node { public: mrd(); virtual ~mrd(); bool check_startup(const char *, bool autoload = true); void start(); void shutdownx(); bool show_mrd_version(base_stream &) const; void show_base_info(base_stream &) const; /*! Is the router already inside the processing loop. */ bool is_running() const { return m_state == Running; } /*! Returns true if the router has the supplied address */ bool has_address(const in6_addr &) const; /*! * Returns true if the supplied address is in one of the * router's subnets */ bool in_same_subnet(const in6_addr &) const; /*! Returns a reference to the MRIB */ mrib_def &mrib() { return m_mrib; } /*! Returns a reference to the RIB */ rib_def &rib() const { return *m_rib_handler; } /*! Returns a reference to the ICMP handling instance */ icmp_base &icmp() const { return *m_icmp; } /*! Registers a new routing protocol */ bool register_router(router *); /*! Unregisters a routing protocol */ void unregister_router(router *); /*! * Registers a RIB handler. This is only possible during the * initial configuration parsing. Returns false if we already * have an handler. */ bool register_rib(rib_def *); /*! * Nodes may register themselves to be notified of the startup * event. That is, that they may use all MRD6's components. */ void register_startup(node *); /*! Returns a previously registered router instance */ router *get_router(const char *) const; mfa_core *mfa() const { return m_mfa; } /*! Returns an interface instance referenced by index */ interface *get_interface_by_index(int dev) const; /*! Returns an interface instance referenced by name */ interface *get_interface_by_name(const char *) const; interface *get_loopback_interface() const; typedef std::map interface_list; /*! returns a list with all the registered interfaces */ const interface_list &intflist() const { return m_intflist; } /*! Should be called by OS modules whenever a new interface is found. */ interface *found_interface(int index, const char *name, int type, int mtu, int flags); void check_enabled_interfaces(intfconf *); void broadcast_interface_state_changed(interface *); /*! Should be called by OS modules whenever an interface is lost */ void lost_interface(int); void remove_interface(interface *); /*! Loads the specified module from the configured module path */ bool load_modulex(const char *); bool remove_module(const char *); bool remove_module(mrd_module *); /*! Returns a reference to the root configuration node */ node *rootconf() { return this; } intfconf *get_interface_configuration(const char *); intfconf *default_interface_configuration(); /*! if origin=0, unregisters the supplied source discovery method * named 'name'. */ bool register_source_discovery(const char *name, source_discovery_origin *origin); source_discovery_origin *get_source_discovery(const char *) const; void discovered_source(int ifindex, const inet6_addr &, const inet6_addr &, source_discovery_origin *); void lost_source(const inet6_addr &, const inet6_addr &, source_discovery_origin *); bool register_source_sink(source_discovery_sink *, bool); bool register_generic_source_sink(source_discovery_sink *, bool); bool set_property(const char *, const char *); bool increment_property(const char *, const char *); bool call_method(int, base_stream &, const std::vector &); std::list configured_group_set(const char *rt = 0) const; groupconf *match_group_configuration(const inet6_addr &) const; groupconf *get_group_configuration(const inet6_addr &) const; groupconf *get_similiar_groupconf_node(const groupconf *) const; // // Group Management stuff // struct create_group_context { int iif; inet6_addr groupaddr; inet6_addr requester; std::string origin_name; group *result; }; bool create_group(router *, node *caller, create_group_context *); void release_group(group *); /*! if prio is <= 0, the supplied instance is removed from the list */ void register_group_creation_auth(group_request_interface *, int prio = 10); /*! Returns an existing group reference by address */ group *get_group_by_addr(const inet6_addr &) const; typedef std::map group_list; const group_list &group_table() const { return m_grplist; } /*! * Registers a socket into the main event loop. Ready status events * are delivered to the socket_base handle */ bool register_sock(socket_base *, int sock, uint32_t); /*! Unregisters a socket from the main event loop. */ bool unregister_sock(socket_base *); bool monitor_sock(socket_base *, uint32_t); /* MRD events */ enum { CreatedGroup = 'C', RemoveGroup = 'R', StartupEvent = 'S', NewGroup = 'G', ReleasedGroup = 'g', InterfaceStateChanged = 'I', ActiveStateNotification = 'A', }; struct task { enum { LowPrio = 0, HighPrio = 10, }; event_sink *target; int event, prio; void *argument; }; static task make_task(event_sink *target, int event, void *opt = 0, int prio = task::LowPrio); /*! * Registers a task to be executed by the main loop. The task * event is then delivered to the target node. */ void register_task(const task &); /*! * Registers a task to be executed by the main loop. The task * event is then delivered to the target node. */ void register_task(event_sink *target, int event, void *opt = 0, int prio = task::LowPrio); /*! Removes any pendings tasks which target is the supplied one (slow operation) */ void clear_tasks(event_sink *target); struct active_state_report { group *group_instance; in6_addr source_address; bool active; }; void interested_in_active_states(event_sink *, bool); bool interest_in_active_states() const; void state_is_active(group *, const in6_addr &, bool); void load_early_module(const char *); timermgr *timemgr() { return &m_timermgr; } packet_buffer *ipktb; packet_buffer *opktb; void output_backtrace(base_stream &) const; char *obtain_frame_description(void *) const; struct posix_uctx { posix_uctx(ucontext_t *); /* this depends on the operating system _and_ platform */ void *get_current_frame() const; ucontext_t *base; }; bool should_log(int level) const; base_stream &log() const; base_stream &fatal() const; static uint32_t get_randu32(); protected: virtual group *allocate_group(const inet6_addr &, groupconf *) const; private: bool prepare_os_components(); void prepare_second_components(); void add_static_modules(); const char *loopback_interface_name() const; void processloop(); void change_user(); bool check_module_path(const char *, std::string &); bool add_module(const char *, mrd_module *); static void handle_signal(int); group *create_group(const inet6_addr &); group *create_group(const inet6_addr &, groupconf *); typedef std::map routers; enum mrd_state { Initial, PreConfiguration, Configuration, PostConfiguration, Running, ShuttingDown, } m_state; void change_state(mrd_state); timermgr m_timermgr; time_t m_startup; typedef std::deque module_path; module_path m_module_path; typedef std::map source_disc; source_disc m_source_disc; typedef std::vector source_sinks; source_sinks m_source_sinks, m_all_source_sinks; typedef std::vector node_vector; node_vector m_startup_nodes; static_source_discovery m_static_source_disc; mutable log_base g_rlog; mrib_def m_mrib; routers m_routers; interface_list m_intflist; node m_intflist_node; group_list m_grplist; node m_grplist_node; void invalidate_intf_cache(); #define _INTERFACE_CACHE_LEN 32 mutable interface *m_intf_cache[_INTERFACE_CACHE_LEN]; typedef std::list > create_group_acl; create_group_acl m_create_group_acl; class intfconf_list : public node { public: intfconf_list(node *); ~intfconf_list(); bool check_startup(); bool call_method(int, base_stream &, const std::vector &); bool negate_method(int, base_stream &, const std::vector &); bool is_interface_disabled(const char *) const; node *create_child(const char *); void remove_child_node(node *); private: struct disable_token { std::string origstr; regex_t r; }; std::list tokens; }; intfconf_list m_intfconfs; class group_configuration : public ptree { public: groupconf *create_child(const inet6_addr &); groupconf *match(const inet6_addr &, const groupconf * = 0) const; void clear(); }; group_configuration m_routing_table; class groups_node : public node { public: groups_node(node *); node *get_child(const char *) const; node *create_child(const char *); }; groups_node m_groups_node; typedef std::list active_state_interest; active_state_interest m_active_state_interest; typedef std::list socket_list; socket_list m_read, m_write; fd_set m_rdst, m_wrst; int m_largestsock; mfa_core *m_mfa; rib_def *m_rib_handler; icmp_base *m_icmp; typedef std::deque tasks; tasks m_tasks; uint32_t m_tasks_stat; uint64_t m_tasks_time_spent; std::map m_modules; typedef mrd_module *module_init_sig(void *, mrd *); typedef std::map static_modules; static_modules m_static_modules; typedef std::set early_modules; early_modules m_early_modules; void event(int, void *); bool shutdown(base_stream &, const std::vector &); bool show_version(base_stream &, const std::vector &); bool show_timers(base_stream &, const std::vector &); bool show_rpf(base_stream &, const std::vector &); bool load_module(base_stream &, const std::vector &); bool unload_module(base_stream &, const std::vector &); bool unicast_regs(base_stream &, const std::vector &); bool socket_regs(base_stream &, const std::vector &); bool show_info(base_stream &, const std::vector &); bool show_conf(base_stream &, const std::vector &); void dump_node_tree(base_stream &, node *) const; void dump_node_tree(base_stream &, node *, std::vector &) const; int show_conf_node(base_stream &, node *, bool print) const; bool show_commands(base_stream &, const std::vector &); void dump_commands(base_stream &, const node *, const std::string &) const; friend class log_base; friend class mfa_core; }; inline interface *mrd::get_interface_by_index(int n) const { interface *possible = m_intf_cache[n & (_INTERFACE_CACHE_LEN-1)]; if (possible && possible->index() == n) return possible; interface_list::const_iterator p = m_intflist.find(n); if (p == m_intflist.end()) return 0; m_intf_cache[n & (_INTERFACE_CACHE_LEN-1)] = p->second; return p->second; } inline bool mrd::interest_in_active_states() const { return !m_active_state_interest.empty(); } /*! * mrd modules implement mrd_module */ class mrd_module { public: mrd_module(mrd *, void *); virtual ~mrd_module(); virtual bool check_startup() = 0; virtual void shutdown() {} virtual void module_loaded(const char *, mrd_module *) {} protected: void *m_dlhandle; mrd *m_mrd; friend class mrd; }; #define module_entry(x, y) \ extern "C" mrd_module *mrd_module_init_##x(void *dlh, mrd *m) { \ return new y (m, dlh); \ } extern mrd *g_mrd; #endif mrd6-0.9.6/include/mrd/group.h0000644000175000017500000001502010550053610014567 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * group.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_group_h_ #define _mrd_group_h_ #include #include #include #include #include #include #include #include #include class group; class group_node; class group_interface; class groupconf_node; class router; /*! * base group configuration node which provides configuration * aggregation to active groups */ class groupconf : public ptree_node, public conf_node { public: groupconf(const inet6_addr &); ~groupconf(); const char *description() const { return "Group configuration"; } bool check_startup(); void fill_defaults(); const inet6_addr &id() const { return prefix; } bool call_method(int, base_stream &, const std::vector &); node *create_child(const char *); virtual node *next_similiar_node() const; typedef std::vector source_discs; const source_discs &get_source_discs() const { return srcdisc; } void set_source_discs(const source_discs &); inet6_addr prefix; private: void remove_child_node(node *n); source_discs srcdisc; friend class group; }; class groupconf_node : public conf_node { public: groupconf_node(groupconf *, const char *); virtual ~groupconf_node() {} virtual bool fill_defaults() { return true; } node *next_similiar_node() const; }; /*! * for each group, there is a group_interface which contains that * contains the corresponding interface's state for the group * (filter list, filter mode, subscriber list, etc). */ class group_interface : public node { public: group_interface(group *, group_node *, interface *); virtual ~group_interface(); const char *description() const { return "Multicast group local interface"; } virtual void shutdown(); /*! * returns the owner group instance. */ group *owner() const { return g_owner; } group_node *owner_node() const { return g_node_owner; } /*! * returns the corresponding network interface. */ interface *intf() const { return g_intf; } enum event_type { added_sources = 1, removed_sources = 2, all_sources = 3 }; enum filter_mode_type { include = 1, exclude = 2 }; filter_mode_type filter_mode() const { return g_filter_mode; } const address_set &include_set() const { return g_include_set; } const address_set &exclude_set() const { return g_exclude_set; } const address_set &active_set() const; bool has_interest_on(const in6_addr &) const; void dump_filter(base_stream &) const; bool output_info(base_stream &, const std::vector &) const; virtual void output_info(base_stream &, bool detailed) const; bool should_log(int) const; base_stream &log() const; protected: void dump_filter() const; group *g_owner; group_node *g_node_owner; interface *g_intf; filter_mode_type g_filter_mode; address_set g_include_set, g_exclude_set; }; /*! * each implemented protocol (PIM, etc) implements a group_node to receive * group information, such as changes in the subscriber list. */ class group_node : public node { public: group_node(router *rt); virtual ~group_node(); virtual void attached(group *owner); virtual void dettached(); group *owner() const { return g_owner; } router *owner_router() const { return g_owner_router; } virtual void subscriptions_changed(const group_interface *, group_interface::event_type, const address_set &); virtual void discovered_source(interface *input, const inet6_addr &, source_discovery_origin *); virtual void lost_source(const inet6_addr &, source_discovery_origin *); virtual void clear_interface_references(interface *) {} virtual group_interface *instantiate_group_interface(interface *intf) { return 0; } virtual bool has_interest_in_group() const { return false; } virtual bool has_downstream_interest(const in6_addr &) const { return false; } bool should_log(int) const; base_stream &log() const; protected: group *g_owner; router *g_owner_router; }; /*! * implements the base group concept node. */ class group : public node { public: group(const inet6_addr &, groupconf *); virtual ~group(); const char *description() const { return "Active multicast group"; } bool check_startup(); void shutdown(); bool attach_node(group_node *); void dettach_node(group_node *); group_node *node_owned_by(const router *) const; groupconf *conf() const { return g_conf; } group_interface *local_oif(int) const; group_interface *local_oif(interface *); bool has_interest_on(const in6_addr &) const; bool has_downstream_interest(const in6_addr &) const; const inet6_addr &id() const { return g_addr; } void clear_interface_references(interface *); typedef std::map group_intfs; const group_intfs &interface_table() const { return g_oifs; } void trigger_mode_event(group_interface *, group_interface::event_type, const address_set &) const; void discovered_source(interface *, const inet6_addr &, source_discovery_origin *) const; void lost_source(const inet6_addr &, source_discovery_origin *) const; bool someone_lost_interest(); bool output_info(base_stream &, const std::vector &) const; void broadcast_source_interest_change(group_node *, bool include = true) const; base_stream &log() const; protected: void broadcast_source_interest_change(groupconf *, group_node *, bool) const; bool output_info(base_stream &, bool detailed) const; bool is_group_node(const property_def &) const; groupconf *groupconf_with_sourcedisc() const; inet6_addr g_addr; groupconf *g_conf; bool g_doomed; group_intfs g_oifs; node g_intflist; friend class mrd; }; #endif mrd6-0.9.6/include/mrd/address_set.h0000644000175000017500000000572110550053610015742 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * address_set.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_address_set_h_ #define _mrd_address_set_h_ #include #include #include #include class base_stream; /*! * \class address_set mrd/address_set.h * \brief implements an address list. */ class address_set : public std::set { public: address_set(); address_set(const in6_addr &); address_set(const inet6_addr &); address_set(const address_set &); address_set &union_with(const address_set &); address_set &union_with(const address_set &, address_set &diff); address_set &diff_with(const address_set &); address_set &diff_with(const address_set &, address_set &diff); address_set &intersect_with(const address_set &); address_set &intersect_with(const address_set &, address_set &diff); address_set &assign_with(const address_set &, address_set &added, address_set &remove); bool has_addr(const in6_addr &) const; bool remove(const in6_addr &); address_set &operator += (const in6_addr &); address_set operator + (const address_set &addrs) const { return address_set(*this).union_with(addrs); } address_set operator - (const address_set &addrs) const { return address_set(*this).diff_with(addrs); } address_set operator * (const address_set &addrs) const { return address_set(*this).intersect_with(addrs); } base_stream &print_to(base_stream &) const; }; // inlines inline address_set::address_set() { } inline address_set::address_set(const in6_addr &addr) { insert(end(), addr); } inline address_set::address_set(const inet6_addr &addr) { insert(end(), addr.address()); } inline address_set::address_set(const address_set &orig) : std::set(orig) { } inline bool address_set::has_addr(const in6_addr &addr) const { return find(addr) != end(); } inline address_set &address_set::operator += (const in6_addr &addr) { insert(end(), addr); return *this; } static inline const char *stream_type_format_parameter(const address_set &) { return "{addrset}"; } static inline void stream_push_formated_type(base_stream &os, const address_set &val) { val.print_to(os); } #endif mrd6-0.9.6/include/mrd/log.h0000644000175000017500000002152610726013264014232 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * log.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_log_h_ #define _mrd_log_h_ #include #include #include #include class base_stream; class stream_flusher { public: virtual ~stream_flusher(); virtual void flushed(const char *buffer, bool newline) = 0; }; const char *stream_type_format_parameter(bool); const char *stream_type_format_parameter(int); const char *stream_type_format_parameter(uint32_t); const char *stream_type_format_parameter(uint64_t); const char *stream_type_format_parameter(const char *); const char *stream_type_format_parameter(const void *); void stream_push_formated_type(base_stream &, bool val); void stream_push_formated_type(base_stream &, int val); void stream_push_formated_type(base_stream &, uint32_t val); void stream_push_formated_type(base_stream &, uint64_t val); void stream_push_formated_type(base_stream &, const char *val); void stream_push_formated_type(base_stream &, const void *val); /*! * base log stream */ class base_stream { public: base_stream(); base_stream(stream_flusher *); ~base_stream(); /*! appends a new string chunk to the stream buffer */ void append_chunk(const char *); void append_chunk(const char *, int); /*! requests a buffer part of minimal size n for direct writing. * if there isn't enough space, returns NULL */ char *req_buffer(int n); /*! after directly accessing the buffer, must commit changes * of n characters */ void commit_change(int n); /*! helper method */ void nprintf(int n, const char *format, ...); /*! clears the stream buffer */ void clear(); /*! returns a null-terminated string pointer to the stream buffer */ const char *str() const; /*! provides printf-like semantics to base_stream */ base_stream &printf(const char *, ...); base_stream &newl(); template base_stream &write(const T &t) { stream_push_formated_type(*this, t); return *this; } base_stream &write(const char *val) { append_chunk(val); return *this; } template base_stream &writeline(const T &t) { stream_push_formated_type(*this, t); return newl(); } base_stream &writeline(const char *val) { append_chunk(val); return newl(); } /*! simulates POSIX's perror using the internal logging mechanism */ void perror(const char *); void start_formating(const char *); base_stream &end_formating(); void advance_format(); void check_format_parameter(const char *); template void push_format_argument(const T &t) { check_format_parameter(stream_type_format_parameter(t)); stream_push_formated_type(*this, t); advance_format(); } template base_stream & xprintf(const char *format, const T1 &t1) { start_formating(format); push_format_argument(t1); return end_formating(); } template base_stream & xprintf(const char *format, const T1 &t1, const T2 &t2) { start_formating(format); push_format_argument(t1); push_format_argument(t2); return end_formating(); } template base_stream & xprintf(const char *format, const T1 &t1, const T2 &t2, const T3 &t3) { start_formating(format); push_format_argument(t1); push_format_argument(t2); push_format_argument(t3); return end_formating(); } template base_stream & xprintf(const char *format, const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4) { start_formating(format); push_format_argument(t1); push_format_argument(t2); push_format_argument(t3); push_format_argument(t4); return end_formating(); } template base_stream & xprintf(const char *format, const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5) { start_formating(format); push_format_argument(t1); push_format_argument(t2); push_format_argument(t3); push_format_argument(t4); push_format_argument(t5); return end_formating(); } template base_stream & xprintf(const char *format, const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5, const T6 &t6) { start_formating(format); push_format_argument(t1); push_format_argument(t2); push_format_argument(t3); push_format_argument(t4); push_format_argument(t5); push_format_argument(t6); return end_formating(); } /*! increase indenting level */ void inc_level(); /*! decrease indenting level */ void dec_level(); /*! produce n spaces of output */ void spaces(int n); /*! * controls whether a stream flush distributes the buffer * for logging or not */ void set_decision(bool); /*! * flushes the stream buffer. if decision=true, all log_nodes * are notified and prompted to log */ void flush(); protected: void ident_start(); void append_chunk(const char *, int, bool); char *req_buffer(int, bool); stream_flusher *fl; int level; bool dec; char buffer[256]; int ptr; const char *currfmt; }; inline void base_stream::start_formating(const char *fmt) { assert(currfmt == 0); currfmt = fmt; advance_format(); } inline base_stream &base_stream::end_formating() { assert(*currfmt == 0); currfmt = 0; return *this; } inline void base_stream::check_format_parameter(const char *param) { assert(currfmt != 0); assert(strncmp(currfmt + 1, param, strlen(param)) == 0); currfmt += 1 + strlen(param); } class log_base; /*! * log nodes are notified by log_base whenever there is info to be * logged. i.e. after a base_stream::flush */ class log_node : public node { public: log_node(log_base *, const char *name, int infolevel); bool check_startup(); void set_level(const char *); bool set_property(const char *, const char *); static bool parse_infolevel(const char *, int &); /*! method called by log_base with logging info */ virtual void log(int, int, const char *, bool newline) = 0; protected: bool will_log(int, int) const; property_def *infolevel; friend class log_base; }; /*! syslog based log_node */ class syslog_log_node : public log_node { public: syslog_log_node(log_base *, const char *, int); ~syslog_log_node(); bool check_startup(); void log(int, int, const char *, bool newline); }; class tb_log_node : public log_node { public: tb_log_node(log_base *, const char *name, int infolevel); protected: const char *timestamp(char *buffer, size_t length) const; }; /*! file based log_node (also supports stderr) */ class file_log_node : public tb_log_node { public: file_log_node(log_base *, const char *, int, const char *, bool flush); file_log_node(log_base *, const char *, int, FILE *); ~file_log_node(); bool check_startup(); void log(int, int, const char *, bool newline); void event(int, void *); FILE *_fp; std::string _base_filename; property_def *_flush; }; enum { FATAL = 0, WARNING = 1 << 0, NORMAL = 1 << 1, VERBOSE = 1 << 2, DEBUG = 1 << 3, EXTRADEBUG = 1 << 4, MESSAGE_ERR = 1 << 5, MESSAGE_SIG = 1 << 6, MESSAGE_CONTENT = 1 << 7, INTERNAL_FLOW = 1 << 8, EVERYTHING = 0xffffff }; /*! * provides the architecture's logging interface */ class log_base : public node, public stream_flusher { public: log_base(node *); ~log_base(); const char *description() const; bool check_startup(); static log_base &instance(); bool change_context(int level); base_stream ¤t_context(); void force_stderr(); /*! returns true if would log at the specified level */ bool would_log(int level) const; /*! attaches a new logging node to the architecture */ bool attach_node(log_node *); void dettach_node(log_node *); bool call_method(int, base_stream &, const std::vector &); bool negate_method(int, base_stream &, const std::vector &); enum { ReloadEvent = 'R', }; void reload_logs(); protected: void flushed(const char *, bool); void remove_child_node(node *); bool attach_node(const std::vector &); int _current, _level; bool _force_stderr; base_stream _base; }; #endif mrd6-0.9.6/include/mrd/support/0000755000175000017500000000000010746353706015020 5ustar hugohugomrd6-0.9.6/include/mrd/support/uint_n.h0000644000175000017500000000370710600720057016456 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * uint_n.h * * Copyright (C) 2007 Hugo Santos * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef __uint_n__h #define __uint_n__h #include namespace priv { /* these are used just to use c++ type-matching * capabilities through overriding */ static inline uint16_t __host_to_net(uint16_t v) { return htons(v); } static inline uint32_t __host_to_net(uint32_t v) { return htonl(v); } static inline uint16_t __net_to_host(uint16_t v) { return ntohs(v); } static inline uint32_t __net_to_host(uint32_t v) { return ntohl(v); } template struct uint_n { _Base __value; uint_n() : __value(0) {} _Base host() const { return __net_to_host(__value); } static uint_n<_Base> net(_Base v) { uint_n<_Base> u; u.__value = __host_to_net(v); return u; } } __attribute__ ((packed)); } typedef priv::uint_n uint16n_t; typedef priv::uint_n uint32n_t; static inline uint16n_t hton(uint16_t value) { return uint16n_t::net(value); } static inline uint32n_t hton(uint32_t value) { return uint32n_t::net(value); } static inline uint16_t ntoh(uint16n_t value) { return value.host(); } static inline uint32_t ntoh(uint32n_t value) { return value.host(); } #endif mrd6-0.9.6/include/mrd/support/objpool.h0000644000175000017500000000753710550053610016631 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * objpool.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _objpool_h_ #define _objpool_h_ #include #include #include /* for placement new */ #ifndef SUPPORT_NO_POOLING class base_objpool { public: base_objpool(uint32_t _count, uint32_t _single); base_objpool(const base_objpool &pool); void *generic_request_obj(); protected: struct _memchunk; struct _objhead { /* 2 * sizeof(void *) per object */ _memchunk *parent; _objhead *next; uint8_t _obj[0]; } __attribute((packed)); struct _memchunk { uint8_t *chunk, *endchunk; uint32_t count, free; _memchunk *prev, *next; _objhead *head; } __attribute((packed)); void base_return_obj(void *obj, _memchunk * &m); _memchunk *_alloc_chunk(uint32_t count); void _free_chunk(_memchunk *m); _memchunk *_find_chunk(_objhead *h); void _clear_memchunks(_memchunk *); void _clear_memchunks(); uint32_t granularity, single; _memchunk *light, *heavy; }; template class objpool : public base_objpool { public: objpool(uint32_t _count) : base_objpool(_count, sizeof(objtype)) {} objpool(const objpool &pool) : base_objpool(pool) {} ~objpool() { clear(); } void clear() { clear(heavy); clear(light); _clear_memchunks(); } objtype *request_obj() { void *p = generic_request_obj(); if (!p) return 0; /* XXX handle exceptions in constructor */ return new (p) objtype(); } /* One of each helper method below for the number of arguments */ template objtype *request_obj(const Arg &arg) { void *p = generic_request_obj(); if (!p) return 0; /* XXX handle exceptions in constructor */ return new (p) objtype(arg); } template objtype *request_obj(const Arg1 &arg1, const Arg2 &arg2) { void *p = generic_request_obj(); if (!p) return 0; /* XXX handle exceptions in constructor */ return new (p) objtype(arg1, arg2); } void return_obj(objtype *obj) { _memchunk *m; base_return_obj(obj, m); obj->~objtype(); if (m->free == m->count) { if (!m->prev) light = m->next; else m->prev->next = m->next; _free_chunk(m); } } private: void clear(_memchunk *head) { uint32_t one_size = sizeof(_objhead) + sizeof(objtype); for (; head; head = head->next) { for (uint8_t *p = head->chunk; p < head->endchunk; p += one_size) { _objhead *h = (_objhead *)p; if (h->next == 0) ((objtype *)&h->_obj)->~objtype(); } } } }; #else template class objpool { public: objpool(uint32_t _count) { } objpool(const objpool &pool) { } ~objpool() { } void clear() { /* empty */ } objtype *request_obj() { return new objtype(); } template objtype *request_obj(const Arg &arg) { return new objtype(arg); } template objtype *request_obj(const Arg1 &arg1, const Arg2 &arg2) { return new objtype(arg1, arg2); } void return_obj(objtype *obj) { delete obj; } }; #endif #endif mrd6-0.9.6/include/mrd/support/ptree.h0000644000175000017500000002111110600171017016262 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * ptree.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ /* * Patricia tree implementation, compressed binary trie * * based on * * PATRICIA - Pratical Algorithm to Retrieve Information Coded * in Alphanumeric - Donald R. Morrison 1968 * */ #ifndef _Ptree_h_ #define _Ptree_h_ #include #include #include /* Patricia nodes MUST derive from ptree_node */ struct ptree_node { ptree_node *_t_parent, *_t_left, *_t_right; /* Black nodes are data-nodes supplied by the user, * while white nodes work as glue/decision nodes */ enum { WHITE = 0, BLACK = 1 }; uint32_t _t_color : 1, _t_bit : 31; }; /* Simple new/delete based allocator */ template struct _simple_allocator { blocktype *request_obj() { return new blocktype; } void return_obj(blocktype *b) { delete b; } }; typedef _simple_allocator ptree_node_allocator; class base_ptree { public: base_ptree(); virtual ~base_ptree(); bool empty() const; uint32_t size() const; bool remove(ptree_node *node); void clear(); void dump_internal_tree(base_stream &os) const; void dump_internal_tree_graphviz(base_stream &os) const; protected: ptree_node *get_parent_node(const ptree_node *n) const; ptree_node *_get_first_black() const; ptree_node *_a_child_black_node(ptree_node *node) const; void _fix_parent(ptree_node *newnode, ptree_node *oldnode); void dump_internal_tree(base_stream &os, const ptree_node *node, const char *desc) const; void dump_internal_tree_graphviz(base_stream &os, const ptree_node *node, const char *color) const; virtual void write_prefix(base_stream &, const ptree_node *) const = 0; ptree_node *_alloc_white(int bit); void _return_white(ptree_node *n); struct iterator { iterator(); iterator(ptree_node *); iterator(const iterator &); ptree_node *increment(); ptree_node *curr, *prev; }; ptree_node_allocator whites; ptree_node *head; uint32_t count; }; /* Patricia implementation */ template class ptree : public base_ptree { public: node_type *search(const key_type &key) const { if (!head) return 0; ptree_node *node = head; uint32_t bitlen = pnode_prefix_length(key); /* descend the binary trie */ while (node->_t_bit < bitlen) { node = pnode_symbol_at(key, node->_t_bit) ? node->_t_right : node->_t_left; if (!node) return 0; } if (node->_t_bit == bitlen && node->_t_color == node_type::BLACK) { if (((node_type *)node)->prefix == key) return (node_type *)node; } return 0; } node_type *longest_match(const key_type &key) const { if (!head) return 0; ptree_node *node = head, *best = 0; uint32_t prevbit = 0, difbit, bitlen = pnode_prefix_length(key); /* descend the binary trie */ while (node && node->_t_bit <= bitlen) { if (node->_t_color == ptree_node::BLACK) { difbit = _first_dif_bit(((node_type *)node)->prefix, key, prevbit, node->_t_bit); if (difbit < node->_t_bit) break; best = node; } prevbit = node->_t_bit; if (pnode_symbol_at(key, node->_t_bit)) node = node->_t_right; else node = node->_t_left; } return (node_type *)best; } node_type *insert(node_type *newnode) { /* prepare node for insertion */ newnode->_t_parent = 0; newnode->_t_left = 0; newnode->_t_right = 0; newnode->_t_color = ptree_node::BLACK; newnode->_t_bit = pnode_prefix_length(newnode->prefix); if (!head) { /* no head? the new node will be our head */ head = newnode; } else { ptree_node *next, *node = head; /* descend the binary trie */ while (node->_t_color == ptree_node::WHITE || node->_t_bit < newnode->_t_bit) { if (pnode_symbol_at(newnode->prefix, node->_t_bit)) next = node->_t_right; else next = node->_t_left; if (!next) break; node = next; } /* check the first different bit between the * current node's key and the one being added */ uint32_t difbit = _first_dif_bit(((node_type *)node)->prefix, newnode->prefix, 0, std::min(node->_t_bit, newnode->_t_bit)); /* if the difference is handled by one of the * node's parent, go up and aggregate */ ptree_node *parent = node->_t_parent; while (parent && parent->_t_bit >= difbit) { node = parent; parent = node->_t_parent; } /* the node's key being added matches exactly with * another one already present in the binary trie */ if (difbit == newnode->_t_bit && node->_t_bit == newnode->_t_bit) { if (node->_t_color == ptree_node::WHITE) { /* if the node was white, replace it with * the new black one */ _fix_parent(newnode, node); _return_white(node); } else { /* else it's a duplicate */ return 0; } return newnode; } /* assert(node->_t_color == ptree_node::BLACK); */ if (node->_t_bit == difbit) { /* the new node will be a leaf */ newnode->_t_parent = node; _pick_set_side(newnode, node); } else if (newnode->_t_bit == difbit) { /* we'll stick the new node in the middle, * between parent and node */ ptree_node *black = _a_child_black_node(node); /* assert(black); */ if (pnode_symbol_at(((node_type *)black)->prefix, difbit)) newnode->_t_right = node; else newnode->_t_left = node; _fix_parent(newnode, node); } else { /* aggregate, we need a new white node */ ptree_node *white = _alloc_white(difbit); if (!white) return 0; if (pnode_symbol_at(newnode->prefix, difbit)) { white->_t_right = newnode; white->_t_left = node; } else { white->_t_left = newnode; white->_t_right = node; } _fix_parent(white, node); newnode->_t_parent = white; } } count ++; return newnode; } node_type *get_parent_node(const node_type *n) const { return (node_type *)base_ptree::get_parent_node(n); } template struct base_iterator : private base_ptree::iterator { typedef std::forward_iterator_tag iterator_category; typedef itertype value_type; typedef ptrdiff_t difference_type; typedef itertype *pointer; typedef itertype &reference; typedef base_iterator this_type; base_iterator() {} base_iterator(itertype *current) : base_ptree::iterator((ptree_node *)current) {} base_iterator(const this_type &i) : base_ptree::iterator(i) {} this_type &operator ++() { increment(); return *this; } this_type operator++(int) { return ++this_type(*this); } pointer operator ->() const { return (itertype *)curr; } reference operator *() const { return *(itertype *)curr; } friend bool operator == (const this_type &i1, const this_type &i2) { return i1.curr == i2.curr; } friend bool operator != (const this_type &i1, const this_type &i2) { return !(i1 == i2); } }; typedef base_iterator iterator; typedef base_iterator const_iterator; iterator begin() { return iterator((node_type *)_get_first_black()); } const_iterator begin() const { return const_iterator((const node_type *)_get_first_black()); } iterator end() { return iterator(NULL); } const_iterator end() const { return const_iterator(NULL); } private: void _pick_set_side(ptree_node *node, ptree_node *parent) { /* assert(node->_t_color == ptree_node::BLACK); */ if (pnode_symbol_at(((node_type *)node)->prefix, parent->_t_bit)) parent->_t_right = node; else parent->_t_left = node; } int _first_dif_bit(const key_type &p1, const key_type &p2, int st, int end) const { while (st < end && pnode_symbol_at(p1, st) == pnode_symbol_at(p2, st)) st++; return st; } void write_prefix(base_stream &os, const ptree_node *n) const { os.write(((const node_type *)n)->prefix); } }; #endif mrd6-0.9.6/include/mrd/support/refcount.h0000644000175000017500000000333610550053610017003 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * refcount.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _support_refcount_h_ #define _support_refcount_h_ class refcountable { public: refcountable(); virtual ~refcountable(); void grab(); void release(); int get_refcount() const; protected: virtual void destructor(); private: int _refcount; }; class auto_grab { public: auto_grab(refcountable *_t) : t(_t) { t->grab(); } ~auto_grab() { if (t) t->release(); } private: refcountable *t; }; inline refcountable::refcountable() : _refcount(0) {} inline refcountable::~refcountable() { /* assert(_refcount == 0); */ } inline void refcountable::grab() { _refcount ++; } inline void refcountable::release() { _refcount --; if (_refcount == 0) destructor(); } inline int refcountable::get_refcount() const { return _refcount; } inline void refcountable::destructor() { delete this; } #endif mrd6-0.9.6/include/mrd/support/lists.h0000644000175000017500000000520510550053610016311 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * lists.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _support_lists_h_ #define _support_lists_h_ struct list_node { struct list_node *next; }; struct dlist_node { struct dlist_node *prev, *next; }; #define container_of(ptr, type, member) \ ((type *)(((char *)ptr) - offsetof(type, member))) template static inline void list_push_front(T * &lst, T *node) { node->next = lst; lst = node; } template static inline T *list_pop_front(T * &lst) { if (!lst) return 0; T *head = lst; lst = lst->next; return head; } template static inline void list_insert_after(T * &lst, T *prev, T *node) { if (prev) { node->next = prev->next; prev->next = node; } else { node->next = lst; lst = node; } } template static inline bool list_search_remove(T * &lst, T *node) { T *prev = 0; for (T *curr = lst; curr; curr = curr->next) { if (curr == node) { /* unlink the node from the list */ if (prev) prev->next = curr->next; else lst = curr->next; return true; } prev = curr; } return false; } template static inline void dlist_push_front(T * &lst, T *node) { node->prev = 0; if (lst) lst->prev = node; node->next = lst; lst = node; } template static inline T *dlist_pop_front(T * &lst) { if (!lst) return 0; T *head = lst; lst = lst->next; if (lst) lst->prev = 0; return head; } template static inline void list_remove(T * &node) { list_pop_front(node); } template static inline void dlist_remove(T * &lst, T *node) { if (!node->prev) { if (node->next) { node->next->prev = 0; lst = node->next; } else { lst = 0; } } else { node->prev->next = node->next; if (node->prev->next) node->prev->next->prev = node->prev; } } #endif mrd6-0.9.6/include/mrd/address.h0000644000175000017500000001075510746067643015114 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * address.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_address_h_ #define _mrd_address_h_ #include #include #include #include #include #include class base_stream; static inline bool operator < (const in6_addr &a1, const in6_addr &a2) { return memcmp(a1.s6_addr, a2.s6_addr, 16) < 0; } static inline bool operator == (const in6_addr &a1, const in6_addr &a2) { return IN6_ARE_ADDR_EQUAL(&a1, &a2); } static inline int pnode_prefix_length(const in6_addr &p) { return sizeof(in6_addr) * 8; } static inline bool pnode_symbol_at(const in6_addr &p, int n) { return p.s6_addr[n / 8] & (0x80 >> (n & 0x07)); } /*! * \class inet6_addr mrd/address.h * \brief provides an in6_addr + prefixlen abstraction and container. */ struct inet6_addr { inet6_addr(); inet6_addr(const in6_addr &); inet6_addr(const in6_addr &, uint8_t prefixlen); inet6_addr(const inet6_addr &); explicit inet6_addr(const std::string &); explicit inet6_addr(const std::vector &); static inet6_addr any() { return inet6_addr(); } bool is_any() const { return IN6_IS_ADDR_UNSPECIFIED(&addr); } bool is_linklocal() const { return IN6_IS_ADDR_LINKLOCAL(&addr); } enum { multicast = 2, network = 4 }; unsigned type() const; inet6_addr prefix() const; bool operator < (const inet6_addr &) const; bool operator > (const inet6_addr &) const; bool operator == (const inet6_addr &) const; bool operator == (const in6_addr &rho) const { return IN6_ARE_ADDR_EQUAL(&addr, &rho); } inet6_addr &operator = (const inet6_addr &base) { set(base.address(), base.prefixlen); return *this; } inet6_addr &operator = (const in6_addr &base) { return (*this) = inet6_addr(base); } bool partial_match(const in6_addr &ma, uint8_t malen) const { uint8_t plen = prefixlen; const uint32_t *ap = (const uint32_t *)&addr; const uint32_t *bp = (const uint32_t *)&ma; while (plen >= 32) { if (*ap != *bp) return false; ap ++; bp ++; plen -= 32; } if (plen > 0) { uint32_t mask = 0xffffffff << (32 - plen); if ((ntohl(*ap) & mask) != (ntohl(*bp) & mask)) return false; } return true; } bool matches(const in6_addr &ma, uint8_t malen = 128) const { if (prefixlen == 0) { return true; } else if (prefixlen == 128) { return IN6_ARE_ADDR_EQUAL(&addr, &ma); } else if (malen < prefixlen) { return false; } return partial_match(ma, malen); } bool matches(const inet6_addr &address) const { return matches(address.address(), address.prefixlen); } const in6_addr &address() const { return addr; } const in6_addr *address_p() const { return &addr; } std::string as_string() const; sockaddr_in6 as_sockaddr() const; char *print_string(char *, int) const; operator std::string () const { return as_string(); } operator in6_addr () const { return addr; } void set(const in6_addr &, uint8_t); bool set(const std::string &); static bool from_string(const std::string &, inet6_addr &); static void to_string(const inet6_addr &, std::string &); void apply_prefixlen(); /* ptree-key implementing methods */ friend int pnode_prefix_length(const inet6_addr &p) { return p.prefixlen; } friend bool pnode_symbol_at(const inet6_addr &p, int n) { return pnode_symbol_at(p.addr, n); } in6_addr addr; uint8_t prefixlen; }; static inline const char *stream_type_format_parameter(const in6_addr &) { return "{addr}"; } static inline const char *stream_type_format_parameter(const inet6_addr &) { return "{Addr}"; } void stream_push_formated_type(base_stream &os, const in6_addr &); void stream_push_formated_type(base_stream &os, const inet6_addr &); #endif mrd6-0.9.6/include/mrd/mfa.h0000644000175000017500000000546110550053610014206 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mfa.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_mfa_h_ #define _mrd_mfa_h_ #include #include #include class router; class interface; struct ip6_hdr; class mfa_group_source { public: mfa_group_source(); virtual ~mfa_group_source(); enum { any_incoming = 0, wrong_iif, event_count }; enum { f_any_incoming = 1, f_wrong_iif = 2, }; enum action { no_action = 0, notify_no_copy, copy_metadata, copy_full_packet }; virtual void change_flags(uint32_t, action) = 0; virtual void set_iif(interface *) = 0; virtual void release_iif(interface *) = 0; virtual void add_oif(interface *) = 0; virtual void release_oif(interface *) = 0; virtual void get_input_counter(uint64_t &bytes) const = 0; virtual void get_forwarding_counter(uint64_t &bytes) const = 0; void *instowner; }; class mfa_group { public: mfa_group(router *owner); virtual ~mfa_group() {} router *owner() const { return m_owner; } virtual void activate(bool) = 0; virtual mfa_group_source *create_source_state(const in6_addr &, void * = 0) = 0; virtual void release_source_state(mfa_group_source *) = 0; virtual void change_default_flags(uint32_t, mfa_group_source::action) = 0; void *instowner; private: router *m_owner; }; /*! * \brief implements the core interface with the current * MFA (Multicast forwarding agent). */ class mfa_core : public node { public: virtual ~mfa_core() {} virtual bool pre_startup(); virtual bool check_startup() = 0; virtual void shutdown() = 0; virtual void added_interface(interface *) {} virtual void removed_interface(interface *) {} virtual mfa_group *create_group(router *, const inet6_addr &, void * = 0) = 0; virtual void release_group(mfa_group *) = 0; virtual void change_group_default_flags(uint32_t, mfa_group_source::action) {} virtual void forward(interface *, ip6_hdr *, uint16_t) const = 0; static mfa_core *mfa(); protected: mfa_core(); }; #endif mrd6-0.9.6/rpm/0000755000175000017500000000000010746353706011675 5ustar hugohugomrd6-0.9.6/rpm/mrd6.spec0000644000175000017500000000246210550050250013402 0ustar hugohugoSummary: Multicast Routing Daemon for IPv6 Name: mrd6 Version: 0.9.5 Release: 1 URL: http://fivebits.net/mrd6/ License: GPL Source0: http://fivebits.net/mrd6/download/mrd6-0.9.5.tar.gz Group: Networking Packager: Hugo Santos BuildRoot: %{_builddir}/%{name}-%{version}-root %description MRD6 is a modular IPv6 Multicast Routing Daemon which implements: * MLDv1 and MLDv2 with forwarding capabilities - MLD proxying * PIM-SM (ASM and SSM) - Bootstrap (BSR) Mechanism support - Static RP configuration - Embedded-RP support * partial MBGP support - Uses IPv6 Multicast SAFI prefixes announced by peers to update local MRIB - Is able to announce local prefixes - Filter support * Native and virtual (tunnel) interfaces support * CLI support (remote configuration and management) via telnet or local access %prep %setup -q -n mrd6-0.9.5 %build make %install [ %{buildroot} != "/" ] && rm -rf %{buildroot} PREFIX=%{_prefix} DESTDIR=%{buildroot} make install %clean [ %{buildroot} != "/" ] && rm -rf %{buildroot} %files %defattr(-, root, root) %doc README MRD6shQuickRef.txt src/confs/mrd.conf %{_prefix}/sbin/mrd6 %{_prefix}/lib/mrd6/* %{_prefix}/bin/mrd6sh %changelog * Sun Jan 8 2006 Hugo Santos 0.9.5 - 0.9.5 mrd6-0.9.6/src/0000755000175000017500000000000010746353706011666 5ustar hugohugomrd6-0.9.6/src/scripts/0000755000175000017500000000000010746353706013355 5ustar hugohugomrd6-0.9.6/src/scripts/generate-modules-cpp.pl0000755000175000017500000000067210600365723017731 0ustar hugohugo#!/usr/bin/perl -w use strict; my $i; my $m; print "/* This file was automatically generated */\n"; print "#include \n"; for ($i = 0; $i <= $#ARGV; $i++) { $m = lc $ARGV[$i]; print "extern \"C\" mrd_module *mrd_module_init_$m(void *, mrd *);\n"; } print "void mrd::add_static_modules() {\n"; for ($i = 0; $i <= $#ARGV; $i++) { $m = lc $ARGV[$i]; print "\tm_static_modules[\"$m\"] = &mrd_module_init_$m;\n"; } print "}\n"; mrd6-0.9.6/src/linux/0000755000175000017500000000000010746353706013025 5ustar hugohugomrd6-0.9.6/src/linux/linux_icmp_raw.cpp0000644000175000017500000001222110550053317016533 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * linux/linux_icmp_raw.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct ip6_rta { uint8_t type; uint8_t length; uint16_t value; } __attribute__ ((packed)); /* Not all systems include the IPv6 definitions */ struct _ip6_ext { uint8_t ip6e_nxt; uint8_t ip6e_len; }; linux_icmp_raw::linux_icmp_raw() : m_rawicmpsock("icmpv6 (raw)", this, std::mem_fun(&linux_icmp_raw::data_available)) { } static uint8_t ibuffer[8192]; bool linux_icmp_raw::check_startup() { if (!icmp_inet6::check_startup()) return false; /* we don't need the INET6 sock to receive */ ::shutdown(m_icmpsock.fd(), SHUT_RD); m_icmpsock.unregister(false); /* Linux bridges consume the packets before they reach the * protocol handlers leaving us without signaling */ bool bridges = g_mrd->get_property_bool("handle-proper-bridge"); int sock = socket(PF_PACKET, SOCK_DGRAM, htons(bridges ? ETH_P_ALL : ETH_P_IPV6)); if (sock < 0) return false; m_rawicmpsock.register_fd(sock); return true; } void linux_icmp_raw::shutdown() { m_rawicmpsock.unregister(); icmp_inet6::shutdown(); } void linux_icmp_raw::data_available(uint32_t) { sockaddr_ll sa; socklen_t salen = sizeof(sa); int recvlen = recvfrom(m_rawicmpsock.fd(), ibuffer, sizeof(ibuffer), 0, (sockaddr *)&sa, &salen); if (recvlen < 0 || sa.sll_protocol != htons(ETH_P_IPV6)) return; if (sa.sll_pkttype == PACKET_OUTGOING) return; ip6_hdr *hdr = (ip6_hdr *)ibuffer; uint8_t nxt = hdr->ip6_nxt; _ip6_ext *ext = (_ip6_ext *)(ibuffer + sizeof(ip6_hdr)); int pointer = sizeof(ip6_hdr); bool has_mld_rta = false; while (pointer < recvlen && nxt != IPPROTO_ICMPV6) { if (nxt == IPPROTO_HOPOPTS) { uint8_t *ptr = ibuffer + pointer + 2; uint8_t *endptr = ibuffer + pointer + (ext->ip6e_len + 1) * 8; while (ptr < endptr) { ip6_rta *rta = (ip6_rta *)ptr; if (rta->type == 5) { if (rta->length == 2 && rta->value == 0) has_mld_rta = true; } ptr = ptr + rta->length + 2; } } nxt = ext->ip6e_nxt; pointer += (ext->ip6e_len + 1) * 8; ext = (_ip6_ext *)(ibuffer + pointer); } if (nxt != IPPROTO_ICMPV6 || !has_mld_rta) return; if (g_mrd->should_log(MESSAGE_SIG)) { g_mrd->log().xprintf("[ICMPv6] Message from %{addr} to " "%{addr} dev %i\n", hdr->ip6_src, hdr->ip6_dst, (int)sa.sll_ifindex); } icmp6_hdr *icmphdr = (icmp6_hdr *)ext; uint16_t chksum = icmphdr->icmp6_cksum; icmphdr->icmp6_cksum = 0; if (ipv6_checksum(IPPROTO_ICMPV6, hdr->ip6_src, hdr->ip6_dst, icmphdr, recvlen - pointer) != chksum) { if (g_mrd->should_log(MESSAGE_ERR)) { g_mrd->log().xprintf("[ICMPv6] Bad checksum on " "ICMPv6 message from %{addr}, dropping.\n", hdr->ip6_src); } } else { interface *intf = g_mrd->get_interface_by_index(sa.sll_ifindex); if (!intf) return; icmp_message_available(intf, hdr->ip6_src, hdr->ip6_dst, icmphdr, recvlen - pointer); } } void linux_icmp_raw::added_interface(interface *intf) { packet_mreq mreq; memset(&mreq, 0, sizeof(mreq)); mreq.mr_ifindex = intf->index(); mreq.mr_type = PACKET_MR_ALLMULTI; if (setsockopt(m_rawicmpsock.fd(), SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { if (g_mrd->should_log(VERBOSE)) { g_mrd->log().xprintf("[ICMPv6] Will not be able to" " listen to ICMPv6 messages in %s (%i)," " reported error was %s.\n", intf->name(), intf->index(), strerror(errno)); } } } void linux_icmp_raw::removed_interface(interface *intf) { packet_mreq mreq; memset(&mreq, 0, sizeof(mreq)); mreq.mr_ifindex = intf->index(); mreq.mr_type = PACKET_MR_ALLMULTI; setsockopt(m_rawicmpsock.fd(), SOL_PACKET, PACKET_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); } void linux_icmp_raw::registration_changed() { } void linux_icmp_raw::internal_require_mgroup(const in6_addr &, bool) { /* XXX only join specific L2 mcast groups */ } mrd6-0.9.6/src/linux/Module.options0000644000175000017500000000024510345320120015643 0ustar hugohugoboolean MMAP default on description "Enable Packet Socket MMAP support" boolean ICMPRAW default on description "Use PF_PACKET ICMP backend on Linux. Supports MLDv1" mrd6-0.9.6/src/linux/mrd_components.cpp0000644000175000017500000000467010550053317016553 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mrd_components.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #if defined(__GLIBC__) && !defined(__UCLIBC__) #include #endif bool mrd::prepare_os_components() { m_mfa = new us_mfa(); if (!instantiate_property_b("handle-proper-bridge", false)) return false; #ifndef LINUX_NO_ICMPRAW m_icmp = new linux_icmp_raw(); #endif return true; } void mrd::prepare_second_components() { if (!m_rib_handler) m_rib_handler = new linux_unicast_router(); } const char *mrd::loopback_interface_name() const { return "lo"; } #define MAX_DEEP_BACKTRACE 32 void mrd::output_backtrace(base_stream &out) const { #if defined(__GLIBC__) && !defined(__UCLIBC__) void *bt[MAX_DEEP_BACKTRACE]; int count = backtrace(bt, MAX_DEEP_BACKTRACE); char **btnames = backtrace_symbols(bt, count); for (int i = 0; i < count; i++) { out.xprintf("#%i %s\n", i+1, btnames[i]); } free(btnames); #else out.writeline("Backtraces aren't available in this system."); #endif } char *mrd::obtain_frame_description(void *ptr) const { #if defined(__GLIBC__) && !defined(__UCLIBC__) void *p[1] = { ptr }; char **names = backtrace_symbols(p, 1); char *ret = strdup(names[0]); free(names); return ret; #else return 0; #endif } void *mrd::posix_uctx::get_current_frame() const { #if defined(__GLIBC__) #if defined(__i386__) return (void *)base->uc_mcontext.gregs[REG_EIP]; #elif defined(__mips__) return (void *)base->uc_mcontext.gpregs[CTX_EPC]; #endif #endif return 0; } mrd6-0.9.6/src/linux/us_mfa.cpp0000644000175000017500000004602410550053317014775 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * us_mfa.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include us_mfa_group_source::us_mfa_group_source(us_mfa_group *grp, const in6_addr &addr, uint32_t flags, action *acts) : m_owner(grp), m_addr(addr) { instowner = 0; m_flags = flags; m_interest_flags = 0; for (int i = 0; i < event_count; i++) change_flags(1 << i, acts[i]); memset(m_stats, 0, sizeof(m_stats)); m_fw_bag = 0; m_fw_pkt_bag = 0; m_iif = 0; stat_octet_count60s = 0; stat_packet_count60s = 0; } void us_mfa_group_source::set_iif(interface *iif) { m_iif = iif; } void us_mfa_group_source::release_iif(interface *iif) { if (m_iif == iif) m_iif = 0; } void us_mfa_group_source::add_oif(interface *oif) { if (!has_oif(oif)) { m_oifs.push_back(oif); } } void us_mfa_group_source::release_oif(interface *oif) { for (oifs::iterator k = m_oifs.begin(); k != m_oifs.end(); ++k) { if (*k == oif) { m_oifs.erase(k); return; } } } void us_mfa_group_source::change_flags(uint32_t flags, action act) { if (act == no_action) { m_interest_flags &= ~flags; } else { m_interest_flags |= flags; } } void us_mfa_group_source::update_stats() { stat_octet_count60s = (uint64_t)floor(stat_octet_count60s * (29/30.)) + m_fw_bag; stat_packet_count60s = (uint64_t)floor(stat_packet_count60s * (29/30.)) + m_fw_pkt_bag; m_fw_bag = 0; m_fw_pkt_bag = 0; } void us_mfa_group_source::route(int iif, ip6_hdr *hdr, uint16_t len) { uint32_t f = f_any_incoming; if (is_iif(iif)) { m_stats[stat_input] += len; int count = 0; for (oifs::const_iterator i = m_oifs.begin(); i != m_oifs.end(); ++i) { if ((*i)->index() == iif) continue; ((us_mfa *)g_mrd->mfa())->forward(*i, hdr, len); count ++; } if (count) { m_stats[stat_forwarded] ++; m_stats[stat_forwarded_size] += len; m_fw_bag += len; m_fw_pkt_bag ++; m_owner->m_fw_bag += len; m_owner->m_fw_pkt_bag ++; } } else { f |= f_wrong_iif; m_stats[stat_wrong_iif]++; } if (f & m_interest_flags) { m_owner->owner()->mfa_notify(this, hdr->ip6_dst, hdr->ip6_src, f & m_interest_flags, copy_full_packet, g_mrd->get_interface_by_index(iif), hdr, len, len); } } void us_mfa_group_source::clear_interface_references(const inet6_addr &grpid, interface *intf) { if (is_iif(intf->index())) { if (mfa_core::mfa()->should_log(EXTRADEBUG)) { mfa_core::mfa()->log().xprintf( "(%{addr}, %{Addr}) releasing Iif %s as it was" "removed.\n", m_addr, grpid, intf->name()); } release_iif(intf); } if (has_oif(intf)) { if (mfa_core::mfa()->should_log(EXTRADEBUG)) { mfa_core::mfa()->log().xprintf( "(%{addr}, %{Addr}) releasing Oif %s as it was" "removed.\n", m_addr, grpid, intf->name()); } release_oif(intf); } } static void output(base_stream &out, const std::vector &ifs) { out.write("{ "); for (std::vector::const_iterator i = ifs.begin(); i != ifs.end(); ++i) { if (i != ifs.begin()) out.write(", "); out.write((*i)->name()); } out.write(" }"); } void us_mfa_group_source::output_info(base_stream &out, bool counters, bool noempty) const { if (counters) { if (noempty && !stat_packet_count60s) return; out.xprintf("%{addr}", m_addr); out.inc_level(); if (stat_packet_count60s) { out.write("Activity statistics: "); const char *format = "%.2f %s"; double rate = 8 * stat_octet_count60s / 60000.; const char *unit = "Kb/s"; if (rate > 1000) { rate /= 1000.; unit = "Mb/s"; } out.printf(format, rate, unit); out.printf(" (%.2f pkt/s)", stat_packet_count60s / 60.f); out.newl(); out.printf("Last 60 secs: %llu bytes (%llu packets, %.2lf bytes/packet)", stat_octet_count60s, stat_packet_count60s, stat_octet_count60s / (double)stat_packet_count60s); out.newl(); } else { out.writeline("No activity in the last 60 seconds"); } out.dec_level(); } else { out.xprintf("%{addr} from %s to ", m_addr, m_iif ? m_iif->name() : "(None)"); output(out, m_oifs); out.newl(); } } void us_mfa_group_source::get_input_counter(uint64_t &bytes) const { bytes = m_stats[stat_input]; } void us_mfa_group_source::get_forwarding_counter(uint64_t &bytes) const { bytes = m_fw_bag; } us_mfa_group::us_mfa_group(router *owner, const inet6_addr &id) : mfa_group(owner) { instowner = 0; us_mfa *m = (us_mfa *)g_mrd->mfa(); m_flags = m->m_grpflags; for (int i = 0; i < mfa_group_source::event_count; i++) m_actions[i] = m->m_grpactions[i]; m_state = pending; invalidate_source_cache(); m_useful_cache = false; m_fw_bag = 0; m_fw_pkt_bag = 0; stat_octet_count60s = 0; stat_packet_count60s = 0; } void us_mfa_group::activate(bool accept) { if (accept && m_state == running) return; if (!accept) { m_state = denied; } else { m_state = running; } if (!accept) { ((us_mfa *)g_mrd->mfa())->release_group(this); } } void us_mfa_group::route(int iif, ip6_hdr *hdr, uint16_t len) { if (m_state == denied) return; else if (m_state == pending) { return; } us_mfa_group_source *src = match_source(hdr->ip6_src); if (src) { src->route(iif, hdr, len); } else { ((us_mfa *)g_mrd->mfa())->discovered_source(iif, hdr->ip6_dst, hdr->ip6_src); } } void us_mfa_group::clear_interface_references(const inet6_addr &grpid, interface *intf) { for (sources::iterator i = m_sources.begin(); i != m_sources.end(); ++i) { i->second->clear_interface_references(grpid, intf); } } void us_mfa_group::invalidate_source_cache() { memset(m_source_cache, 0, sizeof(m_source_cache)); } mfa_group_source *us_mfa_group::create_source_state(const in6_addr &addr, void *instowner) { mfa_group_source *src = get_source_state(addr); if (!src) { src = new us_mfa_group_source(this, addr, m_flags, m_actions); if (src) { if (mfa_core::mfa()->should_log(EXTRADEBUG)) mfa_core::mfa()->log().xprintf("Created source state for %{addr}.\n", addr); m_sources[addr] = (us_mfa_group_source *)src; m_useful_cache = m_sources.size() < _SOURCE_CACHE_LEN; } } if (src) src->instowner = instowner; return src; } mfa_group_source *us_mfa_group::get_source_state(const in6_addr &addr) const { return match_source(addr); } void us_mfa_group::update_stats() { for (sources::iterator i = m_sources.begin(); i != m_sources.end(); ++i) { i->second->update_stats(); } stat_octet_count60s = (uint64_t)ceil(stat_octet_count60s * (29/30.)) + m_fw_bag; stat_packet_count60s = (uint64_t)ceil(stat_packet_count60s * (29/30.)) + m_fw_pkt_bag; m_fw_bag = 0; m_fw_pkt_bag = 0; } void us_mfa_group::release_source_state(mfa_group_source *_src) { us_mfa_group_source *src = (us_mfa_group_source *)_src; for (sources::iterator i = m_sources.begin(); i != m_sources.end(); ++i) { if (src == i->second) { delete src; m_sources.erase(i); invalidate_source_cache(); m_useful_cache = m_sources.size() < _SOURCE_CACHE_LEN; return; } } } void us_mfa_group::change_default_flags(uint32_t flags, mfa_group_source::action act) { for (int i = mfa_group_source::any_incoming; i < mfa_group_source::event_count; i++) { if (flags & (1 << i)) m_actions[i] = act; } } void us_mfa_group::output_info(base_stream &out, bool counters, bool noempty) const { if (counters) { out.writeline("Aggregate activity statistics:"); out.inc_level(); if (stat_packet_count60s) { const char *format = "%.2f %s"; double rate = 8 * stat_octet_count60s / 60000.; const char *unit = "Kb/s"; if (rate > 1000) { rate /= 1000.; unit = "Mb/s"; } out.write("Current rate: "); out.printf(format, rate, unit); out.printf(" (%.2f pkt/s)", stat_packet_count60s / 60.f); out.newl(); out.printf("Last 60 secs: %llu bytes (%llu packets, %.2lf bytes/packet)", stat_octet_count60s, stat_packet_count60s, stat_octet_count60s / (double)stat_packet_count60s); out.newl(); } else { out.writeline("No available statistics"); } out.dec_level(); } /* no active sources */ if (counters && noempty && !stat_packet_count60s) return; out.writeline("Sources:"); out.inc_level(); if (m_sources.empty()) { out.writeline("(None)"); } else { for (sources::const_iterator i = m_sources.begin(); i != m_sources.end(); ++i) { i->second->output_info(out, counters, noempty); } } out.dec_level(); } void us_mfa::change_group_default_flags(uint32_t flags, mfa_group_source::action act) { for (int i = mfa_group_source::any_incoming; i < mfa_group_source::event_count; i++) { if (flags & (1 << i)) m_grpactions[i] = act; } } mfa_group *us_mfa::create_group(router *r, const inet6_addr &id, void *instowner) { mfa_group *grp = get_group(id); if (!grp) { grp = new us_mfa_group(r, id); if (grp) { if (mfa_core::mfa()->should_log(EXTRADEBUG)) mfa_core::mfa()->log().xprintf("Create state for group %{Addr}.\n", id); m_groups[id] = (us_mfa_group *)grp; m_singles.clear(); invalidate_group_cache(id); } } if (grp) grp->instowner = instowner; return grp; } mfa_group *us_mfa::get_group(const inet6_addr &id) const { groups::const_iterator k = m_groups.find(id); if (k == m_groups.end()) return 0; return k->second; } void us_mfa::release_group(mfa_group *grp) { for (groups::iterator i = m_groups.begin(); i != m_groups.end(); ++i) { if (grp == i->second) { delete i->second; m_groups.erase(i); m_singles.clear(); invalidate_group_cache(); return; } } } void us_mfa::invalidate_group_cache() { memset(m_grp_cache, 0, sizeof(m_grp_cache)); m_singles.clear(); } void us_mfa::invalidate_group_cache(const in6_addr &addr) { m_grp_cache[_GROUP_CACHE_HASH(addr)].entry = 0; singles::iterator i = m_singles.find(addr); if (i != m_singles.end()) m_singles.erase(i); } us_mfa::us_mfa() : m_rawsock("us-mfa sock", this, std::mem_fun(&us_mfa::data_available)), m_stat_timer("mfa stat update timer", this, std::mem_fun(&us_mfa::update_stats), 2000, true) { #ifndef LINUX_NO_MMAP m_mmaped = 0; m_framesize = 2048; m_mmapedlen = 1024 * 1024; #endif m_grpflags = 0; for (int i = 0; i < mfa_group_source::event_count; i++) m_grpactions[i] = mfa_group_source::no_action; invalidate_group_cache(); } bool us_mfa::pre_startup() { if (!mfa_core::pre_startup()) return false; if (!m_sourcedisc.check_startup()) return false; return g_mrd->register_source_discovery("data-plane", &m_sourcedisc); } bool us_mfa::check_startup() { bool bridges = g_mrd->get_property_bool("handle-proper-bridge"); int sock = socket(PF_PACKET, SOCK_DGRAM, htons(bridges ? ETH_P_ALL : ETH_P_IPV6)); if (sock < 0) { should_log(FATAL); if (errno == EPERM) { log().writeline("Administrative privileges are required to run mrd."); } else { log().perror("Failed to create packet socket"); } return false; } #ifndef LINUX_NO_MMAP if (g_mrd->has_property("mfa-framesize")) { m_framesize = atoi(g_mrd->get_property_string("mfa-framesize")); if (m_framesize < 2048) m_framesize = 2048; } uint32_t block_max_size = 256 * 1024; if (g_mrd->has_property("mfa-mmap-size")) { m_mmapedlen = atoi(g_mrd->get_property_string("mfa-mmap-size")); m_mmapedlen &= ~(0x1000-1); if (m_mmapedlen < block_max_size) m_mmapedlen = block_max_size; } tpacket_req req; req.tp_frame_size = m_framesize; req.tp_block_size = block_max_size; req.tp_block_nr = m_mmapedlen / block_max_size; req.tp_frame_nr = m_mmapedlen / req.tp_frame_size; m_mmapedlen = req.tp_block_nr * block_max_size; if (setsockopt(sock, SOL_PACKET, PACKET_RX_RING, (void *)&req, sizeof(req)) == 0) { if ((m_mmaped = mmap(0, m_mmapedlen, PROT_READ|PROT_WRITE, MAP_SHARED, sock, 0)) == MAP_FAILED) { m_mmaped = 0; } else { m_mmapbuf = (uint8_t *)m_mmaped; } } if (!m_mmaped && should_log(WARNING)) { log().perror("Failed to memory map packet socket, continuing with socket interface"); } #endif if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { should_log(FATAL); log().writeline("Failed to change working socket to non-blocking mode."); return false; } int val = 256 * 1024; setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)); m_rawsock.register_fd(sock); m_stat_timer.start(); return true; } void us_mfa::shutdown() { #ifndef LINUX_NO_MMAP if (m_mmaped) { munmap(m_mmaped, m_mmapedlen); tpacket_req req; memset(&req, 0, sizeof(req)); setsockopt(m_rawsock.fd(), SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)); } #endif m_rawsock.unregister(); g_mrd->register_source_discovery("data-plane", 0); } void us_mfa::discovered_source(int ifindex, const inet6_addr &grp, const inet6_addr &src) { m_sourcedisc.discovered_source(ifindex, grp, src); } void us_mfa::update_stats() { for (groups::const_iterator k = m_groups.begin(); k != m_groups.end(); ++k) { k->second->update_stats(); } } #define MAX_EAT_ONE_CYCLE 100 void us_mfa::data_available(uint32_t) { #ifndef LINUX_NO_MMAP if (m_mmaped) { int i = 0; while (i < MAX_EAT_ONE_CYCLE && *(unsigned long *)m_mmapbuf) { tpacket_hdr *hdr = (tpacket_hdr *)m_mmapbuf; sockaddr_ll *sa = (sockaddr_ll *)(((uint8_t *)hdr) + TPACKET_ALIGN(sizeof(*hdr))); uint8_t *bp = ((uint8_t *)hdr) + hdr->tp_mac; if (sa->sll_protocol == htons(ETH_P_IPV6) && sa->sll_pkttype != PACKET_OUTGOING) handle_ipv6(sa->sll_ifindex, bp, hdr->tp_len); hdr->tp_status = 0; m_mmapbuf += m_framesize; if (m_mmapbuf >= (((uint8_t *)m_mmaped) + m_mmapedlen)) m_mmapbuf = (uint8_t *)m_mmaped; i++; } } else { #endif sockaddr_ll sa; socklen_t salen = sizeof(sa); int len; while ((len = g_mrd->ipktb->recvfrom(m_rawsock.fd(), (sockaddr *)&sa, &salen)) > 0) { if (sa.sll_protocol == htons(ETH_P_IPV6) && sa.sll_pkttype != PACKET_OUTGOING) handle_ipv6(sa.sll_ifindex, g_mrd->ipktb->buffer(), len); } #ifndef LINUX_NO_MMAP } #endif } void us_mfa::handle_ipv6(int dev, uint8_t *buf, uint16_t len) { ip6_hdr *hdr = (ip6_hdr *)buf; if (len < sizeof(ip6_hdr)) return; if (hdr->ip6_hlim <= 1) return; if (!IN6_IS_ADDR_MULTICAST(&hdr->ip6_dst)) return; if (IN6_IS_ADDR_MULTICAST(&hdr->ip6_src) || IN6_IS_ADDR_UNSPECIFIED(&hdr->ip6_src) || IN6_IS_ADDR_LINKLOCAL(&hdr->ip6_src)) return; if ((hdr->ip6_dst.s6_addr[1] & ~0x3) == 0) return; hdr->ip6_hlim--; us_mfa_group *grp = match_group(hdr->ip6_dst); if (grp) { grp->route(dev, hdr, len); } else { discovered_source(dev, hdr->ip6_dst, hdr->ip6_src); } } void us_mfa::forward(interface *intf, ip6_hdr *hdr, uint16_t len) const { if (len > intf->mtu()) { send_icmpv6_toobig(intf, hdr, len); return; } sockaddr_ll sa; memset(&sa, 0, sizeof(sa)); sa.sll_family = AF_PACKET; sa.sll_protocol = htons(ETH_P_IPV6); sa.sll_ifindex = intf->index(); sa.sll_halen = 6; // construct the destination ethernet mac address (per rfc2464) sa.sll_addr[0] = 0x33; sa.sll_addr[1] = 0x33; memcpy(sa.sll_addr + 2, hdr->ip6_dst.s6_addr + 12, 4); if (::sendto(m_rawsock.fd(), hdr, len, 0, (const sockaddr *)&sa, sizeof(sa)) < 0) { if (errno == ENETDOWN) g_mrd->remove_interface(intf); else log_failed_packet(intf, len); } else { // send was OK // update our statistics // mif_stat_bytes_sent += pktb.rlength; // mif_stat_pkt_sent++; } } void us_mfa::log_failed_packet(const interface *intf, int len) const { if (should_log(DEBUG)) { log().xprintf("Sending %u bytes via %s failed: %s\n", (uint32_t)len, intf->name(), strerror(errno)); } } void us_mfa::send_icmpv6_toobig(interface *intf, ip6_hdr *hdr, uint16_t len) const { const in6_addr *src = &hdr->ip6_src; if (IN6_IS_ADDR_UNSPECIFIED(src) || IN6_IS_ADDR_MULTICAST(src)) return; sockaddr_in6 dstaddr; memset(&dstaddr, 0, sizeof(dstaddr)); dstaddr.sin6_family = AF_INET6; dstaddr.sin6_addr = *src; uint8_t buffer[1280]; icmp6_hdr *icmphdr = (icmp6_hdr *)buffer; icmphdr->icmp6_type = ICMP6_PACKET_TOO_BIG; icmphdr->icmp6_code = 0; icmphdr->icmp6_cksum = 0; icmphdr->icmp6_mtu = htonl(intf->mtu()); int max = 1280 - sizeof(ip6_hdr) - sizeof(icmp6_hdr); int howmuch = len > max ? max : len; memcpy(buffer + sizeof(icmp6_hdr), hdr, howmuch); if (!g_mrd->icmp().send_icmp(intf, *src, icmphdr, howmuch + sizeof(icmp6_hdr))) { if (should_log(EXTRADEBUG)) { log().xprintf("Packet too big to %{addr}, dropping.\n", *src); } } } void us_mfa::added_interface(interface *intf) { packet_mreq mreq; memset(&mreq, 0, sizeof(mreq)); mreq.mr_ifindex = intf->index(); mreq.mr_type = PACKET_MR_ALLMULTI; if (setsockopt(m_rawsock.fd(), SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { if (should_log(WARNING)) { log().xprintf("Failed to set ALLMULTI on %s, may " "miss packets on this interface.\n", intf->name()); } } } void us_mfa::removed_interface(interface *intf) { packet_mreq mreq; memset(&mreq, 0, sizeof(mreq)); mreq.mr_ifindex = intf->index(); mreq.mr_type = PACKET_MR_ALLMULTI; setsockopt(m_rawsock.fd(), SOL_PACKET, PACKET_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); /* some bad boys may have left us pending state, clean it */ for (groups::iterator k = m_groups.begin(); k != m_groups.end(); ++k) { k->second->clear_interface_references(k->first, intf); } } bool us_mfa::output_info(base_stream &out, bool counters, bool noempty) const { for (groups::const_iterator i = m_groups.begin(); i != m_groups.end(); ++i) { out.xprintf("Group %{Addr} (%s)\n", i->first, i->second->owner()->name()); out.inc_level(); i->second->output_info(out, counters, noempty); out.dec_level(); } return true; } bool us_mfa::output_info(base_stream &out, const std::vector &args) const { bool counters = false; bool noempty = false; if (!args.empty()) { if (args[0] == "counters") { counters = true; if (args.size() > 1) { if (args[1] == "no-empty") noempty = true; else return false; } } else if (args[0] == "dataplane-source-cache") { m_sourcedisc.dump_cache(out); return true; } else { return false; } } return output_info(out, counters, noempty); } mrd6-0.9.6/src/linux/linux_unicast_route.cpp0000644000175000017500000003441510637602444017636 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * linux_unicast_route.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #ifndef ARPHRD_NONE #define ARPHRD_NONE 0xfffe #endif #define RT_PROTOS_PATH "/etc/iproute2/rt_protos" enum linux_method_name { method_filter_routes = 8000, method_show_filtered_routes, }; static const method_info linux_methods[] = { { "filter-routes", "Filter routes imported from Linux\'s RIB.", method_filter_routes, false, 0 }, { "filtered-routes", "Display which protocols are being blocked from being imported from Linux\'s RIB.", method_show_filtered_routes, true, 0 }, { NULL } }; struct netlink_msg { nlmsghdr n; rtmsg r; void build(int type) { memset(this, 0, sizeof(*this)); n.nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg)); n.nlmsg_flags = NLM_F_REQUEST; n.nlmsg_type = type; memset(&r, 0, sizeof(r)); r.rtm_family = AF_INET6; } void addattr(uint32_t attr, const void *data, int len) { int rlen = RTA_LENGTH(len); rtattr *rta = (rtattr *)(((uint8_t *)&n) + NLMSG_ALIGN(n.nlmsg_len)); rta->rta_type = attr; rta->rta_len = rlen; memcpy(RTA_DATA(rta), data, len); n.nlmsg_len = NLMSG_ALIGN(n.nlmsg_len) + rlen; } size_t length() const { return n.nlmsg_len; } static void parse_rtatable(rtattr *tb[], int max, rtattr *rta, int len) { for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { if (rta->rta_type <= max) tb[rta->rta_type] = rta; } } }; linux_unicast_router::linux_unicast_router() : rt_bcast_sock("linux netlink routing msgs", this, std::mem_fun(&linux_unicast_router::data_available)) { rt_sock = -1; rt_nlseq = 0; bufferlen = instantiate_property_u("netlink-buffer", 0xffff); buffer = 0; import_methods(linux_methods); filter_protos.insert(RTPROT_KERNEL); rt_dumping = false; } linux_unicast_router::~linux_unicast_router() { delete [] buffer; buffer = 0; } bool linux_unicast_router::check_startup() { if (!rib_def::check_startup()) return false; if (!bufferlen) return false; buffer = new uint8_t[bufferlen->get_unsigned()]; if (!buffer) return false; rt_sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (rt_sock < 0) { g_mrd->fatal().writeline("Failed to establish kernel " "connection for unicast routing."); return false; } int bcast_sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (bcast_sock < 0) { close(rt_sock); g_mrd->fatal().writeline("Failed to establish kernel" " connection for unicast routing [2]"); return false; } sockaddr_nl localaddr; memset(&localaddr, 0, sizeof(localaddr)); localaddr.nl_family = AF_NETLINK; localaddr.nl_groups = ~RTMGRP_TC; if (bind(bcast_sock, (sockaddr*)&localaddr, sizeof(localaddr)) < 0) { close(rt_sock); close(bcast_sock); return false; } rt_bcast_sock.register_fd(bcast_sock); return true; } struct interface_desc { int index; std::string name; int mtu, flags, type; bool up; }; static std::vector _interfaces; static int _conv_intf(int type, int flags) { switch (type) { case ARPHRD_LOOPBACK: return interface::Loopback; case ARPHRD_ETHER: case ARPHRD_EETHER: return interface::Ethernet; case ARPHRD_PPP: return interface::PPP; #ifdef ARPHRD_IEEE1394 case ARPHRD_IEEE1394: return interface::IEEE1394; #endif case ARPHRD_TUNNEL: case ARPHRD_TUNNEL6: case ARPHRD_SIT: case ARPHRD_IPGRE: return interface::Tunnel; case ARPHRD_IEEE80211: return interface::IEEE802_11; case ARPHRD_NONE: if (flags & IFF_POINTOPOINT) return interface::TUN; default: return interface::None; } } static void _install_interface(const interface_desc &d) { bool had = (g_mrd->get_interface_by_index(d.index) != 0); interface *intf = g_mrd->found_interface(d.index, d.name.c_str(), d.type, d.mtu, d.flags); if (intf) { if (!had) { ((linux_unicast_router &)g_mrd->rib()).do_dump(RTM_GETADDR); } intf->change_state(d.up ? interface::Up : interface::Down); } } void linux_unicast_router::check_initial_interfaces() { do_dump(RTM_GETLINK); for (std::vector::const_iterator i = _interfaces.begin(); i != _interfaces.end(); ++i) { _install_interface(*i); } _interfaces.clear(); do_dump(RTM_GETROUTE); update_all(); } void linux_unicast_router::do_dump(int id) { rt_dumping = true; dump_request(id); while (process_message() > 0); rt_dumping = false; } void linux_unicast_router::dump_request(int type) { struct { nlmsghdr n; rtgenmsg g; } r; memset(&r, 0, sizeof(r)); r.n.nlmsg_len = NLMSG_LENGTH(sizeof(r.g)); r.n.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; r.n.nlmsg_type = type; r.n.nlmsg_seq = rt_nlseq++; // r.g.rtgen_family = AF_INET6; r.g.rtgen_family = 0; send(rt_bcast_sock.fd(), &r, sizeof(r), 0); } void linux_unicast_router::shutdown() { if (rt_sock > 0) { close(rt_sock); rt_sock = -1; } rt_bcast_sock.unregister(); rib_def::shutdown(); } bool linux_unicast_router::set_property(const char *name, const char *value) { if (!strcmp(name, "netlink-buffer")) { char *end; int val = strtol(value, &end, 10); if (*end || val < 2048) return false; uint8_t *tmp = new uint8_t[val]; if (!tmp) return false; delete [] buffer; buffer = tmp; } return node::set_property(name, value); } void linux_unicast_router::data_available(uint32_t) { process_message(); } #define NLMSG_OK2(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ (int)(nlh)->nlmsg_len <= (len)) int linux_unicast_router::process_message() { int len = recv(rt_bcast_sock.fd(), buffer, bufferlen->get_unsigned(), MSG_DONTWAIT | MSG_NOSIGNAL); if (len < 0) { return -1; } for (nlmsghdr *hd = (struct nlmsghdr *)buffer; NLMSG_OK2(hd, len); hd = NLMSG_NEXT(hd, len)) { if (hd->nlmsg_type == NLMSG_DONE) return 0; else if (hd->nlmsg_type == NLMSG_ERROR) return -1; if (hd->nlmsg_len == 0) break; switch (hd->nlmsg_type) { case RTM_NEWROUTE: case RTM_DELROUTE: handle_route_event(hd->nlmsg_type == RTM_NEWROUTE, hd); break; case RTM_NEWLINK: case RTM_DELLINK: handle_intf_event(hd->nlmsg_type != RTM_DELLINK, hd); break; case RTM_NEWADDR: case RTM_DELADDR: handle_addr_event(hd->nlmsg_type != RTM_DELADDR, hd); break; default: break; } } return 1; } void linux_unicast_router::handle_route_event(bool isnew, nlmsghdr *hdr) { rtattr *tb[RTA_MAX + 1]; memset(tb, 0, sizeof(tb)); netlink_msg *msg = (netlink_msg *)hdr; if (msg->r.rtm_family != AF_INET6 || (msg->r.rtm_flags & RTM_F_CLONED) == RTM_F_CLONED) { return; } netlink_msg::parse_rtatable(tb, RTA_MAX, RTM_RTA(NLMSG_DATA(hdr)), hdr->nlmsg_len - NLMSG_LENGTH(sizeof(rtmsg))); if (tb[RTA_DST]) { lookup_result res; parse_prefix_rec(tb, msg->r.rtm_dst_len, msg->r.rtm_protocol, res); if (g_mrd->should_log(MESSAGE_SIG)) { g_mrd->log().xprintf( "(NETLINK) route event: %s route for " "%{Addr} with dev %i gw %{addr} prefsrc " "%{addr}\n", isnew ? "new" : "lost", res.dst, res.dev, res.nexthop, res.source); } if (res.protocol == RTPROT_UNSPEC || res.protocol == RTPROT_REDIRECT) return; if (filter_protos.find(res.protocol) != filter_protos.end()) { if (g_mrd->should_log(INTERNAL_FLOW)) g_mrd->log().xprintf("(LINUX) Filtered %s route for " "%{Addr} with dev %i gw %{addr} prefsrc " "%{addr} by policy.\n", isnew ? "new" : "lost", res.dst, res.dev, res.nexthop, res.source); } prefix_changed(isnew, res); } } void linux_unicast_router::handle_intf_event(bool isnew, nlmsghdr *hdr) { ifinfomsg *ifi = (ifinfomsg *)NLMSG_DATA(hdr); if (isnew) { rtattr *tb[IFLA_MAX + 1]; memset(tb, 0, sizeof(tb)); netlink_msg::parse_rtatable(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(hdr)); if (tb[IFLA_IFNAME] && tb[IFLA_MTU]) { interface_desc desc; desc.index = ifi->ifi_index; desc.name = (const char *)RTA_DATA(tb[IFLA_IFNAME]); desc.mtu = *(int *)RTA_DATA(tb[IFLA_MTU]); desc.flags = ifi->ifi_flags; desc.type = _conv_intf(ifi->ifi_type, ifi->ifi_flags); desc.up = ((ifi->ifi_flags & IFF_UP) == IFF_UP); if (rt_dumping) { _interfaces.push_back(desc); } else { _install_interface(desc); } } } else { g_mrd->lost_interface(ifi->ifi_index); } } void linux_unicast_router::handle_addr_event(bool isnew, nlmsghdr *hdr) { ifaddrmsg *ifa = (ifaddrmsg *)NLMSG_DATA(hdr); if (ifa->ifa_family == AF_INET6) { rtattr *tb[IFA_MAX + 1]; memset(tb, 0, sizeof(tb)); netlink_msg::parse_rtatable(tb, IFA_MAX, IFA_RTA(ifa), hdr->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); rtattr *arta = tb[IFA_LOCAL]; if (!arta) arta = tb[IFA_ADDRESS]; if (arta) { inet6_addr addr; memcpy(&addr.addr, RTA_DATA(arta), RTA_PAYLOAD(arta)); addr.prefixlen = ifa->ifa_prefixlen; if (addr.type() & (inet6_addr::multicast | inet6_addr::network)) return; if (addr.is_any()) return; interface *intf = g_mrd->get_interface_by_index(ifa->ifa_index); if (intf) { intf->address_added_or_removed(isnew, addr); } } } } bool linux_unicast_router::lookup_prefix(const in6_addr &addr, lookup_result &reply) const { /* Not fully inited yet */ if (rt_bcast_sock.fd() < 0) return false; netlink_msg *msg = (netlink_msg *)buffer; msg->build(RTM_GETROUTE); msg->addattr(RTA_DST, &addr, sizeof(in6_addr)); msg->r.rtm_dst_len = 128; reply.dev = -1; if (send_nlmsg(msg, msg)) { if (msg->r.rtm_family != AF_INET6) return false; rtattr *tb[RTA_MAX + 1]; memset(tb, 0, sizeof(tb)); msg->parse_rtatable(tb, RTA_MAX, RTM_RTA(NLMSG_DATA(&msg->n)), msg->length() - NLMSG_LENGTH(sizeof(rtmsg))); parse_prefix_rec(tb, msg->r.rtm_dst_len, msg->r.rtm_protocol, reply); return true; } return false; } bool linux_unicast_router::send_nlmsg(const netlink_msg *msg, netlink_msg *reply) const { if (send(rt_sock, msg, msg->length(), 0) < 0) return false; int res = recv(rt_sock, buffer, bufferlen->get_unsigned(), 0); if (res < 0) return false; for (nlmsghdr *hd = (nlmsghdr *)buffer; NLMSG_OK(hd, (uint32_t)res); hd = NLMSG_NEXT(hd, res)) { if (hd->nlmsg_type == NLMSG_ERROR) return false; memmove(reply, hd, hd->nlmsg_len); return true; } return false; } void linux_unicast_router::parse_prefix_rec(rtattr *tb[], int plen, int protocol, lookup_result &reply) const { reply.dst = inet6_addr(in6addr_any, plen); reply.dev = -1; reply.protocol = protocol; reply.metric = 0xffffffff; reply.nexthop = in6addr_any; reply.source = in6addr_any; if (tb[RTA_DST]) reply.dst.addr = *(in6_addr *)RTA_DATA(tb[RTA_DST]); if (tb[RTA_OIF]) reply.dev = *(int *)RTA_DATA(tb[RTA_OIF]); if (tb[RTA_GATEWAY]) memcpy(&reply.nexthop, RTA_DATA(tb[RTA_GATEWAY]), sizeof(in6_addr)); if (tb[RTA_PREFSRC]) memcpy(&reply.source, RTA_DATA(tb[RTA_PREFSRC]), sizeof(in6_addr)); if (tb[RTA_PRIORITY]) reply.metric = *(uint32_t *)RTA_DATA(tb[RTA_PRIORITY]); } bool linux_unicast_router::call_method(int id, base_stream &out, const std::vector &args) { switch (id) { case method_filter_routes: return filter_routes(out, args); case method_show_filtered_routes: return show_filter_routes(out, args); } return rib_def::call_method(id, out, args); } bool linux_unicast_router::filter_routes(base_stream &out, const std::vector &args) { std::map pmap = parse_rt_protos(); std::map proto_map; for (std::map::const_iterator i = pmap.begin(); i != pmap.end(); ++i) proto_map[i->second] = i->first; std::set new_filter; for (std::vector::const_iterator i = args.begin(); i != args.end(); ++i) { int value = -1; if (isdigit(*i->c_str())) { char *endp; value = strtol(i->c_str(), &endp, 10); if (*endp) value = -1; } else { std::map::const_iterator j = proto_map.find(*i); if (j != proto_map.end()) value = j->second; } if (value < 0) { out.xprintf("Invalid argument `%s`.\n", i->c_str()); return true; } new_filter.insert(value); } filter_protos = new_filter; return true; } bool linux_unicast_router::show_filter_routes(base_stream &out, const std::vector &args) { if (!args.empty()) { out.writeline("This method accepts no arguments."); return true; } const std::map pmap = parse_rt_protos(); for (std::set::const_iterator i = filter_protos.begin(); i != filter_protos.end(); ++i) { std::map::const_iterator j = pmap.find(*i); if (j == pmap.end()) out.xprintf("? (%i)\n", *i); else out.xprintf("%s (%i)\n", j->second.c_str(), *i); } return true; } std::map linux_unicast_router::parse_rt_protos() const { FILE *rt_p = fopen(RT_PROTOS_PATH, "r"); if (rt_p == NULL) return std::map(); std::map proto_map; char line_b[256]; while (fgets(line_b, sizeof(line_b), rt_p)) { char *linep = line_b; line_b[strlen(line_b)-1] = 0; while (isspace(*linep)) linep++; if (*linep == '#') continue; if (!isdigit(*linep)) continue; char *next = linep; while (isdigit(*next)) next++; if (!isspace(*next)) continue; while (isspace(*next)) next++; if (!isalpha(*next)) continue; char *end_next = next; while (*end_next && !isspace(*end_next) && *end_next != '#') end_next++; *end_next = 0; proto_map[strtol(linep, NULL, 10)] = next; } fclose(rt_p); return proto_map; } mrd6-0.9.6/src/dummy/0000755000175000017500000000000010746353706013021 5ustar hugohugomrd6-0.9.6/src/dummy/mrd_components.cpp0000644000175000017500000000213610550053317016542 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mrd_components.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include void mrd::start_os_components() { m_mfa = new dummy_mfa(); g_unirouter = new dummy_rib(); } mrd6-0.9.6/src/dummy/dummy.cpp0000644000175000017500000000564210550053317014653 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * dummy.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include dummy_mfa_group_source::dummy_mfa_group_source(dummy_mfa_group *, const in6_addr &, uint32_t, action *) {} void dummy_mfa_group_source::change_flags(uint32_t, action) {} void dummy_mfa_group_source::set_iif(interface *) {} void dummy_mfa_group_source::release_iif(interface *) {} void dummy_mfa_group_source::add_oif(interface *) {} void dummy_mfa_group_source::release_oif(interface *) {} void dummy_mfa_group_source::forward(ip6_hdr *, uint16_t) const {} dummy_mfa_group::dummy_mfa_group(dummy_mfa_instance *, const inet6_addr &) {} void dummy_mfa_group::activate(bool) {} mfa_group_source *dummy_mfa_group::create_source_state(const in6_addr &addr, void *) { return new dummy_mfa_group_source(this, addr, 0, 0); } void dummy_mfa_group::release_source_state(mfa_group_source *src) { delete src; } void dummy_mfa_group::change_default_flags(uint32_t, mfa_group_source::action) {} dummy_mfa_instance::dummy_mfa_instance(dummy_mfa *, router *) {} mfa_group *dummy_mfa_instance::create_group(const inet6_addr &addr, void *) { return new dummy_mfa_group(this, addr); } void dummy_mfa_instance::release_group(mfa_group *grp) { delete grp; } void dummy_mfa_instance::change_group_default_flags(uint32_t, mfa_group_source::action) {} dummy_mfa::dummy_mfa() {} bool dummy_mfa::check_startup() { return node::check_startup(); } void dummy_mfa::shutdown() {} mfa_instance *dummy_mfa::alloc_instance(router *r) { return new dummy_mfa_instance(this, r); } void dummy_mfa::added_interface(interface *intf) {} void dummy_mfa::removed_interface(interface *intf) {} dummy_rib::dummy_rib() {} bool dummy_rib::check_startup() { return true; } void dummy_rib::shutdown() {} void dummy_rib::register_route(rib_watcher_base *, const inet6_addr &) {} void dummy_rib::unregister_route(rib_watcher_base *) {} void dummy_rib::update_route(rib_watcher_base *) {} interface *dummy_rib::path_towards(const inet6_addr &, inet6_addr &, inet6_addr &, inet6_addr &) const { return 0; } mrd6-0.9.6/src/router.cpp0000644000175000017500000000514610550053317013704 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * router.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include router::router(const char *name) : node(0, name) {} router::~router() { } void router::attach(mrd *m) { m_parent = m; } bool router::check_startup() { if (!node::check_startup()) return false; return true; } void router::shutdown() { } base_stream &router::log() const { return log_router_desc(node::log()); } base_stream &router::log_router_desc(base_stream &os) const { return os; } interface *router::get_interface_by_index(int index) const { interface *intf = g_mrd->get_interface_by_index(index); if (intf->conf()->is_router_enabled(name())) return intf; return 0; } void router::created_group(group *) { /* empty */ } void router::released_group(group *) { /* empty */ } void router::add_interface(interface *) { /* empty */ } void router::remove_interface(interface *) { /* empty */ } intfconf_node *router::create_interface_configuration(intfconf *) { return 0; } groupconf_node *router::create_group_configuration(groupconf *) { return 0; } void router::mfa_notify(mfa_group_source *, const in6_addr &, const in6_addr &, uint32_t flags, mfa_group_source::action, interface *iif, ip6_hdr *, uint16_t alen, uint16_t len) { } void router::event(int id, void *param) { switch (id) { case mrd::InterfaceStateChanged: { interface *intf = (interface *)param; if (intf->up()) { if (intf->conf()->is_router_enabled(name())) add_interface(intf); } else { remove_interface(intf); } } break; case mrd::NewGroup: created_group((group *)param); break; case mrd::ReleasedGroup: released_group((group *)param); break; default: node::event(id, param); } } mrd6-0.9.6/src/icmp.cpp0000644000175000017500000000644710550053317013321 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * icmp.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include bool icmp_base::register_handler(int type, icmp_handler *h) { handlers::iterator i = m_handlers.find(type); if (!h) { if (i != m_handlers.end()) m_handlers.erase(i); else return false; } else { if (i != m_handlers.end()) return false; m_handlers[type] = h; } registration_changed(); return true; } void icmp_base::registration_changed() { } bool icmp_base::send_icmp(const in6_addr &dst, icmp6_hdr *hdr, uint16_t len) const { interface *intf = g_mrd->rib().path_towards(dst); if (!intf) return false; return send_icmp(intf, dst, hdr, len); } bool icmp_base::send_icmp(const interface *intf, const in6_addr &dst, icmp6_hdr *hdr, uint16_t len) const { return send_icmp(intf, dst, -1, hdr, len); } bool icmp_base::send_icmp(const interface *intf, const in6_addr &dst, int rta, icmp6_hdr *hdr, uint16_t len) const { if (IN6_IS_ADDR_LINKLOCAL(&dst) || IN6_IS_ADDR_MC_LINKLOCAL(&dst)) return send_icmp(intf, *intf->linklocal(), dst, rta, hdr, len); else { if (intf->globals().empty()) { if (g_mrd->should_log(DEBUG)) { g_mrd->log().xprintf( "[ICMPv6] Failed to send message to " "%{addr}, no global address.\n", dst); } return false; } return send_icmp(intf, *intf->globals().begin(), dst, rta, hdr, len); } } bool icmp_base::send_icmp(const interface *intf, const in6_addr &src, const in6_addr &to, icmp6_hdr *hdr, uint16_t len) const { return send_icmp(intf, src, to, -1, hdr, len); } void icmp_base::icmp_message_available(interface *intf, const in6_addr &src, const in6_addr &dst, icmp6_hdr *hdr, int len) { handlers::iterator i = m_handlers.find((int)hdr->icmp6_type); if (i != m_handlers.end()) { i->second->icmp_message_available(intf, src, dst, hdr, len); } else { if (g_mrd->should_log(MESSAGE_CONTENT)) { g_mrd->log().xprintf("[ICMPv6] No handler for type ", "%i.\n", (int)hdr->icmp6_type); } } } void icmp_base::require_mgroup(const in6_addr &mgroup, bool include) { mgroups::iterator i = m_mgroups.find(mgroup); if (include) { if (i == m_mgroups.end()) { m_mgroups[mgroup] = 1; internal_require_mgroup(mgroup, true); } else { i->second ++; } } else { if (i != m_mgroups.end()) { i->second --; if (i->second == 0) { m_mgroups.erase(i); internal_require_mgroup(mgroup, false); } } } } mrd6-0.9.6/src/bsd/0000755000175000017500000000000010746353706012436 5ustar hugohugomrd6-0.9.6/src/bsd/bsd_rib.cpp0000644000175000017500000002355010746067643014555 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * bsd/rib.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include #include #include #include bsd_rib::bsd_rib() : evsock("rib events", this, std::mem_fun(&bsd_rib::data_pending)) { rtseq = mrd::get_randu32(); } bool bsd_rib::check_startup() { if (!rib_def::check_startup()) return false; int sock = socket(PF_ROUTE, SOCK_RAW, 0); if (sock < 0) { shutdown(); return false; } evsock.register_fd(sock); return true; } void bsd_rib::check_initial_interfaces() { while (1) { int mib[6] = { CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0 }; size_t needed; if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) { return; } uint8_t *buf = new uint8_t[needed]; if (!buf) { return; } if (sysctl(mib, 6, buf, &needed, 0, 0) < 0) { if (errno == ENOMEM) { delete buf; continue; } } process_messages((rt_msghdr *)buf, needed); delete buf; break; } } void bsd_rib::shutdown() { evsock.unregister(); } static uint8_t buffer[1024]; #ifdef _NETBSD_SOURCE #define _RT_PAD(x) ((x) > 0 ? (1 + (((x) - 1) | (sizeof(long) - 1))) : sizeof(long)) #else #define _RT_PAD(x) (x) #endif static void transform(uint8_t *ptr, uint8_t *end, rt_addrinfo *rtinfo) { #if 0 cerr << "addrs: " << hex << rtinfo->rti_addrs << endl; for (int j = 0; j < (end - ptr); j++) { cerr << hex << setw(2) << (int)ptr[j] << " "; if ((j % 16) == 15) cerr << endl; } cerr << dec << endl; #endif for (int i = 0; (i < RTAX_MAX) && (ptr < end); i++) { if ((rtinfo->rti_addrs & (1 << i)) == 0) continue; rtinfo->rti_info[i] = (sockaddr *)ptr; ptr += _RT_PAD(rtinfo->rti_info[i]->sa_len); } } static void fix_ll(sockaddr_in6 &addr) { if (IN6_IS_ADDR_LINKLOCAL(&addr.sin6_addr)) { addr.sin6_scope_id = *((uint16_t *)&addr.sin6_addr.s6_addr[2]); *((uint16_t *)&addr.sin6_addr.s6_addr[2]) = 0; } } static int countprefix(in6_addr *addr) { uint8_t *ptr = (uint8_t *)addr; int i; for (i = 0; i < 16; i++) { if (ptr[i] != 0xff) break; } if (i == 16) return 128; else if (ptr[i] == 0) return i * 8; uint8_t m = ptr[i]; while ((m & 0x1) == 0) m >>= 1; return i * 8 + m; } bool bsd_rib::lookup_prefix(const in6_addr &dst, lookup_result &res) const { if (evsock.fd() < 0) return false; rt_msghdr *hdr = (rt_msghdr *)buffer; hdr->rtm_type = RTM_GET; hdr->rtm_flags = RTF_STATIC | RTF_UP | RTF_GATEWAY; hdr->rtm_version = RTM_VERSION; hdr->rtm_seq = ++rtseq; hdr->rtm_addrs = RTA_DST; memset(&hdr->rtm_rmx, 0, sizeof(hdr->rtm_rmx)); hdr->rtm_inits = 0; sockaddr *sa = (sockaddr *)(buffer + sizeof(rt_msghdr)); memset(sa, 0, sizeof(sockaddr_in6)); sa->sa_family = AF_INET6; sa->sa_len = sizeof(sockaddr_in6); ((sockaddr_in6 *)sa)->sin6_addr = dst; sa = (sockaddr *)(buffer + sizeof(rt_msghdr) + sizeof(sockaddr_in6)); memset(sa, 0, sizeof(sockaddr_dl)); sa->sa_family = AF_LINK; sa->sa_len = sizeof(sockaddr_dl); hdr->rtm_addrs |= RTA_IFP; hdr->rtm_msglen = sizeof(rt_msghdr) + sizeof(sockaddr_in6) + sizeof(sockaddr_dl); if (write(evsock.fd(), hdr, hdr->rtm_msglen) < 0) { if (g_mrd->should_log(WARNING)) g_mrd->log().perror("(BSD-RIB) Failed while writing to route socket"); return 0; } hdr = read_until(rtseq); if (!hdr) return false; return fill_lookup_result(res, hdr); } bool bsd_rib::fill_lookup_result(lookup_result &res, rt_msghdr *hdr) const { int length = hdr->rtm_msglen; rt_addrinfo ai; memset(&ai, 0, sizeof(ai)); ai.rti_addrs = hdr->rtm_addrs; transform((uint8_t *)(hdr + 1), ((uint8_t *)hdr) + length, &ai); if (ai.rti_info[RTAX_DST] && ai.rti_info[RTAX_GATEWAY]) { res.dev = hdr->rtm_index; int prefixlen = 128; if (ai.rti_info[RTAX_NETMASK] && ai.rti_info[RTAX_NETMASK]->sa_len) prefixlen = countprefix(&((sockaddr_in6 *)ai.rti_info[RTAX_NETMASK])->sin6_addr); if (ai.rti_info[RTAX_GATEWAY]->sa_family == AF_INET6) res.nexthop = ((sockaddr_in6 *)ai.rti_info[RTAX_GATEWAY])->sin6_addr; else res.nexthop = in6addr_any; /* XXX todo */ res.source = in6addr_any; res.dst = inet6_addr(((sockaddr_in6 *)ai.rti_info[RTAX_DST])->sin6_addr, prefixlen); return true; } return false; } void bsd_rib::process_messages(rt_msghdr *hdr, int len) { uint8_t *ptr = (uint8_t *)hdr; uint8_t *end = ptr + len; while (ptr < end) { if_msghdr *ifm = (if_msghdr *)ptr; ifa_msghdr *ifam = 0; if (ifm->ifm_type != RTM_IFINFO) { return; } ptr += ifm->ifm_msglen; int count = 0; while (ptr < end) { ifa_msghdr *currifam = (ifa_msghdr *)ptr; if (currifam->ifam_type != RTM_NEWADDR) break; if (ifam == 0) ifam = currifam; ptr += currifam->ifam_msglen; count++; } process_if_msg(ifm); while (count > 0) { process_ifa_msg(ifm->ifm_index, ifam, true); ifam = (ifa_msghdr *)((uint8_t *)ifam + ifam->ifam_msglen); count--; } } } static int _conv_intf(int type) { if (type == IFT_ETHER) return interface::Ethernet; else if (type == IFT_PPP) return interface::PPP; else if (type == IFT_IEEE1394) return interface::IEEE1394; else if (type == IFT_L2VLAN) return interface::IEEE802_1Q; return interface::None; } void bsd_rib::process_if_msg(if_msghdr *hdr) { char name[IFNAMSIZ]; const char *pname = if_indextoname(hdr->ifm_index, name); if (!pname) return; interface *intf = g_mrd->found_interface(hdr->ifm_index, pname, _conv_intf(hdr->ifm_data.ifi_type), hdr->ifm_data.ifi_mtu, hdr->ifm_flags); if (intf) { if (hdr->ifm_flags & IFF_UP) intf->change_state(interface::Up); else intf->change_state(interface::Down); } } void bsd_rib::process_ifa_msg(int index, ifa_msghdr *ifam, bool added) { rt_addrinfo ai; memset(&ai, 0, sizeof(ai)); ai.rti_addrs = ifam->ifam_addrs; transform((uint8_t *)(ifam + 1), ((uint8_t *)ifam) + ifam->ifam_msglen, &ai); ifam = (ifa_msghdr *)((uint8_t *)ifam + ifam->ifam_msglen); process_addrinfo(index, &ai, added); } void bsd_rib::process_addrinfo(int index, rt_addrinfo *rti, bool added) { if (!rti->rti_info[RTAX_IFA]) return; interface *intf = g_mrd->get_interface_by_index(index); if (!intf) return; if (rti->rti_info[RTAX_IFA]->sa_family == AF_INET6) { int prefixlen = 128; if (rti->rti_info[RTAX_NETMASK]) { prefixlen = countprefix(&((sockaddr_in6 *)rti->rti_info[RTAX_NETMASK])->sin6_addr); } sockaddr_in6 *sin6 = (sockaddr_in6 *)rti->rti_info[RTAX_IFA]; fix_ll(*sin6); inet6_addr addr(sin6->sin6_addr, prefixlen); intf->address_added_or_removed(added, addr); } } void bsd_rib::data_pending(uint32_t) { int l = read(evsock.fd(), buffer, sizeof(buffer)); if (l < 0) return; int ptr = 0; for (rt_msghdr *hdr1 = (rt_msghdr *)buffer; ptr < l; ptr += hdr1->rtm_msglen, hdr1 = (rt_msghdr *)(buffer + ptr)) { event_pending(hdr1); } } static const char *_rtmsg_type_name(int type) { switch (type) { case RTM_ADD: return "ADD"; case RTM_DELETE: return "DELETE"; case RTM_CHANGE: return "CHANGE"; case RTM_GET: return "GET"; case RTM_LOSING: return "LOSING"; case RTM_REDIRECT: return "REDIRECT"; case RTM_MISS: return "MISS"; case RTM_LOCK: return "LOCK"; case RTM_RESOLVE: return "RESOLVE"; case RTM_NEWADDR: return "NEWADDR"; case RTM_DELADDR: return "DELADDR"; case RTM_IFINFO: return "IFINFO"; #ifdef RTM_IFANNOUNCE case RTM_IFANNOUNCE: return "IFANNOUNCE"; #endif #ifdef RTM_NEWMADDR case RTM_NEWMADDR: return "NEWMADDR"; case RTM_DELMADDR: return "DELMADDR"; #endif default: return "UNKNOWN"; } } void bsd_rib::event_pending(rt_msghdr *rtm) { union { rt_msghdr *rt; if_msghdr *ifm; ifa_msghdr *ifam; #ifdef RTM_IFANNOUNCE if_announcemsghdr *ann; #endif } u; u.rt = rtm; if (g_mrd->should_log(EXTRADEBUG)) { g_mrd->log().xprintf("(BSD-RIB) Pending event type %s\n", _rtmsg_type_name(u.rt->rtm_type)); } switch (u.rt->rtm_type) { case RTM_ADD: case RTM_DELETE: case RTM_CHANGE: { lookup_result res; if (fill_lookup_result(res, u.rt)) prefix_changed(u.rt->rtm_type != RTM_DELETE, res); } break; case RTM_NEWADDR: case RTM_DELADDR: process_ifa_msg(u.ifam->ifam_index, u.ifam, u.rt->rtm_type == RTM_NEWADDR); break; case RTM_IFINFO: process_if_msg(u.ifm); break; #ifdef RTM_IFANNOUNCE case RTM_IFANNOUNCE: if (u.ann->ifan_what == IFAN_DEPARTURE) { interface *intf = g_mrd->get_interface_by_name(u.ann->ifan_name); if (intf) { g_mrd->lost_interface(intf->index()); } } break; #endif default: /* nothing */ break; } } rt_msghdr *bsd_rib::read_until(unsigned seq) const { rt_msghdr *hdr = (rt_msghdr *)buffer; while (1) { int l = read(evsock.fd(), buffer, sizeof(buffer)); if (l < 0) return 0; int ptr = 0; for (rt_msghdr *hdr1 = hdr; ptr < l; ptr += hdr1->rtm_msglen, hdr1 = (rt_msghdr *)(buffer + ptr)) { if ((uint32_t)hdr1->rtm_seq == seq && hdr1->rtm_pid == getpid()) { return hdr1; } else { const_cast(this)->event_pending(hdr1); } } } return 0; } mrd6-0.9.6/src/bsd/mrd_components.cpp0000644000175000017500000000275410550053317016165 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mrd_components.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include bool mrd::prepare_os_components() { m_mfa = new bsd_mfa(); return true; } void mrd::prepare_second_components() { if (!m_rib_handler) m_rib_handler = new bsd_rib(); } const char *mrd::loopback_interface_name() const { return "lo0"; } void mrd::output_backtrace(base_stream &out) const { out.writeline("Backtraces aren't available in this system."); } char *mrd::obtain_frame_description(void *ptr) const { return 0; } void *mrd::posix_uctx::get_current_frame() const { return 0; } mrd6-0.9.6/src/bsd/bsd_mfa.cpp0000644000175000017500000003032310550053317014522 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * bsd_mfa.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #else #include #endif #include #include #include #include #include #include #include #include #ifdef _NETBSD_SOURCE #include #else #include #endif bsd_mfa_group_source::bsd_mfa_group_source(bsd_mfa_group *grp, const in6_addr &addr, uint32_t flags, action *acts) : m_owner(grp), m_addr(addr) { instowner = 0; m_flags = flags; m_interest_flags = 0; for (int i = 0; i < event_count; i++) change_flags(1 << i, acts[i]); m_iif = 0; memset(&m_bsd_state, 0, sizeof(m_bsd_state)); m_bsd_state.mf6cc_origin = m_addr.as_sockaddr(); m_bsd_state.mf6cc_mcastgrp = m_owner->addr().as_sockaddr(); } bsd_mfa_group_source::~bsd_mfa_group_source() { ((bsd_mfa *)g_mrd->mfa())->commit(&m_bsd_state, true); } void bsd_mfa_group_source::get_input_counter(uint64_t &val) const { ((bsd_mfa *)g_mrd->mfa())->get_input_counter(this, val); } void bsd_mfa_group_source::get_forwarding_counter(uint64_t &val) const { ((bsd_mfa *)g_mrd->mfa())->get_forwarding_counter(this, val); } void bsd_mfa_group_source::set_iif(interface *iif) { m_iif = iif; m_bsd_state.mf6cc_parent = iif ? ((bsd_mfa *)g_mrd->mfa())->vif(iif) : 0; ((bsd_mfa *)g_mrd->mfa())->commit(&m_bsd_state); } void bsd_mfa_group_source::release_iif(interface *iif) { if (m_iif == iif) { set_iif(0); } } void bsd_mfa_group_source::add_oif(interface *oif) { int vif = ((bsd_mfa *)g_mrd->mfa())->vif(oif); if (vif < 0) return; if (!has_oif(oif)) { m_oifs.push_back(oif); } IF_SET(vif, &m_bsd_state.mf6cc_ifset); ((bsd_mfa *)g_mrd->mfa())->commit(&m_bsd_state); } void bsd_mfa_group_source::release_oif(interface *oif) { for (oifs::iterator k = m_oifs.begin(); k != m_oifs.end(); ++k) { if (*k == oif) { m_oifs.erase(k); int vif = ((bsd_mfa *)g_mrd->mfa())->vif(oif); if (vif >= 0) { IF_CLR(vif, &m_bsd_state.mf6cc_ifset); ((bsd_mfa *)g_mrd->mfa())->commit(&m_bsd_state); } return; } } } void bsd_mfa_group_source::change_flags(uint32_t flags, action act) { if (act == no_action) { m_interest_flags &= ~flags; } else { m_interest_flags |= flags; } } static void output(base_stream &out, const std::vector &ifs) { out.write("{"); for (std::vector::const_iterator i = ifs.begin(); i != ifs.end(); ++i) { if (i != ifs.begin()) out.write(", "); out.write((*i)->name()); } out.write("}"); } void bsd_mfa_group_source::output_info(base_stream &out) const { out.xprintf("Iif: %s\n", m_iif ? m_iif->name() : "(None)"); base_stream &oso = out.write("Oifs: "); output(oso, m_oifs); oso.newl(); } bsd_mfa_group::bsd_mfa_group(router *owner, const inet6_addr &id) : mfa_group(owner), m_addr(id) { instowner = 0; bsd_mfa *m = (bsd_mfa *)g_mrd->mfa(); m_flags = m->m_grpflags; for (int i = 0; i < mfa_group_source::event_count; i++) m_actions[i] = m->m_grpactions[i]; m_state = pending; } void bsd_mfa_group::activate(bool accept) { if (accept && m_state == running) return; if (!accept) { m_state = denied; } else { m_state = running; } if (!accept) { ((bsd_mfa *)g_mrd->mfa())->release_group(this); } } mfa_group_source *bsd_mfa_group::create_source_state(const in6_addr &addr, void *instowner) { mfa_group_source *src = get_source_state(addr); if (!src) { src = new bsd_mfa_group_source(this, addr, m_flags, m_actions); if (src) { if (mfa_core::mfa()->should_log(DEBUG)) mfa_core::mfa()->log().xprintf( "BSD-MFA: created source state for %{addr}\n", addr); m_sources[addr] = (bsd_mfa_group_source *)src; } } if (src) src->instowner = instowner; return src; } mfa_group_source *bsd_mfa_group::get_source_state(const in6_addr &addr) const { return match_source(addr); } void bsd_mfa_group::release_source_state(mfa_group_source *_src) { bsd_mfa_group_source *src = (bsd_mfa_group_source *)_src; for (sources::iterator i = m_sources.begin(); i != m_sources.end(); ++i) { if (src == i->second) { delete src; m_sources.erase(i); return; } } } void bsd_mfa_group::change_default_flags(uint32_t flags, mfa_group_source::action act) { for (int i = mfa_group_source::any_incoming; i < mfa_group_source::event_count; i++) { if (flags & (1 << i)) m_actions[i] = act; } } void bsd_mfa_group::output_info(base_stream &out) const { for (sources::const_iterator i = m_sources.begin(); i != m_sources.end(); ++i) { out.writeline(i->first); out.inc_level(); i->second->output_info(out); out.dec_level(); } } void bsd_mfa::change_group_default_flags(uint32_t flags, mfa_group_source::action act) { for (int i = mfa_group_source::any_incoming; i < mfa_group_source::event_count; i++) { if (flags & (1 << i)) m_grpactions[i] = act; } } mfa_group *bsd_mfa::create_group(router *r, const inet6_addr &id, void *instowner) { mfa_group *grp = get_group(id); if (!grp) { grp = new bsd_mfa_group(r, id); if (grp) { if (mfa_core::mfa()->should_log(DEBUG)) mfa_core::mfa()->log().xprintf( "BSD-MFA: created group state for %{Addr}\n", id); m_groups[id] = (bsd_mfa_group *)grp; } } if (grp) grp->instowner = instowner; return grp; } mfa_group *bsd_mfa::get_group(const inet6_addr &id) const { groups::const_iterator k = m_groups.find(id); if (k == m_groups.end()) return 0; return k->second; } void bsd_mfa::release_group(mfa_group *grp) { for (groups::iterator i = m_groups.begin(); i != m_groups.end(); ++i) { if (grp == i->second) { delete i->second; m_groups.erase(i); return; } } } bsd_mfa::bsd_mfa() : m_sock("kernel sock", this, std::mem_fun(&bsd_mfa::kernel_data_pending)) { m_icmpsock = -1; m_grpflags = 0; for (int i = 0; i < mfa_group_source::event_count; i++) m_grpactions[i] = mfa_group_source::no_action; } bool bsd_mfa::pre_startup() { if (!mfa_core::pre_startup()) return false; if (!data_plane_sourcedisc.check_startup()) return false; m_icmpsock = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6); if (m_icmpsock < 0) { if (should_log(WARNING)) log().perror("(MFA) Failed to create ICMPv6 socket"); return false; } int vers = 1; if (setsockopt(m_icmpsock, IPPROTO_IPV6, MRT6_INIT, &vers, sizeof(vers)) < 0) { if (should_log(WARNING)) log().perror("(BSD-MFA) MRT6_INIT Failed"); } #if 0 if (setsockopt(m_icmpsock, IPPROTO_IPV6, MRT6_PIM, &vers, sizeof(vers)) < 0) { g_mrd->log().info(DEBUG) << "BSD-MFA: MRD6_PIM Failed: " << strerror(errno) << endl; } #endif m_sock.register_fd(m_icmpsock); return g_mrd->register_source_discovery("data-plane", &data_plane_sourcedisc); } bool bsd_mfa::check_startup() { return true; } void bsd_mfa::shutdown() { setsockopt(m_icmpsock, IPPROTO_IPV6, MRT6_DONE, 0, 0); if (m_icmpsock > 0) { close(m_icmpsock); } g_mrd->register_source_discovery("data-plane", 0); } void bsd_mfa::forward(interface *intf, ip6_hdr *hdr, uint16_t len) const { if (should_log(WARNING)) log().writeline("(BSD-MFA) Failed to dispatch, not supported."); } bool bsd_mfa::output_info(base_stream &out, const std::vector &args) const { for (groups::const_iterator i = m_groups.begin(); i != m_groups.end(); ++i) { out.writeline(i->first); out.inc_level(); i->second->output_info(out); out.dec_level(); } return true; } void bsd_mfa::added_interface(interface *intf) { for (int vif = 1; vif < MAXMIFS; vif++) { if (rev_vifs.find(vif) == rev_vifs.end()) { mif6ctl mc; mc.mif6c_mifi = vif; mc.mif6c_flags = 0; mc.mif6c_pifi = intf->index(); if (setsockopt(m_icmpsock, IPPROTO_IPV6, MRT6_ADD_MIF, &mc, sizeof(mc)) < 0) { if (should_log(WARNING)) log().perror("(BSD-MFA) Failed to MRT6_ADD_MIF"); } else { vifs[intf] = vif; rev_vifs[vif] = intf; if (should_log(DEBUG)) log().xprintf("(BSD-MFA) Added interface %s with vif %i\n", intf->name(), vif); } return; } } if (should_log(WARNING)) log().xprintf("(BSD-MFA) Failed to enable multicast " "forwarding in %s, no available MIFs\n", intf->name()); } void bsd_mfa::removed_interface(interface *intf) { if (should_log(DEBUG)) log().xprintf("(BSD-MFA) Removed interface %s.\n", intf->name()); std::map::iterator i = vifs.find(intf); if (i != vifs.end()) { uint16_t index = i->second; vifs.erase(i); rev_vifs.erase(rev_vifs.find(index)); setsockopt(m_icmpsock, IPPROTO_IPV6, MRT6_DEL_MIF, &index, sizeof(index)); } } int bsd_mfa::vif(interface *iif) const { std::map::const_iterator i = vifs.find(iif); if (i != vifs.end()) return i->second; return -1; } void bsd_mfa::commit(mf6cctl *msg, bool remove) { if (IN6_IS_ADDR_UNSPECIFIED(&msg->mf6cc_origin.sin6_addr)) return; if (should_log(EXTRADEBUG)) { log().xprintf("(BSD-MFA) Commited MFC with src: %{addr} dst: %{addr}\n", msg->mf6cc_origin.sin6_addr, msg->mf6cc_mcastgrp.sin6_addr); } if (setsockopt(m_icmpsock, IPPROTO_IPV6, remove ? MRT6_DEL_MFC : MRT6_ADD_MFC, msg, sizeof(*msg)) < 0) { if (should_log(DEBUG)) log().perror("Failed to commit MFC"); } } static uint8_t buf[2048]; void bsd_mfa::kernel_data_pending(uint32_t) { sockaddr_in6 from; socklen_t slen = sizeof(from); int len = recvfrom(m_icmpsock, buf, sizeof(buf), 0, (sockaddr *)&from, &slen); if (len > 0) { icmp6_hdr *hdr = (icmp6_hdr *)buf; if (hdr->icmp6_type == 0) { mrt6msg *msg = (mrt6msg *)hdr; std::map::const_iterator i = rev_vifs.find(msg->im6_mif); if (i == rev_vifs.end()) { return; } if (msg->im6_msgtype == MRT6MSG_NOCACHE) { #if 1 if (should_log(DEBUG)) log().xprintf("(BSD-MFA) Cache miss mif %s src %{addr} dst %{addr}\n", i->second->name(), msg->im6_src, msg->im6_dst); #endif discovered_source(i->second->index(), msg->im6_dst, msg->im6_src); } else if (msg->im6_msgtype == MRT6MSG_WRONGMIF) { bsd_mfa_group *grp = (bsd_mfa_group *)get_group(msg->im6_dst); if (grp) { grp->owner()->mfa_notify( grp->get_source_state(msg->im6_src), msg->im6_dst, msg->im6_src, mfa_group_source::f_wrong_iif, mfa_group_source::notify_no_copy, i->second, 0, 0, 0); } } else if (msg->im6_msgtype == MRT6MSG_WHOLEPKT) { /* we don't use BSD PIM tunnels */ } } } } void bsd_mfa::discovered_source(int ifindex, const inet6_addr &grp, const inet6_addr &src) { data_plane_sourcedisc.discovered_source(ifindex, grp, src); } void bsd_mfa::get_source_counters(const bsd_mfa_group_source *src, sioc_sg_req6 *r) { r->src = src->m_addr.as_sockaddr(); r->grp = src->m_owner->addr().as_sockaddr(); if (ioctl(m_icmpsock, SIOCGETSGCNT_IN6, r) < 0) { r->pktcnt = 0; r->bytecnt = 0; r->wrong_if = 0; } } void bsd_mfa::get_input_counter(const bsd_mfa_group_source *src, uint64_t &val) { sioc_sg_req6 r; get_source_counters(src, &r); val = r.bytecnt; } void bsd_mfa::get_forwarding_counter(const bsd_mfa_group_source *src, uint64_t &val) { sioc_sg_req6 r; get_source_counters(src, &r); val = r.bytecnt - r.wrong_if; } mrd6-0.9.6/src/rib.cpp0000644000175000017500000001470710550053317013143 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * rib.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include enum { rib_method_lookup = 4000, }; static const method_info rib_methods[] = { { "lookup", 0, rib_method_lookup, true, 0 }, { 0 } }; rib_watcher_base::rib_watcher_base() : valid(false), dev(0), dst(in6addr_any), gateway(in6addr_any), prefsrc(in6addr_any), protocol(0xffffffff), metric(0xffffffff) {} rib_watcher_base::~rib_watcher_base() { release(); } void rib_watcher_base::set_destination(const inet6_addr &target) { g_mrd->rib().register_route(this, target); } void rib_watcher_base::release() { g_mrd->rib().unregister_route(this); } rib_def::rib_def() : node(g_mrd, "rib") { populate_mrib = instantiate_property_b("populate-mrib", true); base_distance = instantiate_property_u("base-mrib-distance", 100); } bool rib_def::check_startup() { if (!node::check_startup()) return false; import_methods(rib_methods); if (!populate_mrib || !base_distance) return false; return true; } void rib_def::shutdown() { g_mrd->mrib().origin_lost(this); } void rib_def::check_initial_interfaces() { /* empty */ } const char *rib_def::description() const { return "RIB"; } void rib_def::return_prefix(mrib_def::prefix *p) { delete p; } bool rib_def::dump_info(base_stream &os) const { for (notify_list::const_iterator i = rt_notify_list.begin(); i != rt_notify_list.end(); ++i) { os.xprintf("%{addr}\n", i->first); } return true; } bool rib_def::call_method(int id, base_stream &out, const std::vector &args) { if (id == rib_method_lookup) { if (args.empty()) return false; inet6_addr dst, nh, src; if (!dst.set(args[0].c_str())) return false; interface *intf = path_towards(dst, nh, src); if (intf) { out.xprintf("Output interface is %s, nexthop is %{Addr} " "(from %{Addr})\n", intf->name(), nh, src); } else { out.writeline("No path."); } } else { return node::call_method(id, out, args); } return true; } interface *rib_def::path_towards(const inet6_addr &dst) const { inet6_addr a, b, c; return path_towards(dst, a, b, c); } interface *rib_def::path_towards(const inet6_addr &dst, inet6_addr &a) const { inet6_addr b, c; return path_towards(dst, a, b, c); } interface *rib_def::path_towards(const inet6_addr &dst, inet6_addr &a, inet6_addr &b) const { inet6_addr c; return path_towards(dst, a, b, c); } interface *rib_def::path_towards(const inet6_addr &dst, inet6_addr &a, inet6_addr &b, inet6_addr &c) const { lookup_result res; if (lookup_prefix(dst, res)) { a = res.source; b = res.nexthop; c = res.dst; return g_mrd->get_interface_by_index(res.dev); } return 0; } void rib_def::update_route(rib_watcher_base *watch) { if (IN6_IS_ADDR_UNSPECIFIED(&watch->dst)) return; lookup_result result; bool wasvalid = watch->valid; uint32_t changedflags = 0; if (lookup_prefix(watch->dst, result)) { watch->valid = true; if (wasvalid) { if (result.dev != watch->dev) changedflags |= rib_watcher_base::DEV; if (!(result.nexthop == watch->gateway)) changedflags |= rib_watcher_base::GATEWAY; if (!(result.source == watch->prefsrc)) changedflags |= rib_watcher_base::PREFSRC; if (!(result.protocol == watch->protocol)) changedflags |= rib_watcher_base::PROTOCOL; if (!(result.metric == watch->metric)) changedflags |= rib_watcher_base::METRIC; } else { changedflags = 0xffffffff; } watch->dev = result.dev; watch->gateway = result.nexthop; watch->prefsrc = result.source; watch->protocol = result.protocol; watch->metric = result.metric; } else { watch->valid = false; changedflags = wasvalid ? rib_watcher_base::HAS_ROUTE : 0; } if (changedflags) watch->route_changed(changedflags); } void rib_def::register_route(rib_watcher_base *watch, const inet6_addr &addr) { unregister_route(watch); watch->valid = false; watch->dst = addr; if (!addr.is_any()) { rt_notify_list.insert(std::make_pair(watch->dst, watch)); update_route(watch); } else { watch->route_changed(rib_watcher_base::HAS_ROUTE); } } void rib_def::unregister_route(rib_watcher_base *watch) { notify_list::iterator i = rt_notify_list.lower_bound(watch->dst); while (i != rt_notify_list.end()) { if (i->second == watch) { rt_notify_list.erase(i); return; } if (i->first == watch->dst) ++i; else break; } } void rib_def::transfer_watchers(rib_def *target) { target->rt_notify_list = rt_notify_list; rt_notify_list.clear(); target->update_all(); } void rib_def::update_all() { notify_list::iterator k = rt_notify_list.begin(); while (k != rt_notify_list.end()) { notify_list::iterator j = k; ++k; update_route(j->second); } } void rib_def::prefix_changed(bool isnew, const lookup_result &r) { notify_list::iterator k = rt_notify_list.begin(); while (k != rt_notify_list.end()) { notify_list::iterator j = k; ++k; if (r.dst.matches(j->first)) update_route(j->second); } if (!populate_mrib->get_bool()) return; /* don't feed multicast prefixes into MRIB */ if (IN6_IS_ADDR_MULTICAST(&r.dst.addr)) return; if (IN6_IS_ADDR_LINKLOCAL(&r.dst.addr)) return; if (isnew) { interface *intf = g_mrd->get_interface_by_index(r.dev); if (!intf) return; mrib_def::prefix *p = new mrib_def::prefix(this); if (!p) return; p->distance = base_distance->get_unsigned() + r.protocol; p->metric = r.metric; p->intf = intf; p->nexthop = r.nexthop; p->flags = mrib_def::prefix::NO_EXPORT; if (!g_mrd->mrib().install_prefix(r.dst, p)) delete p; } else { mrib_def::prefix *p = g_mrd->mrib().get_prefix(r.dst, this); if (p) { g_mrd->mrib().remove_prefix(p); } } } mrd6-0.9.6/src/mfa.cpp0000644000175000017500000000264010550053317013123 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mfa.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include mfa_group_source::mfa_group_source() { } mfa_group_source::~mfa_group_source() { } mfa_group::mfa_group(router *owner) : m_owner(owner) { } mfa_core::mfa_core() : node(g_mrd, "mfa") { change_group_default_flags(mfa_group_source::f_wrong_iif, mfa_group_source::copy_metadata); } bool mfa_core::pre_startup() { if (!node::check_startup()) return false; g_mrd->add_child(this); return true; } mfa_core *mfa_core::mfa() { return g_mrd->m_mfa; } mrd6-0.9.6/src/Makefile0000644000175000017500000001314510637602444013325 0ustar hugohugo-include Makefile.options CXX ?= g++ PLATFORM ?= OS_LINUX SUPPORT_MODULES ?= yes TARGET ?= mrd PREFIX ?= /usr/local STATIC_STDCXX ?= no FULL_STATIC ?= no NO_INLINE ?= no OBJ_DIR ?= build DEPS_DIR = $(OBJ_DIR)/.deps MRD_VERSION_CPP = $(OBJ_DIR)/mrd.version.cpp MODULES_CPP = $(OBJ_DIR)/modules.cpp STATIC_MODULES ?= MLD PIM CONSOLE ifeq ($(FULL_STATIC),yes) SUPPORT_MODULES = no LDCMD = -static endif ifeq ($(SUPPORT_MODULES),yes) MODULES ?= BGP LDCMD = -rdynamic else MODULE_OPTIONS += -DMRD_NO_DYNAMIC_MODULE_LOADING endif INCLUDES = -I../include NOW = $(shell date -u) SOURCES = address.cpp address_set.cpp group.cpp icmp_inet6.cpp icmp.cpp \ interface.cpp log.cpp mfa.cpp mrd.cpp mrib.cpp node.cpp \ parser.cpp rib.cpp router.cpp timers.cpp support/objpool.cpp \ support/ptree.cpp ifeq ($(PLATFORM),OS_LINUX) SOURCES += linux/us_mfa.cpp linux/linux_icmp_raw.cpp \ linux/linux_unicast_route.cpp linux/mrd_components.cpp LDFLAGS += -ldl endif ifeq ($(PLATFORM),OS_BSD) SOURCES += bsd/bsd_rib.cpp bsd/bsd_mfa.cpp bsd/mrd_components.cpp endif MLD_SOURCES = mld/mld_def.cpp mld/mld_router.cpp mld/mld_conf.cpp mld/mld_module.cpp PIM_SOURCES = pim/pim_def.cpp pim/pim_router.cpp pim/pim_interface.cpp \ pim/pim_group.cpp pim/pim_source.cpp pim/pim_oif.cpp \ pim/pim_neighbour.cpp pim/pim_conf.cpp pim/pim_bsr.cpp \ pim/pim_module.cpp CONSOLE_SOURCES = console/console.cpp console/telnet_console.cpp \ console/unix_console.cpp BGP_SOURCES = bgp/bgp.cpp bgp/bgp_def.cpp MSNIP_SOURCES = msnip/msnip_module.cpp MRDISC_SOURCES = mrdisc/mrdisc_module.cpp RIPNG_SOURCES = ripng/ripng.cpp ZEBRA_SOURCES = zebra/zebra.cpp MLD_EXT_SOURCES = mld/mld_def.cpp mld/mld_router.cpp mld/mld_conf.cpp \ extra/mld_ext.cpp TEST_SOURCES = $(SOURCES) no-modules.cpp $(MRD_VERSION_CPP) MRD_SOURCES = $(SOURCES) main.cpp $(MODULES_CPP) $(MRD_VERSION_CPP) # used to build object list ALL_SOURCES = $(SOURCES) main.cpp $(MRD_SOURCES) $(MLD_SOURCES) \ $(PIM_SOURCES) $(CONSOLE_SOURCES) $(BGP_SOURCES) \ $(MSNIP_SOURCES) $(MRDISC_SOURCES) $(RIPNG_SOURCES) \ $(ZEBRA_SOURCES) # used in dependency generation BUILT_SOURCES = $(SOURCES) main.cpp MLD_TARGET = mld.so PIM_TARGET = pim.so CONSOLE_TARGET = console.so BGP_TARGET = bgp.so MSNIP_TARGET = msnip.so MRDISC_TARGET = mrdisc.so RIPNG_TARGET = ripng.so ZEBRA_TARGET = zebra.so MLD_EXT_TARGET = mld_ext.so EXTERNAL_MODULES = $(foreach module,$(MODULES),$($(module)_TARGET)) TESTS = tests/address_unittest tests/ptree_unittest tests/mrib_unittest DEST_PREFIX = $(DESTDIR)$(PREFIX) CXXFLAGS = $(INCLUDES) -ansi -Wall -Wno-multichar -fno-exceptions -fPIC \ -D$(PLATFORM) $(addprefix -D,$(MODULE_OPTIONS)) ifeq ($(OPTIMIZE),yes) ifeq ($(SPACE_OPTIMIZE),yes) CXXFLAGS += -O3 -Os else CXXFLAGS += -O3 endif else CXXFLAGS += -g ifeq ($(NO_INLINE),yes) CXXFLAGS += -O0 -fno-inline else CXXFLAGS += -O2 endif endif LDFLAGS += -lm ifeq ($(STATIC_STDCXX),no) LDFLAGS += -lstdc++ else LDFLAGS += `$(CXX) -print-file-name=libstdc++.a` endif TEST_OBJECTS = $(addprefix $(OBJ_DIR)/,$(TEST_SOURCES:.cpp=.o)) MRD_OBJECTS = $(addprefix $(OBJ_DIR)/,$(MRD_SOURCES:.cpp=.o)) all: $(TARGET) $(EXTERNAL_MODULES) define module_template BUILT_SOURCES += $$($(1)_SOURCES) $$($(1)_TARGET): $$(addprefix $(OBJ_DIR)/,$($(1)_SOURCES:.cpp=.o)) @echo "Module $$($(1)_TARGET)" @$(CXX) -shared $(CXXFLAGS) -o $$($(1)_TARGET) \ $$(addprefix $(OBJ_DIR)/,$($(1)_SOURCES:.cpp=.o)) endef define static_module_template BUILT_SOURCES += $$($(1)_SOURCES) MRD_OBJECTS += $$(addprefix $(OBJ_DIR)/,$($(1)_SOURCES:.cpp=.o)) endef define unittest_template BUILT_SOURCES += $(1).cpp $(1): $(TEST_OBJECTS) $(1).cpp @echo "Linking $(1)" @$(CXX) $(LDCMD) $(CXXFLAGS) -o $(1) $(1).cpp $(TEST_OBJECTS) \ $(LDFLAGS) -lboost_unit_test_framework endef # generate the required build rules for each module $(foreach module,$(MODULES),$(eval $(call module_template,$(module)))) $(foreach module,$(STATIC_MODULES),$(eval $(call static_module_template,$(module)))) # generate the required build rules for each unit test $(foreach test,$(TESTS),$(eval $(call unittest_template,$(test)))) $(TARGET): $(MRD_OBJECTS) @echo "Linking $(TARGET)" @$(CXX) $(LDCMD) $(CXXFLAGS) -o $@ $(MRD_OBJECTS) $(LDFLAGS) install: $(TARGET) $(EXTERNAL_MODULES) install -D $(TARGET) $(DEST_PREFIX)/sbin/$(TARGET) install -D ../tools/mrd6sh $(DEST_PREFIX)/bin/mrd6sh ifneq (,$(EXTERNAL_MODULES)) mkdir -p $(DEST_PREFIX)/lib/mrd6/ install -D $(EXTERNAL_MODULES) $(DEST_PREFIX)/lib/mrd6/ endif $(MRD_VERSION_CPP): $(SOURCES) Makefile Makefile.options @set -e; mkdir -p $(dir $@); \ echo '/* This file is automatically generated */' > $(MRD_VERSION_CPP); \ echo 'const char *BuildDate = "$(NOW)";' >> $(MRD_VERSION_CPP) $(MODULES_CPP): Makefile Makefile.options @set -e; mkdir -p $(dir $@); \ echo "Generating modules.cpp"; \ scripts/generate-modules-cpp.pl $(STATIC_MODULES) > $(MODULES_CPP) .PHONY: tests tests: $(TESTS) OPTIONS = Makefile.options $(OPTIONS): @touch $@ $(DEPS_DIR)/%.d: %.cpp $(OPTIONS) @echo "Deps $<" @set -e; mkdir -p $(dir $@); \ $(CXX) -MM -MT $@ -MT $(addprefix $(OBJ_DIR)/,$(<:.cpp=.o)) \ $(CXXFLAGS) $< > $@ DEPENDENCIES = $(addprefix $(DEPS_DIR)/,$(BUILT_SOURCES:.cpp=.d)) ifneq ($(MAKECMDGOALS),clean) ifneq ($(MAKECMDGOALS),distclean) -include $(DEPENDENCIES) endif endif $(OBJ_DIR)/%.o: %.cpp $(OPTIONS) @echo "C++ $<" @set -e; mkdir -p $(dir $@); \ $(CXX) -c $(CXXFLAGS) $< -o $@ clean: rm -rf $(TARGET) $(TESTS) $(EXTERNAL_MODULES) \ $(addprefix $(OBJ_DIR)/,$(ALL_SOURCES:.cpp=.o)) distclean: clean rm -rf $(DEPS_DIR) $(MRD_VERSION_CPP) $(MODULES_CPP) .PHONY: install clean distclean mrd6-0.9.6/src/main.cpp0000644000175000017500000000404210746067714013317 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * main.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include void usage() { printf("Usage: mrd [OPTIONS...]\n\n"); printf(" -D run in the background\n"); printf(" -A don't auto-load static modules\n"); printf(" -f configuration file to use. mrd.conf is used by default\n"); printf(" -h this screen\n"); } int main(int argc, char **argv) { mrd m; static option longopts[] = { { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; int c, optint; bool forkself = false; bool autoload = true; const char *conffile = 0; while ((c = getopt_long(argc, argv, "DAhf:m:", longopts, &optint)) != -1) { switch (c) { case 'D': forkself = true; break; case 'A': autoload = false; break; case 'h': usage(); return 1; case 'f': conffile = optarg; break; case 'm': m.load_early_module(optarg); break; } } if (forkself) { if (daemon(1, 0) != 0) { fprintf(stderr, "(MRD) failed to daemonize."); return -1; } } if (!m.check_startup(conffile, autoload)) { return -1; } m.start(); return 0; } mrd6-0.9.6/src/bgp/0000755000175000017500000000000010746353706012436 5ustar hugohugomrd6-0.9.6/src/bgp/bgp.cpp0000644000175000017500000013672610746336636013734 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * bgp.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include /* times */ #include #include #include #include class bgp_module; static const bgp_community no_export(65535, 65281); static const bgp_community no_advertise(65535, 65282); /* default eBGP weight and local-pref in IOS, Zebra, etc */ static const uint32_t DEFAULT_BGP_DISTANCE = 20; static const uint32_t DEFAULT_LOCAL_PREF = 100; static inline const char *stream_type_format_parameter(const bgp_community &) { return "{community}"; } static inline void stream_push_formated_type(base_stream &os, const bgp_community &c) { os.xprintf("%u:%u", (uint32_t)c.first, (uint32_t)c.second); } enum { BGP_OPEN = 1, BGP_UPDATE = 2, BGP_NOTIFICATION = 3, BGP_KEEPALIVE = 4 }; static const char *error_messages[] = { "Message Header Error", "Open Message Error", "Update Message Error", "Hold Timer Expired", "Finite State Machine Error", "Cease" }; enum { MessageHeaderError = 1, OpenMessageError, UpdateMessageError, HoldTimerExpired, FiniteStateMachineError, Cease }; static const char *suberror_messages[][11] = { { "Connection Not Synchronized", "Bad Message Length", "Bad Message Type" }, { "Unsupported Version Number", "Bad Peer AS", "Bad BGP Identifier", "Unsupported Optional Parameter", "Authentication Failure", "Unacceptable HoldTime", "Unsupported Capability" }, { "Malformed Attribute List", "Unrecognized Well-Known Attribute", "Missing Well-Known Attribute", "Attribute Flags Error", "Attribute Length Error", "Invalid Origin Attribute", "AS Routing Loop", "Invalid Next Hop Attribute", "Optional Attribute Error", "Invalid Network Field", "Malformed AS_Path" } }; enum { bgp_neigh_method_filter = 10000, bgp_neigh_method_route_map, bgp_neigh_method_activate, bgp_neigh_method_reconnect, bgp_neigh_method_debug, bgp_neigh_method_alias }; static const method_info bgp_neigh_methods[] = { { "filter", 0, bgp_neigh_method_filter, false, property_def::NEGATE }, { "route-map", 0, bgp_neigh_method_route_map, false, property_def::NEGATE }, { "activate", "Activates the peering to the specified neighbor", bgp_neigh_method_activate, false, property_def::NEGATE }, { "reconnect", "Forces a reconnect to the specified neighbor", bgp_neigh_method_reconnect, false, 0 }, { "debug", 0, bgp_neigh_method_debug, true, 0 }, { "alias", "Sets a name alias for the neighbor", bgp_neigh_method_alias, false, 0 }, { 0 } }; enum { bgp_acl_method_prefix = 11000, }; static const method_info bgp_acl_methods[] = { { "prefix", "Adds a new ACL prefix entry", bgp_acl_method_prefix, false, property_def::NEGATE }, { 0 } }; enum { bgp_rmap_method_match = 12000, bgp_rmap_method_set, bgp_rmap_method_prepend_aspath }; static const method_info bgp_rmap_methods[] = { { "match", 0, bgp_rmap_method_match, false, property_def::NEGATE }, { "set", 0, bgp_rmap_method_set, false, property_def::NEGATE }, { "prepend-aspath", 0, bgp_rmap_method_prepend_aspath, false, property_def::NEGATE }, { 0 } }; enum { AllCount = 0, KeepaliveCount, OpenCount, UpdateCount, NotificationCount, MessageCount }; enum { RX = 0, TX, Bad }; static const char *stats_descriptions[] = { "All", "Keepalive", "Open", "Update", "Notification", }; static inline bool valid_error(uint8_t error, uint8_t suberror) { if (error < 1 || error > 6) return false; if (error == 1) return suberror >= 1 && suberror <= 3; else if (error == 2) return suberror >= 1 && suberror <= 7; else if (error == 3) return suberror >= 1 && suberror <= 11; return true; } class bgp_neighbor : public node, public mrib_origin, public rib_watcher_base { public: bgp_neighbor(node *, const inet6_addr &); ~bgp_neighbor(); bool check_startup(); void shutdown(); const char *description() const { return "MBGP"; } uint16_t as_number() const { return get_property_unsigned("peer-as"); } enum bgp_mode { EBGP, IBGP }; enum { WorkPending = 'W' }; bgp_mode mode() const { return strcasecmp(get_property_string("mode"), "EBGP") ? IBGP : EBGP; } uint32_t holdtime() const { return get_property_unsigned("holdtime"); } void return_prefix(mrib_def::prefix *); interface *peer_interface() const; bool active() const { return currstate == ESTABLISHED; } void activate_with(int); struct bgp_prefix : mrib_def::prefix { bgp_prefix(bgp_neighbor *owner, const bgp_as_path &_aspath) : mrib_def::prefix(owner, DEFAULT_BGP_DISTANCE), as_path(_aspath) { should_export = should_advertise = true; localpref = DEFAULT_LOCAL_PREF; } uint8_t bgp_origin; bgp_as_path as_path; bool should_export, should_advertise; uint32_t localpref; }; void prefix_added(const inet6_addr &, mrib_def::metric_def, const mrib_def::prefix &); void prefix_lost(const inet6_addr &, mrib_def::metric_def, const mrib_def::prefix &); bool set_property(const char *, const char *); bool call_method(int id, base_stream &out, const std::vector &); bool negate_method(int id, base_stream &out, const std::vector &); bool output_info(base_stream &ctx, const std::vector &) const; bool output_info(base_stream &ctx, bool) const; void output_prefix_info(base_stream &, const mrib_def::prefix &) const; bool send_message(const bgp_message &); bool send_update(const bgp_update_message &); bool send_open(const bgp_open_message &); enum state { INACTIVE, IDLE, CONNECT, ACTIVE, OPEN_SENT, OPEN_CONFIRM, ESTABLISHED }; void change_state_to(state); bool new_connection_from(int sock); base_stream &log() const; private: void data_available(uint32_t); void event(int, void *); void timed_out(); void start_connect(); void connected(); void finish_connect_setup(); bool handle_open(bgp_open_message &); void build_update_work(bgp_update_message &); bool handle_notify(bgp_notification_message &); void handle_keepalive(); bool encode_msg(const bgp_message &); bool trigger_open(); void send_keepalive(); void send_notification(uint8_t, uint8_t = 0); void handle_localholdtime(); void route_changed(uint32_t); void install_prefix(const inet6_addr &prefix, uint8_t origin, const in6_addr &nh, const bgp_as_path &aspath, const bgp_communities &communities); message_stats_node m_stats; property_def *AS; std::string alias; inet6_addr peeraddr; /* to avoid tons of inet6_addr -> string conversions */ std::string peeraddr_s; socket0 sock; tval lastconnect; tval lastka, lastsentka; state currstate; bool work_pending; enum { InstallPrefix = 1, RemovePrefix = 2 }; struct work_token { int action; uint8_t origin; inet6_addr prefix; in6_addr nexthop; bgp_as_path as_path; bgp_communities communities; }; typedef std::deque work_buffer_def; work_buffer_def work_buffer; uint32_t max_work_buffer_size; static const char *_state_name(state); timer localholdtimer, holdtimer; encoding_buffer ibuf, obuf; void trigger_send_peer(); int prefixcount; std::map input_filter, output_filter, input_rmap, output_rmap; bool run_filter(const std::map &, const inet6_addr &) const; bool run_route_map(const std::map &, const inet6_addr &, in6_addr &, bgp_as_path &, mrib_def::metric_def &metric, uint32_t &localpref) const; bool conf_filter_rmap(bool filter, const std::vector &); bool reconnect(); }; class bgp_neighbors : public node { public: bgp_neighbors(node *); const char *description() const { return "BGP neighbors"; } node *get_child(const char *) const; node *create_child(const char *); bgp_neighbor *get_neigh(const in6_addr &) const; void remove_all(); bool has_neigh(bgp_neighbor *n) const; bgp_neighbor *get_alias(const char *) const; void add_alias(const char *, bgp_neighbor *); void remove_alias(const char *); bool output_info(base_stream &ctx, const std::vector &) const; private: typedef std::map neighbors; neighbors m_neighs; typedef std::map aliases; aliases m_aliases; }; class bgp_access_lists; class bgp_route_maps; class bgp_acl : public node { public: bgp_acl(bgp_access_lists *, const char *); bool check_startup(); bool call_method(int, base_stream &, const std::vector &); bool negate_method(int, base_stream &, const std::vector &); bool prefix(const std::vector &); bool no_prefix(const std::vector &); bool output_info(base_stream &, const std::vector &) const; bool accepts(const inet6_addr &) const; struct entry { bool mode; inet6_addr prefix; int ge, le; }; typedef std::map entries; entries m_entries; }; class bgp_access_lists : public node { public: bgp_access_lists(node *); const char *description() const { return "Access lists"; } node *create_child(const char *); bool output_info(base_stream &, const std::vector &) const; }; class bgp_rmap : public node { public: bgp_rmap(bgp_route_maps *, const char *); bool check_startup(); bool call_method(int, base_stream &, const std::vector &); bool negate_method(int, base_stream &, const std::vector &); bool output_info(base_stream &, const std::vector &) const; bool applies(const inet6_addr &, in6_addr &, bgp_as_path &, mrib_def::metric_def &, uint32_t &) const; std::string m_match_filter; enum action_type { PREPEND_ASPATH = 1, LOCAL_PREF, METRIC, COMMUNITY, }; struct action { action_type type; union { uint16_t as; int metric; struct { uint16_t first, second; } c; } v; }; typedef std::vector actions; actions m_actions; }; class bgp_route_maps : public node { public: bgp_route_maps(node *); const char *description() const { return "Route maps"; } node *create_child(const char *); bool output_info(base_stream &, const std::vector &) const; }; class bgp_module : public mrd_module, public node { public: bgp_module(mrd *m, void *dlh); const char *description() const; bool check_startup(); void shutdown(); void listen_for_neighs(); uint16_t as_number() const { return get_property_unsigned("router-as"); } uint32_t id() const { return get_property_unsigned("id"); } bool set_property(const char *, const char *); bool output_info(base_stream &ctx, const std::vector &) const; bool has_neighbor(bgp_neighbor *) const; bgp_acl *get_acl(const char *) const; bgp_rmap *get_rmap(const char *) const; bgp_neighbors &neighs() { return m_neighs; } base_stream &log() const; objpool prefix_pool; private: void connection_pending(uint32_t); bgp_neighbors m_neighs; bgp_access_lists m_acls; bgp_route_maps m_rmaps; socket0 sock; }; module_entry(bgp, bgp_module); static bgp_module *bgp = 0; static inline bool _parse_asnumber(const char *value, uint16_t &res) { char *end; uint32_t val = strtoul(value, &end, 10); if (*end || val > 0xffff) return false; res = (val & 0xffff); return true; } static bool _parse_int(const std::string &in, int &out) { char *end; out = strtol(in.c_str(), &end, 10); return !*end; } static bool _parse_community(const std::string &in, bgp_community &out) { std::string tmp = in; int k = tmp.find(':'); if (k >= (int)tmp.size()) return false; std::string p1(in.begin(), in.begin() + k); if (!_parse_asnumber(p1.c_str(), out.first)) return false; std::string p2(in.begin() + k + 1, in.end()); if (!_parse_asnumber(p2.c_str(), out.second)) return false; return true; } bgp_neighbors::bgp_neighbors(node *parent) : node(parent, "neighbor") { } node *bgp_neighbors::get_child(const char *name) const { aliases::const_iterator i = m_aliases.find(name); if (i != m_aliases.end()) return i->second; inet6_addr addr; if (!addr.set(name) || addr.prefixlen < 128) return 0; return get_neigh(addr); } node *bgp_neighbors::create_child(const char *name) { inet6_addr addr; if (!addr.set(name)) return 0; if (addr.prefixlen < 128) return 0; bgp_neighbor *neigh = new bgp_neighbor(this, addr); if (!neigh || !neigh->check_startup()) { delete neigh; return 0; } m_neighs[addr] = neigh; add_child(neigh); bgp->listen_for_neighs(); return neigh; } void bgp_neighbors::remove_all() { for (neighbors::iterator i = m_neighs.begin(); i != m_neighs.end(); ++i) { i->second->shutdown(); delete i->second; } m_neighs.clear(); m_aliases.clear(); clear_childs(); } bgp_neighbor *bgp_neighbors::get_neigh(const in6_addr &addr) const { neighbors::const_iterator i = m_neighs.find(addr); if (i == m_neighs.end()) return 0; return i->second; } bool bgp_neighbors::has_neigh(bgp_neighbor *n) const { for (neighbors::const_iterator i = m_neighs.begin(); i != m_neighs.end(); ++i) { if (i->second == n) return true; } return false; } bool bgp_neighbors::output_info(base_stream &ctx, const std::vector &args) const { if (m_neighs.empty()) { ctx.writeline("(None)"); } else { for (neighbors::const_iterator i = m_neighs.begin(); i != m_neighs.end(); ++i) { i->second->output_info(ctx, args); } } return true; } bgp_neighbor *bgp_neighbors::get_alias(const char *name) const { aliases::const_iterator i = m_aliases.find(name); if (i == m_aliases.end()) return 0; return i->second; } void bgp_neighbors::add_alias(const char *name, bgp_neighbor *neigh) { m_aliases[name] = neigh; add_child(neigh, false, name); } void bgp_neighbors::remove_alias(const char *name) { aliases::iterator i = m_aliases.find(name); if (i != m_aliases.end()) { m_aliases.erase(i); remove_child(name); } } bgp_module::bgp_module(mrd *m, void *dlh) : mrd_module(m, dlh), node(m, "bgp"), prefix_pool(256), m_neighs(this), m_acls(this), m_rmaps(this), sock("bgp listen", this, std::mem_fun(&bgp_module::connection_pending)) { bgp = this; add_child(&m_neighs); add_child(&m_acls); add_child(&m_rmaps); instantiate_property_u("router-as", 0); /* XXX */ instantiate_property_u("id", 0xdeadbeef); instantiate_property_a("local-bind", inet6_addr::any()); } const char *bgp_module::description() const { return "Border Gateway Protocol (Multicast SAFI)"; } bool bgp_module::check_startup() { if (!node::check_startup()) return false; if (!m_neighs.check_startup()) return false; if (!m_acls.check_startup()) return false; if (!m_rmaps.check_startup()) return false; m_mrd->add_child(this); return has_property("router-as") && has_property("id") && has_property("local-bind"); } void bgp_module::listen_for_neighs() { if (sock.fd() > 0) return; int s = socket(PF_INET6, SOCK_STREAM, 0); if (s < 0) return; sockaddr_in6 localaddr = get_property_address("local-bind").as_sockaddr(); localaddr.sin6_port = htons(179); int on = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (bind(s, (sockaddr *)&localaddr, sizeof(localaddr)) < 0 || listen(s, 5) < 0) { close(s); return; } sock.register_fd(s); } void bgp_module::shutdown() { m_neighs.remove_all(); if (sock.fd() > 0) { ::shutdown(sock.fd(), SHUT_RDWR); sock.unregister(); } m_mrd->remove_child("bgp"); } void bgp_module::connection_pending(uint32_t) { sockaddr_in6 src; socklen_t socklen = sizeof(src); int newclsock = accept(sock.fd(), (sockaddr *)&src, &socklen); if (newclsock < 0) { if (should_log(DEBUG)) log().perror("failed during accept in connection_pending"); return; } if (should_log(EXTRADEBUG)) log().xprintf("Accepted new connection from %{addr}, fd %i.\n", src.sin6_addr, newclsock); bgp_neighbor *neigh = m_neighs.get_neigh(src.sin6_addr); if (neigh) { if (neigh->new_connection_from(newclsock)) return; } else if (should_log(NORMAL)) { log().xprintf("%{addr} has no configuration, ignoring.\n", src.sin6_addr); } close(newclsock); } bool bgp_module::has_neighbor(bgp_neighbor *neigh) const { return m_neighs.has_neigh(neigh); } bgp_acl *bgp_module::get_acl(const char *name) const { return (bgp_acl *)m_acls.get_child(name); } bgp_rmap *bgp_module::get_rmap(const char *name) const { return (bgp_rmap *)m_rmaps.get_child(name); } bool bgp_module::set_property(const char *key, const char *value) { if (!strcmp(key, "router-as")) { uint16_t asnumber; if (!_parse_asnumber(value, asnumber)) return false; } return node::set_property(key, value); } bool bgp_module::output_info(base_stream &ctx, const std::vector &args) const { if (!args.empty()) return false; ctx.writeline("BGP"); ctx.inc_level(); ctx.xprintf("AS: %u\n", (uint32_t)as_number()); ctx.writeline("Neighbors:"); ctx.inc_level(); m_neighs.output_info(ctx, args); ctx.dec_level(); ctx.dec_level(); return true; } bgp_neighbor::bgp_neighbor(node *parent, const inet6_addr &addr) : node(parent, addr.as_string().c_str()), m_stats(this, MessageCount, stats_descriptions), peeraddr(addr), sock("bgp neighbor conn", this, std::mem_fun(&bgp_neighbor::data_available)), localholdtimer("bgp local holdtime", this, std::mem_fun(&bgp_neighbor::handle_localholdtime), 60000, true), holdtimer("bgp holdtimer", this, std::mem_fun(&bgp_neighbor::timed_out)), ibuf(4096), obuf(4096) { peeraddr_s = peeraddr.as_string(); prefixcount = 0; AS = instantiate_property_u("peer-as", 0); instantiate_property_s("mode", "EBGP"); instantiate_property_u("holdtime", 180); currstate = INACTIVE; work_pending = false; max_work_buffer_size = 0; g_mrd->register_startup(this); } bgp_neighbor::~bgp_neighbor() { } bool bgp_neighbor::check_startup() { if (!node::check_startup()) return false; if (!m_stats.setup()) return false; m_stats.disable_counter(AllCount, TX); if (!ibuf.check_startup() || !obuf.check_startup()) return false; if (!AS) return false; import_methods(bgp_neigh_methods); localholdtimer.start(); return true; } void bgp_neighbor::route_changed(uint32_t which) { if (currstate < IDLE) return; if (which & DEV) { if (currstate > IDLE && should_log(DEBUG)) log().writeline("Route towards peer changed, reconnecting."); change_state_to(IDLE); start_connect(); } } void bgp_neighbor::shutdown() { change_state_to(INACTIVE); if (!alias.empty()) bgp->neighs().remove_alias(alias.c_str()); } void bgp_neighbor::activate_with(int newsock) { sock.register_fd(newsock); if (should_log(VERBOSE)) log().writeline("Peer Connected."); finish_connect_setup(); } void bgp_neighbor::return_prefix(mrib_def::prefix *p) { bgp->prefix_pool.return_obj((bgp_prefix *)p); } interface *bgp_neighbor::peer_interface() const { return valid ? g_mrd->get_interface_by_index(dev) : 0; } void bgp_neighbor::finish_connect_setup() { lastconnect = tval::now(); change_state_to(ACTIVE); } void bgp_neighbor::start_connect() { if (sock.fd() > 0) return; localholdtimer.start_or_update(60000, true); int s = socket(PF_INET6, SOCK_STREAM, 0); if (s > 0) { int flags = fcntl(s, F_GETFL, 0); if (fcntl(s, F_SETFL, flags | O_NONBLOCK) != 0) { close(s); return; } sockaddr_in6 peer = peeraddr.as_sockaddr(); peer.sin6_port = htons(179); if (connect(s, (sockaddr *)&peer, sizeof(peer)) == 0) { change_state_to(CONNECT); connected(); } else if (errno == EINPROGRESS) { change_state_to(CONNECT); sock.register_fd(s, socket_base::Write); } else { close(s); } } } void bgp_neighbor::connected() { int s = sock.fd(); ibuf.clear(); obuf.clear(); int err; socklen_t errlen = sizeof(err); if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &errlen) == 0 && err == 0) { if (should_log(VERBOSE)) log().writeline("Connected to peer."); sock.monitor(socket_base::Read); finish_connect_setup(); trigger_open(); } else { sock.unregister(); if (should_log(VERBOSE)) log().perror("Failed to connect to peer"); change_state_to(IDLE); } } bool bgp_neighbor::new_connection_from(int sock) { if (active()) { if (should_log(DEBUG)) log().writeline("Refused connection, already connected."); return false; } if (currstate < IDLE) { if (should_log(DEBUG)) log().writeline("Refused connection, disabled by configuration."); return false; } activate_with(sock); return true; } void bgp_neighbor::timed_out() { if (currstate <= IDLE) return; if (should_log(VERBOSE)) log().writeline("Hold-time timeout."); change_state_to(IDLE); localholdtimer.restart(true); } bool bgp_neighbor::handle_open(bgp_open_message &msg) { if (msg.version < 4) { if (should_log(DEBUG)) log().xprintf("Bad message version (%i).\n", (int)msg.version); send_notification(2, 1); change_state_to(IDLE); return false; } uint16_t as = as_number(); if (as && as != msg.as) { if (should_log(VERBOSE)) log().xprintf("AS number mismatch, expected %u got %u.\n", (uint32_t)as, (uint32_t)msg.as); send_notification(2, 2); change_state_to(IDLE); return false; } if (currstate == ACTIVE) { if (!trigger_open()) { change_state_to(IDLE); return false; } send_keepalive(); } else if (currstate != OPEN_SENT) { change_state_to(IDLE); return false; } if (!as) { /* sigh */ char foo[64]; snprintf(foo, sizeof(foo), "%u", msg.as); set_property("peer-as", foo); } if (should_log(NORMAL)) log().xprintf("Neighbor is AS %u.\n", (uint32_t)msg.as); holdtimer.start_or_update(msg.holdtime * 1000, false, false); send_keepalive(); localholdtimer.restart(false); change_state_to(OPEN_CONFIRM); return true; } void bgp_neighbor::handle_keepalive() { if (currstate == OPEN_CONFIRM) change_state_to(ESTABLISHED); if (currstate == ESTABLISHED) { holdtimer.restart(); } lastka = tval::now(); } const char *bgp_neighbor::_state_name(state s) { switch (s) { case INACTIVE: return "INACTIVE"; case IDLE: return "IDLE"; case CONNECT: return "CONNECT"; case ACTIVE: return "ACTIVE"; case OPEN_SENT: return "OPEN_SENT"; case OPEN_CONFIRM: return "OPEN_CONFIRM"; case ESTABLISHED: return "ESTABLISHED"; } return "UNKNOWN"; } void bgp_neighbor::change_state_to(state s) { if (s == currstate) return; if (should_log(EXTRADEBUG)) log().xprintf("State change %s -> %s.\n", _state_name(currstate), _state_name(s)); if (s == ESTABLISHED) { prefixcount = 0; g_mrd->mrib().install_listener(this); } else { if (currstate == ESTABLISHED) g_mrd->mrib().origin_lost(this); } if (s <= IDLE) { if (sock.fd() > 0) { send_notification(Cease); ::shutdown(sock.fd(), SHUT_RDWR); sock.unregister(); holdtimer.stop(); } g_mrd->clear_tasks(this); work_pending = false; work_buffer.clear(); } currstate = s; } void bgp_neighbor::prefix_added(const inet6_addr &prefix, mrib_def::metric_def metric, const mrib_def::prefix &pinfo) { bgp_update_message msg; if (pinfo.flags & mrib_def::prefix::NO_EXPORT) return; /* No path to neighbor? */ if (!peer_interface()) return; if (!run_filter(output_filter, prefix)) return; const bgp_prefix *bgppinfo = 0; if (bgp->has_neighbor((bgp_neighbor *)pinfo.owner)) { bgppinfo = (const bgp_prefix *)&pinfo; // Don't advertise IBGP originated routes to IBGP neighs if (mode() == IBGP && ((bgp_neighbor *)pinfo.owner)->mode() == IBGP) return; if (mode() == EBGP && (!bgppinfo->should_export || !bgppinfo->should_advertise)) return; } if (bgppinfo) { msg.origin = bgppinfo->bgp_origin; msg.as_path = bgppinfo->as_path; msg.localpref = bgppinfo->localpref; msg.med = bgppinfo->metric; } else { msg.origin = bgp_update_message::IGP; } in6_addr nh = peer_interface()->primary_addr(); inet6_addr linklocal = *peer_interface()->linklocal(); if (mode() == EBGP) { msg.as_path.prepend(bgp->as_number()); } if (!run_route_map(output_rmap, prefix, nh, msg.as_path, msg.localpref, msg.med)) return; if (!IN6_IS_ADDR_UNSPECIFIED(&nh)) msg.nexthops.push_back(nh); if (!linklocal.is_any()) msg.nexthops.push_back(linklocal); if (msg.nexthops.empty()) return; msg.prefixes.push_back(prefix); send_update(msg); if (should_log(DEBUG)) log().xprintf("Uploaded prefix %{Addr}.\n", prefix); } void bgp_neighbor::prefix_lost(const inet6_addr &prefix, mrib_def::metric_def metric, const mrib_def::prefix &pinfo) { /* XXX unimplemented, route withdrawal */ } void bgp_neighbor::output_prefix_info(base_stream &ctx, const mrib_def::prefix &_info) const { const bgp_prefix &info = (const bgp_prefix &)_info; base_stream &os = ctx.write("AS_PATH:"); for (bgp_as_path::const_iterator i = info.as_path.begin(); i != info.as_path.end(); ++i) os.xprintf(" %u", (uint32_t)*i); os.xprintf(", BGP Metric: %u", (uint32_t)info.metric); if (info.localpref != DEFAULT_LOCAL_PREF) os.xprintf(", LocalPref: %u", (uint32_t)info.localpref); os.newl(); } bool bgp_neighbor::set_property(const char *key, const char *value) { if (!strcmp(key, "peer-as")) { // can't change AS while connected if (currstate > IDLE) return false; uint16_t asnumber; if (!_parse_asnumber(value, asnumber)) return false; AS->set_readonly(); } else if (!strcmp(key, "mode")) { if (!strcasecmp(value, "eBGP") && !strcasecmp(value, "iBGP")) return false; } return node::set_property(key, value); } bool bgp_neighbor::call_method(int id, base_stream &out, const std::vector &args) { switch (id) { case bgp_neigh_method_filter: case bgp_neigh_method_route_map: return conf_filter_rmap(id == bgp_neigh_method_filter, args); case bgp_neigh_method_activate: { if (!args.empty()) return false; if (currstate < IDLE) change_state_to(IDLE); return true; } case bgp_neigh_method_reconnect: return reconnect(); case bgp_neigh_method_debug: return output_info(out, true); case bgp_neigh_method_alias: { if (args.size() != 1) return false; const char *value = args[0].c_str(); inet6_addr addr; /* addresses cant be aliases */ if (addr.set(value)) return false; bgp_neighbor *n = bgp->neighs().get_alias(value); if (n) return n == this; if (!alias.empty()) { if (strcmp(alias.c_str(), value)) { bgp->neighs().remove_alias(alias.c_str()); } } alias = value; bgp->neighs().add_alias(value, this); return true; } } return node::call_method(id, out, args); } bool bgp_neighbor::negate_method(int id, base_stream &out, const std::vector &args) { switch (id) { case bgp_neigh_method_activate: { if (!args.empty()) return false; if (currstate >= IDLE) change_state_to(INACTIVE); return true; } } return node::negate_method(id, out, args); } static inline int _next_seq(const std::map &l) { if (l.empty()) return 100; else return l.rbegin()->first + 100; } bool bgp_neighbor::conf_filter_rmap(bool filter, const std::vector &args) { if (args.empty()) return false; int seq = -1; bool in = false; int k = 1; if (args[0] != "in" && args[0] != "out") { if (args.size() != 3) return false; if (!_parse_int(args[0].c_str(), seq)) return false; if (args[1] == "in") in = true; else if (args[1] == "out") in = false; else return false; k = 2; } else { if (args.size() != 2) return false; in = (args[0] == "in"); } std::map *target = 0; if (filter) target = in ? &input_filter : &output_filter; else target = in ? &input_rmap : &output_rmap; if (seq < 0) seq = _next_seq(*target); (*target)[seq] = args[k]; return true; } bool bgp_neighbor::output_info(base_stream &ctx, const std::vector &) const { return output_info(ctx, false); } static void _dump_fltrmap(base_stream &ctx, const char *type, const std::map &flt) { for (std::map::const_iterator i = flt.begin(); i != flt.end(); ++i) { ctx.xprintf("%i %s %s\n", i->first, type, i->second.c_str()); } } bool bgp_neighbor::output_info(base_stream &ctx, bool debug) const { ctx.writeline(peeraddr_s.c_str()); ctx.inc_level(); if (currstate == ESTABLISHED) { ctx.xprintf("AS: %u\n", (uint32_t)as_number()); ctx.xprintf("Status: Connected for %{duration} [KAs: " "%{duration} / %{duration}]\n", time_duration(tval::now() - lastconnect), time_duration(tval::now() - lastka), time_duration(tval::now() - lastsentka)); if (debug) { ctx.xprintf("InB: %ub OutB: %ub\n", (uint32_t)ibuf.data_length(), (uint32_t)obuf.data_length()); ctx.xprintf("WorkBuffer: %u (Max: %u)\n", (uint32_t)work_buffer.size(), (uint32_t)max_work_buffer_size); } else ctx.xprintf("Prefix Count: %u\n", (uint32_t)prefixcount); } else { ctx.xprintf("Status: Disconnected (current state %s)", _state_name(currstate)); if (currstate > INACTIVE) { ctx.xprintf(", reconnecting in %{duration}", localholdtimer.time_left_d()); } ctx.newl(); } interface *intf = peer_interface(); ctx.xprintf("Peer interface: %s\n", intf ? intf->name() : "None"); if (!input_filter.empty() || !output_filter.empty()) { ctx.writeline("Filters:"); ctx.inc_level(); _dump_fltrmap(ctx, "in", input_filter); _dump_fltrmap(ctx, "out", output_filter); ctx.dec_level(); } if (!input_rmap.empty() || !output_rmap.empty()) { ctx.writeline("Route-maps:"); ctx.inc_level(); _dump_fltrmap(ctx, "in", input_rmap); _dump_fltrmap(ctx, "out", output_rmap); ctx.dec_level(); } ctx.dec_level(); return true; } void bgp_neighbor::trigger_send_peer() { if (!obuf.empty()) sock.monitor(socket_base::Read | socket_base::Write); } base_stream &bgp_neighbor::log() const { return node::log().xprintf("%s ", peeraddr_s.c_str()); } void bgp_neighbor::data_available(uint32_t type) { if (currstate == CONNECT) { connected(); return; } if (type == socket_base::Write) { if (!obuf.empty()) { int consumed = send(sock.fd(), obuf.head(), obuf.data_length(), MSG_DONTWAIT); if (consumed > 0) { obuf.advance_head(consumed); obuf.compact(); } } if (obuf.empty()) sock.monitor(socket_base::Read); return; } int len; if ((len = recv(sock.fd(), ibuf.tail(), ibuf.available_length(), MSG_DONTWAIT)) <= 0) { if (errno != EAGAIN && errno != EINTR && errno != EINPROGRESS) { if (should_log(MESSAGE_ERR)) log().perror("Error while receiving"); change_state_to(IDLE); } return; } ibuf.advance_tail(len); while (1) { bgp_message msg; if (!msg.decode(ibuf)) break; m_stats.counter(AllCount, RX)++; if (should_log(MESSAGE_CONTENT)) log().xprintf("Received %s Message, length = %u\n", msg.type_name(), (uint32_t)msg.len); if (msg.type == BGP_KEEPALIVE) { m_stats.counter(KeepaliveCount, RX)++; handle_keepalive(); } else if (msg.type == BGP_OPEN) { m_stats.counter(OpenCount, RX)++; bgp_open_message open(msg); if (open.decode(ibuf)) { if (!handle_open(open)) { return; } } else { m_stats.counter(OpenCount, Bad)++; } } else if (msg.type == BGP_UPDATE) { m_stats.counter(UpdateCount, RX)++; bgp_update_message update(msg); if (update.decode(ibuf)) build_update_work(update); else m_stats.counter(UpdateCount, Bad)++; } else if (msg.type == BGP_NOTIFICATION) { m_stats.counter(NotificationCount, RX)++; bgp_notification_message notify; if (notify.decode(ibuf)) { if (!handle_notify(notify)) return; } else { m_stats.counter(NotificationCount, Bad)++; } } else { m_stats.counter(AllCount, Bad)++; if (should_log(MESSAGE_ERR)) log().writeline("Received bad message, dropping."); } } ibuf.compact(); if (!work_pending && !work_buffer.empty()) { if (should_log(INTERNAL_FLOW)) log().writeline("Registering WorkPending"); work_pending = true; g_mrd->register_task(this, WorkPending); } } static inline bool has_community(const bgp_communities &coms, const bgp_community &c) { return std::find(coms.begin(), coms.end(), c) != coms.end(); } void bgp_neighbor::event(int event, void *ptr) { if (event == mrd::StartupEvent) { set_destination(peeraddr); return; } if (event != WorkPending) { node::event(event, ptr); return; } if (!work_buffer.empty()) { tms _tmp; clock_t start = times(&_tmp); const work_token &t = work_buffer.front(); if (should_log(MESSAGE_CONTENT)) log().xprintf("Working on prefix %{Addr}\n", t.prefix); if (t.action == InstallPrefix) { if (run_filter(input_filter, t.prefix)) install_prefix(t.prefix, t.origin, t.nexthop, t.as_path, t.communities); } else if (t.action == RemovePrefix) { mrib_def::prefix *pinfo = g_mrd->mrib().get_prefix(t.prefix, this); if (pinfo) { g_mrd->mrib().remove_prefix(pinfo); } } work_buffer.pop_front(); clock_t end = times(&_tmp); /* add this to 'if debug' block */ uint32_t spent = (uint32_t)(((end - start) * 1000) / sysconf(_SC_CLK_TCK)); if (should_log(INTERNAL_FLOW)) log().xprintf("Spent %u milisecs.\n", spent); } /* possibly still work todo */ if (!work_buffer.empty()) { g_mrd->register_task(this, WorkPending); } else { work_pending = false; if (should_log(INTERNAL_FLOW)) log().writeline("Finished all pending Work."); } } void bgp_neighbor::install_prefix(const inet6_addr &prefix, uint8_t origin, const in6_addr &nh, const bgp_as_path &as_path, const bgp_communities &communities) { bgp_prefix *pinfo = (bgp_prefix *)g_mrd->mrib().get_prefix(prefix, this); if (pinfo) { if (pinfo->as_path != as_path) { pinfo = 0; } } bool update = pinfo != 0; if (!pinfo) { pinfo = bgp->prefix_pool.request_obj(this, as_path); if (pinfo) pinfo->nexthop = nh; } else { if (should_log(INTERNAL_FLOW)) log().xprintf("Updating %{Addr}, had previous record.\n", prefix); } if (pinfo) { /* XXX if update, match BGP rules, local-pref, as-path, metric */ bool accepted = run_route_map(input_rmap, prefix, pinfo->nexthop, pinfo->as_path, pinfo->metric, pinfo->localpref); if (accepted) { pinfo->bgp_origin = origin; if (has_community(communities, no_export)) pinfo->should_export = false; if (has_community(communities, no_advertise)) pinfo->should_advertise = false; pinfo->intf = peer_interface(); /* this is bogus, we must have a BGP mrib and only * install prefixes into the main mrib that are better * in terms of local-pref, as_path, etc */ pinfo->metric = pinfo->as_path.size() * 10 + (6000 - (20 * pinfo->localpref)); if (update) { g_mrd->mrib().update_prefix(pinfo); } else { if (g_mrd->mrib().install_prefix(prefix, pinfo)) { prefixcount++; } else { if (should_log(DEBUG)) log().xprintf("Failed to install prefix %{Addr}.\n", prefix); } } } else { if (update) g_mrd->mrib().remove_prefix(pinfo); else delete pinfo; if (should_log(EXTRADEBUG)) log().xprintf("Filter rejected prefix %{Addr}.\n", prefix); } } else { if (should_log(DEBUG)) log().xprintf("Failed to install prefix %{Addr}, " "not enough memory.\n", prefix); } } bool bgp_neighbor::run_filter(const std::map &args, const inet6_addr &prefix) const { for (std::map::const_iterator i = args.begin(); i != args.end(); ++i) { bgp_acl *acl = bgp->get_acl(i->second.c_str()); if (!acl || !acl->accepts(prefix)) return false; } return true; } bool bgp_neighbor::run_route_map(const std::map &args, const inet6_addr &prefix, in6_addr &nh, bgp_as_path &aspath, mrib_def::metric_def &m, uint32_t &localpref) const { for (std::map::const_iterator i = args.begin(); i != args.end(); ++i) { bgp_rmap *rmap = bgp->get_rmap(i->second.c_str()); if (!rmap || !rmap->applies(prefix, nh, aspath, m, localpref)) return false; } return true; } void bgp_neighbor::build_update_work(bgp_update_message &msg) { if (should_log(INTERNAL_FLOW)) log().xprintf("Handle update with %u prefixes and %u " "nexthops.\n", (uint32_t)msg.prefixes.size(), (uint32_t)msg.nexthops.size()); if (msg.nexthops.empty()) return; work_token t; t.origin = msg.origin; t.as_path = msg.as_path; t.communities = msg.communities; for (std::vector::const_iterator i = msg.prefixes.begin(); i != msg.prefixes.end(); ++i) { t.action = InstallPrefix; t.prefix = *i; t.nexthop = *msg.nexthops.begin(); work_buffer.push_back(t); } for (std::vector::const_iterator i = msg.unreach_prefixes.begin(); i != msg.unreach_prefixes.end(); i++) { t.action = RemovePrefix; t.prefix = *i; t.nexthop = in6addr_any; work_buffer.push_back(t); } if (work_buffer.size() > max_work_buffer_size) max_work_buffer_size = work_buffer.size(); } bool bgp_neighbor::handle_notify(bgp_notification_message &msg) { const char *err = "Unknown", *suberr = "Unknown"; if (valid_error(msg.errorcode, msg.suberrorcode)) { err = error_messages[msg.errorcode-1]; if (msg.errorcode >= 1 && msg.errorcode <= 3) suberr = suberror_messages[msg.errorcode-1][msg.suberrorcode-1]; } if (should_log(NORMAL)) log().xprintf("Neighbour terminated connection, reason: %s (%s)\n", err, suberr); change_state_to(IDLE); return false; } static const bgp_open_message::capability ipv6_multicast(bgp_open_message::IPV6, bgp_open_message::MULTICAST); bool bgp_neighbor::encode_msg(const bgp_message &msg) { if (!msg.encode(obuf)) { if (should_log(EXTRADEBUG)) log().xprintf("Failed to encode %s message.\n", msg.type_name()); return false; } return true; } bool bgp_neighbor::trigger_open() { bgp_open_message msg; msg.as = bgp->as_number(); msg.holdtime = holdtime(); msg.bgpid = bgp->id(); msg.capabilities.push_back(ipv6_multicast); if (!send_open(msg)) return false; change_state_to(OPEN_SENT); return true; } void bgp_neighbor::send_keepalive() { bgp_message ka(4); if (!ka.encode(obuf)) { if (should_log(DEBUG)) log().writeline("Failed to send Keep-Alive, no buffer space."); change_state_to(IDLE); } else { m_stats.counter(KeepaliveCount, TX)++; trigger_send_peer(); lastsentka = tval::now(); if (should_log(MESSAGE_SIG)) log().writeline("Sent Keep-Alive"); } } void bgp_neighbor::send_notification(uint8_t code, uint8_t subcode) { bgp_notification_message msg; msg.errorcode = code; msg.suberrorcode = subcode; if (encode_msg(msg)) { m_stats.counter(NotificationCount, TX)++; trigger_send_peer(); } } void bgp_neighbor::handle_localholdtime() { if (should_log(INTERNAL_FLOW)) log().xprintf("Handle holdtime timer in %s\n", _state_name(currstate)); if (currstate == ESTABLISHED) send_keepalive(); else if (currstate == IDLE) start_connect(); else if (currstate > IDLE) change_state_to(IDLE); } bool bgp_neighbor::send_message(const bgp_message &msg) { if (!encode_msg(msg)) return false; trigger_send_peer(); return true; } bool bgp_neighbor::send_update(const bgp_update_message &msg) { if (send_message(msg)) { m_stats.counter(UpdateCount, TX)++; return true; } return false; } bool bgp_neighbor::send_open(const bgp_open_message &msg) { if (send_message(msg)) { m_stats.counter(OpenCount, TX)++; return true; } return false; } bool bgp_neighbor::reconnect() { if (currstate > INACTIVE) { change_state_to(IDLE); localholdtimer.start_or_update(1000, true); } return true; } bgp_acl::bgp_acl(bgp_access_lists *parent, const char *name) : node(parent, name) { } bool bgp_acl::check_startup() { if (!node::check_startup()) return false; import_methods(bgp_acl_methods); return true; } bool bgp_acl::call_method(int id, base_stream &out, const std::vector &args) { switch (id) { case bgp_acl_method_prefix: return prefix(args); } return node::call_method(id, out, args); } bool bgp_acl::negate_method(int id, base_stream &out, const std::vector &args) { switch (id) { case bgp_acl_method_prefix: return no_prefix(args); } return node::negate_method(id, out, args); } bool bgp_acl::prefix(const std::vector &args) { entry e; bool has_pf = false; int seq = -1; e.mode = false; e.ge = e.le = -1; for (std::vector::const_iterator i = args.begin(); i != args.end(); ++i) { if (*i == "permit" || *i == "deny") { if (has_pf) return false; e.mode = (*i == "permit"); ++i; if (i == args.end()) return false; if (!e.prefix.set(i->c_str())) return false; has_pf = true; } else if (*i == "seq") { ++i; if (seq != -1 || i == args.end()) return false; char *end; uint32_t val = strtoul(i->c_str(), &end, 10); if (*end || val > INT_MAX) return false; seq = val; } else if (*i == "ge" || *i == "le") { bool l = (*i == "le"); ++i; if (i == args.end()) return false; if ((l && (e.le != -1)) || (!l && (e.ge != -1))) return false; char *end; uint32_t val = strtoul(i->c_str(), &end, 10); if (*end || val > 128) return false; if (l) e.le = val; else e.ge = val; } else { return false; } } if (e.ge != -1 && e.le != -1) { if (e.ge > e.le) return false; } if (seq == -1) { if (m_entries.empty()) seq = 100; else seq = (m_entries.rbegin()->first / 100) * 100 + 200; } m_entries[seq] = e; return true; } bool bgp_acl::no_prefix(const std::vector &args) { return false; } bool bgp_acl::output_info(base_stream &out, const std::vector &args) const { if (!args.empty()) return false; for (entries::const_iterator i = m_entries.begin(); i != m_entries.end(); ++i) { out.xprintf("prefix seq %i %s %{Addr}", i->first, i->second.mode ? "permit" : "deny", i->second.prefix); if (i->second.ge != -1) out.xprintf(" ge %i", (int)i->second.ge); if (i->second.le != -1) out.xprintf(" le %i", (int)i->second.le); out.writeline(";"); } return true; } bool bgp_acl::accepts(const inet6_addr &prefix) const { for (entries::const_iterator i = m_entries.begin(); i != m_entries.end(); ++i) { if (i->second.prefix.matches(prefix)) { if (i->second.ge != -1) if (i->second.ge > (int)prefix.prefixlen) continue; if (i->second.le != -1) if (i->second.le < (int)prefix.prefixlen) continue; return i->second.mode; } } return false; } bgp_access_lists::bgp_access_lists(node *parent) : node(parent, "access-list") { } node *bgp_access_lists::create_child(const char *name) { node *n = new bgp_acl(this, name); if (!n || !n->check_startup()) { delete n; return 0; } add_child(n); return n; } bool bgp_access_lists::output_info(base_stream &out, const std::vector &args) const { for (properties::const_iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (i->second.is_child()) { bgp_acl *n = (bgp_acl *)i->second.get_node(); out.xprintf("access-list %s {\n", n->name()); out.inc_level(); n->output_info(out, args); out.dec_level(); out.writeline("}"); } } return true; } bgp_rmap::bgp_rmap(bgp_route_maps *parent, const char *name) : node(parent, name) { } bool bgp_rmap::check_startup() { if (!node::check_startup()) return false; import_methods(bgp_rmap_methods); return true; } bool bgp_rmap::call_method(int id, base_stream &out, const std::vector &args) { if (id == bgp_rmap_method_match) { if (args.size() != 1) return false; m_match_filter = args[0]; return true; } else if (id == bgp_rmap_method_prepend_aspath) { if (args.size() != 1) return false; action a; a.type = PREPEND_ASPATH; if (!_parse_asnumber(args[0].c_str(), a.v.as)) return false; m_actions.push_back(a); return true; } else if (id == bgp_rmap_method_set) { if (args.size() != 2) return false; action a; if (args[0] == "local-pref" || args[0] == "metric") { a.type = (args[0] == "local-pref") ? LOCAL_PREF : METRIC; if (!_parse_int(args[1], a.v.metric)) return false; if (a.v.metric < 0) return false; /* XXX */ if (a.type == LOCAL_PREF && a.v.metric > 300) return false; } else if (args[0] == "community") { a.type = COMMUNITY; bgp_community c; if (!_parse_community(args[1], c)) return false; a.v.c.first = c.first; a.v.c.second = c.second; } else return false; m_actions.push_back(a); return true; } return node::call_method(id, out, args); } bool bgp_rmap::negate_method(int id, base_stream &out, const std::vector &args) { if (id == bgp_rmap_method_match) { m_match_filter = std::string(); return true; } else if (id == bgp_rmap_method_prepend_aspath) { } else if (id == bgp_rmap_method_set) { } return node::negate_method(id, out, args); } bool bgp_rmap::output_info(base_stream &out, const std::vector &args) const { if (!args.empty()) return false; if (!m_match_filter.empty()) out.xprintf("match %s;\n", m_match_filter.c_str()); for (actions::const_iterator i = m_actions.begin(); i != m_actions.end(); ++i) { switch (i->type) { case PREPEND_ASPATH: out.xprintf("prepend-aspath %u;\n", (uint32_t)i->v.as); break; case LOCAL_PREF: out.xprintf("set local-pref %u;\n", (uint32_t)i->v.metric); break; case METRIC: out.xprintf("set metric %u;\n", (uint32_t)i->v.metric); break; case COMMUNITY: out.xprintf("set community %u:%u;\n", (uint32_t)i->v.c.first, (uint32_t)i->v.c.second); break; } } return true; } bool bgp_rmap::applies(const inet6_addr &prefix, in6_addr &nh, bgp_as_path &aspath, mrib_def::metric_def &m, uint32_t &localpref) const { if (!m_match_filter.empty()) { bgp_acl *acl = bgp->get_acl(m_match_filter.c_str()); if (!acl || !acl->accepts(prefix)) return false; } for (actions::const_iterator i = m_actions.begin(); i != m_actions.end(); ++i) { if (i->type == PREPEND_ASPATH) { /* i->v.as */ } else if (i->type == LOCAL_PREF) { localpref = i->v.metric; } else if (i->type == METRIC) { m = i->v.metric; } else if (i->type == COMMUNITY) { /* i->v.c */ } } return true; } bgp_route_maps::bgp_route_maps(node *parent) : node(parent, "route-map") { } node *bgp_route_maps::create_child(const char *name) { node *n = new bgp_rmap(this, name); if (!n || !n->check_startup()) { delete n; return 0; } add_child(n); return n; } bool bgp_route_maps::output_info(base_stream &out, const std::vector &args) const { for (properties::const_iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (i->second.is_child()) { bgp_rmap *n = (bgp_rmap *)i->second.get_node(); out.xprintf("route-map %s {\n", n->name()); out.inc_level(); n->output_info(out, args); out.dec_level(); out.writeline("}"); } } return true; } base_stream &bgp_module::log() const { return node::log().write("BGP, "); } mrd6-0.9.6/src/bgp/bgp_def.cpp0000644000175000017500000002417610600744155014531 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * bgp_def.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include enum { ORIGIN = 1, AS_PATH = 2, MULTI_EXIT_DISC = 4, LOCAL_PREF = 5, COMMUNITIES = 8, MP_REACH_NLRI = 14, MP_UNREACH_NLRI = 15 }; enum { CAPABILITY_NEGOTIATION = 2 }; enum { AS_SEQUENCE = 2 }; enum { IPV4_AFI = 1, IPV6_AFI = 2 }; enum { UNICAST_SAFI = 1, MULTICAST_SAFI = 2, }; struct bgp_base_header { uint8_t marker[16]; uint16n_t length; uint8_t type; } __attribute__ ((packed)); static const uint8_t good_marker[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; bgp_message::bgp_message() : len(0), type(0) { } bgp_message::bgp_message(const bgp_message &msg) : len(msg.len), type(msg.type) { } bgp_message::bgp_message(uint8_t basetype) : len(19), type(basetype) { } bgp_message::~bgp_message() {} bool bgp_message::decode(encoding_buffer &buff) { if (!buff.require(19)) return false; bgp_base_header *h = (bgp_base_header *)buff.head(); if (memcmp(h->marker, good_marker, 16) != 0) return false; len = ntoh(h->length); type = h->type; if (!buff.require(len)) return false; buff.eat(19); return true; } bool bgp_message::encode(encoding_buffer &buff) const { if (!buff.tail_require(length())) return false; memcpy(buff.put(16), good_marker, 16); buff.put() = hton(length()); buff.put() = type; return true; } const char *bgp_message::type_name() const { switch (type) { case 1: return "OPEN"; case 2: return "UPDATE"; case 3: return "NOTIFICATION"; case 4: return "KEEPALIVE"; default: return "UNKNOWN"; } } bgp_open_message::bgp_open_message() : bgp_message(1) { version = 4; as = 0; holdtime = 0; bgpid = 0; } bgp_open_message::bgp_open_message(const bgp_message &msg) : bgp_message(msg) { } uint16_t bgp_open_message::length() const { return bgp_message::length() + 9 + 1 + 8; } bool bgp_open_message::decode(encoding_buffer &b) { version = b.eat(); as = ntoh(b.eat()); holdtime = ntoh(b.eat()); bgpid = ntoh(b.eat()); uint32_t extralen = b.eat(); for (uint32_t i = 0; i < extralen; ) { uint8_t type = b.eat(); uint8_t len = b.eat(); if (type == CAPABILITY_NEGOTIATION) { uint8_t code = b.eat(); uint32_t codelen = b.eat(); if (code == 1 && ((codelen % 4) == 0)) { for (uint32_t j = 0; j < codelen; j += 4) { uint16_t family = ntoh(b.eat()); b.eat(1); uint8_t subfamily = b.eat(); capabilities.push_back(capability(family, subfamily)); } } else { b.eat(codelen); } } else { b.eat(len); } i += len + 2; } return true; } bool bgp_open_message::encode(encoding_buffer &b) const { if (!bgp_message::encode(b)) return false; b.put() = version; b.put() = hton(as); b.put() = hton(holdtime); b.put() = hton(bgpid); if (capabilities.empty()) { b.put() = 0; } else { b.put() = 2 + (2 + 4 * capabilities.size()); b.put() = CAPABILITY_NEGOTIATION; b.put() = 2 + 4 * capabilities.size(); b.put() = 1; // code b.put() = 4 * capabilities.size(); for (std::vector::const_iterator i = capabilities.begin(); i != capabilities.end(); i++) { b.put() = hton(i->first); b.put() = 0; b.put() = i->second; } } return true; } bgp_update_message::bgp_update_message() : bgp_message(2) { origin = 2; localpref = 0; med = 0; } bgp_update_message::bgp_update_message(const bgp_message &msg) : bgp_message(msg) { } static void read_uint32(encoding_buffer &b, uint32_t *target, int len) { if (len == 4) (*target) = ntoh(b.eat()); else b.eat(len); } bool bgp_update_message::decode(encoding_buffer &b) { uint16_t url = ntoh(b.eat()); b.eat(url); uint32_t tpal = ntoh(b.eat()); uint32_t i = 0; while (i < tpal) { uint8_t flags = b.eat(); uint8_t type = b.eat(); uint16_t len; if (flags & 0x10) len = ntoh(b.eat()); else len = b.eat(); int avail; uint16_t family; uint8_t subfamily; switch (type) { case ORIGIN: origin = b.eat(); b.eat(len - 1); break; case AS_PATH: avail = len; while (avail >= 2) { uint8_t segtype = b.eat(); uint16_t seglen = b.eat(); if (segtype == AS_SEQUENCE) { for (uint16_t j = 0; j < seglen; j++) as_path.push_back(ntoh(b.eat())); } else { b.eat(2 * seglen); } avail -= 2 * seglen + 2; } b.eat(avail); break; case MULTI_EXIT_DISC: read_uint32(b, &med, len); break; case LOCAL_PREF: read_uint32(b, &localpref, len); break; case COMMUNITIES: for (uint8_t j = 0; j < len; j += 4) communities.push_back(bgp_community(ntoh(b.eat()), ntoh(b.eat()))); break; case MP_REACH_NLRI: family = ntoh(b.eat()); subfamily = b.eat(); if (family == IPV6_AFI && subfamily == MULTICAST_SAFI) { // NextHop uint8_t nexthoplen = b.eat(); for (uint8_t j = 0; j < nexthoplen; j += 16) { in6_addr addr = b.eat(); nexthops.push_back(addr); } // Subnetwork uint8_t subnetlen = b.eat(); b.eat(subnetlen); // Prefixes int avail = len - 3 - nexthoplen - 1 - subnetlen - 1; while (avail > 0) { inet6_addr prefix; prefix.prefixlen = b.eat(); int plen = prefix.prefixlen / 8; if (prefix.prefixlen & 0x7) { plen++; } memcpy(&prefix.addr, b.eat(plen), plen); avail -= 1 + plen; prefixes.push_back(prefix); } } else { b.eat(len - 3); } break; case MP_UNREACH_NLRI: family = ntoh(b.eat()); subfamily = b.eat(); if (family == IPV6_AFI && subfamily == MULTICAST_SAFI) { // Prefixes int avail = len - 3; while (avail > 0) { inet6_addr prefix; prefix.prefixlen = b.eat(); int plen = prefix.prefixlen / 8; if (prefix.prefixlen & 0x7) plen++; memcpy(&prefix.addr, b.eat(plen), plen); avail -= 1 + plen; unreach_prefixes.push_back(prefix); } } else { b.eat(len - 3); } break; default: b.eat(len); break; } i += len + 3 + ((flags & 0x10) ? 1 : 0); } return true; } uint16_t bgp_update_message::length() const { uint16_t length = bgp_message::length() + 2 + 2 + (3 + 1) // origin + (3 + 2 + 2 * as_path.size()) // as_path + (communities.empty() ? 0 : (3 + 4 * communities.size())) // communities + (3 + 3 + (1 + 16 * nexthops.size()) + (1)); // mp_reach_nlri for (std::vector::const_iterator i = prefixes.begin(); i != prefixes.end(); ++i) { length += i->prefixlen / 8; if (i->prefixlen & 0x7) length++; length++; } return length; } bool bgp_update_message::encode(encoding_buffer &b) const { if (!bgp_message::encode(b)) return false; uint16_t len = length(); len -= bgp_message::length(); len -= 2 + 2; b.put() = hton((uint16_t)0); b.put() = hton(len); b.put() = 0x40; b.put() = ORIGIN; b.put() = 1; b.put() = origin; b.put() = 0x40; b.put() = AS_PATH; b.put() = 2 + 2 * as_path.size(); b.put() = AS_SEQUENCE; b.put() = as_path.size(); for (bgp_as_path::const_iterator i = as_path.begin(); i != as_path.end(); ++i) b.put() = hton(*i); if (!communities.empty()) { b.put() = 0xc0; b.put() = COMMUNITIES; b.put() = 4 * communities.size(); for (std::vector::const_iterator i = communities.begin(); i != communities.end(); ++i) { b.put() = hton(i->first); b.put() = hton(i->second); } } b.put() = 0x80; b.put() = MP_REACH_NLRI; uint8_t &nlri_len = b.put(); nlri_len = 2 + 1 + (1 + 16 * nexthops.size()) + 1; b.put() = hton((uint16_t)IPV6_AFI); b.put() = MULTICAST_SAFI; b.put() = 16 * nexthops.size(); for (std::vector::const_iterator i = nexthops.begin(); i != nexthops.end(); ++i) { b.put() = i->address(); } b.put() = 0; for (std::vector::const_iterator i = prefixes.begin(); i != prefixes.end(); ++i) { uint8_t len = (i->prefixlen / 8); if (i->prefixlen & 0x7) len++; b.put() = i->prefixlen; memcpy(b.put(len), &i->addr, len); nlri_len += len + 1; } return true; } bgp_notification_message::bgp_notification_message() : bgp_message(3), errorcode(0), suberrorcode(0) {} bgp_notification_message::bgp_notification_message(const bgp_message &msg) : bgp_message(msg) { } uint16_t bgp_notification_message::length() const { return bgp_message::length() + 2; } bool bgp_notification_message::decode(encoding_buffer &b) { errorcode = b.eat(); suberrorcode = b.eat(); return true; } bool bgp_notification_message::encode(encoding_buffer &b) const { if (!bgp_message::encode(b)) return false; b.put() = errorcode; b.put() = suberrorcode; return true; } mrd6-0.9.6/src/mld/0000755000175000017500000000000010746353706012442 5ustar hugohugomrd6-0.9.6/src/mld/mld_conf.cpp0000644000175000017500000001173010550053317014715 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mld_conf.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include enum { mld_intfconf_method_signaling_filter = 3000, }; static const method_info mld_intfconf_node_methods[] = { { "signaling-filter", 0, mld_intfconf_method_signaling_filter, false, 0 }, { 0 } }; mld_intfconf_node::mld_intfconf_node(intfconf *conf) : intfconf_node(conf, "mld") { } bool mld_intfconf_node::check_startup() { if (!intfconf_node::check_startup()) return false; import_methods(mld_intfconf_node_methods); return true; } bool mld_intfconf_node::fill_defaults() { instantiate_property_u("robustness", 2); instantiate_property_u("query_interval", 125000); instantiate_property_u("query_response_interval", 10000); instantiate_property_u("startup_query_interval", 125000 / 4); instantiate_property_u("startup_query_count", 2); instantiate_property_u("last_listener_query_interval", 1000); instantiate_property_u("last_listener_query_count", 2); instantiate_property_u("unsolicited_report_interval", 1000); instantiate_property_u("version", 2); instantiate_property_b("querier", true); instantiate_property("proxy_to", property_def::VAL_STRING); return m_properties.size() == 11; } bool mld_intfconf_node::call_method(int id, base_stream &out, const std::vector &args) { if (id == mld_intfconf_method_signaling_filter) { std::set filter; for (std::vector::const_iterator i = args.begin(); i != args.end(); ++i) { inet6_addr addr; if (!addr.set(i->c_str())) return false; filter.insert(filter.end(), addr); } m_signaling_filter = filter; return true; } return intfconf_node::call_method(id, out, args); } bool mld_intfconf_node::set_property(const char *key, const char *value) { if (!next_similiar_node()->has_property(key)) return false; return set_property_inst(key, property_def::VAL_UNSIGNED, value); } uint32_t mld_intfconf_node::robustness() const { return get_property_unsigned("robustness"); } uint32_t mld_intfconf_node::query_interval() const { return get_property_unsigned("query_interval"); } uint32_t mld_intfconf_node::query_response_interval() const { return get_property_unsigned("query_response_interval"); } uint32_t mld_intfconf_node::mali() const { return robustness() * query_interval() + query_response_interval(); } uint32_t mld_intfconf_node::other_querier_present_timeout() const { return robustness() * query_interval() + query_response_interval() / 2; } uint32_t mld_intfconf_node::startup_query_interval() const { return get_property_unsigned("startup_query_interval"); } uint32_t mld_intfconf_node::startup_query_count() const { return get_property_unsigned("startup_query_count"); } uint32_t mld_intfconf_node::last_listener_query_interval() const { return get_property_unsigned("last_listener_query_interval"); } uint32_t mld_intfconf_node::last_listener_query_count() const { return get_property_unsigned("last_listener_query_count"); } uint32_t mld_intfconf_node::last_listener_query_time() const { return last_listener_query_interval() * last_listener_query_count(); } uint32_t mld_intfconf_node::unsolicited_report_interval() const { return get_property_unsigned("unsolicited_report_interval"); } uint32_t mld_intfconf_node::older_version_querier_present_timeout() const { /* XXX query_interval should be the last query_interval received */ return robustness() * query_interval() + query_response_interval(); } uint32_t mld_intfconf_node::version() const { return get_property_unsigned("version"); } bool mld_intfconf_node::querier() const { return get_property_bool("querier"); } const std::set &mld_intfconf_node::signaling_filter() const { return m_signaling_filter; } mld_groupconf_node::mld_groupconf_node(groupconf *parent) : groupconf_node(parent, "mld") { } bool mld_groupconf_node::fill_defaults() { if (!instantiate_property_b("forward", false)) return false; return true; } bool mld_groupconf_node::set_property(const char *key, const char *value) { return !strcmp(key, "forward") && set_property_inst(key, property_def::VAL_BOOL, value); } mrd6-0.9.6/src/mld/mld_router.cpp0000644000175000017500000010263410600737663015325 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mld_router.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef IP6_ALERT_MLD #define IP6_ALERT_MLD 0x00 #endif mld_router *mld = 0; in6_addr in6addr_linkscope_allnodes; enum { mld_router_method_show_groups = 9500, }; static const method_info mld_router_methods[] = { { "groups", "Display MLD membership information", mld_router_method_show_groups, true, 0 }, { 0 } }; enum { QueryCount = 0, ReportCount, ReductionCount, ReportV2Count, MessageCount }; enum { RX = 0, TX, Bad }; static const char *stats_descriptions[] = { "Query", "Report", "Reduction", "Report (V2)", }; mld_interface::mld_interface() : interface_node(mld), mif_query_timer_id("mld query", this, std::mem_fun(&mld_interface::handle_send_query_timeout)), mif_other_querier_present_timer_id("other mld querier present", this, std::mem_fun(&mld_interface::handle_other_querier_present_timeout)), m_stats(this, MessageCount, stats_descriptions) { mif_isquerier = true; mif_mld_version = 2; mif_query_count = 0; } mld_interface::~mld_interface() { } void mld_interface::attached(interface *node) { interface_node::attached(node); mif_isquerier = conf()->querier(); mif_mld_version = conf()->version(); std::string tmrname; tmrname = "mld query ("; tmrname += owner()->name(); tmrname += ")"; mif_query_timer_id.name = tmrname; tmrname = "other mld querier present ("; tmrname += owner()->name(); tmrname += ")"; mif_other_querier_present_timer_id.name = tmrname; mif_query_timer_id.update(conf()->query_interval(), true); mif_other_querier_present_timer_id.update( conf()->other_querier_present_timeout(), false); } bool mld_interface::check_startup() { if (!m_stats.setup("MLD Statistics")) return false; m_stats.disable_counter(ReportCount, TX); m_stats.disable_counter(ReductionCount, TX); m_stats.disable_counter(ReportV2Count, TX); return interface_node::check_startup(); } bool mld_interface::output_info(base_stream &ctx, const std::vector &) const { ctx.xprintf("MLD, version %i", (int)mif_mld_version); if (owner()->linklocals().empty()) { ctx.writeline(", not running"); return true; } ctx.newl(); ctx.inc_level(); if (!mif_isquerier) { if (mif_querier_addr.is_any()) { ctx.writeline("Querier: None"); } else { ctx.xprintf("Querier: %{Addr} for %{duration}\n", mif_querier_addr, mif_other_querier_present_timer_id.time_left_d()); } } else { ctx.writeline("Querier: self"); } ctx.dec_level(); return true; } void mld_interface::address_added_or_removed(bool added, const inet6_addr &addr) { if (added) { if (addr.is_linklocal()) { mif_query_count = 0; if (mif_isquerier) { start_querying(); } } } else { if (addr.is_linklocal()) { // XXX // clear interface references from group etc // stop timers and reset } } } void mld_interface::start_querying() { if (conf()->startup_query_count() <= 1) { mif_query_timer_id.update(conf()->query_interval(), true); mif_query_count = 0xffffffff; } else { mif_query_timer_id.update(conf()->startup_query_interval(), true); mif_query_count = 1; } mif_query_timer_id.start(); send_mld_query(in6addr_any); } void mld_interface::shutdown() { owner()->dettach_node(this); mif_query_timer_id.stop(); mif_other_querier_present_timer_id.stop(); } static inline bool _is_mld_message(int code) { switch (code) { case MLD_LISTENER_QUERY: case MLD_LISTENER_REPORT: case MLD_LISTENER_REDUCTION: case MLDv2_LISTENER_REPORT: case MLDv2_LISTENER_REPORT_OLD: return true; default: return false; } } static inline const char *_mld_message_name(int code) { switch (code) { case MLD_LISTENER_REPORT: return "MLDv1 Membership Report"; case MLD_LISTENER_REDUCTION: return "MLDv1 Membership Reduction"; case MLD_LISTENER_QUERY: return "MLD Membership Query"; case MLDv2_LISTENER_REPORT: return "MLDv2 Membership Report"; case MLDv2_LISTENER_REPORT_OLD: return "MLDv2 Membership Report (old)"; default: return "Unknown"; } } void mld_interface::message_available(const in6_addr &from, const in6_addr &to, icmp6_hdr *icmphdr, int len) { if (should_log(MESSAGE_SIG)) { log().xprintf("Received a %s from %{addr} to %{addr}\n", _mld_message_name(icmphdr->icmp6_type), from, to); } switch (icmphdr->icmp6_type) { case MLD_LISTENER_REPORT: handle_mldv1_membership_report(from, (mldv1 *)icmphdr); break; case MLD_LISTENER_REDUCTION: handle_mldv1_membership_reduction(from, (mldv1 *)icmphdr); break; case MLD_LISTENER_QUERY: handle_membership_query(from); break; case MLDv2_LISTENER_REPORT: case MLDv2_LISTENER_REPORT_OLD: handle_mldv2_membership_report(from, (mldv2_report *)icmphdr, len); break; } } void mld_interface::icmp_message_available(const in6_addr &from, const in6_addr &to, icmp6_hdr *icmphdr, int len) { if (!_is_mld_message(icmphdr->icmp6_type)) { return; } if (conf()->has_property("proxy_to")) { const char *newintfname = conf()->get_property_string("proxy_to"); interface *newintf = g_mrd->get_interface_by_name(newintfname); if (newintf) { mld_interface *newmldintf = mld->get_interface(newintf->index()); if (newmldintf) { if (newmldintf == this) { if (should_log(DEBUG)) log().writeline("Trying to proxy to same interface?"); } else { newmldintf->message_available(from, to, icmphdr, len); return; } } } if (should_log(DEBUG)) { log().xprintf("Tried to redirect MLD signaling to %s" "but failed. Signaling is being dropped.\n", newintfname); } return; } message_available(from, to, icmphdr, len); } bool mld_interface::send_mld_query(const in6_addr &addr) { if (should_log(MESSAGE_SIG)) { if (IN6_IS_ADDR_UNSPECIFIED(&addr)) { log().writeline("Sending General Query"); } else { log().xprintf("Sending Multicast Group Address " "specific Query for %{addr}\n", addr); } } bool res; if (mif_mld_version >= 2) { res = send_mldv2_query(addr); } else { res = send_mldv1_query(addr); } if (res) { m_stats.counter(QueryCount, TX)++; mld->stats().counter(QueryCount, TX)++; } else { /*m_stats.counter(FailedSendQueryCount)++; mld->stats().counter(FailedSendQueryCount)++;*/ } return res; } bool mld_interface::send_mldv2_query(const in6_addr &addr) { mldv2_query query; query.construct_query(addr, conf()); return mld->send_icmp(owner(), in6addr_linkscope_allnodes, (icmp6_hdr *)&query, query.length()); } bool mld_interface::send_mldv1_query(const in6_addr &addr) { mldv1_query q; q.construct(addr, conf()); return mld->send_icmp(owner(), in6addr_linkscope_allnodes, (icmp6_hdr *)&q, q.length()); } bool mld_interface::send_mld_query(const in6_addr &addr, const std::set &sources) { if (!sources.empty() && mif_mld_version >= 2) { mldv2_query *query = g_mrd->opktb->header(); query->construct_query(addr, conf()); query->nsrcs = hton((uint16_t)sources.size()); in6_addr *addr = g_mrd->opktb->header(sizeof(mldv2_query)); for (std::set::const_iterator i = sources.begin(); i != sources.end(); ++i) { *addr = *i; addr++; } if (mld->send_icmp(owner(), in6addr_linkscope_allnodes, (icmp6_hdr *)query, query->length())) { m_stats.counter(QueryCount, TX)++; mld->stats().counter(QueryCount, TX)++; return true; } else { /*m_stats.counter(FailedSendQueryCount)++; mld->stats().counter(FailedSendQueryCount)++;*/ return false; } } return true; } struct create_group_mld_context : mrd::create_group_context { int mld_mode; address_set mld_sources; }; void mld_interface::handle_mode_change_for_group(int ver, const inet6_addr &reqsrc, const inet6_addr &grpaddr, int mode, const address_set &srcs) { const std::set &signaling_filter = conf()->signaling_filter(); if (!signaling_filter.empty()) { bool accept = false; for (std::set::const_iterator i = signaling_filter.begin(); !accept && i != signaling_filter.end(); ++i) { accept = i->matches(grpaddr); } if (!accept) { if (should_log(DEBUG)) { log().xprintf("Rejected mode change for group " "%{Addr} by filter.\n", grpaddr); } return; } } if (((mode == MLD_SSM_MODE_INCLUDE || mode == MLD_SSM_CHANGE_TO_INCLUDE) && srcs.empty()) || (mode == MLD_SSM_ALLOW_SOURCES || mode == MLD_SSM_BLOCK_SOURCES)) { /* if mode for this record is Include {}, TO_IN {}, ALLOW {A} or BLOCK {A} * dont create group if it doesnt exist */ group *grp = g_mrd->get_group_by_addr(grpaddr); if (grp) { mld_group_interface *oif = mld->match(grp)->local_oif(this); if (oif) oif->refresh(reqsrc, mode, srcs); } return; } create_group_mld_context *ctx = new create_group_mld_context; if (!ctx) return; ctx->iif = owner()->index(); ctx->groupaddr = grpaddr; ctx->requester = reqsrc; ctx->mld_mode = mode; ctx->mld_sources = srcs; g_mrd->create_group(mld, this, ctx); } void mld_interface::event(int event, void *ptr) { if (event != mrd::CreatedGroup) { interface_node::event(event, ptr); return; } create_group_mld_context *ctx = (create_group_mld_context *)ptr; if (ctx->result) { mld_group_interface *oif = mld->match(ctx->result)->local_oif(this); if (oif) oif->refresh(ctx->requester, ctx->mld_mode, ctx->mld_sources); } else { if (mld->should_log(VERBOSE)) { mld->log().xprintf("Creation of group %{Addr}" " was denied for %{Addr}\n", ctx->groupaddr, ctx->requester); } } delete ctx; } void mld_interface::handle_mldv1_membership_report(const in6_addr &src, mldv1 *mldhdr) { m_stats.counter(ReportCount, RX)++; mld->stats().counter(ReportCount, RX)++; if (!IN6_IS_ADDR_MULTICAST(&mldhdr->mcaddr)) { m_stats.counter(ReportCount, Bad)++; mld->stats().counter(ReportCount, Bad)++; return; } if (IN6_IS_ADDR_MC_NODELOCAL(&mldhdr->mcaddr) || IN6_IS_ADDR_MC_LINKLOCAL(&mldhdr->mcaddr)) return; handle_mode_change_for_group(1, src, mldhdr->mcaddr, MLD_SSM_MODE_EXCLUDE, address_set()); } void mld_interface::handle_mldv2_membership_report(const in6_addr &src, mldv2_report *mldhdr, int len) { m_stats.counter(ReportV2Count, RX)++; mld->stats().counter(ReportV2Count, RX)++; mldv2_mrec *mrec = mldhdr->mrecs(); int clen = 0; int reccount = ntoh(mldhdr->nmrecs()); for (int i = 0; i < reccount && clen < len; i++, mrec = mrec->next()) { clen += sizeof(mldv2_mrec); if (clen <= len) clen += sizeof(in6_addr) * ntoh(mrec->nsrcs); } if (clen > len) { if (should_log(MESSAGE_ERR)) log().writeline("Dropped badly formed MLDv2 Membership"); m_stats.counter(ReportV2Count, Bad)++; mld->stats().counter(ReportV2Count, Bad)++; return; } mrec = mldhdr->mrecs(); for (int i = 0; i < reccount; i++, mrec = mrec->next()) { if (!IN6_IS_ADDR_MULTICAST(&mrec->mca) || IN6_IS_ADDR_MC_NODELOCAL(&mrec->mca) || IN6_IS_ADDR_MC_LINKLOCAL(&mrec->mca)) continue; address_set sources; in6_addr *srcs = mrec->sources(); for (uint16_t j = 0; j < ntoh(mrec->nsrcs); j++) sources += srcs[j]; handle_mode_change_for_group(2, src, mrec->mca, mrec->type, sources); } } void mld_interface::handle_mldv1_membership_reduction(const in6_addr &src, mldv1 *mldhdr) { m_stats.counter(ReductionCount, RX)++; mld->stats().counter(ReductionCount, RX)++; if (!IN6_IS_ADDR_MULTICAST(&mldhdr->mcaddr)) { m_stats.counter(ReductionCount, Bad)++; mld->stats().counter(ReductionCount, Bad)++; return; } if (IN6_IS_ADDR_MC_NODELOCAL(&mldhdr->mcaddr) || IN6_IS_ADDR_MC_LINKLOCAL(&mldhdr->mcaddr)) return; handle_mode_change_for_group(1, src, mldhdr->mcaddr, MLD_SSM_CHANGE_TO_INCLUDE, address_set()); } void mld_interface::handle_membership_query(const in6_addr &src) { m_stats.counter(QueryCount, RX)++; mld->stats().counter(QueryCount, RX)++; // there should only be a MLD querier in a subnet network // so, if we are currently a querier, let's check if we have priority if (mif_isquerier) { if (src < *owner()->linklocal()) { change_is_querier(false); mif_querier_addr = src; if (should_log(NORMAL)) { log().xprintf("No longer the MLD querier in " "this interface. Querier is at " "%{Addr}\n", mif_querier_addr); } } } else { if (mif_querier_addr.is_any() || src < mif_querier_addr) { mif_querier_addr = src; if (should_log(NORMAL)) { log().xprintf("Querier is now at %{Addr}\n", mif_querier_addr); } } } if (!mif_isquerier) mif_other_querier_present_timer_id.restart(); } void mld_interface::change_is_querier(bool is) { if (mif_isquerier == is) return; mif_isquerier = is; if (mif_isquerier) start_querying(); else mif_query_timer_id.stop(); } void mld_interface::handle_other_querier_present_timeout() { change_is_querier(conf()->querier()); if (!mif_isquerier) mif_querier_addr = inet6_addr(); if (should_log(NORMAL)) { base_stream &os = log().write("The other querier timed out"); if (mif_isquerier) os.write(", switching to Querier mode."); os.newl(); } } void mld_interface::handle_send_query_timeout() { /* assert(mif_isquerier) */ if (mif_isquerier) { send_mld_query(in6addr_any); if (mif_query_count != 0xffffffff) { mif_query_count ++; if (mif_query_count == conf()->startup_query_count()) { mif_query_timer_id.update(conf()->query_interval(), true); mif_query_count = 0xffffffff; } } } } /// /// /// mld_group_interface::mld_group_interface(mld_group *grp, mld_interface *intf) : group_interface(grp->owner(), grp, intf->owner()), g_filter_timer("filter mode timer", this, std::mem_fun(&mld_group_interface::handle_filter_timer)), g_last_listener_timer("last listener timer", this, std::mem_fun(&mld_group_interface::handle_last_listener_query)) { g_owner = grp; g_intf = intf; g_creation_time = tval::now(); g_last_listener_query_count = 0; } mld_group_interface::~mld_group_interface() { g_sources_timers.clear(); } uint32_t mld_group_interface::mali() const { return g_intf->conf()->mali(); } void mld_group_interface::output_info(base_stream &ctx, bool detailed) const { ctx.xprintf("Group-Interface %s [MLD]\n", intf()->name()); ctx.inc_level(); int maxsources = 3; const address_set &srcs = active_set(); if (detailed) { maxsources = srcs.size(); } ctx.xprintf("Filter: %s ", filter_mode() == include ? "Include" : "Exclude"); if (srcs.empty()) { ctx.write("{}"); } else { ctx.write("{"); address_set::const_iterator i = srcs.begin(); ctx.xprintf("%{addr}", *i); ++i; for (int k = 1; k < maxsources && i != srcs.end(); ++i) { ctx.xprintf(", %{addr}", *i); k++; } if (i != srcs.end()) ctx.write(", ..."); ctx.write(" }"); } if (g_filter_timer.is_running()) { ctx.xprintf(" (Reset in %{duration})", g_filter_timer.time_left_d()); } ctx.newl(); if (detailed) { if (!g_sources_timers.empty()) { ctx.writeline("Sources:"); ctx.inc_level(); std::vector::const_iterator i; for (i = g_sources_timers.begin(); i != g_sources_timers.end(); ++i) { ctx.xprintf("%{addr} for %{duration}", i->argument(), i->time_left_d()); } ctx.dec_level(); } } output_inner_info(ctx, detailed); ctx.dec_level(); } void mld_group_interface::output_inner_info(base_stream &ctx, bool detailed) const { } static const char *_mode_name(int mode) { switch (mode) { case MLD_SSM_CHANGE_TO_INCLUDE: return "CHANGE_TO_INCLUDE"; case MLD_SSM_ALLOW_SOURCES: return "ALLOW_SOURCES"; case MLD_SSM_MODE_INCLUDE: return "MODE_INCLUDE"; case MLD_SSM_CHANGE_TO_EXCLUDE: return "CHANGE_TO_EXCLUDE"; case MLD_SSM_MODE_EXCLUDE: return "MODE_EXCLUDE"; case MLD_SSM_BLOCK_SOURCES: return "BLOCK_SOURCES"; default: return "UNKNOWN"; } } void mld_group_interface::send_mld_query(bool general, const address_set &srcs) { if (general) g_intf->send_mld_query(owner()->id()); else if (!general && !srcs.empty()) g_intf->send_mld_query(owner()->id(), srcs); } void mld_group_interface::refresh(const inet6_addr &src, int mode, const address_set &sources) { if (g_last_listener_timer.is_running() && !((mode == MLD_SSM_CHANGE_TO_INCLUDE || mode == MLD_SSM_MODE_INCLUDE) && sources.empty())) { /* Cancel fast-leave */ g_last_listener_timer.stop(); } g_last_reporter = src; if (should_log(MESSAGE_SIG)) { log().xprintf("Refresh triggered by %{Addr} with mode" " %s and sources %{addrset}.\n", src, _mode_name(mode), sources); } address_set diff; switch (g_filter_mode) { case include: switch (mode) { case MLD_SSM_CHANGE_TO_INCLUDE: /* Router State: INCLUDE(A) * Report Received: TO_IN(B) * New Router State: INCLUDE(A+B) * Actions: (B)=MALI, Send Q(MA,A-B) */ /* Send Q(MA,A-B) */ send_mld_query(false, g_include_set - sources); /* the rest is processed below */ case MLD_SSM_ALLOW_SOURCES: case MLD_SSM_MODE_INCLUDE: /* Router State: INCLUDE(A) * Report Received: ALLOW(B), INCLUDE(B) * New Router State: INCLUDE(A+B) * Actions: (B)=MALI */ /* A = A+B */ g_include_set.union_with(sources, diff); /* (B)=MALI */ update_sources_timer(sources); if (!diff.empty()) { dump_filter(); owner()->trigger_mode_event(this, added_sources, diff); } break; case MLD_SSM_CHANGE_TO_EXCLUDE: /* Router State: INCLUDE(A) * Report Received: TO_EX(B) * New Router State: EXCLUDE(A*B,B-A) * Actions: (B-A)=0, Delete (A-B), Send Q(MA,A*B), Filter Timer=MALI */ /* Send Q(MA,A*B) */ send_mld_query(false, g_include_set * sources); /* the rest is processed below */ case MLD_SSM_MODE_EXCLUDE: /* Router State: INCLUDE(A) * Report Received: EXCLUDE(B) * New Router State: EXCLUDE(A*B,B-A) * Actions: (B-A)=0, Delete (A-B), Filter Timer=MALI */ g_filter_mode = exclude; /* X = A*B */ g_request_set = g_include_set * sources; /* Y = B-A */ g_exclude_set = sources - g_include_set; /* Delete (A-B) */ delete_sources(g_include_set - sources); /* Filter Timer=MALI */ restart_filter_timer(); dump_filter(); owner()->trigger_mode_event(this, all_sources, address_set()); break; case MLD_SSM_BLOCK_SOURCES: /* Router State: INCLUDE(A) * Report Received: BLOCK(B) * New Router State: INCLUDE(A) * Actions: Send Q(MA,A*B) */ /* Fast leave */ update_sources_timer(sources, g_intf->conf()->last_listener_query_interval() + 500); send_mld_query(false, sources); /* Send Q(MA,A*B) */ send_mld_query(false, g_include_set * sources); break; } break; case exclude: switch (mode) { case MLD_SSM_CHANGE_TO_INCLUDE: /* Router State: EXCLUDE(X,Y) * Report Received: TO_IN(A) * New Router State: EXCLUDE(X+A,Y-A) * Actions: (A)=MALI, Send Q(MA,X-A), Send Q(MA) */ /* Send Q(MA,X-A) */ send_mld_query(false, g_request_set - sources); /* Send Q(MA) */ send_mld_query(true); start_fast_leave(); case MLD_SSM_MODE_INCLUDE: /* Router State: EXCLUDE(X,Y) * Report Received: INCLUDE(A) * New Router State: EXCLUDE(X+A,Y-A) * Actions: (A)=MALI */ /* X = X+A */ g_request_set.union_with(sources); /* Y = Y-A */ g_exclude_set.diff_with(sources, diff); /* (A)=MALI */ update_sources_timer(sources); if (!diff.empty()) { dump_filter(); owner()->trigger_mode_event(this, removed_sources, diff); } break; case MLD_SSM_CHANGE_TO_EXCLUDE: /* Router State: EXCLUDE(X,Y) * Report Received: TO_EX(A) * New Router State: EXCLUDE(A-Y,Y*A) * Actions: (A-X-Y)= Filter Timer, Delete (X-A), Delete (Y-A) * Send Q(MA,A-Y), Filter Timer=MALI */ /* Send Q(MA,A-Y) */ send_mld_query(false, sources - g_exclude_set); case MLD_SSM_MODE_EXCLUDE: /* Router State: EXCLUDE(X,Y) * Report Received: EXCLUDE(A) * New Router State: EXCLUDE(A-Y,Y*A) * Actions: (A-X-Y)=Filter Timer, Delete (X-A), Delete (Y-A) * Filter Timer=MALI */ /* (A-X-Y)=Filter Timer */ update_sources_timer(sources - g_request_set - g_exclude_set, g_filter_timer.time_left()); /* Delete (X-A) */ delete_sources(g_request_set - sources); /* Delete (Y-A) */ delete_sources(g_exclude_set - sources); /* X = A-Y */ g_request_set = sources - g_exclude_set; /* Y = Y*A */ g_exclude_set.intersect_with(sources); /* Filter Timer=MALI */ restart_filter_timer(); break; case MLD_SSM_ALLOW_SOURCES: /* Router State: EXCLUDE(X,Y) * Report Received: ALLOW(A) * New Router State: EXCLUDE(X+A,Y-A) * Actions: (A)=MALI */ /* X = X+A */ g_request_set.union_with(sources); /* Y = Y-A */ g_exclude_set.diff_with(sources, diff); /* (A)=MALI */ update_sources_timer(sources); if (!diff.empty()) { dump_filter(); owner()->trigger_mode_event(this, added_sources, diff); } break; case MLD_SSM_BLOCK_SOURCES: /* Router State: EXCLUDE(X,Y) * Report Received: BLOCK(A) * New Router State: EXCLUDE(X+(A-Y),Y) * Actions: (A-X-Y)=Filter Timer, Send Q(MA,A-Y) */ /* (A-X-Y)=Filter Timer */ update_sources_timer(sources - g_request_set - g_exclude_set, g_filter_timer.time_left()); /* Send Q(MA,A-Y) */ send_mld_query(false, sources - g_exclude_set); /* X = X+(A-Y) */ g_request_set.union_with(sources - g_exclude_set); break; } break; } } void mld_group_interface::start_fast_leave() { /* Implement fast leave */ if (g_last_listener_timer.is_running()) return; /* We already sent a Q(MA) */ g_last_listener_query_count = 1; g_last_listener_timer.start(g_intf->conf()->last_listener_query_time(), true); } void mld_group_interface::handle_last_listener_query() { if (g_last_listener_query_count == g_intf->conf()->last_listener_query_count()) { /* If we reach here, no one reported */ g_last_listener_timer.stop(); delete_sources(g_exclude_set); g_include_set.clear(); g_request_set.clear(); g_exclude_set.clear(); g_filter_mode = include; dump_filter(); owner()->trigger_mode_event(this, all_sources, address_set()); owner()->someone_lost_interest(); } else { /* Send Q(MA) */ send_mld_query(true); g_last_listener_query_count ++; } } uint32_t mld_group_interface::time_left_to_expiry(bool withft) const { uint32_t val = withft ? g_filter_timer.time_left() : 0; if (!g_sources_timers.empty()) { std::vector::const_iterator i = g_sources_timers.begin(); for (; i != g_sources_timers.end(); ++i) { val = std::max(val, i->time_left()); } } return val; } void mld_group_interface::delete_sources(const address_set &sources) { bool changed = false; for (address_set::const_iterator i = sources.begin(); i != sources.end(); ++i) { for (std::vector::iterator k = g_sources_timers.begin(); k != g_sources_timers.end(); ++k) { if (k->argument() == *i) { g_sources_timers.erase(k); if (g_include_set.has_addr(*i)) { g_include_set.remove(*i); if (g_filter_mode == include) { changed = true; owner()->trigger_mode_event(this, removed_sources, *i); if (g_include_set.empty()) { if (owner()->someone_lost_interest()) return; } } } else if (g_request_set.has_addr(*i)) { g_request_set.remove(*i); g_exclude_set.insert(*i); changed = true; owner()->trigger_mode_event(this, added_sources, *i); } else if (g_exclude_set.has_addr(*i)) { g_exclude_set.remove(*i); changed = true; owner()->trigger_mode_event(this, removed_sources, *i); } break; } } } if (changed) dump_filter(); } void mld_group_interface::update_sources_timer(const address_set &sources, uint32_t interval) { if (interval == 0) interval = mali(); for (address_set::const_iterator i = sources.begin(); i != sources.end(); ++i) { std::vector::iterator k = g_sources_timers.begin(); for (; k != g_sources_timers.end() && !(k->argument() == *i); ++k); if (k == g_sources_timers.end()) { std::string tmp; tmp = "mld source timer ("; tmp += inet6_addr(*i).as_string(); tmp += ")"; k = g_sources_timers.insert(g_sources_timers.end(), source_timer(tmp, this, std::mem_fun(&mld_group_interface::handle_source_timeout), *i)); } k->start_or_update(interval, false); } } void mld_group_interface::handle_source_timeout(in6_addr &addr) { delete_sources(addr); } void mld_group_interface::restart_filter_timer() { g_filter_timer.start_or_update(mali(), false); } void mld_group_interface::handle_filter_timer() { delete_sources(g_exclude_set); g_include_set = g_request_set; g_request_set.clear(); g_exclude_set.clear(); g_filter_mode = include; dump_filter(); owner()->trigger_mode_event(this, all_sources, address_set()); if (g_include_set.empty()) owner()->someone_lost_interest(); } /// /// /// mld_group::mld_group(router *rt) : group_node(rt) { } mld_group::~mld_group() { } bool mld_group::output_info(base_stream &ctx, const std::vector &) const { /* No Info */ return true; } void mld_group::attached(group *grp) { group_node::attached(grp); /* if (!owner()->get_conf()->create_child("mld")) { return; } m_conf = owner()->get_conf()->get_child("mld"); if (m_conf->get_property_bool("forward")) { m_mfa_inst = mld->mfa()->create_group(g_owner->id()); if (m_mfa_inst) { m_mfa_wildcard = m_mfa_inst->create_source_state(inet6_addr::any()); } }*/ } void mld_group::dettached() { group_node::dettached(); } void mld_group::subscriptions_changed(const group_interface *gintf, group_interface::event_type event, const address_set &sources) { } group_interface *mld_group::instantiate_group_interface(interface *intf) { mld_interface *mldintf = mld->get_interface(intf->index()); if (mldintf) return new mld_group_interface(this, mldintf); return 0; } bool mld_group::has_interest_in_group() const { std::map::const_iterator j; for (j = owner()->interface_table().begin(); j != owner()->interface_table().end(); ++j) { if (const_cast(j->second->owner_node()) == this) { if (!(j->second->filter_mode() == group_interface::include && j->second->include_set().empty())) { return true; } } } return false; } mld_group_interface *mld_group::local_oif(mld_interface *intf) { group_interface *gif = g_owner->local_oif(intf->owner()); if (gif && gif->owner_node() == this) return static_cast(gif); return 0; } /// /// /// mld_router::mld_router() : router("mld"), m_stats(this, MessageCount, stats_descriptions) { in6addr_linkscope_allnodes = inet6_addr("ff02::1").address(); } mld_router::~mld_router() { } const char *mld_router::description() const { return "Multicast Listener Discovery (MLD) protocol"; } bool mld_router::check_startup() { if (!m_stats.setup("MLD Statistics")) return false; m_stats.disable_counter(ReportCount, TX); m_stats.disable_counter(ReductionCount, TX); m_stats.disable_counter(ReportV2Count, TX); if (!router::check_startup()) return false; import_methods(mld_router_methods); g_mrd->icmp().register_handler(MLD_LISTENER_REPORT, this); g_mrd->icmp().register_handler(MLD_LISTENER_REDUCTION, this); g_mrd->icmp().register_handler(MLD_LISTENER_QUERY, this); g_mrd->icmp().register_handler(MLDv2_LISTENER_REPORT, this); g_mrd->icmp().register_handler(MLDv2_LISTENER_REPORT_OLD, this); in6_addr all_routers; in6_addr mld_all_routers; all_routers = inet6_addr("ff02::2").address(); mld_all_routers = inet6_addr("ff02::16").address(); g_mrd->icmp().require_mgroup(all_routers, true); g_mrd->icmp().require_mgroup(mld_all_routers, true); return true; } void mld_router::shutdown() { const mrd::interface_list &intflist = g_mrd->intflist(); for (mrd::interface_list::const_iterator i = intflist.begin(); i != intflist.end(); ++i) { mld_interface *mldintf = (mld_interface *)i->second->node_owned_by(this); if (mldintf) { mldintf->shutdown(); delete mldintf; } } g_mrd->icmp().register_handler(MLD_LISTENER_REPORT, 0); g_mrd->icmp().register_handler(MLD_LISTENER_REDUCTION, 0); g_mrd->icmp().register_handler(MLD_LISTENER_QUERY, 0); g_mrd->icmp().register_handler(MLDv2_LISTENER_REPORT, 0); g_mrd->icmp().register_handler(MLDv2_LISTENER_REPORT_OLD, 0); in6_addr all_routers; in6_addr mld_all_routers; all_routers = inet6_addr("ff02::2").address(); mld_all_routers = inet6_addr("ff02::16").address(); g_mrd->icmp().require_mgroup(all_routers, false); g_mrd->icmp().require_mgroup(mld_all_routers, false); router::shutdown(); } void mld_router::icmp_message_available(interface *intf, const in6_addr &src, const in6_addr &dst, icmp6_hdr *hdr, int len) { if (IN6_IS_ADDR_MULTICAST(&dst)) { mld_interface *mintf = get_interface(intf->index()); if (mintf) { mintf->icmp_message_available(src, dst, hdr, len); } else if (_is_mld_message(hdr->icmp6_type) && should_log(MESSAGE_ERR)) { mld->log_router_desc(intf->log()).writeline( "Incoming MLD message discarded, " "interface disabled."); } } } void mld_router::add_interface(interface *intf) { if (!intf->conf()->create_child("mld")) return; mld_interface *mldif = new mld_interface(); if (!mldif) return; if (!intf->attach_node(mldif)) { delete mldif; return; } if (!mldif->check_startup()) { intf->dettach_node(mldif); delete mldif; } } void mld_router::remove_interface(interface *intf) { mld_interface *mldintf = (mld_interface *)intf->node_owned_by(this); if (mldintf) { mldintf->shutdown(); delete mldintf; } } void mld_router::created_group(group *grp) { mld_group *mldgrp = allocate_group(); if (mldgrp) { if (!mldgrp->check_startup() || !grp->attach_node(mldgrp)) delete mldgrp; } else { /// XXX } } mld_group *mld_router::allocate_group() { return new mld_group(this); } void mld_router::released_group(group *grp) { mld_group *mldgrp = (mld_group *)grp->node_owned_by(this); if (mldgrp) { grp->dettach_node(mldgrp); // mldgrp->shutdown(); delete mldgrp; } } bool mld_router::call_method(int id, base_stream &out, const std::vector &args) { if (id == mld_router_method_show_groups) { inet6_addr mask; if (args.size() == 1) { if (!mask.set(args[0].c_str())) return false; } else if (args.size() > 1) { return false; } mrd::group_list::const_iterator i = g_mrd->group_table().begin(); for (; i != g_mrd->group_table().end(); ++i) { if (!mask.matches(i->first)) continue; mld_group *grp = (mld_group *)i->second->node_owned_by(this); if (!grp) continue; group::group_intfs::const_iterator j; int count = 0; j = i->second->interface_table().begin(); for (; j != i->second->interface_table().end(); ++j) { if (j->second->owner_node() == grp) count++; } if (!count) continue; out.xprintf("%{Addr}\n", i->first); out.inc_level(); j = i->second->interface_table().begin(); for (; j != i->second->interface_table().end(); ++j) { if (j->second->owner_node() == grp) { mld_group_interface *mldintf = (mld_group_interface *)j->second; out.write(mldintf->intf()->name()).write(": "); mldintf->dump_filter(out); out.xprintf(", Uptime: %{duration}", time_duration(mldintf->uptime())); uint32_t val = mldintf->time_left_to_expiry(true); if (val > 0) { out.xprintf(", Time left: %{duration}", time_duration(val)); } out.xprintf(", Last reporter: %{Addr}\n", mldintf->last_reporter()); } } out.dec_level(); } return true; } return router::call_method(id, out, args); } bool mld_router::output_info(base_stream &, const std::vector &) const { return false; } intfconf_node *mld_router::create_interface_configuration(intfconf *conf) { return new mld_intfconf_node(conf); } groupconf_node *mld_router::create_group_configuration(groupconf *conf) { return new mld_groupconf_node(conf); } mld_interface *mld_router::get_interface(int index) const { interface *intf = g_mrd->get_interface_by_index(index); if (intf) return (mld_interface *)intf->node_owned_by(this); return 0; } mld_group *mld_router::match(group *grp) const { return (mld_group *)grp->node_owned_by(this); } bool mld_router::send_icmp(const interface *intf, const in6_addr &addr, icmp6_hdr *hdr, uint16_t len) const { return g_mrd->icmp().send_icmp(intf, addr, IP6_ALERT_MLD, hdr, len); } base_stream &mld_router::log_router_desc(base_stream &os) const { return os.write("MLD, "); } mrd6-0.9.6/src/mld/mld_def.cpp0000644000175000017500000000406310600737663014540 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mld_def.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include void mldv1::construct(const in6_addr &addr, int _type, mld_intfconf_node *n) { type = _type; code = 0; checksum = 0; /* General Query * max_response_delay = [Query Response Interval] * Multicast-Address-Specific Query * max_response_delay = [Last Listener Query Interval] */ if (IN6_IS_ADDR_UNSPECIFIED(&addr)) maxdelay = hton((uint16_t)n->query_response_interval()); else maxdelay = hton((uint16_t)n->last_listener_query_interval()); data = hton((uint16_t)0); mcaddr = addr; } void mldv1_query::construct(const in6_addr &mcaddr, mld_intfconf_node *node) { mldv1::construct(mcaddr, MLD_LISTENER_QUERY, node); } void mldv2_query::construct(const in6_addr &addr, int type, mld_intfconf_node *n) { mldv1::construct(addr, type, n); qrv = n->robustness(); suppress = 0; resv2 = 0; uint32_t qis = n->query_interval() / 1000; if (qis < 128) qqic = qis; else { int exp = 0; while ((qis >> (exp+3)) > 0x1f) exp++; qis >>= exp+3; qis -= 0x10; qqic = 0x80 | (exp << 4) | qis; } nsrcs = hton((uint16_t)0); } mrd6-0.9.6/src/mld/mld_module.cpp0000644000175000017500000000333010550053317015252 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mld_module.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include extern mld_router *mld; class mld_module : public mrd_module { public: mld_module(mrd *m, void *dlh); bool check_startup(); void shutdown(); void module_loaded(const char *, mrd_module *); }; module_entry(mld, mld_module); mld_module::mld_module(mrd *m, void *dlh) : mrd_module(m, dlh) { } bool mld_module::check_startup() { mld = new mld_router(); if (!mld) return false; if (!g_mrd->register_router(mld)) { delete mld; mld = 0; return false; } return true; } void mld_module::shutdown() { g_mrd->unregister_router(mld); mld->shutdown(); delete mld; mld = 0; } void mld_module::module_loaded(const char *name, mrd_module *) { } mrd6-0.9.6/src/confs/0000755000175000017500000000000010746353706012776 5ustar hugohugomrd6-0.9.6/src/confs/mrd.m6bone-bgp.conf0000644000175000017500000000412610325017416016351 0ustar hugohugo/* MRD example configuration file */ log { /* Logs are controlled via the 'attach' method */ /* syntax (one of): attach syslog [level] attach stderr [level] attach name filename [level] where level is one of: quiet, normal, verbose, debug or extradebug loglevels may be checked via console, e.g.: ../tools/mrd6sh show log \* */ attach stderr normal; attach default "mrd.log" debug; } /* Enable the console, MLD, PIM and BGP modules */ load-module console; load-module mld; load-module pim; load-module bgp; console { /* Allow access from any host with admin/admin */ /* allow-access admin admin any; */ /* Command format: */ /* allow-access [username [password [address mask]]]; */ } bgp { /* local AS number */ router-as 65000; /* neighbours are added using the following format neighbor { address { peer-as neigh_AS; [filter [num] (in|out) ;] } } where a filter is defined in the following way access-list in1 { /* accept any prefix that matches 2000::/3 with * prefixlen >= 24 and <= 64. */ prefix permit 2000::/3 ge 24 le 64; /* command format */ /* prefix (permit|deny) address [seq num] [ge val] [le val]; */ /* "ge val" (greater-or-equal) or "le val" (less-or-equal) */ } /* a access filter must explicitly permit, as it denies by default */ access-list out1 { /* accept any prefix except 2001:2002::/64 */ prefix deny 2001:2002::/64; prefix permit any; } e.g. access-list one { prefix permit 2000::/3 ge 24 le 64; } access-list two { prefix permit 2001:cafe::/32; } neighbor { 2001:cafe::1 { peer-as 65122; filter in one; filter out two; } 2001:beef::2 { peer-as 65123; /* no filters? accept by default */ } } */ } /* Group configuration */ groups { /* For ff0e::/16, ff1e::/16 and ff3e:30:2001:700::/64 the configuration is static */ ff0e::/16 { pim { rp 2001:660:3007:300:1::; } } ff1e::/16 { pim { rp 2001:660:3007:300:1::; } } ff3e:30:2001:700::/64 { pim { rp 2001:700:e000:501::2; } } } mrd6-0.9.6/src/confs/mrd.conf0000644000175000017500000000160010325017416014410 0ustar hugohugo/* MRD example configuration file */ log { /* Logs are controlled via the 'attach' method */ /* syntax (one of): attach syslog [level] attach stderr [level] attach name filename [level] where level is one of: quiet, normal, verbose, debug or extradebug */ attach stderr normal; attach default "mrd.log" debug; } load-module console; load-module mld; load-module pim; console { /* Allow access from any host with admin/admin */ /* allow-access admin admin any; */ /* Command format: */ /* allow-access [username [password [address mask]]]; */ } /* Global pim variable configuration */ pim { /* we want to be a BSR candidate */ enable bsr-candidate; /* we want to be a RP candidate */ enable rp-candidate; } /* Groups configuration */ groups { /* group mask */ ff0e::/16 { pim { /* include this group mask in our RP adv */ enable rp_adv; } } } mrd6-0.9.6/src/confs/mrd.m6bone.conf0000644000175000017500000000233510325017416015603 0ustar hugohugo/* MRD example configuration file */ log { /* Logs are controlled via the 'attach' method */ /* syntax (one of): attach syslog [level] attach stderr [level] attach name filename [level] where level is one of: quiet, normal, verbose, debug or extradebug loglevels may be checked via console, e.g.: ../tools/mrd6sh show log \* */ attach stderr normal; attach default "mrd.log" debug; } /* Enable the console, MLD and PIM modules */ load-module console; load-module mld; load-module pim; console { /* Allow access from any host with admin/admin */ /* allow-access admin admin any; */ /* Command format: */ /* allow-access [password [address mask]]; */ } /* Static MRIB configuration */ mrib { /* Available commands: local ; prefix via [dev ] [metric ] [export]; e.g. local 2001:2002:2003::/48; prefix 2000::/3 via 2001:2002::1; */ } /* Group configuration */ groups { /* For ff0e::/16, ff1e::/16 and ff3e:30:2001:700::/64 the configuration is static */ ff0e::/16 { pim rp 2001:660:3007:300:1::; } ff1e::/16 { pim rp 2001:660:3007:300:1::; } ff3e:30:2001:700::/64 { pim rp 2001:700:e000:501::2; } } mrd6-0.9.6/src/interface.cpp0000644000175000017500000002717110746336636014345 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * interface.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include static const char *_kernel_state_names[] = { "Down", "No Link", "Up", }; enum { intfconf_method_disable_router = 1000 }; static const method_info intfconf_methods[] = { { "disable-router", "Disables the specified router in the interface", intfconf_method_disable_router, false, 0 }, { 0 } }; intfconf::intfconf(const char *name) : conf_node(g_mrd->get_child("interfaces"), name) { } intfconf::~intfconf() { clear_childs(); } bool intfconf::check_startup() { if (!conf_node::check_startup()) return false; import_methods(intfconf_methods); return true; } void intfconf::remove_child_node(node *n) { delete (intfconf_node *)n; } void intfconf::fill_defaults() { instantiate_property_b("enabled", true); instantiate_property_b("install-mrib-prefixes", true); for (properties::iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (i->second.is_child()) ((intfconf_node *)i->second.get_node())->fill_defaults(); } } void intfconf::property_changed(node *, const char *key) { if (!strcmp(key, "enabled")) { g_mrd->check_enabled_interfaces(this); } } bool intfconf::call_method(int id, base_stream &out, const std::vector &args) { switch (id) { case intfconf_method_disable_router: return disable_router(args); } return conf_node::call_method(id, out, args); } bool intfconf::disable_router(const std::vector &args) { if (args.empty()) return false; for (std::vector::const_iterator i = args.begin(); i != args.end(); ++i) { disabled_routers.insert(*i); } return true; } bool intfconf::is_enabled() const { return get_property_bool("enabled"); } bool intfconf::is_router_enabled(const char *name) const { return disabled_routers.find(name) == disabled_routers.end(); } node *intfconf::next_similiar_node() const { if (!strcmp(name(), "all")) return 0; return g_mrd->default_interface_configuration(); } node *intfconf::create_child(const char *name) { node *child = get_child(name); if (child) return child; properties::const_iterator i = m_properties.find(name); if (i != m_properties.end()) return 0; router *rt = g_mrd->get_router(name); if (rt) child = rt->create_interface_configuration(this); if (!child || !child->check_startup()) { delete child; return 0; } return add_child(child); } void intfconf::update_interface_configuration(interface *intf) { intf->mif_conf = this; } intfconf_node::intfconf_node(intfconf *parent, const char *name) : conf_node(parent, name) { } node *intfconf_node::next_similiar_node() const { if (strcmp(parent()->name(), "all") == 0) return 0; return g_mrd->default_interface_configuration()->get_child(name()); } interface_node::interface_node(router *rt) : node(0, rt->name()), n_owner(0), n_owner_router(rt) {} interface_node::~interface_node() { } void interface_node::attached(interface *owner) { m_parent = owner; n_owner = owner; } void interface_node::dettached() { m_parent = 0; n_owner = 0; } bool interface_node::should_log(int level) const { if (owner() && owner()->should_log(level)) return owner_router() && owner_router()->should_log(level); return false; } base_stream &interface_node::log() const { return owner_router()->log_router_desc(owner()->log()); } interface::interface(intfconf *cnf, int indx, const char *ifname, int type, int mtu, int flags) : node(g_mrd->get_child("interface"), ifname) { memset(&mif_localaddr, 0, sizeof(mif_localaddr)); mif_conf = cnf; mif_index = indx; mif_name = ifname; mif_type = type; mif_mtu = mtu; mif_flags = flags; mif_state = Down; mif_enabled = true; mif_localaddr.sin6_family = AF_INET6; mif_localaddr.sin6_scope_id = mif_index; mif_creationtime = tval::now(); } interface::~interface() { } void interface::shutdown() { if (should_log(VERBOSE)) log().xprintf("(MRD) Removing %s.\n", name()); } base_stream &interface::log() const { return node::log().xprintf("[%s] ", name()); } static const char *_type_name(int type, int flags) { switch (type) { case interface::Loopback: return "loopback"; case interface::Ethernet: return "ethernet"; case interface::PPP: return "ppp"; case interface::Tunnel: return "tunnel"; case interface::TUN: return "tun"; case interface::IEEE1394: return "ieee1394"; case interface::IEEE802_11: return "802.11"; case interface::IEEE802_1Q: return "vlan"; default: return "unknown"; } } const char *interface::type_str() const { return _type_name(mif_type, mif_flags); } bool interface::is_multiaccess() const { /* We'll just take Ethernet for now */ return mif_type == Ethernet; } bool interface::up(bool ignoremrd) const { if (state() == Up && mif_enabled) { if (ignoremrd) return true; return g_mrd->is_running(); } return false; } void interface::change_state(kernel_state newstate) { if (mif_state == newstate) return; bool wasdown = !up(); if (should_log(VERBOSE)) { log().xprintf("State changed: %s -> %s.\n", _kernel_state_names[mif_state], _kernel_state_names[newstate]); } mif_state = newstate; broadcast_change_state(wasdown); } void interface::broadcast_change_state(bool wasdown) { if (up() != !wasdown) { g_mrd->broadcast_interface_state_changed(this); } } void interface::set_enabled(bool newstate) { bool wasdown = !up(); mif_enabled = newstate; broadcast_change_state(wasdown); } bool interface::attach_node(interface_node *node) { if (!add_child(node)) return false; node->attached(this); for (std::set::const_iterator i = mif_linklocals.begin(); i != mif_linklocals.end(); i++) { node->address_added_or_removed(true, *i); } for (std::set::const_iterator i = mif_globals.begin(); i != mif_globals.end(); i++) { node->address_added_or_removed(true, *i); } return true; } void interface::dettach_node(interface_node *node) { interface_node *n = (interface_node *)get_child(node->name()); if (n && n == node) { remove_child(node->name()); node->dettached(); } } interface_node *interface::node_owned_by(const router *rt) const { return (interface_node *)get_child(rt->name()); } void interface::address_added_or_removed(bool isnew, const inet6_addr &addr) { // filter duplicates if (addr.is_any() || (isnew && mif_linklocals.find(addr) != mif_linklocals.end())) return; if (isnew && !addr.is_linklocal() && mif_globals.find(addr) != mif_globals.end()) return; add_remove_address(isnew, addr); } void interface::add_remove_address(bool isnew, const inet6_addr &addr) { bool loopback = IN6_IS_ADDR_LOOPBACK(&addr.addr); if (loopback && mif_type != Loopback) return; if (isnew) { if (loopback || addr.is_linklocal()) { if (mif_linklocal.is_any()) { mif_linklocal = addr; mif_localaddr.sin6_addr = addr; } mif_linklocals.insert(addr); } else { mif_globals.insert(addr); if (addr.prefixlen < 128 && conf()->get_property_bool("install-mrib-prefixes")) { g_mrd->mrib().local().register_prefix(addr.prefix(), this); } } } else { if (loopback || addr.is_linklocal()) { std::set::iterator k = mif_linklocals.find(addr); if (k == mif_linklocals.end()) { return; } mif_linklocals.erase(k); if (mif_linklocal == addr) { if (mif_linklocals.empty()) { mif_linklocal = in6addr_any; } else { mif_linklocal = *mif_linklocals.begin(); } mif_localaddr.sin6_addr = mif_linklocal.address(); } } else { std::set::iterator k = mif_globals.find(addr); if (k == mif_globals.end()) { return; } mif_globals.erase(k); if (addr.prefixlen < 128) { g_mrd->mrib().local().unregister_prefix(addr.prefix(), this); } } } for (properties::iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (i->second.is_child()) { interface_node *in = (interface_node *)i->second.get_node(); in->address_added_or_removed(isnew, addr); } } } bool interface::in_same_subnet(const in6_addr &addr) const { for (std::set::iterator i = mif_globals.begin(); i != mif_globals.end(); ++i) { if (i->matches(addr)) return true; } return false; } static inline uint16_t inchksum(const void *data, uint32_t length) { long sum = 0; const uint16_t *wrd = reinterpret_cast(data); long slen = static_cast(length); while (slen > 1) { sum += *wrd++; slen -= 2; } if (slen > 0) sum += *reinterpret_cast(wrd); while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); return static_cast(sum); } uint16_t ipv6_checksum(uint8_t protocol, const in6_addr &src, const in6_addr &dst, const void *data, uint16_t len) { struct { in6_addr src; in6_addr dst; uint16_t length; uint16_t zero1; uint8_t zero2; uint8_t next; } __attribute__ ((packed)) pseudo; uint32_t chksum = 0; pseudo.src = src; pseudo.dst = dst; pseudo.length = htons(len); pseudo.zero1 = 0; pseudo.zero2 = 0; pseudo.next = protocol; chksum = inchksum(&pseudo, sizeof(pseudo)); chksum += inchksum(data, len); chksum = (chksum >> 16) + (chksum & 0xffff); chksum += (chksum >> 16); chksum = static_cast(~chksum); if (chksum == 0) chksum = 0xffff; return chksum; } bool interface::has_global(const in6_addr &addr) const { for (std::set::const_iterator i = mif_globals.begin(); i != mif_globals.end(); ++i) { if (i->address() == addr) return true; } return false; } const inet6_addr &interface::primary_addr() const { if (mif_globals.empty()) return *mif_linklocals.begin(); return *mif_globals.begin(); } bool interface::output_info(base_stream &_out, const std::vector &args) const { if (!args.empty()) return false; _out.xprintf("Interface %s (%i) is ", name(), index()); if (mif_enabled) { _out.write(mif_state == Up ? "Up" : "Down"); } else { _out.write("Disabled"); } _out.xprintf(" (Uptime: %{duration})\n", time_duration(tval::now() - mif_creationtime)); _out.inc_level(); if (mif_linklocals.empty()) { _out.writeline("Link-Local: (None)"); } else { bool first = mif_globals.empty(); for (std::set::const_iterator i = mif_linklocals.begin(); i != mif_linklocals.end(); ++i) { _out.xprintf("Link-Local: %{Addr}", *i); if (first) { _out.write(" "); first = false; } _out.newl(); } } bool first = true; for (std::set::const_iterator i = mif_globals.begin(); i != mif_globals.end(); ++i) { _out.xprintf("Global: %{Addr}", *i); if (first) { _out.write(" "); first = false; } _out.newl(); } node::output_info(_out, std::vector()); _out.dec_level(); return true; } mrd6-0.9.6/src/tests/0000755000175000017500000000000010746353706013030 5ustar hugohugomrd6-0.9.6/src/tests/stress-mrib.cpp0000644000175000017500000000765110550053317016003 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * stress-mrib.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ /* * stress_mrib - a simple module that when loaded will flood the * MRIB with lots of prefixes. good to test responsiveness * during install as well as memory usage. */ #include #include #include struct stress_prefix : mrib_def::prefix { stress_prefix(mrib_origin *owner) : mrib_def::prefix(owner) {} /* possible private data */ uint8_t _space[16 + 8]; }; static objpool _stress_prefixes(256); /* besides being a module, we also are a MRIB origin and an event sink */ class stress_mrib_module : public mrd_module, public mrib_origin, public event_sink { public: stress_mrib_module(mrd *, void *); ~stress_mrib_module(); const char *description() const { return "stress-mrib"; } void return_prefix(mrib_def::prefix *p) { _stress_prefixes.return_obj((stress_prefix *)p); } bool check_startup(); void shutdown(); void event(int, void *); inet6_addr nh; int count; timeval start; double accum; }; module_entry(stress_mrib, stress_mrib_module); stress_mrib_module::stress_mrib_module(mrd *m, void *p) : mrd_module(m, p) { count = 0; accum = 0; } stress_mrib_module::~stress_mrib_module() { } bool stress_mrib_module::check_startup() { /* bogus nexthop, we dont care */ if (!nh.set("2001:2002::3")) return false; gettimeofday(&start, 0); /* as soon as we are loaded, start the hammering */ g_mrd->register_task(this, 0); return true; } void stress_mrib_module::shutdown() { /* on shutdown, clear all how MRIB damage */ g_mrd->mrib().origin_lost(this); } void stress_mrib_module::event(int, void *) { /* dont install more than 500000 prefixes */ if (count >= 500000) { timeval end; gettimeofday(&end, 0); uint32_t diff = (end.tv_sec - start.tv_sec) * 1000000; if (end.tv_usec > start.tv_usec) diff += end.tv_usec - start.tv_usec; else diff += 1000000 + end.tv_usec - start.tv_usec; g_mrd->log().info(QUIET) << "[STRESS] Took " << accum << "us to install " << count << " (real " << diff << "us)" << " prefixes, " << (accum / (double)count) << "us per prefix in average" << endl; return; } count++; /* generate a random prefix */ inet6_addr p; for (int i = 0; i < 4; i++) p.addr.s6_addr32[i] = rand(); /* with a random prefix len 10-96 */ p.prefixlen = 10 + rand() % 87; p.apply_prefixlen(); stress_prefix *pr = _stress_prefixes.request_obj(this); if (pr) { pr->distance = rand() % 100; pr->metric = rand() % 10000; pr->nexthop = nh; pr->intf = 0; timeval ts, te; gettimeofday(&ts, 0); /* install the prefix with random metric */ g_mrd->mrib().install_prefix(p, pr); gettimeofday(&te, 0); uint32_t diff = (te.tv_sec - ts.tv_sec) * 1000000; if (te.tv_usec > ts.tv_usec) diff += te.tv_usec - ts.tv_usec; else diff += (1000000 + te.tv_usec - ts.tv_usec); accum += diff; } else { _stress_prefixes.return_obj(pr); g_mrd->log().info(QUIET) << "[STRESS] failed to add prefix " << p << endl; } /* keep hammering */ g_mrd->register_task(this, 0); } mrd6-0.9.6/src/tests/ptree_unittest.cpp0000644000175000017500000000601210600166750016577 0ustar hugohugo#define BOOST_AUTO_TEST_MAIN #include #include #include using namespace std; typedef pair test_prefix; int pnode_prefix_length(const test_prefix &p) { return p.second; } bool pnode_symbol_at(const test_prefix &p, int n) { return (p.first >> (31 - n)) & 0x1; } #include void stream_push_formated_type(base_stream &os, const test_prefix &p) { char tmp[32]; snprintf(tmp, sizeof(tmp), "%x", p.first); os.xprintf("[%s / %i]", tmp, p.second); } ostream & operator << (ostream &os, const test_prefix &p) { return os << "[" << hex << p.first << " / " << dec << p.second << "]"; } #include bool operator == (const test_prefix &p1, const test_prefix &p2) { return p1.first == p2.first && p1.second == p2.second; } struct test_node : ptree_node { test_node(const test_prefix &pfx) : prefix(pfx) {} test_node(const test_node &n) : prefix(n.prefix) {} test_prefix prefix; friend bool operator == (const test_node &n1, const test_node &n2) { return n1.prefix == n2.prefix; } }; static void fill_nodes(vector &nodes) { nodes.push_back(test_node(test_prefix(0xffff0000, 16))); nodes.push_back(test_node(test_prefix(0x7fff0000, 16))); nodes.push_back(test_node(test_prefix(0x7fff1230, 30))); nodes.push_back(test_node(test_prefix(0x7fff1231, 32))); } BOOST_AUTO_UNIT_TEST(ptree_test1) { ptree p; BOOST_REQUIRE(p.size() == 0); vector nodes; fill_nodes(nodes); size_t count = 0; for (vector::iterator i = nodes.begin(); i != nodes.end(); ++i) { test_node *node = &(*i); BOOST_CHECK(p.insert(node) == node); ++count; BOOST_CHECK(p.size() == count); } for (vector::iterator i = nodes.begin(); i != nodes.end(); ++i) { test_node *node = &(*i); BOOST_CHECK(p.search(node->prefix) == node); } log_base log(NULL); BOOST_REQUIRE(log.check_startup()); log.attach_node(new file_log_node(&log, "stderr", EVERYTHING, stderr)); p.dump_internal_tree(log.current_context()); p.clear(); BOOST_REQUIRE(p.size() == 0); } BOOST_AUTO_UNIT_TEST(ptree_test2) { ptree p; BOOST_REQUIRE(p.size() == 0); vector nodes; fill_nodes(nodes); for (vector::iterator i = nodes.begin(); i != nodes.end(); ++i) BOOST_REQUIRE(p.insert(&(*i)) != NULL); BOOST_CHECK(p.longest_match(test_prefix(0x00000000, 32)) == NULL); BOOST_CHECK(p.longest_match(test_prefix(0xfffe0000, 32)) == NULL); BOOST_CHECK(p.longest_match(test_prefix(0xffff0000, 32)) == &nodes[0]); BOOST_CHECK(p.longest_match(test_prefix(0xffff8000, 32)) == &nodes[0]); BOOST_CHECK(p.longest_match(test_prefix(0xffff1234, 32)) == &nodes[0]); BOOST_CHECK(p.longest_match(test_prefix(0x7fff1200, 32)) == &nodes[1]); BOOST_CHECK(p.longest_match(test_prefix(0x7fff1230, 32)) == &nodes[2]); BOOST_CHECK(p.longest_match(test_prefix(0x7fff1232, 32)) == &nodes[2]); BOOST_CHECK(p.longest_match(test_prefix(0x7fff1231, 32)) == &nodes[3]); p.clear(); BOOST_REQUIRE(p.size() == 0); } mrd6-0.9.6/src/tests/mrib-1.sh0000755000175000017500000000014110276757744014462 0ustar hugohugo#!/bin/sh for i in `seq 1 9999`; do $1 mrib prefix 2001:$i::/32 via 2001:$i::1 metric $i done mrd6-0.9.6/src/tests/address_unittest.cpp0000644000175000017500000000130610600171656017107 0ustar hugohugo#define BOOST_AUTO_TEST_MAIN #include #include BOOST_AUTO_UNIT_TEST(address_test1) { inet6_addr a0(std::string("::/0")); inet6_addr a1(std::string("::")); inet6_addr a2(std::string("2000::/3")); inet6_addr a3(std::string("2001:123:456::/3")); inet6_addr a4(std::string("2001::1")); inet6_addr a5(std::string("2002::/16")); BOOST_CHECK(a0 == inet6_addr::any()); BOOST_CHECK(a1.addr == in6addr_any); BOOST_CHECK(a1.prefixlen == 128); BOOST_CHECK(a2.addr.s6_addr[0] == 0x20); BOOST_CHECK(a2.prefixlen == 3); a3.apply_prefixlen(); BOOST_CHECK(a2 == a3); BOOST_CHECK(a0.matches(a4)); BOOST_CHECK(a2.matches(a4)); BOOST_CHECK(!a5.matches(a4)); } mrd6-0.9.6/src/tests/run-all.sh0000755000175000017500000000017210600157174014727 0ustar hugohugo#!/bin/sh for t in tests/*_unittest; do if [ -x $t ]; then N=`basename $t` echo " --- Running $N ---" $t fi done mrd6-0.9.6/src/tests/mrib_unittest.cpp0000644000175000017500000000370710600171040016405 0ustar hugohugo#define BOOST_AUTO_TEST_MAIN #include #include #include using namespace std; class test_origin : public mrib_origin { const char *description() const { return "test"; } void return_prefix(mrib_def::prefix *p) { delete p; } }; static inet6_addr ADDR(const char *str) { return inet6_addr(string(str)); } static inet6_addr ANY() { return inet6_addr::any(); } static mrib_def::prefix *new_prefix(mrib_origin *o) { mrib_def::prefix *p = new mrib_def::prefix(o); p->nexthop = inet6_addr::any(); p->distance = 0; p->metric = 0; p->flags = 0; p->intf = NULL; return p; } static void test1(mrib_def &m, test_origin &o, const inet6_addr &pfx1, mrib_def::prefix *p1, const inet6_addr &pfx2, mrib_def::prefix *p2) { BOOST_CHECK(m.get_prefix(pfx1, NULL) == p1); BOOST_CHECK(m.get_prefix(pfx1, &o) == p1); BOOST_CHECK(m.get_prefix(pfx2, NULL) == p2); BOOST_CHECK(m.get_prefix(pfx2, &o) == p2); BOOST_CHECK(m.prefix_lookup(ADDR("2001:124::1"), ANY()) == p1); BOOST_CHECK(m.prefix_lookup(ADDR("2001:123::1"), ANY()) == p2); BOOST_CHECK(m.prefix_lookup(ADDR("7000::"), ANY()) == NULL); } BOOST_AUTO_UNIT_TEST(mrib_test1) { mrib_def m(NULL); test_origin o; BOOST_REQUIRE(m.check_startup()); mrib_def::prefix *p1 = new_prefix(&o); mrib_def::prefix *p2 = new_prefix(&o); inet6_addr pfx1(ADDR("2000::/3")); inet6_addr pfx2(ADDR("2001:123::/32")); BOOST_REQUIRE(m.install_prefix(pfx1, p1)); BOOST_REQUIRE(m.install_prefix(pfx2, p2)); test1(m, o, pfx1, p1, pfx2, p2); m.shutdown(); } BOOST_AUTO_UNIT_TEST(mrib_test1_rev) { mrib_def m(NULL); test_origin o; BOOST_REQUIRE(m.check_startup()); mrib_def::prefix *p1 = new_prefix(&o); mrib_def::prefix *p2 = new_prefix(&o); inet6_addr pfx1(ADDR("2000::/3")); inet6_addr pfx2(ADDR("2001:123::/32")); BOOST_REQUIRE(m.install_prefix(pfx2, p2)); BOOST_REQUIRE(m.install_prefix(pfx1, p1)); test1(m, o, pfx1, p1, pfx2, p2); m.shutdown(); } mrd6-0.9.6/src/ripng/0000755000175000017500000000000010746353706013005 5ustar hugohugomrd6-0.9.6/src/ripng/ripng.cpp0000644000175000017500000002414610550053317014623 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * ripng.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include struct ripng_header { uint8_t command; uint8_t version; uint16_t zero; }; struct ripng_rte { in6_addr prefix; uint16_t route_tag; uint8_t prefixlen; uint8_t metric; }; enum { RIPNG_REQUEST = 1, RIPNG_RESPONSE = 2, }; static const int RIP_INFINITY = 16; static const int HoldTime = 60000; class ripng_module : public mrd_module { public: ripng_module(mrd *, void *); bool check_startup(); void shutdown(); }; module_entry(ripng, ripng_module); class ripng_router : public router, public mrib_origin { public: ripng_router(); bool check_startup(); void shutdown(); const char *description() const { return "RIPng"; } void add_interface(interface *); void remove_interface(interface *); struct ripng_prefix : public mrib_def::prefix { ripng_prefix(ripng_router *owner) : mrib_def::prefix(owner), metric(16) {} tval lastupdate; int metric; }; void prefix_added(const inet6_addr &, mrib_def::metric_def, const mrib_def::prefix &); void prefix_lost(const inet6_addr &, mrib_def::metric_def, const mrib_def::prefix &); void return_prefix(mrib_def::prefix *); private: void data_available(uint32_t); void send_table(interface * &); void send_request(interface *); void broadcast(ripng_header *, uint16_t); void garbage_collect(); socket6 m_sock; sockaddr_in6 m_ripnggrp; typedef timer1 intf_timer; std::vector m_intftimers; timer m_garbcol_timer; }; ripng_router *ripng = 0; ripng_module::ripng_module(mrd *m, void *v) : mrd_module(m, v) { } bool ripng_module::check_startup() { if (ripng) return false; ripng = new ripng_router(); if (!ripng || !m_mrd->register_router(ripng)) { delete ripng; ripng = 0; return false; } return true; } void ripng_module::shutdown() { if (ripng) { m_mrd->unregister_router(ripng); ripng->shutdown(); delete ripng; ripng = 0; } } ripng_router::ripng_router() : router("ripng"), m_sock("ripng sock", this, std::mem_fun(&ripng_router::data_available)), m_garbcol_timer("ripng garbage collector", this, std::mem_fun(&ripng_router::garbage_collect), 30000, true) { m_ripnggrp = inet6_addr("ff02::9").as_sockaddr(); m_ripnggrp.sin6_port = htons(522); } bool ripng_router::check_startup() { if (!router::check_startup()) return false; int sock = socket(PF_INET6, SOCK_DGRAM, 0); if (sock < 0) return false; sockaddr_in6 local; memset(&local, 0, sizeof(local)); local.sin6_family = AF_INET6; local.sin6_port = htons(522); if (bind(sock, (sockaddr *)&local, sizeof(local)) < 0) { if (should_log(WARNING)) log().perror("Failed to bind"); close(sock); return false; } if (!m_sock.register_fd(sock)) { close(sock); return false; } if (!m_sock.enable_mc_loop(false)) return false; g_mrd->mrib().install_listener(this); m_garbcol_timer.start(); return true; } void ripng_router::shutdown() { g_mrd->mrib().origin_lost(this); m_sock.unregister(); } void ripng_router::garbage_collect() { tval now = tval::now(); std::list removal; mrib_def::visitor v; if (!g_mrd->mrib().visit_origin(v, this)) return; do { ripng_prefix *pinfo = (ripng_prefix *)v.entry(); int32_t diff = now - pinfo->lastupdate; if (pinfo->metric < RIP_INFINITY) { if (diff >= (3 * HoldTime)) { pinfo->metric = RIP_INFINITY; pinfo->lastupdate = now; } } else if (pinfo->metric == RIP_INFINITY) { if (diff >= (2 * HoldTime)) { removal.push_back(pinfo); } } } while (g_mrd->mrib().visit_next(v)); for (std::list::iterator i = removal.begin(); i != removal.end(); ++i) { g_mrd->mrib().remove_prefix(*i); } } void ripng_router::add_interface(interface *intf) { if (!m_sock.join_mc(intf, m_ripnggrp.sin6_addr)) { if (should_log(WARNING)) log().xprintf("Failed to join ff02::9 in %s, reason: %s", intf->name(), strerror(errno)); } else { send_request(intf); } std::string timername = "ripng timer ("; timername += intf->name(); timername += ")"; m_intftimers.push_back(intf_timer(timername, this, std::mem_fun(&ripng_router::send_table), intf, HoldTime / 2, true)); m_intftimers.back().start(true); } void ripng_router::remove_interface(interface *intf) { m_sock.leave_mc(intf, m_ripnggrp.sin6_addr); for (std::vector::iterator i = m_intftimers.begin(); i != m_intftimers.end(); ++i) { if (i->argument() == intf) { m_intftimers.erase(i); break; } } } static uint8_t buffer[2048]; void ripng_router::prefix_added(const inet6_addr &prefix, mrib_def::metric_def metric, const mrib_def::prefix &pfrec) { if (should_log(INTERNAL_FLOW)) log().xprintf("prefix_added %{Addr} metric %i flags %i\n", prefix, (int)metric, (int)pfrec.flags); if (pfrec.flags & mrib_def::prefix::NO_EXPORT) return; mrib_def::prefix *p = g_mrd->mrib().get_prefix(prefix, this); if (p) g_mrd->mrib().remove_prefix(p); /* Triggered update */ ripng_header *hdr = (ripng_header *)buffer; hdr->command = RIPNG_RESPONSE; hdr->version = 1; hdr->zero = 0; ripng_rte *rte = (ripng_rte *)(buffer + sizeof(ripng_header)); rte->prefix = prefix.addr; rte->route_tag = 0; rte->prefixlen = prefix.prefixlen; rte->metric = 1; broadcast(hdr, sizeof(ripng_header) + sizeof(ripng_rte)); } void ripng_router::prefix_lost(const inet6_addr &prefix, mrib_def::metric_def metric, const mrib_def::prefix &pfrec) { /* XXX unimplemented */ } void ripng_router::return_prefix(mrib_def::prefix *p) { delete p; } void ripng_router::send_request(interface *intf) { ripng_header hdr; hdr.command = RIPNG_REQUEST; hdr.version = 1; hdr.zero = 0; m_sock.sendto(&hdr, sizeof(hdr), &m_ripnggrp, intf->localaddr()); } void ripng_router::broadcast(ripng_header *hdr, uint16_t len) { for (std::vector::const_iterator i = m_intftimers.begin(); i != m_intftimers.end(); ++i) { m_sock.sendto(hdr, len, &m_ripnggrp, i->argument()->localaddr()); } } void ripng_router::send_table(interface * &intf) { ripng_header *hdr = (ripng_header *)buffer; hdr->command = RIPNG_RESPONSE; hdr->version = 1; hdr->zero = 0; ripng_rte *rte = (ripng_rte *)(buffer + sizeof(ripng_header)); int count = 0; int avail = intf->mtu() - sizeof(ripng_header) - sizeof(ip6_hdr); int max = avail / sizeof(ripng_rte); mrib_def::visitor v; if (!g_mrd->mrib().visit_best_metric(v)) return; do { mrib_def::prefix *pinfo = v.entry(); if (pinfo->flags & mrib_def::prefix::NO_EXPORT) continue; if (pinfo->intf == intf) continue; if (count == max) { m_sock.sendto(hdr, sizeof(hdr) + count * sizeof(ripng_rte), &m_ripnggrp, intf->localaddr()); rte = (ripng_rte *)(buffer + sizeof(ripng_header)); count = 0; } int metric = 1; if (pinfo->owner == this) metric = pinfo->metric; rte->prefix = v.addr().addr; rte->route_tag = 0; rte->prefixlen = v.addr().prefixlen; rte->metric = metric; rte++; count++; } while (g_mrd->mrib().visit_next(v)); if (count) { m_sock.sendto(hdr, sizeof(hdr) + count * sizeof(ripng_rte), &m_ripnggrp, intf->localaddr()); } } void ripng_router::data_available(uint32_t) { sockaddr_in6 from; int res = m_sock.recvfrom(buffer, sizeof(buffer), &from); if (res <= 0) { // XXX return; } /* ignore messages from self */ if (g_mrd->has_address(from.sin6_addr)) return; if (ntohs(from.sin6_port) != 522) return; if (res < (int)sizeof(ripng_header)) return; if (((res - sizeof(ripng_header)) % sizeof(ripng_rte)) != 0) return; interface *intf = get_interface_by_index(from.sin6_scope_id); if (!intf) return; ripng_header *hdr = (ripng_header *)buffer; if (hdr->version != 1 && hdr->zero != 0) return; if (hdr->command == RIPNG_REQUEST) { send_table(intf); } else if (hdr->command == RIPNG_RESPONSE) { ripng_rte *rte = (ripng_rte *)(buffer + sizeof(ripng_header)); int rtecount = (res - sizeof(ripng_header)) / sizeof(ripng_rte); for (int i = 0; i < rtecount; i++, rte++) { if (rte->metric < 1 || rte->metric > 16) continue; if (rte->prefixlen > 128) continue; inet6_addr prefix(rte->prefix, rte->prefixlen); if (prefix.type() & inet6_addr::multicast) continue; mrib_def::prefix *ex = g_mrd->mrib().get_prefix(prefix, this); int metric = rte->metric + 1; if (ex) { ripng_prefix *rpi = (ripng_prefix *)ex; if (rpi->metric >= metric) { rpi->lastupdate = tval::now(); if (metric < rpi->metric) { rpi->metric = metric; rpi->nexthop = from.sin6_addr; rpi->intf = intf; } } else if (rte->metric == RIP_INFINITY) { if (rpi->nexthop == from.sin6_addr && rpi->metric < RIP_INFINITY) { rpi->lastupdate = tval::now(); rpi->metric = rte->metric; } } else { /* dont mrib::update_prefix */ continue; } g_mrd->mrib().update_prefix(rpi); continue; } if (metric < RIP_INFINITY) { ripng_prefix *pinfo = new ripng_prefix(this); if (pinfo) { pinfo->distance = 120; pinfo->metric = metric; pinfo->nexthop = from.sin6_addr; pinfo->lastupdate = tval::now(); pinfo->intf = intf; pinfo->metric = metric; g_mrd->mrib().install_prefix(prefix, pinfo); } } } } } mrd6-0.9.6/src/ripng/EXPERIMENTAL0000644000175000017500000000006710204770637014623 0ustar hugohugoThis code is experimental and shouldn't be considered. mrd6-0.9.6/src/mrib.cpp0000644000175000017500000005402010600102346013301 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mrib.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include enum { mrib_method_local = 1000, mrib_method_prefix, mrib_method_summary, mrib_method_internal_ptree, mrib_method_internal_ptree_graph, mrib_method_node_watchers, }; static const method_info mrib_methods[] = { { "local", "Add a local prefix", mrib_method_local, false, property_def::NEGATE }, { "prefix", "Add a static MRIB entry", mrib_method_prefix, false, property_def::NEGATE }, { "static", "Add a static MRIB entry", mrib_method_prefix, false, property_def::NEGATE }, { "summary", "Displays a entry summary", mrib_method_summary, true, 0 }, { "internal-ptree", 0, mrib_method_internal_ptree, true, property_def::COMPLETE_M }, { "internal-ptree-graph", 0, mrib_method_internal_ptree_graph, true, property_def::COMPLETE_M }, { "node-watchers", 0, mrib_method_node_watchers, true, property_def::COMPLETE_M }, { 0 } }; static objpool _static_prefix_pool(128); static inet6_addr _any; struct static_prefix : mrib_def::prefix, public mrib_watcher_target { static_prefix(mrib_origin *); const inet6_addr &target_group() const { return _any; } const in6_addr &target_destination() const { return nexthop; } void check_nexthop(); void nexthop_changed(); mrib_watcher global_watcher; }; mrib_def::prefix::prefix(mrib_origin *own, uint32_t dist) : nexthop(in6addr_any), intf(0), owner(own), flags(0), distance(dist), metric(0), refcount(0), trie_owner(0), next(0) { creation = time(0); } static_prefix::static_prefix(mrib_origin *owner) : mrib_def::prefix(owner), global_watcher(this, std::mem_fun(&static_prefix::nexthop_changed)) { /* empty */ } void static_prefix::check_nexthop() { if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop) && !IN6_IS_ADDR_LINKLOCAL(&nexthop)) { global_watcher.invalidate(); } } void static_prefix::nexthop_changed() { intf = global_watcher.intf(); } mrib_origin::~mrib_origin() { /* empty */ } void mrib_origin::prefix_added(const inet6_addr &, mrib_def::metric_def, const mrib_def::prefix &) { /* empty */ } void mrib_origin::prefix_lost(const inet6_addr &, mrib_def::metric_def, const mrib_def::prefix &) { /* empty */ } void mrib_origin::output_prefix_info(base_stream &, const mrib_def::prefix &) const { /* empty */ } mrib_def::mrib_def(node *m) : node(m, "mrib"), m_trie(/*objpool(512)*/), m_trie_nodes(512) { m_local = 0; m_static = 0; } mrib_def::~mrib_def() { delete m_static; m_static = 0; delete m_local; m_local = 0; m_trie.clear(); } bool mrib_def::check_startup() { if (!node::check_startup()) return false; import_methods(mrib_methods); m_local = new mrib_connected_origin(); if (!m_local) return false; m_static = new mrib_static_origin(); if (!m_static) return false; /* always maintain a ::/0 node in the tree to accomodate * for parent-less watchers */ mrib_node *root = m_trie_nodes.request_obj(); if (!root) return false; root->refcount = 1; root->prefix = inet6_addr::any(); root->head = 0; root->watchhead = 0; return m_trie.insert(root) == root; } void mrib_def::shutdown() { origin_lost(m_static); origin_lost(m_local); } const char *mrib_def::description() const { return "Multicast Routing Information Base"; } void mrib_def::event(int type, void *param) { if (type != mrd::InterfaceStateChanged) { node::event(type, param); return; } interface *intf = (interface *)param; for (mrib_trie::iterator i = m_trie.begin(); i != m_trie.end(); ++i) { if (i->head && i->head->intf == intf) { invalidate_node_watchers(&(*i)); } } } const mrib_def::prefix *mrib_def::resolve_nexthop(const inet6_addr &src, const inet6_addr &grp, inet6_addr &result) const { const prefix *p = prefix_lookup(src, grp); if (p) { result = p->nexthop; if (result.is_any()) result = src; } return p; } const mrib_def::prefix *mrib_def::prefix_lookup(const inet6_addr &source, const inet6_addr &group) const { mrib_node *n = prefix_lookup_y(source, group); if (!n) return 0; /* best prefix is always at head */ return n->head; } mrib_def::prefix *mrib_def::get_prefix(const inet6_addr &source, mrib_origin *origin) const { mrib_node *n = m_trie.search(source); if (!n) return 0; if (origin == NULL) { if (n->head != NULL && n->head->next == NULL) return n->head; } for (prefix *curr = n->head; curr; curr = curr->next) { if (curr->owner == origin) { return curr; } } return 0; } bool mrib_def::visit_best_metric(visitor &v) const { if (m_trie.empty()) return false; v.i = m_trie.begin(); v.p = v.i->head; if (!v.p) { ++v.i; if (v.i != m_trie.end()) v.p = v.i->head; } v.bestmetric = true; v.owner = 0; return v.p != 0; } bool mrib_def::visit_origin(visitor &v, mrib_origin *owner) const { if (m_trie.empty()) return false; v.i = m_trie.begin(); v.bestmetric = false; v.owner = owner; v.p = v.i->head; if (!v.p) { ++v.i; if (v.i != m_trie.end()) v.p = v.i->head; } if (!v.p) return false; while (v.i != m_trie.end() && v.p->owner != owner) { v.p = v.p->next; if (!v.p) { ++v.i; if (v.i == m_trie.end()) return false; v.p = v.i->head; } } return v.p != 0; } bool mrib_def::visit_next(visitor &v) const { while (1) { if (v.p) v.p = v.p->next; if (!v.p) { ++ v.i; if (v.i == m_trie.end()) return false; v.p = v.i->head; if (v.bestmetric) break; } else { if (v.bestmetric) { v.p = 0; continue; } if (v.owner && v.p->owner == v.owner) break; } } return true; } const inet6_addr &mrib_def::visitor::addr() const { return i->prefix; } mrib_def::prefix *mrib_def::visitor::entry() const { return p; } mrib_def::mrib_node *mrib_def::prefix_lookup_y(const inet6_addr &source, const inet6_addr &group) const { /* group is ignored for now */ return prefix_lookup_y(source); } mrib_def::mrib_node *mrib_def::prefix_lookup_y(const inet6_addr &source) const { mrib_node *n = m_trie.longest_match(source); return n; } void mrib_def::insert_prefix_in_node(mrib_node *n, prefix *p) { prefix *curr = n->head, *prev = 0; /* first check the proper place based on distance */ while (curr && curr->distance < p->distance) { prev = curr; curr = curr->next; } if (prev && prev->distance == p->distance) { /* if distance matches, take metric into place */ curr = prev; while (curr && curr->metric <= p->metric) { prev = curr; curr = curr->next; } } list_insert_after(n->head, prev, p); } bool mrib_def::install_prefix(const inet6_addr &src, prefix *p) { if (!p) return false; if (should_log(EXTRADEBUG)) log().xprintf("(MRIB) Added entry %{Addr} [%u/%u, %s].\n", src, p->distance, p->metric, p->owner->description()); bool newprefix = false; mrib_node *n = m_trie.search(src); if (!n) { n = m_trie_nodes.request_obj(); if (!n) return false; n->refcount = 1; n->prefix = src; n->head = 0; n->watchhead = 0; m_trie.insert(n); /* assert(m_trie.insert(n)); */ newprefix = true; } /* prepare node for insertion */ p->trie_owner = n; insert_prefix_in_node(n, p); p->refcount ++; if (newprefix) { /* if we installed a new prefix, notify all watching with * a used entry less specific than the current prefix */ mrib_node *parent = m_trie.get_parent_node(n); if (parent) { /* this really is a new more specific prefix, * invalidate all watchers using the less specific * prefix (the parent) */ invalidate_node_watchers(parent); } } else if (n->head == p) { /* if the new entry is the best entry for the * prefix, notify all watching this prefix */ invalidate_node_watchers(n); } for (listeners::iterator i = m_listeners.begin(); i != m_listeners.end(); ++i) { if (p->owner != *i) (*i)->prefix_added(src, p->metric, *p); } return true; } void mrib_def::invalidate_node_watchers(mrib_node *n) { mrib_watcher_base *w = n->watchhead; while (w) { mrib_watcher_base *c = w; w = w->wnext; c->invalidate(); } } void mrib_def::remove_prefix_from_node(prefix *p) { list_search_remove(p->trie_owner->head, p); } void mrib_def::update_prefix(prefix *p) { if (!p->trie_owner) return; bool wasbest = p->is_best_entry(); remove_prefix_from_node(p); insert_prefix_in_node(p->trie_owner, p); if (wasbest != p->is_best_entry()) invalidate_node_watchers(p->trie_owner); } void mrib_def::remove_prefix(prefix *p) { if (!p || !p->trie_owner) { return; } inet6_addr prefix = p->trie_owner->prefix; remove_prefix_from_node(p); mrib_node *invn = p->trie_owner; p->trie_owner = 0; if (!invn->head && invn->prefix.prefixlen > 0) { /* if not root, no longer need the mrib node */ m_trie.remove(invn); } else { /* as we still need the node, prevent removal below */ invn->refcount ++; } if (should_log(EXTRADEBUG)) log().xprintf("(MRIB) Removed entry %{Addr} [%s].\n", prefix, p->owner->description()); invalidate_node_watchers(invn); dec_node_refcount(invn); prefix_lost(prefix, p->metric, *p); dec_prefix_refcount(p); } mrib_def::mrib_node *mrib_def::grab_node(mrib_node *n, mrib_watcher_base *w, bool include) { if (!n) return 0; if (include) { w->wnext = n->watchhead; n->watchhead = w; n->refcount ++; return n; } else { mrib_watcher_base *prev = 0, *curr = n->watchhead; while (curr && curr != w) { prev = curr; curr = curr->wnext; } if (prev) prev->wnext = w->wnext; else n->watchhead = w->wnext; dec_node_refcount(n); return 0; } } mrib_def::prefix *mrib_def::grab_prefix(prefix *p) { p->refcount ++; return p; } void mrib_def::dec_node_refcount(mrib_node *n) { if (!n) return; /* assert(n->refcount > 0); */ n->refcount --; if (n->refcount == 0) m_trie_nodes.return_obj(n); } void mrib_def::dec_prefix_refcount(prefix *p) { if (!p) return; /* assert(p->refcount > 0); */ p->refcount --; if (p->refcount == 0) p->owner->return_prefix(p); } void mrib_def::install_listener(mrib_origin *orig) { m_listeners.push_back(orig); for (mrib_trie::iterator i = m_trie.begin(); i != m_trie.end(); ++i) { for (prefix *curr = i->head; curr; curr = curr->next) { if (curr->owner != orig) orig->prefix_added(i->prefix, curr->metric, *curr); } } } void mrib_def::origin_lost(mrib_origin *orig) { listeners::iterator k = std::find(m_listeners.begin(), m_listeners.end(), orig); if (k != m_listeners.end()) { m_listeners.erase(k); } mrib_trie::iterator i = m_trie.begin(); int count = 0; while (i != m_trie.end()) { prefix *curr = i->head; ++i; while (curr) { prefix *c = curr; curr = curr->next; if (c->owner == orig) { remove_prefix(c); count++; } } } if (should_log(DEBUG)) log().xprintf("(MRIB) Lost origin %s, released %i " "prefixes.\n", orig->description(), count); } void mrib_def::removed_interface(interface *intf) { mrib_trie::iterator i = m_trie.begin(); int count = 0; while (i != m_trie.end()) { prefix *curr = i->head; ++i; while (curr) { prefix *_p = curr; curr = curr->next; if (_p->intf == intf) { remove_prefix(_p); count++; } } } if (count == 0) return; g_mrd->log().xprintf("(MRIB) Removal of %s forced the release " "of %i prefixes.\n", intf->name(), count); } void mrib_def::prefix_lost(const inet6_addr &addr, metric_def metric, const prefix &p) { for (listeners::iterator i = m_listeners.begin(); i != m_listeners.end(); ++i) { (*i)->prefix_lost(addr, metric, p); } } void mrib_def::invalidate_watcher(mrib_watcher_base *watch) { if (watch && !watch->pending_update) { watch->pending_update = true; g_mrd->register_task(watch, mrib_watcher_base::Invalidated, 0); } } mrib_watcher_base::mrib_watcher_base(mrib_watcher_target *t) : _target(t), _rec(0), _prefix(0), _nexthop(in6addr_any), _metric(0xffffffff), _protocol(1000), _intf(0), wnext(0), pending_update(false) { } mrib_watcher_base::~mrib_watcher_base() { release(); } void mrib_watcher_base::event(int type, void *) { if (type != Invalidated) return; /* whaaa? */ if (!pending_update) return; pending_update = false; invalidated(); } void mrib_watcher_base::release() { g_mrd->clear_tasks(this); _rec = g_mrd->mrib().grab_node(_rec, this, false); g_mrd->mrib().dec_prefix_refcount(_prefix); _prefix = 0; _nexthop = in6addr_any; _metric = 0xffffffff; _protocol = 1000; _intf = 0; } void mrib_watcher_base::invalidated() { mrib_def::mrib_node *m = g_mrd->mrib().prefix_lookup_y(target(), group()); mrib_def::prefix *prev = _prefix; if (prev) { /* delay the removal of prefix */ prev->refcount ++; } release(); /* we always grab it, we want to know what is going on */ _rec = g_mrd->mrib().grab_node(m, this, true); mrib_def &mrib = g_mrd->mrib(); if (!m || !m->head) { if (prev) { if (mrib.should_log(EXTRADEBUG)) { mrib.log().xprintf( "Target %{addr}, has no prefix " "record to use.\n", target()); } nexthop_changed(); } } else if (prev != m->head) { _prefix = g_mrd->mrib().grab_prefix(m->head); if (_prefix->is_valid()) { _metric = _prefix->metric; _protocol = _prefix->distance; if (mrib.should_log(EXTRADEBUG)) { mrib.log().xprintf("Target %{addr} using entry" " %{Addr} [%s].\n", target(), _rec->prefix, _prefix->owner->description()); } } else { if (mrib.should_log(EXTRADEBUG)) { mrib.log().xprintf("Target %{addr}, has a " "prefix record but is " "disabled.\n", target()); } _prefix = 0; } nexthop_changed(); } g_mrd->mrib().dec_prefix_refcount(prev); } void mrib_watcher_base::invalidate() { g_mrd->mrib().invalidate_watcher(this); } interface *mrib_watcher_base::intf() const { return _intf; } uint32_t mrib_watcher_base::prefix_protocol() const { return _protocol; } uint32_t mrib_watcher_base::prefix_metric() const { return _metric; } void mrib_watcher_base::nexthop_changed() { _intf = _prefix ? _prefix->intf : 0; if (_prefix) { _nexthop = _prefix->nexthop; if (IN6_IS_ADDR_UNSPECIFIED(&_nexthop)) _nexthop = target(); } else { _nexthop = in6addr_any; } entry_changed(); } bool mrib_def::call_method(int id, base_stream &out, const std::vector &args) { switch (id) { case mrib_method_local: return local(args); case mrib_method_prefix: return confprefix(args); case mrib_method_summary: out.xprintf("MRIB prefix count: %u\n", m_trie.size()); return true; case mrib_method_internal_ptree: m_trie.dump_internal_tree(out); return true; case mrib_method_internal_ptree_graph: m_trie.dump_internal_tree_graphviz(out); return true; case mrib_method_node_watchers: dump_node_watchers(out); return true; } return node::call_method(id, out, args); } bool mrib_def::negate_method(int id, base_stream &out, const std::vector &args) { switch (id) { case mrib_method_local: return negate_local(args); case mrib_method_prefix: return negate_prefix(args); } return node::negate_method(id, out, args); } void mrib_def::dump_node_watchers(base_stream &out) const { for (mrib_trie::const_iterator i = m_trie.begin(); i != m_trie.end(); ++i) { out.xprintf("%{Addr} has", i->prefix); if (i->watchhead) { mrib_watcher_base *w = i->watchhead; while (w) { out.xprintf(" %{addr}", w->target()); w = w->wnext; } } else { out.write(" "); } out.newl(); } } bool mrib_def::local(const std::vector &args) { if (args.empty()) return false; bool local_only = false; for (std::vector::const_iterator i = args.begin(); i != args.end(); ++i) { if (*i == "no-export") { local_only = true; } else if (*i == "export") { local_only = false; } else { inet6_addr addr; if (addr.set(*i)) { prefix *pinfo = new static_prefix(m_static); if (pinfo) { pinfo->metric = 0x1000; if (local_only) pinfo->flags |= prefix::NO_EXPORT; install_prefix(addr, pinfo); } } else { return false; } } } return true; } bool mrib_def::negate_local(const std::vector &args) { if (args.size() != 1) return false; inet6_addr prfx; if (!prfx.set(args[0].c_str())) return false; remove_prefix(get_prefix(prfx, m_local)); return true; } bool mrib_def::confprefix(const std::vector &args) { /* Needs at least 3 args */ if (args.size() < 3) return false; inet6_addr pfix; if (!pfix.set(args[0])) return false; inet6_addr nh; interface *dev = 0; uint32_t metric = 100; bool local_only = true; for (std::vector::const_iterator i = args.begin(); i != args.end(); ++i) { if (*i == "via") { ++i; if (i == args.end() || !nh.set(*i)) return false; } else if (*i == "dev") { ++i; if (i == args.end()) return false; dev = g_mrd->get_interface_by_name(i->c_str()); if (!dev) return false; } else if (*i == "metric") { ++i; if (i == args.end()) return false; char *end; metric = strtoul(i->c_str(), &end, 10); if (*end) return false; } else if (*i == "export") { local_only = false; } } /* No nexthop and no dev supplied, fail. */ if (nh.is_any() && !dev) return false; if (nh.is_linklocal() && !dev) return false; static_prefix *pinfo = new static_prefix(m_static); if (pinfo) { pinfo->distance = 1; pinfo->metric = metric; pinfo->nexthop = nh; pinfo->intf = dev; if (local_only) pinfo->flags |= prefix::NO_EXPORT; pinfo->check_nexthop(); install_prefix(pfix, pinfo); } else { return false; } return true; } bool mrib_def::negate_prefix(const std::vector &args) { if (args.size() < 1) return false; inet6_addr prfx; if (!prfx.set(args[0].c_str())) return false; inet6_addr nh; interface *dev = 0; uint32_t metric = 0xffffffff; std::vector::const_iterator j = args.begin(); ++j; for (; j != args.end(); ++j) { if (*j == "via") { ++j; if (j == args.end() || !nh.set(*j)) return false; } else if (*j == "dev") { ++j; if (j == args.end()) return false; dev = g_mrd->get_interface_by_name(j->c_str()); if (!dev) return false; } else if (*j == "metric") { ++j; if (j == args.end()) return false; char *end; metric = strtoul(j->c_str(), &end, 10); if (*end) return false; } else { return false; } } mrib_node *m = m_trie.search(prfx); if (!m) return false; prefix *curr = m->head; while (curr) { prefix *_p = curr; curr = curr->next; if (!nh.is_any() && !(_p->nexthop == nh)) continue; if (dev && _p->intf != dev) continue; if (metric < 0xffffffff && _p->metric != metric) continue; remove_prefix(_p); } return true; } const char *mrib_connected_origin::description() const { return "Directly Connected"; } const char *mrib_static_origin::description() const { return "Static"; } void mrib_connected_origin::register_prefix(const inet6_addr &addr, interface *intf) { std::pair res = _reg.insert(std::make_pair(addr, std::make_pair(intf, (mrib_def::prefix *)0))); if (res.second) { mrib_def::prefix *pinfo = _static_prefix_pool.request_obj(this); if (pinfo) { pinfo->distance = 0; pinfo->metric = 0; pinfo->flags = mrib_def::prefix::NO_EXPORT; pinfo->intf = intf; g_mrd->mrib().install_prefix(addr, pinfo); res.first->second.second = pinfo; } else { _reg.erase(res.first); } } } void mrib_connected_origin::unregister_prefix(const inet6_addr &addr, interface *intf) { regdef::iterator i = _reg.find(addr); if (i != _reg.end()) { if (i->second.first == intf) { g_mrd->mrib().remove_prefix(i->second.second); _reg.erase(i); } } } void mrib_static_origin::return_prefix(mrib_def::prefix *p) { delete (static_prefix *)p; } void mrib_connected_origin::return_prefix(mrib_def::prefix *p) { _static_prefix_pool.return_obj(p); } bool mrib_def::output_info(base_stream &ctx, const std::vector &args) const { if (!args.empty()) return false; ctx.writeline("MRIB"); ctx.inc_level(); for (mrib_trie::const_iterator m = m_trie.begin(); m != m_trie.end(); ++m) { for (prefix *curr = m->head; curr; curr = curr->next) { ctx.xprintf("Prefix %{Addr} Cost: %u/%u, %s%s\n", m->prefix, (uint32_t)curr->distance, (uint32_t)curr->metric, curr->owner->description(), (curr->flags & prefix::NO_EXPORT) ? " L" : ""); ctx.inc_level(); if (!IN6_IS_ADDR_UNSPECIFIED(&curr->nexthop)) ctx.xprintf("Nexthop: %{addr}\n", curr->nexthop); if (curr->intf) { ctx.xprintf("Interface: %s", curr->intf->name()); if (curr->owner == m_static && !IN6_IS_ADDR_UNSPECIFIED(&curr->nexthop) && !IN6_IS_ADDR_LINKLOCAL(&curr->nexthop)) { static_prefix *p = (static_prefix *)curr; ctx.xprintf(" (via %{Addr}", p->global_watcher._rec->prefix); if (!p->global_watcher.pending_update) ctx.xprintf(", %s", p->global_watcher._prefix->owner->description()); ctx.write(")"); } if (!curr->intf->up()) ctx.write(", Disabled"); ctx.newl(); } else { ctx.writeline("Interface: None"); } curr->owner->output_prefix_info(ctx, *curr); ctx.dec_level(); } } ctx.dec_level(); return true; } mrd6-0.9.6/src/zebra/0000755000175000017500000000000010746353706012771 5ustar hugohugomrd6-0.9.6/src/zebra/zebra.cpp0000644000175000017500000003420110550053317014564 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * zebra.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include enum { ZEBRA_INTERFACE_ADD = 1, ZEBRA_INTERFACE_DELETE, ZEBRA_INTERFACE_ADDRESS_ADD, ZEBRA_INTERFACE_ADDRESS_DELETE, ZEBRA_INTERFACE_UP, ZEBRA_INTERFACE_DOWN, ZEBRA_IPV4_ROUTE_ADD, ZEBRA_IPV4_ROUTE_DELETE, ZEBRA_IPV6_ROUTE_ADD, ZEBRA_IPV6_ROUTE_DELETE, ZEBRA_REDISTRIBUTE_ADD, ZEBRA_REDISTRIBUTE_DELETE, ZEBRA_REDISTRIBUTE_DEFAULT_ADD, ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, ZEBRA_IPV4_NEXTHOP_LOOKUP, ZEBRA_IPV6_NEXTHOP_LOOKUP, ZEBRA_IPV4_IMPORT_LOOKUP, ZEBRA_IPV6_IMPORT_LOOKUP, ZEBRA_INTERFACE_RENAME, ZEBRA_ROUTER_ID_ADD, ZEBRA_ROUTER_ID_DELETE, ZEBRA_ROUTER_ID_UPDATE, /* afi/safi independent messages */ ZEBRA_ROUTE_ADD, ZEBRA_ROUTE_DELETE, ZEBRA_NEXTHOP_LOOKUP, ZEBRA_IMPORT_LOOKUP, ZEBRA_MAX }; enum { ZEBRA_ROUTE_SYSTEM = 0, ZEBRA_ROUTE_KERNEL, ZEBRA_ROUTE_CONNECT, ZEBRA_ROUTE_STATIC, ZEBRA_ROUTE_RIP, ZEBRA_ROUTE_RIPNG, ZEBRA_ROUTE_OSPF, ZEBRA_ROUTE_OSPF6, ZEBRA_ROUTE_ISIS, ZEBRA_ROUTE_BGP, ZEBRA_ROUTE_HSLS, ZEBRA_ROUTE_MAX }; enum { ZAPI_MESSAGE_NEXTHOP = 1, ZAPI_MESSAGE_IFINDEX = 2, ZAPI_MESSAGE_DISTANCE = 4, ZAPI_MESSAGE_METRIC = 8, }; enum { AFI_IP = 1, AFI_IP6 = 2, }; enum { SAFI_UNICAST = 1, SAFI_MULTICAST = 2, }; enum { ZEBRA_KERNEL_DISTANCE_DEFAULT = 0, ZEBRA_CONNECT_DISTANCE_DEFAULT = 0, ZEBRA_STATIC_DISTANCE_DEFAULT = 1, ZEBRA_RIP_DISTANCE_DEFAULT = 120, ZEBRA_OSPF_DISTANCE_DEFAULT = 110, ZEBRA_ISIS_DISTANCE_DEFAULT = 115, ZEBRA_IBGP_DISTANCE_DEFAULT = 200, ZEBRA_EBGP_DISTANCE_DEFAULT = 20, }; enum { ZEBRA_NEXTHOP_IFINDEX = 1, ZEBRA_NEXTHOP_IFNAME = 2, ZEBRA_NEXTHOP_IPV6 = 6, ZEBRA_NEXTHOP_IPV6_IFINDEX = 7, ZEBRA_NEXTHOP_IPV6_IFNAME = 8, }; static const char *command_name[] = { 0, "ZEBRA_INTERFACE_ADD", "ZEBRA_INTERFACE_DELETE", "ZEBRA_INTERFACE_ADDRESS_ADD", "ZEBRA_INTERFACE_ADDRESS_DELETE", "ZEBRA_INTERFACE_UP", "ZEBRA_INTERFACE_DOWN", "ZEBRA_IPV4_ROUTE_ADD", "ZEBRA_IPV4_ROUTE_DELETE", "ZEBRA_IPV6_ROUTE_ADD", "ZEBRA_IPV6_ROUTE_DELETE", "ZEBRA_REDISTRIBUTE_ADD", "ZEBRA_REDISTRIBUTE_DELETE", "ZEBRA_REDISTRIBUTE_DEFAULT_ADD", "ZEBRA_REDISTRIBUTE_DEFAULT_DELETE", "ZEBRA_IPV4_NEXTHOP_LOOKUP", "ZEBRA_IPV6_NEXTHOP_LOOKUP", "ZEBRA_IPV4_IMPORT_LOOKUP", "ZEBRA_IPV6_IMPORT_LOOKUP", "ZEBRA_INTERFACE_RENAME", "ZEBRA_ROUTER_ID_ADD", "ZEBRA_ROUTER_ID_DELETE", "ZEBRA_ROUTER_ID_UPDATE", "ZEBRA_ROUTE_ADD", "ZEBRA_ROUTE_DELETE", "ZEBRA_NEXTHOP_LOOKUP", "ZEBRA_IMPORT_LOOKUP" }; static int _zebra_type_to_distance(int type) { switch (type) { case ZEBRA_ROUTE_SYSTEM: case ZEBRA_ROUTE_KERNEL: return ZEBRA_KERNEL_DISTANCE_DEFAULT; case ZEBRA_ROUTE_CONNECT: return ZEBRA_CONNECT_DISTANCE_DEFAULT; case ZEBRA_ROUTE_STATIC: return ZEBRA_STATIC_DISTANCE_DEFAULT; case ZEBRA_ROUTE_RIP: case ZEBRA_ROUTE_RIPNG: return ZEBRA_RIP_DISTANCE_DEFAULT; case ZEBRA_ROUTE_OSPF: case ZEBRA_ROUTE_OSPF6: return ZEBRA_OSPF_DISTANCE_DEFAULT; case ZEBRA_ROUTE_ISIS: return ZEBRA_ISIS_DISTANCE_DEFAULT; case ZEBRA_ROUTE_BGP: return ZEBRA_EBGP_DISTANCE_DEFAULT; default: return 1000; } } const int INTERFACE_NAMSIZ = 20; class zebra_rib; class zebra_module : public mrd_module, public mrib_origin, public node { public: zebra_module(mrd *, void *); bool check_startup(); void shutdown(); void return_prefix(mrib_def::prefix *); const char *description() const; void send_message(int); void redistribute_send(int, int); void interface_add_delete(int, int); void interface_address_add_delete(int, int); void interface_up_down(int, int); void route_add_delete(int, int); void data_available(uint32_t); void trigger_send(); int check_message(); void process_message(); bool do_lookup(const in6_addr &addr, int &dev, in6_addr &nexthop); zebra_rib *zrib; socket0 zebra_sock; encoding_buffer ibuf, obuf; }; static zebra_module *zebra = 0; class zebra_rib : public rib_def { public: bool lookup_prefix(const in6_addr &addr, lookup_result &res) const; void propagate_prefix(bool, int, const inet6_addr &, const in6_addr &, int, int); }; module_entry(zebra, zebra_module); zebra_module::zebra_module(mrd *m, void *p) : mrd_module(m, p), node(m, "zebra"), zrib(0), zebra_sock("zebra sock", this, std::mem_fun(&zebra_module::data_available)), ibuf(4096), obuf(4096) { zebra = this; g_mrd->add_child(this); } const char *zebra_module::description() const { return "Zebra routing daemon interoperation"; } bool zebra_module::check_startup() { if (!node::check_startup()) return false; if (!ibuf.check_startup() || !obuf.check_startup()) return false; zrib = new zebra_rib(); if (!zrib) return false; if (!g_mrd->register_rib(zrib)) return false; int sock = socket(PF_LOCAL, SOCK_STREAM, 0); if (sock < 0) return false; sockaddr_un zebraaddr; memset(&zebraaddr, 0, sizeof(sockaddr_un)); zebraaddr.sun_family = PF_LOCAL; strcpy(zebraaddr.sun_path, "/var/run/quagga/zserv.api"); if (connect(sock, (sockaddr *)&zebraaddr, sizeof(sockaddr_un)) < 0) { perror("connect()"); close(sock); return false; } zebra_sock.register_fd(sock); send_message(ZEBRA_INTERFACE_ADD); redistribute_send(ZEBRA_REDISTRIBUTE_ADD, ZEBRA_ROUTE_SYSTEM); redistribute_send(ZEBRA_REDISTRIBUTE_ADD, ZEBRA_ROUTE_KERNEL); redistribute_send(ZEBRA_REDISTRIBUTE_ADD, ZEBRA_ROUTE_CONNECT); redistribute_send(ZEBRA_REDISTRIBUTE_ADD, ZEBRA_ROUTE_STATIC); redistribute_send(ZEBRA_REDISTRIBUTE_ADD, ZEBRA_ROUTE_RIPNG); redistribute_send(ZEBRA_REDISTRIBUTE_ADD, ZEBRA_ROUTE_OSPF6); redistribute_send(ZEBRA_REDISTRIBUTE_ADD, ZEBRA_ROUTE_BGP); return true; } void zebra_module::shutdown() { } void zebra_module::return_prefix(mrib_def::prefix *p) { delete p; } void zebra_module::send_message(int command) { obuf.put() = htons(3); obuf.put() = command; trigger_send(); } void zebra_module::redistribute_send(int command, int id) { obuf.put() = htons(4); obuf.put() = command; obuf.put() = id; trigger_send(); } void zebra_module::data_available(uint32_t type) { if (type == socket_base::Write) { /* We can write, let's feed as much data as possible to zebra */ obuf.flush_to(zebra_sock, true); return; } int len = ibuf.consume(zebra_sock); if (len <= 0) return; while (check_message() >= 0) { process_message(); } } int zebra_module::check_message() { if (!ibuf.require(3)) return -1; uint8_t *head = ibuf.head(); int length = ntohs(*(uint16_t *)head); int command = head[2]; /* zebra message isn't fully here yet */ if (!ibuf.require(length)) { return -1; } return command; } void zebra_module::process_message() { uint8_t *head = ibuf.head(); int length = ntohs(*(uint16_t *)head); int command = head[2]; if (command == 0 || command >= ZEBRA_MAX) { /* unknown message, consume it */ ibuf.eat(length); return; } ibuf.eat(3); length -= 3; if (should_log(DEBUG)) log().xprintf("Zebra message %s length %i\n", command_name[command], length); switch (command) { case ZEBRA_INTERFACE_ADD: case ZEBRA_INTERFACE_DELETE: interface_add_delete(command, length); break; case ZEBRA_INTERFACE_ADDRESS_ADD: case ZEBRA_INTERFACE_ADDRESS_DELETE: interface_address_add_delete(command, length); break; case ZEBRA_INTERFACE_UP: case ZEBRA_INTERFACE_DOWN: interface_up_down(command, length); break; case ZEBRA_IPV6_ROUTE_ADD: case ZEBRA_IPV6_ROUTE_DELETE: route_add_delete(command, length); break; case ZEBRA_ROUTE_ADD: case ZEBRA_ROUTE_DELETE: route_add_delete(command, length); break; default: ibuf.eat(length); } ibuf.compact(); } void zebra_module::trigger_send() { if (!obuf.empty()) { zebra_sock.monitor(socket_base::Read | socket_base::Write); obuf.flush_to(zebra_sock, true); } } void zebra_module::interface_add_delete(int command, int length) { char name[INTERFACE_NAMSIZ+1]; int index, status, flags, mtu; memcpy(name, ibuf.eat(INTERFACE_NAMSIZ), INTERFACE_NAMSIZ); name[INTERFACE_NAMSIZ] = 0; index = ibuf.neatl(); status = ibuf.neatu8(); flags = ibuf.neatl(); /* metric = */ ibuf.neatl(); mtu = ibuf.neatl(); /* mtu6 = */ ibuf.neatl(); /* bandwidth = */ ibuf.neatl(); if (command == ZEBRA_INTERFACE_ADD) { int addrlen = ibuf.neatl(); ibuf.eat(addrlen); interface *intf = g_mrd->found_interface(index, name, 0, mtu, flags); if (flags & IFF_UP) { intf->change_state(interface::Up); } } else { g_mrd->lost_interface(index); } } void zebra_module::interface_address_add_delete(int command, int length) { int index, flags, family; index = ibuf.neatl(); flags = ibuf.neatu8(); family = ibuf.neatu8(); if (family == AF_INET) { /* we ignore IPv4 addresses */ ibuf.eat(length - (4 + 1 + 1)); } else { inet6_addr addr; memcpy(&addr.addr, ibuf.eat(16), 16); addr.prefixlen = ibuf.neatu8(); in6_addr dest; /* ? */ memcpy(&dest, ibuf.eat(16), 16); bool add = (command == ZEBRA_INTERFACE_ADDRESS_ADD); interface *intf = g_mrd->get_interface_by_index(index); if (intf) intf->address_added_or_removed(add, addr); } } void zebra_module::interface_up_down(int command, int length) { int index; ibuf.eat(INTERFACE_NAMSIZ); index = ibuf.neatl(); /* status = */ ibuf.neatu8(); /* flags = */ ibuf.neatl(); /* metric = */ ibuf.neatl(); /* mtu = */ ibuf.neatl(); /* mtu6 = */ ibuf.neatl(); /* bandwidth = */ ibuf.neatl(); interface *intf = g_mrd->get_interface_by_index(index); if (intf) { if (command == ZEBRA_INTERFACE_UP) { intf->change_state(interface::Up); } else { intf->change_state(interface::Down); } } } void zebra_module::route_add_delete(int command, int length) { int afi = AFI_IP6, safi = SAFI_UNICAST; if (command == ZEBRA_ROUTE_ADD || command == ZEBRA_ROUTE_DELETE) { afi = ibuf.neatu16(); safi = ibuf.neatu8(); } bool add = (command == ZEBRA_ROUTE_ADD) || (command == ZEBRA_IPV6_ROUTE_ADD); int type, flags, msgflags; type = ibuf.neatu8(); flags = ibuf.neatu8(); msgflags = ibuf.neatu8(); inet6_addr prefix; prefix.prefixlen = ibuf.neatu8(); int blen = (prefix.prefixlen + 7) / 8; memcpy(&prefix.addr, ibuf.eat(blen), blen); int ifindex = 0; in6_addr nexthop = in6addr_any; int distance = 0, metric = 0; if (msgflags & ZAPI_MESSAGE_NEXTHOP) { int count = ibuf.neatu8(); for (int i = 0; i < count; i++) memcpy(&nexthop, ibuf.eat(sizeof(in6_addr)), sizeof(in6_addr)); } if (msgflags & ZAPI_MESSAGE_IFINDEX) { int count = ibuf.neatu8(); for (int i = 0; i < count; i++) ifindex = ibuf.neatl(); } if (msgflags & ZAPI_MESSAGE_DISTANCE) distance = ibuf.neatu8(); if (msgflags & ZAPI_MESSAGE_METRIC) metric = ibuf.neatl(); if (type == ZEBRA_ROUTE_CONNECT) { /* we already handle directly connected prefixes */ return; } if (safi == SAFI_UNICAST) { zrib->propagate_prefix(add, ifindex, prefix, nexthop, _zebra_type_to_distance(type), metric); } else if (safi == SAFI_MULTICAST) { if (add) { mrib_def::prefix *p = new mrib_def::prefix(this); if (p) { p->distance = _zebra_type_to_distance(type); p->metric = metric; p->nexthop = nexthop; p->intf = g_mrd->get_interface_by_index(ifindex); p->flags = mrib_def::prefix::NO_EXPORT; g_mrd->mrib().install_prefix(prefix, p); } } else { mrib_def::prefix *p = g_mrd->mrib().get_prefix(prefix, this); if (p) g_mrd->mrib().remove_prefix(p); } } } bool zebra_module::do_lookup(const in6_addr &addr, int &dev, in6_addr &nexthop) { /* flush everything */ while (!obuf.empty()) obuf.flush_to(zebra_sock, true, true); obuf.put() = htons(3 + sizeof(in6_addr)); obuf.put() = ZEBRA_IPV6_NEXTHOP_LOOKUP; memcpy(obuf.put(sizeof(in6_addr)), &addr, sizeof(in6_addr)); while (!obuf.empty()) obuf.flush_to(zebra_sock, true, true); while (1) { int len = ibuf.consume(zebra_sock, true); if (len <= 0) return false; int command; if ((command = zebra->check_message()) == ZEBRA_IPV6_NEXTHOP_LOOKUP) { /* addr = */ ibuf.eat(sizeof(in6_addr)); /* metric = */ ibuf.neatl(); int nexthop_count = ibuf.neatu8(); for (int i = 0; i < nexthop_count; i++) { int type = ibuf.neatu8(); switch (type) { case ZEBRA_NEXTHOP_IPV6: memcpy(&nexthop, ibuf.eat(sizeof(in6_addr)), sizeof(in6_addr)); break; case ZEBRA_NEXTHOP_IPV6_IFINDEX: case ZEBRA_NEXTHOP_IPV6_IFNAME: memcpy(&nexthop, ibuf.eat(sizeof(in6_addr)), sizeof(in6_addr)); dev = ibuf.neatl(); break; case ZEBRA_NEXTHOP_IFINDEX: case ZEBRA_NEXTHOP_IFNAME: dev = ibuf.neatl(); break; } } if (nexthop_count > 0) { return true; } else { break; } } else { zebra->process_message(); } } return false; } bool zebra_rib::lookup_prefix(const in6_addr &addr, lookup_result &res) const { res.dst = addr; res.source = in6addr_any; /* XXX */ res.dev = -1; return zebra->do_lookup(addr, res.dev, res.nexthop); } void zebra_rib::propagate_prefix(bool add, int ifindex, const inet6_addr &prefix, const in6_addr &nexthop, int protocol, int metric) { /* this function exists because zebra_module doesn't access lookup_result */ lookup_result res; res.dev = ifindex; res.dst = prefix; res.nexthop = nexthop; res.protocol = protocol; res.metric = metric; prefix_changed(add, res); } mrd6-0.9.6/src/mrd.cpp0000644000175000017500000015400410746067643013162 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mrd.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef IPV6_RECVPKTINFO #define IPV6_RECVPKTINFO IPV6_PKTINFO #endif #define CRASH_COMMAND mrd *g_mrd = 0; extern const char *BuildDate; static const char *VersionInfo = "mrd6 0.9.6 ($Rev: 1711 $)"; static const char *defaultconffiles[] = { "mrd6.conf", "mrd.conf", "/etc/mrd6/mrd6.conf", "/etc/mrd6/conf", "/etc/mrd6.conf", "/etc/mrd.conf", 0 }; enum mrd_method_name { method_shutdown = 1000, method_version, method_timers, method_rpf, method_load_module, method_unload_module, method_unicast_regs, method_socket_regs, method_show_info, method_conf, method_show_commands, method_show, method_dump_tree, #ifdef CRASH_COMMAND method_crash, #endif }; static const method_info mrd_methods[] = { { "shutdown", "Terminates the router execution", method_shutdown, false, property_def::COMPLETE_M }, { "version", "Displays MRD6 version", method_version, true, 0 }, { "timers", "Display timer information", method_timers, true, 0 }, { "rpf", "Reverse path forwarding check", method_rpf, true, 0 }, { "load-module", 0, method_load_module, false, 0 }, { "unload-module", 0, method_unload_module,false, 0 }, { "unicast-regs", 0, method_unicast_regs, true, 0 }, { "socket-regs", 0, method_socket_regs, true, 0 }, { "info", 0, method_show_info, true, 0 }, /* { "conf", "Current configuration", method_conf, true, 0 }, */ { "commands", "Display all available commands", method_show_commands, true, 0 }, { "show", "Show running system information", method_show, false, 0 }, { "node-tree", 0, method_dump_tree, true, 0 }, #ifdef CRASH_COMMAND { "crash", 0, method_crash, false, property_def::COMPLETE_M }, #endif { 0 } }; enum { method_interfaces_disable_range = 1100, }; static const method_info interfaces_methods[] = { { "disable-range", "Adds a new regexp to ignore system interfaces", method_interfaces_disable_range, false, property_def::NEGATE }, { 0 } }; // simple recursive parser class conf_parser { public: conf_parser(); bool parse(const char *); typedef std::vector state; int proplist(const state &); int prop(const state &); int value(); bool check_value(bool eat); int prop_type(const state &); int set_value(bool, const char *, const state &); FILE *fp; parser_context ctx; char *buffer; int bufsize; }; conf_parser::conf_parser() { buffer = 0; } bool conf_parser::parse(const char *filename) { fp = fopen(filename, "r"); if (!fp) return false; bool ret = false; if (fseek(fp, 0, SEEK_END) == 0) { bufsize = ftell(fp); if (bufsize >= 0) { if (fseek(fp, 0, SEEK_SET) == 0) { buffer = new char[bufsize+1]; int res = fread(buffer, bufsize, 1, fp); buffer[bufsize] = 0; if (res == 1) { ctx = parser_context(buffer); state ns; ns.push_back(g_mrd); if (proplist(ns) >= 0) { ret = true; } } } } } delete [] buffer; buffer = 0; fclose(fp); return ret; } int conf_parser::proplist(const state &s) { int res; if ((res = prop(s)) < 1) return res; return proplist(s); } int conf_parser::prop(const state &s) { int res; if ((res = ctx.read()) < 1) return res; if (ctx.head().sym == parser_context::LPARENT) { state ns; ctx.eat(); while (1) { if ((res = ctx.eat()) < 1) return res; if (ctx.head().sym == parser_context::RPARENT) break; else if (ctx.head().sym == parser_context::COMMA) continue; else if (!check_value(false)) return -1; for (state::const_iterator i = s.begin(); i != s.end(); ++i) { node *n = (*i)->get_or_create_child(ctx.head().value.c_str()); if (n) { ns.push_back(n); } else { if (g_mrd->should_log(EXTRADEBUG)) { g_mrd->log().xprintf( "(CONF) %s has no child %s.\n", (*i)->full_name().c_str(), ctx.head().value.c_str()); } } } } return prop(ns); } if (!check_value(true)) { return 0; } if ((res = prop_type(s)) < 1) return res; return 1; } int conf_parser::value() { int res; if ((res = ctx.read()) < 1) return res; return check_value(true) ? 1 : 0; } bool conf_parser::check_value(bool eat) { if (ctx.head().sym == parser_context::TOKEN || ctx.head().sym == parser_context::STRING) { if (eat) ctx.eat(); return true; } return false; } int conf_parser::set_value(bool set, const char *name, const state &s) { if (value() < 1) return -1; std::string val = ctx.head().value; if (ctx.eat(parser_context::TERM) < 1) return -1; for (state::const_iterator i = s.begin(); i != s.end(); ++i) { bool res; if (set) res = (*i)->set_property(name, val.c_str()); else res = (*i)->increment_property(name, val.c_str()); if (!res) { if (g_mrd->should_log(WARNING)) { g_mrd->log().xprintf("(CONF) Failed to change " "property %s.\n", name); } return -1; } } return 1; } int conf_parser::prop_type(const state &s) { std::string ident = ctx.head().value; int res; if ((res = ctx.read()) < 1) return res; parser_context::symbol symb = ctx.head(); if (symb.sym == parser_context::EQUAL || symb.sym == parser_context::PLUSEQUAL) { ctx.eat(); return set_value(symb.sym == parser_context::EQUAL, ident.c_str(), s); } else if (symb.sym == parser_context::LCURLY) { ctx.eat(); state ns; for (state::const_iterator i = s.begin(); i != s.end(); ++i) { node *n = (*i)->get_or_create_child(ident.c_str()); if (n) ns.push_back(n); } if (proplist(ns) == -1) return -1; if (ctx.eat(parser_context::RCURLY) < 1) return -1; } else { state ns; for (state::const_iterator i = s.begin(); i != s.end(); ++i) { node *n = (*i)->get_or_create_child(ident.c_str()); if (n) { ns.push_back(n); } else if ((*i)->has_property(ident.c_str())) { if (value() < 1) return -1; std::string val = ctx.head().value; if (ctx.eat(parser_context::TERM) < 1) return -1; /* XXX check return value */ (*i)->set_property(ident.c_str(), val.c_str()); } else { const property_def *mth = (*i)->get_any_property(ident.c_str()); if (mth && mth->is_method() && !mth->is_readonly()) { std::vector args; while (value() > 0) { args.push_back(std::string(ctx.head().value)); } if (ctx.eat(parser_context::TERM) < 1) return -1; if (!(*i)->call_method(mth->get_method_info()->id, g_mrd->log(), args)) { if (g_mrd->should_log(DEBUG)) { g_mrd->log().xprintf( "(CONF) Failed while calling " "%s in %s.\n", ident.c_str(), (*i)->name()); } } } else { node *p = *i; while (1) { p = p->next_similiar_node(); if (!p) { return -1; } else if (p->has_property(ident.c_str())) { if (value() < 1) return -1; std::string val = ctx.head().value; if (ctx.eat(parser_context::TERM) < 1) return -1; (*i)->set_property(ident.c_str(), val.c_str()); break; } } } } } if (!ns.empty()) return prop(ns); } return 1; } mrd::intfconf_list::intfconf_list(node *parent) : node(parent, "interfaces") {} mrd::intfconf_list::~intfconf_list() { for (std::list::iterator i = tokens.begin(); i != tokens.end(); ++i) { regfree(&i->r); } tokens.clear(); } bool mrd::intfconf_list::check_startup() { if (!node::check_startup()) return false; import_methods(interfaces_methods); return true; } bool mrd::intfconf_list::call_method(int id, base_stream &out, const std::vector &args) { if (id == method_interfaces_disable_range) { if (args.size() != 1) return false; tokens.push_back(disable_token()); disable_token &tok = tokens.back(); tok.origstr = args[0]; if (regcomp(&tok.r, tok.origstr.c_str(), REG_EXTENDED | REG_NOSUB) != 0) { tokens.pop_back(); return false; } return true; } return node::call_method(id, out, args); } bool mrd::intfconf_list::negate_method(int id, base_stream &out, const std::vector &args) { if (id == method_interfaces_disable_range) { if (args.size() != 1) return false; for (std::list::iterator i = tokens.begin(); i != tokens.end(); ++i) { if (i->origstr == args[0]) { regfree(&i->r); tokens.erase(i); break; } } return true; } return node::negate_method(id, out, args); } bool mrd::intfconf_list::is_interface_disabled(const char *name) const { for (std::list::const_iterator i = tokens.begin(); i != tokens.end(); ++i) { if (regexec(&i->r, name, 0, 0, 0) == 0) { return true; } } return false; } node *mrd::intfconf_list::create_child(const char *name) { intfconf *conf = (intfconf *)get_child(name); if (!conf) { if (has_child_property(name)) return 0; conf = new intfconf(name); if (!conf || !conf->check_startup()) { delete conf; return 0; } add_child(conf); interface *intf = g_mrd->get_interface_by_name(name); if (intf) conf->update_interface_configuration(intf); } return conf; } void mrd::intfconf_list::remove_child_node(node *n) { delete (intfconf *)n; } socket_base::socket_base(const char *name) : _name(name), _fd(-1), _hits(0) {} socket_base::~socket_base() { unregister(true); } bool socket_base::register_fd(int sock, uint32_t flags) { return g_mrd->register_sock(this, sock, flags); } void socket_base::unregister(bool close) { if (_fd > 0) { g_mrd->unregister_sock(this); if (close) { if (g_mrd->should_log(INTERNAL_FLOW)) { g_mrd->log().xprintf( "Socket, unregister %s (%i).\n", name(), _fd); } ::close(_fd); _fd = -1; } } } bool socket_base::monitor(uint32_t flags) { return g_mrd->monitor_sock(this, flags); } socket6_base::socket6_base(const char *name) : socket_base(name) { memset(_ctlbuf, 0, sizeof(_ctlbuf)); memset(&_h, 0, sizeof(_h)); } bool socket6_base::register_fd(int fd, uint32_t flags) { int on = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0) return false; return socket_base::register_fd(fd, flags); } int socket6_base::sendto(const void *buf, uint16_t buflen, const sockaddr_in6 *to) { return ::sendto(fd(), buf, buflen, 0, (const sockaddr *)to, sizeof(sockaddr_in6)); } int socket6_base::sendto(const void *buf, uint16_t buflen, const sockaddr_in6 *to, const sockaddr_in6 *from, int extractl) { iovec v = { (void *)buf, buflen }; _h.msg_name = (void *)to; _h.msg_namelen = sizeof(sockaddr_in6); _h.msg_iov = &v; _h.msg_iovlen = 1; _h.msg_control = _ctlbuf; _h.msg_controllen = CMSG_SPACE(sizeof(in6_pktinfo)) + extractl; _h.msg_flags = 0; cmsghdr *chdr = (cmsghdr *)CMSG_FIRSTHDR(&_h); chdr->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); chdr->cmsg_level = IPPROTO_IPV6; chdr->cmsg_type = IPV6_PKTINFO; in6_pktinfo *pktinfo = (in6_pktinfo *)CMSG_DATA(chdr); pktinfo->ipi6_ifindex = from->sin6_scope_id; pktinfo->ipi6_addr = from->sin6_addr; return ::sendmsg(fd(), &_h, 0); } int socket6_base::recvfrom(void *buf, uint16_t buflen, sockaddr_in6 *from) { socklen_t fromlen = sizeof(sockaddr_in6); return ::recvfrom(fd(), buf, buflen, 0, (sockaddr *)from, &fromlen); } int socket6_base::recvfrom(void *buf, uint16_t buflen) { iovec v = { buf, buflen }; _h.msg_name = (void *)&_recvfrom; _h.msg_namelen = sizeof(sockaddr_in6); _h.msg_iov = &v; _h.msg_iovlen = 1; _h.msg_control = _ctlbuf; _h.msg_controllen = sizeof(_ctlbuf); _h.msg_flags = 0; return ::recvmsg(fd(), &_h, 0); } static bool _mc_method(int fd, int action, int index, const in6_addr &addr) { ipv6_mreq mreq; memset(&mreq, 0, sizeof(mreq)); mreq.ipv6mr_interface = index; mreq.ipv6mr_multiaddr = addr; return setsockopt(fd, IPPROTO_IPV6, action, &mreq, sizeof(mreq)) == 0; } bool socket6_base::join_mc(interface *intf, const in6_addr &addr) { return _mc_method(fd(), IPV6_JOIN_GROUP, intf->index(), addr); } bool socket6_base::leave_mc(interface *intf, const in6_addr &addr) { return _mc_method(fd(), IPV6_LEAVE_GROUP, intf->index(), addr); } bool socket6_base::enable_mc_loop(bool yes) { int loop = yes ? 1 : 0; return setsockopt(fd(), IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)) == 0; } bool socket6_base::set_hoplimit(int n) { return setsockopt(fd(), IPPROTO_IPV6, IPV6_HOPLIMIT, &n, sizeof(n)) == 0; } bool socket6_base::set_mcast_hoplimit(int n) { return setsockopt(fd(), IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &n, sizeof(n)) == 0; } bool socket6_base::destination_address(sockaddr_in6 &dst, int &index) { for (cmsghdr *hdr = CMSG_FIRSTHDR(&_h); hdr; hdr = CMSG_NXTHDR(&_h, hdr)) { if (hdr->cmsg_level == IPPROTO_IPV6 && hdr->cmsg_type == IPV6_PKTINFO && hdr->cmsg_len == CMSG_LEN(sizeof(in6_pktinfo))) { in6_pktinfo *pktinfo = (in6_pktinfo *)CMSG_DATA(hdr); dst.sin6_family = AF_INET6; dst.sin6_addr = pktinfo->ipi6_addr; index = pktinfo->ipi6_ifindex; if (IN6_IS_ADDR_LINKLOCAL(&pktinfo->ipi6_addr)) dst.sin6_scope_id = index; else dst.sin6_scope_id = 0; return true; } } return false; } cmsghdr *socket6_base::next_cmsghdr(int maxlen) const { if ((CMSG_SPACE(sizeof(in6_pktinfo)) + CMSG_SPACE(maxlen)) > sizeof(_ctlbuf)) return NULL; return (cmsghdr *)(_ctlbuf + CMSG_SPACE(sizeof(in6_pktinfo))); } encoding_buffer::encoding_buffer(int avail) { m_buffer = new uint8_t[avail]; m_end = m_buffer + avail; m_head = m_tail = m_buffer; } encoding_buffer::~encoding_buffer() { delete [] m_buffer; } bool encoding_buffer::check_startup() { return m_buffer != 0; } void *encoding_buffer::eat(int len) { if (!require(len)) return 0; uint8_t *head = m_head; m_head += len; return head; } void *encoding_buffer::put(int len) { if (!tail_require(len)) return 0; uint8_t *tail = m_tail; m_tail += len; return tail; } void encoding_buffer::advance_head(int len) { m_head += len; } void encoding_buffer::advance_tail(int len) { m_tail += len; // if (length > max_ptr) // max_ptr = length; } void encoding_buffer::compact() { int length = m_tail - m_head; if (length) memmove(m_buffer, m_head, length); m_head = m_buffer; m_tail = m_head + length; } void encoding_buffer::clear() { m_head = m_tail = m_buffer; } int encoding_buffer::consume(socket_base &sk, bool blocking) { int len; if ((len = recv(sk.fd(), tail(), available_length(), blocking ? 0 : MSG_DONTWAIT)) > 0) advance_tail(len); return len; } int encoding_buffer::flush_to(socket_base &sk, bool wantsread, bool blocking) { int consumed = 0; if (!empty()) { consumed = send(sk.fd(), head(), data_length(), blocking ? 0 : MSG_DONTWAIT); if (consumed > 0) { advance_head(consumed); compact(); } } if (empty()) sk.monitor(wantsread ? socket_base::Read : 0); return consumed; } mrd::mrd() : node(0, "mrd"), m_state(Initial), g_rlog(this), m_mrib(this), m_intflist_node(this, "interface"), m_grplist_node(this, "group"), m_intfconfs(this), m_groups_node(this) { FD_ZERO(&m_rdst); FD_ZERO(&m_wrst); g_mrd = this; ipktb = new packet_buffer(); opktb = new packet_buffer(); add_child(&g_rlog); add_child(&m_mrib); add_child(&m_intfconfs, true); add_child(&m_groups_node, true); add_child(&m_intflist_node, false, 0, 0 /* "Display interface information" */); add_child(&m_grplist_node, false, 0, 0 /*"Display active group information" */); m_mfa = 0; m_rib_handler = 0; m_icmp = 0; m_largestsock = 0; m_module_path.push_back("/usr/local/lib/mrd6"); m_module_path.push_back("/usr/local/lib/mrd"); m_module_path.push_back("/usr/lib/mrd6"); m_module_path.push_back("/usr/lib/mrd"); m_module_path.push_back("."); invalidate_intf_cache(); m_tasks_stat = 0; m_tasks_time_spent = 0; m_startup = 0; } mrd::~mrd() { delete ipktb; delete opktb; ipktb = 0; opktb = 0; } bool mrd::register_router(router *r) { if (!r->check_startup()) return false; m_routers[r->name()] = r; r->attach(this); add_child(r); if (should_log(NORMAL)) log().xprintf("Registered router %s.\n", r->name()); intfconf_node *intfnode = ((intfconf_node *) g_mrd->default_interface_configuration()->create_child(r->name())); if (intfnode) intfnode->fill_defaults(); groupconf *defaultgc = get_group_configuration(inet6_addr::any()); groupconf_node *grpnode = (groupconf_node *)defaultgc->create_child(r->name()); if (grpnode) grpnode->fill_defaults(); if (m_state == Running) { for (interface_list::iterator i = m_intflist.begin(); i != m_intflist.end(); i++) { r->event(InterfaceStateChanged, i->second); } } return true; } void mrd::unregister_router(router *r) { std::map::iterator i = m_routers.find(r->name()); if (i == m_routers.end()) return; m_routers.erase(i); remove_child(r->name()); if (should_log(VERBOSE)) log().xprintf("Unregistered router %s.\n", r->name()); } router *mrd::get_router(const char *name) const { routers::const_iterator k = m_routers.find(name); if (k == m_routers.end()) return 0; return k->second; } mrd::posix_uctx::posix_uctx(ucontext_t *ctx) : base(ctx) {} static void handle_sigsegv(int id, siginfo_t *info, void *ptr) { ucontext_t *uc = (ucontext_t *)ptr; /* hopefully the logging interface will be intact */ base_stream &out = g_mrd->fatal(); out.writeline("*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*"); out.writeline("It seems MRD6 has crashed. Please contact either the"); out.writeline("package maintainer or the software authors and include"); out.writeline("the following information in your report."); out.writeline("---------------------- CUT HERE ----------------------"); g_mrd->show_base_info(out); utsname name; if (uname(&name) == 0) { out.xprintf("System: %s %s %s %s %s\n", name.sysname, name.nodename, name.release, name.version, name.machine); } mrd::posix_uctx uctx(uc); void *PC = uctx.get_current_frame(); out.printf("Failed when trying to access %p", info->si_addr); if (PC) { out.write(" at "); char *desc = g_mrd->obtain_frame_description(PC); if (desc) { out.write(desc); free(desc); } else { out.printf("%p", PC); } } out.newl().writeline("Backtrace:"); out.inc_level(); g_mrd->output_backtrace(out); out.dec_level(); out.writeline("---------------------- CUT HERE ----------------------"); exit(SIGSEGV); } static bool get_seed_from_file(const char *path, uint32_t *value) { int fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return false; int len = read(fd, value, 4); close(fd); return len == 4; } static uint32_t get_random_seed() { uint32_t value; if (get_seed_from_file("/dev/urandom", &value)) return value; /* Not all systems have a /dev/urandom */ if (get_seed_from_file("/dev/random", &value)) return value; return time(NULL); } bool mrd::check_startup(const char *conffile, bool autoload) { change_state(PreConfiguration); if (!ipktb || !opktb) return false; if (!m_timermgr.check_startup()) return false; if (!node::check_startup()) return false; if (!g_rlog.check_startup() || !m_mrib.check_startup() || !m_intfconfs.check_startup() || !m_groups_node.check_startup() || !m_intflist_node.check_startup() || !m_grplist_node.check_startup()) return false; srand(get_random_seed()); import_methods(mrd_methods); if (!register_source_discovery("static", &m_static_source_disc)) return false; g_rlog.attach_node(new file_log_node(&g_rlog, "stderr", 5, stderr)); intfconf *all = new intfconf("all"); if (!all || !all->check_startup()) { delete all; return false; } all->fill_defaults(); m_intfconfs.add_child(all); groupconf *gc = (groupconf *)m_groups_node.create_child("::/0"); if (gc) gc->fill_defaults(); else return false; if (!prepare_os_components()) return false; if (!m_mfa) { fatal().writeline("No MFA, bailing out."); return false; } if (!m_mfa->pre_startup()) return false; if (!m_icmp) { m_icmp = new icmp_inet6(); } if (!m_icmp) { fatal().writeline("No ICMPv6 handling module, bailing out."); return false; } if (!m_icmp->check_startup()) { fatal().writeline("Failed to init ICMPv6 handling module, bailing out."); return false; } add_static_modules(); for (early_modules::const_iterator i = m_early_modules.begin(); i != m_early_modules.end(); ++i) { load_modulex(i->c_str()); } if (autoload) { for (static_modules::const_iterator i = m_static_modules.begin(); i != m_static_modules.end(); i++) { load_modulex(i->first.c_str()); } } if (!conffile) { for (int k = 0; defaultconffiles[k]; k++) { if (access(defaultconffiles[k], R_OK) == 0) { conffile = defaultconffiles[k]; break; } } } if (!conffile) { fatal().writeline("No configuration file available."); return false; } prepare_second_components(); if (!m_rib_handler) { fatal().writeline("No RIB access module, bailing out."); return false; } if (!m_rib_handler->check_startup()) { fatal().writeline("(MRD) RIB handler setup failed."); return false; } m_rib_handler->check_initial_interfaces(); add_child(m_rib_handler); conf_parser p; change_state(Configuration); if (!p.parse(conffile)) { fatal().xprintf("Failed to parse configuration file " "\"%s\" at line %i.\n", conffile, (int32_t)p.ctx.current_line_number()); return false; } change_state(PostConfiguration); if (!m_mfa->check_startup()) return false; if (should_log(NORMAL)) { base_stream &os = log(); show_mrd_version(os); os.newl(); } struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = handle_signal; sigaction(SIGINT, &act, 0); sigaction(SIGTERM, &act, 0); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, 0); act.sa_handler = 0; act.sa_sigaction = handle_sigsegv; act.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &act, 0); return true; } bool mrd::should_log(int level) const { return g_rlog.change_context(level); } base_stream &mrd::log() const { return g_rlog.current_context(); } base_stream &mrd::fatal() const { should_log(FATAL); return log(); } bool mrd::register_rib(rib_def *rib) { if (m_state > PreConfiguration) return false; if (m_rib_handler) { m_rib_handler->transfer_watchers(rib); remove_child("rib"); delete m_rib_handler; } m_rib_handler = rib; add_child(m_rib_handler); return true; } void mrd::register_startup(node *n) { if (m_state == Running) n->event(StartupEvent, 0); else m_startup_nodes.push_back(n); } static void _check_socks(const char *tok, std::list &l) { for (std::list::const_iterator i = l.begin(); i != l.end(); i++) { if (g_mrd->should_log(EVERYTHING)) { g_mrd->log().xprintf("%s socket \"%s\" fd %i is still " "open.\n", tok, (*i)->name(), (*i)->fd()); } } } void mrd::shutdownx() { /// XXX block signals if (should_log(NORMAL)) log().writeline("Shutting Down"); group_list grplist = m_grplist; for (group_list::iterator k = grplist.begin(); k != grplist.end(); ++k) release_group(k->second); m_grplist_node.clear_childs(); for (routers::iterator j = m_routers.begin(); j != m_routers.end(); ++j) j->second->shutdown(); std::map intflist = m_intflist; for (std::map::iterator i = intflist.begin(); i != intflist.end(); ++i) remove_interface(i->second); for (std::map::iterator k = m_modules.begin(); k != m_modules.end(); ++k) k->second->shutdown(); m_mrib.shutdown(); m_icmp->shutdown(); delete m_icmp; m_icmp = 0; m_rib_handler->shutdown(); delete m_rib_handler; m_rib_handler = 0; m_mfa->shutdown(); delete m_mfa; m_mfa = 0; m_intfconfs.clear_childs(); m_routing_table.clear(); m_timermgr.shutdown(); for (std::map::iterator k = m_modules.begin(); k != m_modules.end(); ++k) { #ifdef MRD_NO_DYNAMIC_MODULE_LOADING delete k->second; #else void *foo = k->second->m_dlhandle; delete k->second; if (foo) dlclose(foo); #endif } m_modules.clear(); _check_socks("Read", m_read); _check_socks("Write", m_write); } void mrd::handle_signal(int sig) { switch (sig) { case SIGINT: case SIGTERM: g_mrd->change_state(ShuttingDown); break; case SIGSEGV: break; } } void mrd::change_user() { if (has_property("run-as")) { passwd *pwd; const char *user = get_property("run-as")->get_string(); if (!(pwd = getpwnam(user))) { fatal().xprintf("(MRD) Failed to drop privileges," " user %s doesn\'t exist.\n", user); } else { setuid(pwd->pw_uid); setgid(pwd->pw_gid); } } } void mrd::start() { change_user(); processloop(); } static inline int _handle_pending_socks(int res, const fd_set *fdset, uint32_t flags, const std::list &l) { std::list::const_iterator i = l.begin(); socket_base *b; while (res > 0 && i != l.end()) { b = *i; ++i; if (FD_ISSET(b->_fd, fdset)) { b->_hits++; b->callback(flags); res--; } } return res; } void mrd::change_state(mrd_state newstate) { if (m_state == newstate) return; bool call_intfs = false; if (m_state == Running) { call_intfs = true; } else if (newstate == Running) { call_intfs = true; } m_state = newstate; if (m_state == Running) { for (node_vector::iterator i = m_startup_nodes.begin(); i != m_startup_nodes.end(); ++i) { (*i)->event(StartupEvent, 0); } m_startup_nodes.clear(); } if (call_intfs) { for (interface_list::iterator i = m_intflist.begin(); i != m_intflist.end(); ++i) { i->second->broadcast_change_state(newstate == Running || !i->second->up(true)); } } } void mrd::check_enabled_interfaces(intfconf *conf) { for (interface_list::iterator i = m_intflist.begin(); i != m_intflist.end(); ++i) { if (i->second->conf() == conf) { i->second->set_enabled(conf->is_enabled()); } } } void mrd::broadcast_interface_state_changed(interface *intf) { if (intf->up()) { m_mfa->added_interface(intf); m_icmp->added_interface(intf); } else { m_icmp->removed_interface(intf); m_mfa->removed_interface(intf); } broadcast_event(mrd::InterfaceStateChanged, intf, true); } void mrd::processloop() { change_state(Running); int res; fd_set rset, wset; timeval tmt, *ptmt; m_startup = time(0); int _clk = sysconf(_SC_CLK_TCK); tms _tmp; clock_t a, b; uint32_t accum; while (m_state == Running) { if (!m_tasks.empty()) { a = times(&_tmp); do { task t = m_tasks.front(); m_tasks.pop_front(); t.target->event(t.event, t.argument); m_tasks_stat++; b = times(&_tmp); accum = (b - a) * 1000 / _clk; /* run tasks while time-spent < 10ms. */ } while (!m_tasks.empty() && accum < 10); m_tasks_time_spent += accum; } rset = m_rdst; wset = m_wrst; ptmt = &tmt; if (m_tasks.empty()) { if (!m_timermgr.time_left(tmt)) ptmt = 0; } else { tmt.tv_sec = 0; tmt.tv_usec = 0; } res = select(m_largestsock + 1, &rset, &wset, 0, ptmt); if (res < 0) { if (errno != EINTR) { if (should_log(DEBUG)) log().perror("(MRD) select() errno"); break; } } if (ptmt) { if (m_timermgr.handle_event()) continue; } if (res > 0) { res = _handle_pending_socks(res, &rset, socket_base::Read, m_read); _handle_pending_socks(res, &wset, socket_base::Write, m_write); } } g_mrd->shutdownx(); } void mrd::remove_interface(interface *intf) { interface_list::iterator p = m_intflist.find(intf->index()); if (p != m_intflist.end()) { m_intflist.erase(p); m_intflist_node.remove_child(intf->name()); /* Only output these kind of messages after startup */ if (m_state == Running) { if (should_log(VERBOSE)) log().xprintf("Removed interface %s.\n", intf->name()); } invalidate_intf_cache(); for (group_list::iterator j = m_grplist.begin(); j != m_grplist.end(); ++j) j->second->clear_interface_references(intf); bool was_up = intf->up(); intf->set_enabled(false); intf->broadcast_change_state(was_up); m_mrib.removed_interface(intf); delete intf; } else if (should_log(EXTRADEBUG)) { log().xprintf("Ignored %s interface removal request," " it isn\'t instantiated.", intf->name()); } } void mrd::invalidate_intf_cache() { memset(m_intf_cache, 0, sizeof(m_intf_cache)); } interface *mrd::get_interface_by_name(const char *name) const { if (!name) return 0; for (interface_list::const_iterator i = m_intflist.begin(); i != m_intflist.end(); ++i) { if (!strcmp(i->second->name(), name)) return i->second; } return 0; } interface *mrd::get_loopback_interface() const { return get_interface_by_name(loopback_interface_name()); } interface *mrd::found_interface(int index, const char *name, int type, int mtu, int flags) { interface *intf; if ((intf = get_interface_by_index(index))) return intf; bool enabled = !m_intfconfs.is_interface_disabled(name); intfconf *conf = (intfconf *)m_intfconfs.get_child(name); if (!conf) { conf = (intfconf *)m_intfconfs.get_child("all"); } if (enabled) enabled = conf->is_enabled(); // we've discovered a new interface intf = new interface(conf, index, name, type, mtu, flags); if (!intf) return 0; m_intflist.insert(std::make_pair(intf->index(), intf)); /* XXX handle interface renames */ m_intflist_node.add_child(intf); if (should_log(VERBOSE)) { log().xprintf("Added %s interface %s with mtu %u.\n", intf->type_str(), intf->name(), (uint32_t)intf->mtu()); } intf->set_enabled(enabled); return intf; } void mrd::lost_interface(int intf) { interface_list::iterator k = m_intflist.find(intf); if (k != m_intflist.end()) { remove_interface(k->second); } } bool mrd::in_same_subnet(const in6_addr &addr) const { for (interface_list::const_iterator i = m_intflist.begin(); i != m_intflist.end(); ++i) { if (i->second->in_same_subnet(addr)) return true; } return false; } intfconf *mrd::get_interface_configuration(const char *name) { return (intfconf *)m_intfconfs.create_child(name); } intfconf *mrd::default_interface_configuration() { return get_interface_configuration("all"); } groupconf *mrd::match_group_configuration(const inet6_addr &addr) const { return m_routing_table.match(addr); } groupconf *mrd::get_similiar_groupconf_node(const groupconf *gc) const { return m_routing_table.match(gc->id(), gc); } groupconf *mrd::get_group_configuration(const inet6_addr &addr) const { return m_routing_table.search(addr); } std::list mrd::configured_group_set(const char *rt) const { std::list addrs; for (group_configuration::const_iterator i = m_routing_table.begin(); i != m_routing_table.end(); ++i) { if (!rt || i->get_child(rt)) addrs.push_back(i->id()); } return addrs; } bool mrd::register_source_discovery(const char *name, source_discovery_origin *origin) { if (!origin) { for (source_disc::iterator i = m_source_disc.find(name); i != m_source_disc.end(); ++i) { if (i->first == name) { m_source_disc.erase(i); break; } } /* XXX notify all groupconf */ return true; } if (get_source_discovery(name)) return false; m_source_disc[name] = origin; /* XXX notify all groupconf */ return true; } bool mrd::register_source_sink(source_discovery_sink *sink, bool include) { source_sinks::iterator i = std::find(m_source_sinks.begin(), m_source_sinks.end(), sink); if (i == m_source_sinks.end()) { if (!include) return false; m_source_sinks.push_back(sink); } else { if (include) return false; m_source_sinks.erase(i); } return true; } bool mrd::register_generic_source_sink(source_discovery_sink *sink, bool include) { source_sinks::iterator i = std::find(m_all_source_sinks.begin(), m_all_source_sinks.end(), sink); if (i == m_all_source_sinks.end()) { if (!include) return false; m_all_source_sinks.push_back(sink); } else { if (include) return false; m_all_source_sinks.erase(i); } return true; } source_discovery_origin *mrd::get_source_discovery(const char *name) const { source_disc::const_iterator i = m_source_disc.find(name); if (i == m_source_disc.end()) return 0; return i->second; } void mrd::discovered_source(int ifindex, const inet6_addr &grpmask, const inet6_addr &src, source_discovery_origin *origin) { interface *input = get_interface_by_index(ifindex); if (!input || !input->up()) return; for (source_sinks::iterator i = m_all_source_sinks.begin(); i != m_all_source_sinks.end(); ++i) { (*i)->discovered_source(input, grpmask, src, origin); } groupconf *conf = match_group_configuration(grpmask); while (conf) { if (conf->get_source_discs().empty()) conf = (groupconf *)conf->next_similiar_node(); else break; } if (!conf) return; const groupconf::source_discs &discs = conf->get_source_discs(); /* kinda hardcore doing string compares here.. */ if (std::find(discs.begin(), discs.end(), origin->origin_description()) == discs.end()) return; if (should_log(MESSAGE_SIG)) { log().xprintf("Discovered Source (%{Addr}, %{Addr}) from %s.\n", src, grpmask, origin->origin_description()); } if (grpmask.prefixlen == 128) { for (source_sinks::iterator i = m_source_sinks.begin(); i != m_source_sinks.end(); ++i) { (*i)->discovered_source(input, grpmask, src, origin); } group *gr = get_group_by_addr(grpmask); if (gr) { gr->discovered_source(input, src, origin); } } else { for (group_list::const_iterator i = m_grplist.begin(); i != m_grplist.end(); ++i) { if (grpmask.matches(i->first)) { i->second->discovered_source(input, src, origin); } } } } void mrd::lost_source(const inet6_addr &grpmask, const inet6_addr &src, source_discovery_origin *origin) { if (grpmask.prefixlen == 128) { group *gr = get_group_by_addr(grpmask); if (gr) { gr->lost_source(src, origin); } } else { for (group_list::const_iterator i = m_grplist.begin(); i != m_grplist.end(); ++i) { if (grpmask.matches(i->first)) { i->second->lost_source(src, origin); } } } } group *mrd::create_group(const inet6_addr &addr) { group *grp = get_group_by_addr(addr); if (!grp) { groupconf *conf = match_group_configuration(addr); if (!conf) return 0; grp = create_group(addr, conf); } return grp; } bool mrd::create_group(router *owner, node *caller, create_group_context *ctx) { if (!caller || !ctx) return false; ctx->result = get_group_by_addr(ctx->groupaddr); if (!ctx->result) { bool accept = true; for (create_group_acl::const_iterator i = m_create_group_acl.begin(); accept && i != m_create_group_acl.end(); ++i) { accept = i->second->request_group(0, ctx->requester, ctx->groupaddr, owner); } if (accept) ctx->result = create_group(ctx->groupaddr); } register_task(caller, CreatedGroup, ctx); return true; } group *mrd::create_group(const inet6_addr &addr, groupconf *entry) { group *grp = allocate_group(addr, entry); if (grp) { /* must have the group in the group list before calling * router::created_group, as the nodes will try to get it */ m_grplist.insert(std::make_pair(addr, grp)); if (grp->check_startup()) { m_grplist_node.add_child(grp); if (should_log(NORMAL)) log().xprintf("Created Group %{Addr}.\n", addr); broadcast_event(NewGroup, grp, true); } else { if (should_log(EXTRADEBUG)) log().xprintf("Group creation failed for " "%{Addr}.\n", addr); delete grp; grp = 0; m_grplist.erase(m_grplist.find(addr)); } } return grp; } void mrd::release_group(group *grp) { group_list::iterator j = m_grplist.find(grp->id()); if (j == m_grplist.end()) return; broadcast_event(ReleasedGroup, grp, true); grp->shutdown(); m_grplist_node.remove_child(j->first.as_string().c_str()); delete grp; if (should_log(NORMAL)) log().xprintf("Released Group %{Addr}.\n", j->first); m_grplist.erase(j); } void mrd::register_group_creation_auth(group_request_interface *gri, int prio) { create_group_acl::iterator i = m_create_group_acl.begin(); if (prio <= 0) { while (i != m_create_group_acl.end()) { if (i->second == gri) { m_create_group_acl.erase(i); return; } ++i; } } else { while (i != m_create_group_acl.end()) { if (prio >= i->first) ++i; } m_create_group_acl.insert(i, std::make_pair(prio, gri)); } } group *mrd::allocate_group(const inet6_addr &addr, groupconf *conf) const { if (should_log(EVERYTHING)) { base_stream &os = log(); os.xprintf("Created group %{Addr} using source discs", addr); for (groupconf::source_discs::const_iterator i = conf->get_source_discs().begin(); i != conf->get_source_discs().end(); ++i) { os.xprintf(" %s", i->c_str()); } os.newl(); } return new group(addr, conf); } group *mrd::get_group_by_addr(const inet6_addr &addr) const { group_list::const_iterator j = m_grplist.find(addr); if (j == m_grplist.end()) return 0; return j->second; } bool mrd::register_sock(socket_base *sock, int fd, uint32_t flags) { if (sock->_fd != fd) { unregister_sock(sock); sock->_fd = fd; } monitor_sock(sock, flags); return true; } static inline bool _release_sock(std::list &l, fd_set *fdset, socket_base *sock) { for (std::list::iterator i = l.begin(); i != l.end(); ++i) { if (*i == sock) { FD_CLR(sock->fd(), fdset); l.erase(i); return true; } } return false; } bool mrd::unregister_sock(socket_base *sock) { if (sock->_fd < 0) return false; _release_sock(m_read, &m_rdst, sock); _release_sock(m_write, &m_wrst, sock); return true; } bool mrd::monitor_sock(socket_base *sock, uint32_t flags) { if (sock->fd() <= 0) return false; _release_sock(m_read, &m_rdst, sock); _release_sock(m_write, &m_wrst, sock); if (flags & socket_base::Read) { FD_SET(sock->fd(), &m_rdst); m_read.push_back(sock); } if (flags & socket_base::Write) { FD_SET(sock->fd(), &m_wrst); m_write.push_back(sock); } if (sock->fd() > m_largestsock) m_largestsock = sock->fd(); return true; } mrd::task mrd::make_task(event_sink *target, int event, void *arg, int prio) { task t; t.target = target; t.event = event; t.prio = prio; t.argument = arg; return t; } void mrd::register_task(const task &t) { if (!t.target) return; /* XXX respect prios */ m_tasks.push_back(t); } void mrd::register_task(event_sink *target, int event, void *opt, int prio) { register_task(make_task(target, event, opt, prio)); } void mrd::clear_tasks(event_sink *target) { tasks newtasks; /* ``Erase in the middle of a deque invalidates all iterators that * refer to the deque.'' */ for (tasks::const_iterator i = m_tasks.begin(); i != m_tasks.end(); ++i) { if (i->target != target) newtasks.push_back(*i); } m_tasks = newtasks; } void mrd::interested_in_active_states(event_sink *s, bool in) { active_state_interest::iterator i = std::find(m_active_state_interest.begin(), m_active_state_interest.end(), s); if (in) { if (i == m_active_state_interest.end()) m_active_state_interest.push_back(s); } else if (i != m_active_state_interest.end()) { m_active_state_interest.erase(i); } } void mrd::state_is_active(group *g, const in6_addr &src, bool active) { active_state_report rep; rep.group_instance = g; rep.source_address = src; rep.active = active; for (active_state_interest::const_iterator i = m_active_state_interest.begin(); i != m_active_state_interest.end(); ++i) { (*i)->event(ActiveStateNotification, &rep); } } bool mrd::has_address(const in6_addr &addr) const { if (IN6_IS_ADDR_UNSPECIFIED(&addr)) return false; for (interface_list::const_iterator i = m_intflist.begin(); i != m_intflist.end(); ++i) { if (*i->second->linklocal() == addr) return true; if (i->second->has_global(addr)) return true; } return false; } bool mrd::check_module_path(const char *name, std::string &path) { for (module_path::const_iterator i = m_module_path.begin(); i != m_module_path.end(); ++i) { path = *i; path += "/"; path += name; path += ".so"; if (access(path.c_str(), F_OK) == 0) return true; } return false; } void mrd::load_early_module(const char *name) { m_early_modules.insert(name); } bool mrd::load_modulex(const char *name) { if (m_modules.find(name) != m_modules.end()) return true; std::map::iterator k = m_static_modules.find(name); if (k != m_static_modules.end()) { return add_module(name, (*k->second)(0, this)); } #ifdef MRD_NO_DYNAMIC_MODULE_LOADING return false; #else std::string path; if (!check_module_path(name, path)) { if (should_log(WARNING)) log().xprintf("(MRD) Failed to load module %s.\n", name); return false; } char tmp[PATH_MAX]; strncpy(tmp, path.c_str(), sizeof(tmp)); void *foo = dlopen(tmp, RTLD_NOW | RTLD_GLOBAL); module_init_sig *load = 0; if (foo) { std::string initfunsym = "mrd_module_init_"; initfunsym += name; load = (module_init_sig *)dlsym(foo, initfunsym.c_str()); } if (!foo || !load) { if (should_log(WARNING)) log().xprintf("(MRD) Failed to load module %s (%s).\n", name, dlerror()); if (foo) dlclose(foo); return false; } mrd_module *mod = (*load)(foo, this); if (!mod) { if (should_log(WARNING)) log().xprintf("(MRD) Failed to init module %s.\n", name); return false; } return add_module(name, mod); #endif } bool mrd::add_module(const char *name, mrd_module *mod) { if (!mod->check_startup()) { if (should_log(WARNING)) log().xprintf("(MRD) Failed to init module %s.\n", name); delete mod; return false; } std::map modls = m_modules; for (std::map::iterator k = modls.begin(); k != modls.end(); ++k) { mod->module_loaded(k->first.c_str(), k->second); k->second->module_loaded(name, mod); } m_modules[name] = mod; return true; } bool mrd::remove_module(const char *n) { std::map::iterator i = m_modules.find(n); if (i == m_modules.end()) return false; return remove_module(i->second); } bool mrd::remove_module(mrd_module *mod) { std::map::iterator i; for (i = m_modules.begin(); i != m_modules.end(); ++i) { if (i->second == mod) break; } if (i == m_modules.end()) return false; #ifndef MRD_NO_DYNAMIC_MODULE_LOADING void *foo = i->second->m_dlhandle; #endif i->second->shutdown(); delete i->second; m_modules.erase(i); #ifndef MRD_NO_DYNAMIC_MODULE_LOADING if (foo) dlclose(foo); #endif return true; } bool mrd::set_property(const char *what, const char *value) { if (!strcmp(what, "module-path")) { m_module_path.clear(); m_module_path.push_back(value); return true; } else if (!strcmp(what, "run-as")) { return set_property_inst(what, property_def::VAL_STRING, value); } return node::set_property(what, value); } bool mrd::increment_property(const char *what, const char *value) { if (!strcmp(what, "modules")) { return load_modulex(value); } else if (!strcmp(what, "module-path")) { m_module_path.push_back(value); return true; } return false; } bool mrd::call_method(int id, base_stream &out, const std::vector &args) { switch (id) { case method_shutdown: return shutdown(out, args); case method_version: return show_version(out, args); case method_timers: return show_timers(out, args); case method_rpf: return show_rpf(out, args); case method_load_module: return load_module(out, args); case method_unload_module: return unload_module(out, args); case method_unicast_regs: return unicast_regs(out, args); case method_socket_regs: return socket_regs(out, args); case method_show_info: return show_info(out, args); case method_conf: return show_conf(out, args); case method_show_commands: return show_commands(out, args); case method_show: return show(out, args); case method_dump_tree: dump_node_tree(out, g_mrd); return true; #ifdef CRASH_COMMAND case method_crash: *((int *)0xdeadbeef) = 0xb00; /* won't reach here */ return true; #endif } return node::call_method(id, out, args); } void mrd::event(int type, void *arg) { if (type == RemoveGroup) { release_group((group *)arg); } else { node::event(type, arg); } } bool mrd::show_timers(base_stream &out, const std::vector &args) { return m_timermgr.output_info(out, !args.empty() && args[0] == "extended"); } bool mrd::show_rpf(base_stream &out, const std::vector &args) { if (args.empty()) { out.writeline("Method usage: show rpf "); } else { inet6_addr addr; if (!addr.set(args[0])) { out.writeline("Argument must be destination address."); } else { bool debug = false; inet6_addr grpaddr; if (args.size() > 1) { if (args[1] == "debug") debug = true; else grpaddr.set(args[1]); } base_stream &os = out.xprintf("RPF information for %{Addr}", addr); if (!grpaddr.is_any()) os.xprintf(" (%{Addr})", grpaddr); os.newl(); out.inc_level(); inet6_addr nh; timeval ts, te; gettimeofday(&ts, 0); const mrib_def::prefix *p = mrib().resolve_nexthop(addr, grpaddr, nh); gettimeofday(&te, 0); if (p) { out.xprintf("Record prefix: %{Addr}\n", p->get_prefix()); out.xprintf("Origin: %s\n", p->owner->description()); out.inc_level(); p->owner->output_prefix_info(out, *p); out.dec_level(); out.xprintf("Nexthop: %{Addr}\n", nh); if (p->intf) { out.xprintf("Outgoing interface: %s\n", p->intf->name()); } else { out.writeline("No available path."); } if (debug) { uint64_t diff = (te.tv_sec - ts.tv_sec) * 1000000; if (te.tv_usec > ts.tv_usec) diff += te.tv_usec - ts.tv_usec; else diff += (1000000 + te.tv_usec - ts.tv_usec); out.printf("Took: %lluus", diff).newl(); out.inc_level(); int i; out.write("Target: "); for (i = 0; i < 128; i++) { if (i == p->get_prefix().prefixlen) out.write(" "); out.write(pnode_symbol_at(addr, i) ? "1" : "0"); } out.newl(); out.write("Prefix: "); for (i = 0; i < 128; i++) { if (i == p->get_prefix().prefixlen) out.write(" "); out.write(pnode_symbol_at(p->get_prefix(), i) ? "1" : "0"); } out.newl(); out.dec_level(); } } else { out.writeline("No information available."); } out.dec_level(); } } return true; } bool mrd::shutdown(base_stream &out, const std::vector &ctx) { if (m_state != Running) return false; change_state(ShuttingDown); return true; } bool mrd::show_mrd_version(base_stream &os) const { utsname name; if (uname(&name) == 0) { os.xprintf("This is `%s\' running %s the IPv6 Multicast " "Routing Daemon, in %s %s", name.nodename, VersionInfo, name.sysname, name.release); return true; } return false; } bool mrd::show_version(base_stream &out, const std::vector &ctx) { bool res = show_mrd_version(out); out.newl(); return res; } bool mrd::load_module(base_stream &out, const std::vector &args) { if (args.empty()) return false; for (std::vector::const_iterator i = args.begin(); i != args.end(); ++i) { load_modulex(i->c_str()); } return true; } bool mrd::unload_module(base_stream &out, const std::vector &args) { if (args.empty()) return false; for (std::vector::const_iterator i = args.begin(); i != args.end(); ++i) { remove_module(i->c_str()); } return true; } bool mrd::unicast_regs(base_stream &out, const std::vector &ctx) { return m_rib_handler->dump_info(out); } static inline void _dump_socks(base_stream &_out, std::list &l) { for (std::list::const_iterator i = l.begin(); i != l.end(); i++) { _out.printf("\"%s\" fd %i hits %llu", (*i)->name(), (*i)->fd(), (*i)->hits()).newl(); } } bool mrd::socket_regs(base_stream &out, const std::vector &ctx) { out.writeline("Sockets"); out.inc_level(); out.writeline("Read"); out.inc_level(); if (!m_read.empty()) { _dump_socks(out, m_read); } else { out.writeline("(None)"); } out.dec_level(); if (!m_write.empty()) { out.writeline("Write"); out.inc_level(); _dump_socks(out, m_write); out.dec_level(); } out.dec_level(); return true; } void mrd::show_base_info(base_stream &out) const { out.xprintf("Version: %s\n", VersionInfo); out.xprintf("Build date: %s\n", BuildDate); } bool mrd::show_info(base_stream &out, const std::vector &ctx) { show_base_info(out); out.xprintf("Uptime: %{duration}\n", time_duration((time(0) - m_startup) * 1000)); out.xprintf("Performed tasks: %u (spent %llu ms)\n", m_tasks_stat, m_tasks_time_spent); out.xprintf("Registered sockets: %u reading, %u writing\n", m_read.size(), m_write.size()); out.xprintf("MRIB prefix count: %u\n", mrib().registry_prefix_count()); out.xprintf("Interface count: %u\n", m_intflist.size()); out.xprintf("Group state count: %u\n", m_grplist.size()); return true; } bool mrd::show_conf(base_stream &out, const std::vector &ctx) { show_conf_node(out, this, true); return true; } int mrd::show_conf_node(base_stream &_out, node *n, bool print) const { if (!n) return 0; std::vector props, childs; const properties &nodeprops = n->get_properties(); for (properties::const_iterator i = nodeprops.begin(); i != nodeprops.end(); ++i) { if (i->second.is_property()) props.push_back(i->first.c_str()); else if (i->second.is_child()) childs.push_back(i->first.c_str()); } int count = 0; bool shouldprint = false; if (print) { shouldprint = show_conf_node(_out, n, false) > 0; } if (shouldprint && n != this) { _out.xprintf("%s {\n", n->name()); _out.inc_level(); } for (std::vector::const_iterator i = props.begin(); i != props.end(); ++i) { const property_def *prop = n->get_property(*i); if (prop && !prop->is_readonly() && !prop->is_default()) { if (shouldprint) { base_stream &os = _out.xprintf("%s = ", *i); prop->output_value(os); os.writeline(";"); } count++; } } for (std::vector::const_iterator i = childs.begin(); i != childs.end(); ++i) { count += show_conf_node(_out, n->get_child(*i), print); } if (shouldprint && n != this) { _out.dec_level(); _out.writeline("}"); } return count; } bool mrd::show_commands(base_stream &out, const std::vector &args) { if (!args.empty()) return false; dump_commands(out, this, std::string()); return true; } void mrd::dump_commands(base_stream &_out, const node *n, const std::string &_path) const { std::string path = _path; if (n->parent()) { path += n->name(); path += " "; } const properties &nodeprops = n->get_properties(); for (properties::const_iterator i = nodeprops.begin(); i != nodeprops.end(); ++i) { if (i->second.is_method() && !(i->second.flags() & property_def::DEFAULT_VALUE)) { if (i->second.is_readonly()) _out.write("show"); _out.xprintf("%s%s\n", path.c_str(), i->first.c_str()); if (i->second.get_method_info()->flags & property_def::NEGATE) _out.xprintf("%sno %s\n", path.c_str(), i->first.c_str()); } } for (properties::const_iterator i = nodeprops.begin(); i != nodeprops.end(); ++i) { if (i->second.is_child()) { dump_commands(_out, i->second.get_node(), path); } } } void mrd::dump_node_tree(base_stream &s, node *n) const { std::vector visited; return dump_node_tree(s, n, visited); } void mrd::dump_node_tree(base_stream &s, node *n, std::vector &visited) const { for (properties::const_iterator i = n->get_properties().begin(); i != n->get_properties().end(); ++i) { if (i->second.is_child()) { s.write(i->first.c_str()); node *ch = i->second.get_node(); if (std::find(visited.begin(), visited.end(), ch) != visited.end()) { s.writeline(" (dup)"); continue; } s.newl(); visited.push_back(ch); s.inc_level(); dump_node_tree(s, ch, visited); s.dec_level(); } } } void mrd::group_configuration::clear() { iterator i; while ((i = begin()) != end()) { groupconf *n = &(*i); remove(n); delete n; } } groupconf *mrd::group_configuration::create_child(const inet6_addr &addr) { groupconf *conf = new groupconf(addr); if (!conf || !conf->check_startup()) { delete conf; return 0; } insert(conf); return conf; } groupconf *mrd::group_configuration::match(const inet6_addr &addr, const groupconf *prev) const { if (prev) { return get_parent_node(prev); } return longest_match(addr); } mrd::groups_node::groups_node(node *p) : node(p, "groups") { } node *mrd::groups_node::get_child(const char *name) const { inet6_addr addr; if (!addr.set(name)) return 0; return node::get_child(addr.as_string().c_str()); } node *mrd::groups_node::create_child(const char *name) { inet6_addr addr; if (!addr.set(name)) return 0; groupconf *gc = g_mrd->m_routing_table.create_child(addr); if (gc) add_child(gc); return gc; } mrd_module::mrd_module(mrd *m, void *dlhandle) : m_dlhandle(dlhandle), m_mrd(m) { } mrd_module::~mrd_module() { } uint32_t mrd::get_randu32() { return rand() * 12345; } mrd6-0.9.6/src/address_set.cpp0000644000175000017500000000663510550053317014670 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * address_set.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include address_set &address_set::union_with(const address_set &b, address_set &diff) { for (const_iterator i = b.begin(); i != b.end(); ++i) { if (insert(*i).second) diff.insert(*i); } return *this; } address_set &address_set::union_with(const address_set &b) { for (const_iterator i = b.begin(); i != b.end(); ++i) { insert(*i); } return *this; } address_set &address_set::diff_with(const address_set &b, address_set &diff) { if (!empty() && !b.empty()) { for (iterator i = b.begin(); i != b.end(); ++i) { iterator j = find(*i); if (j != end()) { diff.insert(*j); erase(j); } } } return *this; } address_set &address_set::diff_with(const address_set &b) { if (!empty() && !b.empty()) { for (iterator i = b.begin(); i != b.end(); ++i) { iterator j = find(*i); if (j != end()) { erase(j); } } } return *this; } address_set &address_set::intersect_with(const address_set &b) { if (b.empty()) { clear(); } else { for (iterator i = begin(); i != end(); ++i) { if (!b.has_addr(*i)) { erase(i); } } for (const_iterator j = b.begin(); j != b.end(); ++j) { insert(*j); } } return *this; } address_set &address_set::intersect_with(const address_set &b, address_set &diff) { if (b.empty()) { diff = *this; clear(); } else { for (iterator i = begin(); i != end(); ++i) { if (!b.has_addr(*i)) { diff.insert(*i); erase(i); } } for (const_iterator j = b.begin(); j != b.end(); ++j) { insert(*j); } } return *this; } address_set &address_set::assign_with(const address_set &b, address_set &added, address_set &removed) { if (b.empty()) { removed = *this; clear(); } else { for (const_iterator i = b.begin(); i != b.end(); ++i) { if (!has_addr(*i)) added.insert(*i); } for (const_iterator i = begin(); i != end(); ++i) { if (!b.has_addr(*i)) removed.insert(*i); } *this = b; } return *this; } bool address_set::remove(const in6_addr &addr) { iterator i = find(addr); if (i == end()) return false; erase(i); return true; } base_stream &address_set::print_to(base_stream &os) const { if (empty()) { os.write("{}"); } else { os.write("{"); for (address_set::const_iterator i = begin(); i != end(); ++i) { if (i != begin()) os.write(", "); stream_push_formated_type(os, *i); } os.write("}"); } return os; } base_stream &operator << (base_stream &os, const address_set &addrs) { return addrs.print_to(os); } mrd6-0.9.6/src/icmp_inet6.cpp0000644000175000017500000001321710725736050014425 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * icmp_inet6.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* this should be in */ #ifndef IP6OPT_ROUTER_ALERT #define IP6OPT_ROUTER_ALERT 5 #endif icmp_inet6::icmp_inet6() : m_icmpsock("icmpv6", this, std::mem_fun(&icmp_inet6::data_available)) { } static uint8_t buffer[8192]; bool icmp_inet6::check_startup() { int sock = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6); if (sock < 0) { if (g_mrd->should_log(WARNING)) g_mrd->log().perror("ICMPv6: Failed to create ICMPv6 socket"); return false; } if (!m_icmpsock.register_fd(sock)) { close(sock); return false; } if (!m_icmpsock.enable_mc_loop(false)) return false; return true; } void icmp_inet6::shutdown() { m_icmpsock.unregister(); } bool icmp_inet6::apply_icmp_filter() { #ifdef ICMP6_FILTER icmp6_filter filter; ICMP6_FILTER_SETBLOCKALL(&filter); for (handlers::const_iterator i = m_handlers.begin(); i != m_handlers.end(); ++i) { ICMP6_FILTER_SETPASS(i->first, &filter); } if (setsockopt(m_icmpsock.fd(), IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) < 0) { if (g_mrd->should_log(VERBOSE)) g_mrd->log().writeline("[ICMPv6] failed to install " "ICMP filter in socket."); return false; } #endif return true; } void icmp_inet6::registration_changed() { apply_icmp_filter(); } void icmp_inet6::data_available(uint32_t) { int recvlen = m_icmpsock.recvfrom(buffer, sizeof(buffer)); if (recvlen < 0) return; sockaddr_in6 dst; int index; if (!m_icmpsock.destination_address(dst, index)) return; if (index == 0) return; const sockaddr_in6 &from = m_icmpsock.source_address(); if (g_mrd->should_log(MESSAGE_SIG)) g_mrd->log().xprintf("[ICMPv6] Message from %{addr} to %{addr}" " dev %i.\n", from.sin6_addr, dst.sin6_addr, index); interface *intf = g_mrd->get_interface_by_index(index); if (!intf) return; icmp_message_available(intf, from.sin6_addr, dst.sin6_addr, (icmp6_hdr *)buffer, recvlen); } static int _add_rta(const socket6_base &b, uint16_t value) { /* Hop-by-hop Option header with RTA * [ 00 00 05 02 00 00 01 00 ] */ const int opt_rta_len = 8; cmsghdr *cmsg = b.next_cmsghdr(opt_rta_len); if (cmsg == NULL) return -1; cmsg->cmsg_len = CMSG_SPACE(opt_rta_len); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_HOPOPTS; uint8_t *extbuf = (uint8_t *)CMSG_DATA(cmsg); extbuf[0] = 0x00; /* next header */ extbuf[1] = 0x00; /* length (8 bytes) */ extbuf[2] = IP6OPT_ROUTER_ALERT; extbuf[3] = 0x02; /* RTA length (2 bytes) */ *(uint16_t *)(extbuf + 4) = htons(value); extbuf[6] = IP6OPT_PADN; extbuf[7] = 0x00; return CMSG_SPACE(opt_rta_len); } bool icmp_inet6::send_icmp(const interface *intf, const in6_addr &src, const in6_addr &to, int rtaval, icmp6_hdr *hdr, uint16_t len) const { sockaddr_in6 dst, from; memset(&dst, 0, sizeof(sockaddr_in6)); memset(&from, 0, sizeof(sockaddr_in6)); dst.sin6_family = AF_INET6; dst.sin6_addr = to; from.sin6_family = AF_INET6; from.sin6_addr = src; if (IN6_IS_ADDR_LINKLOCAL(&src)) from.sin6_scope_id = intf->index(); int optspace = 0; if (rtaval >= 0) { optspace = _add_rta(m_icmpsock, rtaval); if (optspace < 0) { if (g_mrd->should_log(EXTRADEBUG)) g_mrd->log().writeline( "Failed to send ICMPv6 message: wasn't" "able to construct message."); return false; } } if (m_icmpsock.sendto(hdr, len, &dst, &from, optspace) < 0) { if (g_mrd->should_log(EXTRADEBUG)) g_mrd->log().xprintf("Failed to send ICMPv6 message from" " %{addr} to %{addr}: %s.\n", src, to, strerror(errno)); return false; } if (g_mrd->should_log(MESSAGE_SIG)) g_mrd->log().xprintf("Sent ICMPv6 message from %{addr} to " "%{addr} in %s.\n", src, to, intf->name()); return true; } void icmp_inet6::internal_require_mgroup(const in6_addr &mgroup, bool include) { mrd::interface_list::const_iterator i = g_mrd->intflist().begin(); for (; i != g_mrd->intflist().end(); ++i) { if (!i->second->up()) continue; if (include) m_icmpsock.join_mc(i->second, mgroup); else m_icmpsock.leave_mc(i->second, mgroup); } } void icmp_inet6::added_interface(interface *intf) { for (mgroups::const_iterator i = m_mgroups.begin(); i != m_mgroups.end(); ++i) { m_icmpsock.join_mc(intf, i->first); } } void icmp_inet6::removed_interface(interface *intf) { for (mgroups::const_iterator i = m_mgroups.begin(); i != m_mgroups.end(); ++i) { m_icmpsock.leave_mc(intf, i->first); } } mrd6-0.9.6/src/extra/0000755000175000017500000000000010746353706013011 5ustar hugohugomrd6-0.9.6/src/extra/coredumper.cpp0000644000175000017500000000441110550053317015646 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * coredumper.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include static const method_info coredumper_methods[] = { { "dump", 0, 1000, false, 0 }, { 0 } }; class coredumper_module : public mrd_module, public node { public: coredumper_module(mrd *m, void *); ~coredumper_module(); bool check_startup(); void shutdown(); bool call_method(int id, base_stream &, const std::vector &); bool dump(base_stream &); }; module_entry(coredumper, coredumper_module); coredumper_module::coredumper_module(mrd *m, void *dlh) : mrd_module(m, dlh), node(m, "coredumper") {} coredumper_module::~coredumper_module() { } bool coredumper_module::check_startup() { if (!node::check_startup()) return false; import_methods(coredumper_methods); return m_mrd->add_child("coredumper", this) != 0; } void coredumper_module::shutdown() { m_mrd->remove_child("coredumper"); } bool coredumper_module::call_method(int id, base_stream &out, const std::vector &args) { if (id == 1000) return dump(out); return node::call_method(id, out, args); } bool coredumper_module::dump(base_stream &out) { char filename[256]; sprintf(filename, "coredump-%u", (uint32_t)time(0)); if (WriteCoreDump(filename) < 0) { out << "Failed to write coredump." << endl; } else { out << "Core dumped to " << filename << endl; } return true; } mrd6-0.9.6/src/extra/mld_ext.cpp0000644000175000017500000002567710550053317015156 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mld_ext.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include /* method definition */ enum { mld_ext_router_static_listener = 4000, mld_ext_router_local_static, }; static const method_info mld_ext_router_methods[] = { { "static-listener", "Adds a new static listener to the specified interface", mld_ext_router_static_listener, false, property_def::NEGATE }, { "local-static", "Joins the specific group in the router's loopback interface", mld_ext_router_local_static, false, property_def::NEGATE }, { 0 } }; struct create_group_mld_ext_context : mrd::create_group_context { int mld_mode; address_set mld_sources; }; /* module definition */ class mld_ext_module : public mrd_module { public: mld_ext_module(mrd *m, void *); bool check_startup(); void shutdown(); }; module_entry(mld_ext, mld_ext_module); /* mld re-definitions */ class mld_ext_group_interface : public mld_group_interface { public: mld_ext_group_interface(mld_group *, mld_interface *); ~mld_ext_group_interface(); void send_mld_query(bool, const address_set &); void change_listener_filter(const in6_addr &, int mode, const address_set &); void remove_listener(const in6_addr &); struct fake_listener { fake_listener(mld_ext_group_interface *, const in6_addr &); void send_fake_report(); mld_ext_group_interface *owner; /* Fake Listener address */ in6_addr reporter; /* MLD filter */ int mode; address_set sources; /* Report timer */ timer rtimer; }; void send_fake_report(int, fake_listener &); private: typedef std::vector listeners; listeners m_listeners; }; class mld_ext_group : public mld_group { public: mld_ext_group(router *); group_interface *instantiate_group_interface(interface *); }; class mld_ext_router : public mld_router { public: bool check_startup(); bool call_method(int, base_stream &, const std::vector &); bool negate_method(int, base_stream &, const std::vector &); bool do_static(const in6_addr &group, interface *intf, const in6_addr &reporter, int mode, const address_set &); void event(int, void *); mld_group *allocate_group(); }; extern mld_router *mld; /* implementation */ mld_ext_module::mld_ext_module(mrd *m, void *arg) : mrd_module(m, arg) { } bool mld_ext_module::check_startup() { mld = new mld_ext_router(); if (!mld) return false; if (!g_mrd->register_router(mld)) { delete mld; mld = 0; } return mld != 0; } void mld_ext_module::shutdown() { g_mrd->unregister_router(mld); mld->shutdown(); delete mld; mld = 0; } mld_ext_group_interface::mld_ext_group_interface(mld_group *gr, mld_interface *gintf) : mld_group_interface(gr, gintf) { } mld_ext_group_interface::~mld_ext_group_interface() { for (listeners::iterator i = m_listeners.begin(); i != m_listeners.end(); ++i) { delete *i; } m_listeners.clear(); } void mld_ext_group_interface::send_mld_query(bool general, const address_set &srcs) { mld_group_interface::send_mld_query(general, srcs); /* respond in half the time the router gives us */ uint32_t tvalue = g_intf->conf()->last_listener_query_interval() / 2; /* refresh all local fake listeners state */ for (listeners::iterator i = m_listeners.begin(); i != m_listeners.end(); ++i) { fake_listener *l = *i; /* only update time if we had a larger interval */ if (l->rtimer.time_left() > tvalue) l->rtimer.update(tvalue, false); } } void mld_ext_group_interface::change_listener_filter(const in6_addr &addr, int mode, const address_set &sources) { bool leaving = (mode == MLD_SSM_CHANGE_TO_INCLUDE) && sources.empty(); for (listeners::iterator i = m_listeners.begin(); i != m_listeners.end(); ++i) { fake_listener *l = *i; /* already exists in list */ if (l->reporter == addr) { int newmode = mode; if (newmode != l->mode) { newmode = (mode == MLD_SSM_MODE_INCLUDE) ? MLD_SSM_CHANGE_TO_INCLUDE : MLD_SSM_CHANGE_TO_EXCLUDE; } l->mode = mode; l->sources = sources; send_fake_report(newmode, *l); if (leaving) { delete l; m_listeners.erase(i); } return; } } if (leaving) return; fake_listener *l = new fake_listener(this, addr); if (!l) return; l->mode = mode; l->sources = sources; send_fake_report(mode, *l); m_listeners.push_back(l); } void mld_ext_group_interface::remove_listener(const in6_addr &addr) { for (listeners::iterator i = m_listeners.begin(); i != m_listeners.end(); ++i) { fake_listener *l = *i; if (l->reporter == addr) { delete l; m_listeners.erase(i); return; } } } void mld_ext_group_interface::send_fake_report(int mode, fake_listener &l) { refresh(l.reporter, mode, l.sources); l.rtimer.start_or_update(g_intf->conf()->query_interval() / 2, false); } mld_ext_group_interface::fake_listener::fake_listener(mld_ext_group_interface *gi, const in6_addr &addr) : owner(gi), reporter(addr), mode(MLD_SSM_MODE_INCLUDE), rtimer("fake listener rtimer", this, std::mem_fun(&fake_listener::send_fake_report)) { } void mld_ext_group_interface::fake_listener::send_fake_report() { owner->send_fake_report(mode, *this); } mld_ext_group::mld_ext_group(router *rt) : mld_group(rt) { } group_interface *mld_ext_group::instantiate_group_interface(interface *intf) { mld_interface *mldintf = mld->get_interface(intf->index()); if (mldintf) return new mld_ext_group_interface(this, mldintf); return 0; } bool mld_ext_router::check_startup() { if (!mld_router::check_startup()) return false; import_methods(mld_ext_router_methods); return true; } bool mld_ext_router::call_method(int id, base_stream &out, const std::vector &args) { if (id == mld_ext_router_static_listener) { if (args.size() < 4) return false; inet6_addr groupaddr; if (!groupaddr.set(args[0].c_str())) return false; inet6_addr reporter; if (!reporter.set(args[2].c_str())) return false; if (args[3] != "include" && args[3] != "exclude") return false; int mode = (args[3] == "include") ? MLD_SSM_MODE_INCLUDE : MLD_SSM_MODE_EXCLUDE; address_set sources; for (size_t k = 4; k < args.size(); k++) { inet6_addr addr; if (!addr.set(args[k].c_str())) return false; sources += addr.address(); } interface *intf = g_mrd->get_interface_by_name(args[1].c_str()); if (!intf) return false; return do_static(groupaddr, intf, reporter, mode, sources); } else if (id == mld_ext_router_local_static) { if (args.empty()) return false; inet6_addr groupaddr; if (!groupaddr.set(args[0].c_str())) return false; interface *intf = g_mrd->get_loopback_interface(); if (!intf) return false; address_set sources; int mode = MLD_SSM_MODE_EXCLUDE; if (args.size() > 1) { if (args[1] != "include" && args[1] != "exclude") return false; mode = (args[1] == "include") ? MLD_SSM_MODE_INCLUDE : MLD_SSM_MODE_EXCLUDE; address_set sources; for (size_t k = 2; k < args.size(); k++) { inet6_addr addr; if (!addr.set(args[k].c_str())) return false; sources += addr.address(); } } return do_static(groupaddr, intf, in6addr_any, mode, sources); } else { return mld_router::call_method(id, out, args); } } bool mld_ext_router::do_static(const in6_addr &groupaddr, interface *intf, const in6_addr &reporter, int mode, const address_set &sources) { create_group_mld_ext_context *ctx = new create_group_mld_ext_context; if (!ctx) return false; ctx->iif = intf->index(); ctx->groupaddr = groupaddr; ctx->requester = reporter; ctx->mld_mode = mode; ctx->mld_sources = sources; g_mrd->create_group(mld, this, ctx); return true; } bool mld_ext_router::negate_method(int id, base_stream &out, const std::vector &args) { if (id == mld_ext_router_static_listener) { if (args.size() < 3) return false; inet6_addr groupaddr; if (!groupaddr.set(args[0].c_str())) return false; inet6_addr reporter; if (!reporter.set(args[2].c_str())) return false; interface *intf = g_mrd->get_interface_by_name(args[1].c_str()); if (!intf) return false; mld_interface *mldintf = mld->get_interface(intf->index()); if (!mldintf) return false; group *majorgrp = g_mrd->get_group_by_addr(groupaddr); if (!majorgrp) return false; mld_group *grp = (mld_group *)majorgrp->node_owned_by(mld); if (!grp) return false; mld_ext_group_interface *gintf = (mld_ext_group_interface *)grp->local_oif(mldintf); if (!gintf) return false; gintf->remove_listener(reporter); return true; } else if (id == mld_ext_router_local_static) { if (args.empty()) return false; inet6_addr groupaddr; if (!groupaddr.set(args[0].c_str())) return false; interface *intf = g_mrd->get_loopback_interface(); if (!intf) return false; mld_interface *mldintf = mld->get_interface(intf->index()); if (!mldintf) return false; group *majorgrp = g_mrd->get_group_by_addr(groupaddr); if (!majorgrp) return false; mld_group *grp = (mld_group *)majorgrp->node_owned_by(mld); if (!grp) return false; mld_ext_group_interface *gintf = (mld_ext_group_interface *)grp->local_oif(mldintf); if (!gintf) return false; gintf->remove_listener(in6addr_any); return true; } return mld_router::negate_method(id, out, args); } void mld_ext_router::event(int ev, void *ptr) { if (ev == mrd::CreatedGroup) { create_group_mld_ext_context *ctx = (create_group_mld_ext_context *)ptr; if (!ctx || !ctx->result) { delete ctx; return; } mld_interface *mldintf = mld->get_interface(ctx->iif); if (!mldintf) { delete ctx; return; } mld_group *grp = (mld_group *)ctx->result->node_owned_by(mld); if (!grp) { delete ctx; return; } mld_ext_group_interface *gintf = (mld_ext_group_interface *)grp->local_oif(mldintf); if (!gintf) { delete ctx; return; } gintf->change_listener_filter(ctx->requester, ctx->mld_mode, ctx->mld_sources); delete ctx; return; } mld_router::event(ev, ptr); } mld_group *mld_ext_router::allocate_group() { return new mld_ext_group(this); } mrd6-0.9.6/src/address.cpp0000644000175000017500000001134710604255421014011 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * address.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include base_stream &operator << (base_stream &os, const inet6_addr &addr) { return os.xprintf("%{Addr}", addr); } base_stream &operator << (base_stream &os, const in6_addr &addr) { return os.xprintf("%{addr}", addr); } void stream_push_formated_type(base_stream &os, const in6_addr &addr) { char *p = os.req_buffer(64); inet_ntop(AF_INET6, &addr, p, 64); os.commit_change(strlen(p)); } void stream_push_formated_type(base_stream &os, const inet6_addr &addr) { char *p = addr.print_string(os.req_buffer(64), 64); os.commit_change(strlen(p)); } inet6_addr::inet6_addr() : addr(in6addr_any), prefixlen(0) { } inet6_addr::inet6_addr(const in6_addr &addr) : addr(addr), prefixlen(128) { } inet6_addr::inet6_addr(const in6_addr &address, uint8_t plen) : addr(address), prefixlen(plen) { } inet6_addr::inet6_addr(const inet6_addr &addr) : addr(addr.addr), prefixlen(addr.prefixlen) { } inet6_addr::inet6_addr(const std::string &addr) { set(addr); } inet6_addr::inet6_addr(const std::vector &_addr) { if (_addr.size() == 16) { in6_addr addr; for (int i = 0; i < 16; i++) addr.s6_addr[i] = _addr[i]; set(addr, 128); } else { set(in6addr_any, 0); } } unsigned inet6_addr::type() const { unsigned val = 0; if (IN6_IS_ADDR_MULTICAST(&addr)) val |= multicast; if (addr.s6_addr[15] == 0) val |= network; return val; } bool inet6_addr::operator < (const inet6_addr &address) const { if (prefixlen < address.prefixlen) return true; return memcmp(addr.s6_addr, address.addr.s6_addr, sizeof(in6_addr)) < 0; } bool inet6_addr::operator > (const inet6_addr &address) const { if (prefixlen > address.prefixlen) return true; else if (prefixlen < address.prefixlen) return false; return memcmp(addr.s6_addr, address.addr.s6_addr, sizeof(in6_addr)) > 0; } bool inet6_addr::operator == (const inet6_addr &address) const { if (prefixlen != address.prefixlen) return false; return IN6_ARE_ADDR_EQUAL(&addr, &address.addr); } std::string inet6_addr::as_string() const { char buf[64]; return std::string(print_string(buf, sizeof(buf))); } char *inet6_addr::print_string(char *buf, int len) const { if (!inet_ntop(AF_INET6, &addr, buf, len)) { return 0; } else { int l = strlen(buf); if (prefixlen < 128) { len -= l; if (len > 4) { buf[l] = '/'; sprintf(buf + l + 1, "%i", prefixlen); } } return buf; } } sockaddr_in6 inet6_addr::as_sockaddr() const { sockaddr_in6 saddr; memset(&saddr, 0, sizeof(saddr)); saddr.sin6_family = AF_INET6; saddr.sin6_addr = addr; return saddr; } void inet6_addr::set(const in6_addr &address, uint8_t plen) { addr = address; prefixlen = plen; } bool inet6_addr::set(const std::string &str) { if (str == "any") { addr = in6addr_any; prefixlen = 0; return true; } size_t k = str.find('/'); if (k < str.size()) { std::string tmp = str; std::string prefix = str.c_str() + k + 1; tmp.resize(k); if (!inet_pton(AF_INET6, tmp.c_str(), &addr)) return false; char *end; int pl = strtol(prefix.c_str(), &end, 10); if (*end || pl < 0 || pl > 128) return false; prefixlen = pl; } else { if (!inet_pton(AF_INET6, str.c_str(), &addr)) return false; prefixlen = 128; } return true; } bool inet6_addr::from_string(const std::string &value, inet6_addr &res) { return res.set(value); } void inet6_addr::to_string(const inet6_addr &value, std::string &res) { res = value.as_string(); } inet6_addr inet6_addr::prefix() const { inet6_addr copy = *this; copy.apply_prefixlen(); return copy; } void inet6_addr::apply_prefixlen() { uint8_t *ptr = addr.s6_addr; int start = prefixlen / 8; int mask = (0xff << (8 - (prefixlen % 8))) & 0xff; if (start < 15) memset(ptr + start + 1, 0, (16 - start - 1)); ptr[start] &= mask; } mrd6-0.9.6/src/console/0000755000175000017500000000000010746353706013330 5ustar hugohugomrd6-0.9.6/src/console/Module.options0000644000175000017500000000011010332462051016143 0ustar hugohugoboolean TELNET default on description "Enable console's Telnet support" mrd6-0.9.6/src/console/unix_console.cpp0000644000175000017500000000463510550053317016535 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * unix_console.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include extern const char *socketPath; extern console_module *console; bool __console_allow_local(const std::vector &args) { if (args.empty()) return false; group *grp = getgrnam(args[0].c_str()); if (!grp) return false; if (chown(socketPath, 0, grp->gr_gid) != 0) return false; return chmod(socketPath, 0660) == 0; } unix_console_connection::unix_console_connection(mrd *core, int fd) : console_connection(core, fd) { autoclose = true; } void unix_console_connection::flushed(const char *str, bool newline) { writeclient(str); if (newline) writeclient("\n"); } void unix_console_connection::process_input(int len) { int st = 0; while (st < len) { int i; for (i = st; i < len; i++) { if (buffer[i] == '\n' || buffer[i] == ';' || buffer[i] == '?') { break; } } if (buffer[i] == '?') { //std::string in((const char *)buffer + st, i - st); std::string in((const char *)buffer + st, (i + 1) - st); dump_partial(in.c_str()); } else if ((i - st) > 0) { if (buffer[i] == '\n') i--; std::string in((const char *)buffer + st, i - st); process_line(in.c_str()); } st = i + 1; } if (autoclose) { if (bufbuffer.empty()) console->release_connection(this); else doom(); } } void unix_console_connection::release() { delete this; } mrd6-0.9.6/src/console/console.cpp0000644000175000017500000004471210726013264015474 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * console.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum { console_method_allow_access = 1000, console_method_deny_access, console_method_allow_local_access, console_method_attach_log, console_method_show_history }; static const method_info console_methods[] = { { "allow-access", "Adds a console ACL entry", console_method_allow_access, false, 0 }, { "deny-access", "Removes a console ACL entry", console_method_deny_access, false, 0 }, { "allow-local-access", "Adds local access to a system group", console_method_allow_local_access, false, 0 }, { "monitor", "Monitors log output in the console terminal", console_method_attach_log, false, property_def::NEGATE }, { "history", "Displays a list of previous commands", console_method_show_history, true, 0 }, { 0 } }; const char *socketPath = "/var/run/mrd6"; extern bool __console_allow_local(const std::vector &); console_module *console; static const char *s_b = "\033[1m"; static const char *e_b = "\033[0m"; bool partial_match(const char *base, const char *oper) { return strncmp(base, oper, strlen(base)) == 0; } module_entry(console, console_module); console_module::console_module(mrd *m, void *dlh) : mrd_module(m, dlh), node(m, "console"), #ifndef CONSOLE_NO_TELNET srvsock("console listener", this, std::mem_fun(&console_module::new_client)), #endif unix_srvsock("unix console listener", this, std::mem_fun(&console_module::new_unix_client)) { console = this; } console_module::~console_module() { } const char *console_module::description() const { return "Interactive configuration"; } bool console_module::check_startup() { if (!node::check_startup()) return false; import_methods(console_methods); if (!instantiate_property_u("client-timeout", 5)) return false; int one = 1; #ifndef CONSOLE_NO_TELNET int sock = socket(PF_INET6, SOCK_STREAM, 0); if (sock < 0) return false; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); sockaddr_in6 lhaddr; memset(&lhaddr, 0, sizeof(lhaddr)); lhaddr.sin6_family = PF_INET6; lhaddr.sin6_addr = in6addr_any; lhaddr.sin6_port = ntohs(44510); if (bind(sock, (sockaddr *)&lhaddr, sizeof(lhaddr)) < 0) { close(sock); return false; } if (listen(sock, 5) < 0) { close(sock); return false; } srvsock.register_fd(sock); #endif int unixsock = socket(PF_LOCAL, SOCK_STREAM, 0); if (unixsock < 0) { } else { setsockopt(unixsock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); sockaddr_un unlhaddr; memset(&unlhaddr, 0, sizeof(unlhaddr)); unlhaddr.sun_family = PF_LOCAL; strcpy(unlhaddr.sun_path, socketPath); unlink(socketPath); if (bind(unixsock, (sockaddr *)&unlhaddr, sizeof(unlhaddr)) < 0) { /* empty */ } else { if (listen(unixsock, 5) < 0) { if (should_log(WARNING)) log().writeline("Failed to listen in UNIX socket."); } else { unix_srvsock.register_fd(unixsock); } } } m_mrd->add_child(this); return true; } void console_module::shutdown() { #ifndef CONSOLE_NO_TELNET if (srvsock.fd() > 0) { ::shutdown(srvsock.fd(), SHUT_RDWR); srvsock.unregister(); } #endif if (unix_srvsock.fd() > 0) { unix_srvsock.unregister(); unlink(socketPath); } for (std::list::iterator k = connections.begin(); k != connections.end(); ++k) { (*k)->shutdown(); delete *k; } connections.clear(); } bool console_module::call_method(int id, base_stream &out, const std::vector &args) { switch (id) { case console_method_allow_access: return allow_addr(args); case console_method_deny_access: return deny_addr(args); case console_method_allow_local_access: return allow_local(args); case console_method_attach_log: return attach_log(out, args); case console_method_show_history: if (!args.empty()) return false; return show_history(out); } return node::call_method(id, out, args); } bool console_module::negate_method(int id, base_stream &out, const std::vector &args) { if (id == console_method_attach_log) { if (!args.empty()) return false; console_connection *conn = calling_connection(out); if (!conn || !conn->clog) return false; log_base::instance().dettach_node(conn->clog); conn->clog = 0; return true; } return node::negate_method(id, out, args); } bool console_module::output_info(base_stream &ctx, const std::vector &args) const { if (!args.empty()) return false; ctx.writeline("Console"); ctx.inc_level(); ctx.writeline("Allowed:"); ctx.inc_level(); if (acl.empty()) { ctx.writeline("(None)"); } else { for (allow_local_def::const_iterator i = acl.begin(); i != acl.end(); i++) { for (std::list::const_iterator j = i->second.begin(); j != i->second.end(); j++) { if (j->username.empty() || j->username == "*") ctx.write("Any"); else ctx.write(j->username.c_str()); if (j->password.empty() || j->password == "*") ctx.write(" with no password"); ctx.xprintf(" from %{Addr}\n", i->first); } } } ctx.dec_level(); ctx.dec_level(); return true; } bool console_module::allow_addr(const std::vector &args) { inet6_addr mask = inet6_addr(in6addr_any, 0); auth_desc desc; if (!args.empty()) { desc.username = args[0]; if (args.size() > 1) { desc.password = args[1]; if (args.size() > 2) { if (!mask.set(args[2])) return false; } } } else { return false; } acl[mask].push_back(desc); return true; } bool console_module::deny_addr(const std::vector &args) { if (args.empty()) return false; std::string username = args[0]; inet6_addr mask = inet6_addr(in6addr_any, 0); if (args.size() > 1) { if (!mask.set(args[1])) { return false; } } console_module::allow_local_def::iterator i = acl.begin(); while (i != acl.end()) { allow_local_def::iterator m = i; ++i; if (mask.matches(m->first)) { std::list::iterator j = m->second.begin(); while (j != m->second.end()) { std::list::iterator k = j; ++j; if (username.empty() || username == "*" || username == j->username) { m->second.erase(k); } } if (m->second.empty()) { acl.erase(m); } } } return true; } bool console_module::allow_local(const std::vector &args) { return __console_allow_local(args); } console_connection *console_module::calling_connection(base_stream &out) const { /* hack to find calling console connection */ base_stream *_b = &out; for (std::list::const_iterator i = connections.begin(); i != connections.end(); ++i) { console_connection *cc = *i; /* hackish, we find the calling console_connection * by matching the current output stream vs. each * of the console_connection's output stream */ if (_b == &cc->_output) return cc; } return 0; } bool console_module::attach_log(base_stream &out, const std::vector &args) { if (args.size() > 1) return false; const char *level = "extradebug"; if (!args.empty()) { int tmp; if (!log_node::parse_infolevel(args[0].c_str(), tmp)) return false; level = args[0].c_str(); } console_connection *cc = calling_connection(out); if (cc) { if (cc->clog) log_base::instance().dettach_node(cc->clog); cc->clog = new console_log_node(cc); if (!cc->clog) return false; char name[64]; snprintf(name, sizeof(name), "console-%i", cc->sock.fd()); cc->clog->rename(name); cc->clog->set_level(level); if (log_base::instance().attach_node(cc->clog)) { cc->autoclose = false; return true; } } return false; } bool console_module::show_history(base_stream &out) { console_connection *cc = calling_connection(out); if (!cc) return false; cc->dump_history(out); return true; } #ifndef CONSOLE_NO_TELNET void console_module::new_client(uint32_t) { sockaddr_in6 from; socklen_t fromlen = sizeof(from); memset(&from, 0, sizeof(from)); from.sin6_family = PF_INET6; int foo = accept(srvsock.fd(), (sockaddr *)&from, &fromlen); if (foo < 0) { return; } if (fromlen != sizeof(sockaddr_in6)) { close(foo); return; } if (should_log(DEBUG)) log().xprintf("(CONSOLE) New connection from %{addr}\n", from.sin6_addr); uint32_t tim = get_property_unsigned("client-timeout"); console_connection *conn = new telnet_console_connection(m_mrd, foo, from.sin6_addr, tim); if (conn && conn->check_startup()) { connections.push_back(conn); return; } delete conn; close(foo); } #endif void console_module::new_unix_client(uint32_t) { sockaddr_un from; socklen_t fromlen = sizeof(from); memset(&from, 0, sizeof(from)); from.sun_family = PF_LOCAL; int foo = accept(unix_srvsock.fd(), (sockaddr *)&from, &fromlen); if (foo < 0) { return; } console_connection *conn = new unix_console_connection(m_mrd, foo); if (conn && conn->check_startup()) { connections.push_back(conn); return; } delete conn; close(foo); } void console_module::release_connection(console_connection *conn) { for (std::list::iterator i = connections.begin(); i != connections.end(); ++i) { if (*i == conn) { conn->shutdown(); conn->release(); connections.erase(i); return; } } } bool console_module::password_for(const inet6_addr &addr, const char *username, std::string &passwd) const { for (allow_local_def::const_iterator i = acl.begin(); i != acl.end(); ++i) { if (i->first.matches(addr)) { for (std::list::const_iterator j = i->second.begin(); j != i->second.end(); ++j) { if (j->username == username) { passwd = j->password; return true; } } for (std::list::const_iterator j = i->second.begin(); j != i->second.end(); ++j) { if (j->username == "*" || j->username.empty()) { passwd = j->password; return true; } } } } return false; } console_log_node::console_log_node(console_connection *conn) : tb_log_node(&log_base::instance(), "console", EVERYTHING), _conn(conn) {} void console_log_node::rename(const char *n) { m_name = n; } void console_log_node::log(int, int, const char *buf, bool newline) { if (newline) { _conn->log(false); char buf[64]; _conn->_output.printf("- LOG %s- ", timestamp(buf, sizeof(buf))); if (!_buf.empty()) _conn->_output.append_chunk(_buf.c_str(), _buf.size()); _conn->_output.write(buf); if (newline) _conn->_output.newl(); _conn->log(true); _buf = ""; } else { _buf += buf; } } console_connection::console_connection(mrd *core, int s) : m_mrd(core), sock("console connection", this, std::mem_fun(&console_connection::data_available)), _output(this) { is_doomed = false; clog = 0; autoclose = false; sock.register_fd(s); } void console_connection::doom() { is_doomed = true; } bool console_connection::check_startup() { return true; } void console_connection::shutdown() { if (sock.fd() > 0) { ::shutdown(sock.fd(), SHUT_RDWR); sock.unregister(); } if (clog) { log_base::instance().dettach_node(clog); clog = 0; } } void console_connection::release() { doom(); } void console_connection::data_available(uint32_t flags) { if (flags == socket_base::Write) { if (!bufbuffer.empty()) { int k = send(sock.fd(), bufbuffer.c_str(), bufbuffer.size(), MSG_DONTWAIT); if (k > 0) { bufbuffer.erase(0, k); } } if (bufbuffer.empty()) { if (is_doomed) console->release_connection(this); else sock.monitor(socket_base::Read); } return; } int len = recv(sock.fd(), buffer, sizeof(buffer), 0); if (len <= 0) { console->release_connection(this); return; } process_input(len); } bool console_connection::process_line(const char *in) { parser_context ctxcn(in); process_deep_line(&ctxcn); return !is_doomed; } void console_connection::process_deep_line(parser_context *ctx) { int res = advance_one(ctx, g_mrd); if (res == -1) { _output.writeline("\% Error in input."); } } int console_connection::check_termination(parser_context *ctx, node *n) { int res = ctx->eat(); if (res < 0) return -1; return res == 0 ? 0 : (ctx->head().sym == parser_context::TERM ? advance_one(ctx, n) : -1); } int console_connection::advance_one(parser_context *ctx, node *n) { int res; if (!n) n = g_mrd; if ((res = ctx->eat(5, parser_context::TOKEN, parser_context::LCURLY, parser_context::RCURLY, parser_context::LPARENT, parser_context::PARTIAL_TOKEN)) < 1) { return res; } node::content_type ctype; const char *cmatch; int cres = n->match_property(node::property | node::method | node::child, ctx->head().value.c_str(), ctype, cmatch); if (cres == 0) { n = n->create_child(ctx->head().value.c_str()); if (n) { cres = 1; cmatch = ctx->head().value.c_str(); ctype = node::child; } } if (cres == 0) { _output.writeline("% No such method/child."); return -2; } else if (cres > 1) { _output.xprintf("%% Inconsistency in input when parsing `%s`.\n", ctx->head().value.c_str()); return -2; } else { if (ctype == node::child) { n = n->get_child(cmatch); if (!n) return -1; return advance_one(ctx, n); } else if (ctype == node::property) { if ((res = ctx->eat()) < 1) return res; n->set_property(cmatch, ctx->head().value.c_str()); return advance_one(ctx, 0); } else { std::vector args; while ((res = ctx->eat()) > 0) { if (ctx->head().sym == parser_context::TERM) break; args.push_back(std::string(ctx->head().value)); } if (res < 0) return res; const property_def *mth = n->get_any_property(cmatch); if (!mth || !mth->is_method() || mth->is_readonly()) { _output.xprintf("%% No such method %s.\n", cmatch); return -2; } else if (!n->call_method(mth->get_method_info()->id, _output, args)) { _output.xprintf("`%s` execution failed.\n", cmatch); return -2; } return advance_one(ctx, 0); } } return -1; } void console_connection::writeclient(const char *what) { if (!bufbuffer.empty()) { bufbuffer += what; } else { if (send(sock.fd(), what, strlen(what), MSG_DONTWAIT) < 0) { if (errno == EAGAIN) { bufbuffer = what; sock.monitor(socket_base::Read | socket_base::Write); } } } } void console_connection::dump_partial(const char *in) { parser_context ctxcn(in); node *n; int res = transform(&ctxcn, g_mrd, node::method, n); if (res == OK) { if (ctxcn.head().sym != parser_context::PARTIAL_TOKEN) { node::content_type ctype; const char *cmatch; res = n->match_property(node::method, ctxcn.head().value.c_str(), ctype, cmatch); if (res == 1 && ctype == node::method && !strcmp(cmatch, "show")) { res = transform(&ctxcn, n, node::info_method, n); if (res == OK && ctxcn.head().sym == parser_context::PARTIAL_TOKEN) dump_partial(n, &ctxcn, true); } } else { dump_partial(n, &ctxcn, false); } } } void console_connection::dump_partial(node *n, parser_context *ctx, bool ro) { std::string m = ctx->head().value; m.resize(m.size() - 1); int cn = 0; for (node::properties::const_iterator i = n->get_properties().begin(); i != n->get_properties().end(); ++i) { if (i->second.is_method() && i->second.is_readonly() != ro) continue; if (m.empty() || partial_match(m.c_str(), i->first.c_str())) { const char *desc = i->second.description(); if (desc && (int)i->first.size() > cn) cn = i->first.size(); } } const int Cols = 72; for (node::properties::const_iterator i = n->get_properties().begin(); i != n->get_properties().end(); ++i) { if (i->second.is_method() && i->second.is_readonly() != ro) continue; if (m.empty() || partial_match(m.c_str(), i->first.c_str())) { const char *desc = i->second.description(); if (desc) { _output.xprintf(" %s%s%s", s_b, i->first.c_str(), e_b); _output.spaces((cn + 2) - i->first.size()); int dl = strlen(desc); if ((dl + cn + 4) > (Cols-1)) { char buf[Cols]; int k = (sizeof(buf)-1) - cn - 4; int dlx = dl; do { strncpy(buf, desc, k); buf[k] = 0; _output.xprintf("%s+\n", buf); _output.spaces(cn + 4); dlx -= k; desc += k; } while (dlx > k); } _output.writeline(desc); } } } } int console_connection::transform(parser_context *ctx, node *base, node::content_type m, node * &result, std::string &lasttent) const { result = base; while (1) { int res = ctx->eat(2, parser_context::TOKEN, parser_context::PARTIAL_TOKEN); if (res < 0) break; else if (res == 0) return END_LINE; else if (ctx->head().sym == parser_context::PARTIAL_TOKEN) return OK; node::content_type ctype; const char *cmatch; lasttent = ctx->head().value; int cres = result->match_property(node::child | m, lasttent.c_str(), ctype, cmatch); if (cres == 0 || (cres == 1 && ctype != node::child)) return OK; else if (cres == 1) { result = result->get_child(cmatch); if (!result) break; } else { return CONSISTENCY_ERROR; } } return INPUT_ERROR; } int console_connection::transform(parser_context *ctx, node *base, node::content_type m, node * &result) const { std::string tmp; return transform(ctx, base, m, result, tmp); } void console_connection::dump_history(base_stream &) const { } void console_connection::log(bool) { } mrd6-0.9.6/src/console/telnet_console.cpp0000644000175000017500000002523310550053317017042 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * telnet_console.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef CONSOLE_NO_TELNET #include #include #include #include #include #include telnet_console_connection::telnet_console_connection(mrd *core, int fd, const inet6_addr &addr, uint32_t timeout) : console_connection(core, fd), conn_timer("console conn timeout", this, std::mem_fun(&telnet_console_connection::release_connection), timeout * 60000, false), c_peeraddr(addr) { pmode = 0; will_echo = false; should_echo = true; is_doomed = false; authenticate_state = 0; history_pos = 0; } telnet_console_connection::~telnet_console_connection() { } void telnet_console_connection::release_connection() { console->release_connection(this); } bool telnet_console_connection::check_startup() { if (!console_connection::check_startup()) return false; conn_timer.start(); cmd(WILL, TELOPT_ECHO); cmd(WILL, TELOPT_SGA); set_prompt("Username: "); return true; } void telnet_console_connection::release() { if (console->should_log(DEBUG)) console->log().xprintf("(CONSOLE) releasing connection from " "%{Addr}.\n", peeraddr()); console_connection::release(); } bool telnet_console_connection::authenticate(const char *in) { bool denied = false; std::string password; writeclient("\r\n"); authenticate_state++; if (authenticate_state == WaitingPassword) { username = in; set_prompt("Password: "); should_echo = false; } if (!console->password_for(c_peeraddr, username.c_str(), password)) { denied = true; } else { if (authenticate_state == GotAllData) { denied = password != in; } else { if (password.empty() || password == "*") { authenticate_state++; } } } if (denied) { clearline(); writeclient("Your connection is not permited. Contact the system administrator.\r\n"); if (console->should_log(VERBOSE)) console->log().xprintf("(CONSOLE) denied connection" " from %{Addr}\n", c_peeraddr); console->release_connection(this); return false; } else if (authenticate_state == GotAllData) { writeclient("\r\n"); g_mrd->show_mrd_version(_output); _output.newl(); set_prompt("# "); should_echo = true; } return true; } void telnet_console_connection::process_input(int len) { input_is_updated = false; int last = -1; int i, min_was = inputbuf.size(); int was_input = inputbuf.size(); for (i = 0; i < len; i++) { if (pmode == 0) { if ((buffer[i] == '\n' || buffer[i] == 0) && last == '\r') { if (!process_line(inputbuf.c_str())) { shutdown(); delete this; return; } inputbuf = ""; // eh.. g++ 2.95's libstdc++ doesn't have string::clear() } else if (buffer[i] == '\r') { } else if (buffer[i] == 4) { // Ctrl-D console->release_connection(this); return; } else if (buffer[i] == IAC) { pmode = 1; } else if (buffer[i] == 127 || buffer[i] == 8) { if (!inputbuf.empty()) { inputbuf.resize(inputbuf.size() - 1); if ((int)inputbuf.size() < min_was) min_was = inputbuf.size(); } } else { bool eat = false; if (authenticate_state == GotAllData) { if (buffer[i] == '\t') { if (tabcomplete()) redisplay_input(); } else if (buffer[i] == '?') { std::string wki = inputbuf; wki += '?'; writeclient("?\r\n"); dump_partial(wki.c_str()); redisplay_input(); } else if (isprint(buffer[i])) { eat = true; } else if (buffer[i] == 21) { /* Control-U */ inputbuf.clear(); clearline(); redisplay_input(); } else if (buffer[i] == 23) { /* Control-W */ int i = inputbuf.size(); for (; i > 0 && isspace(inputbuf[i-1]); i--); for (; i > 0 && !isspace(inputbuf[i-1]); i--); inputbuf.resize(i); clearline(); redisplay_input(); } else if (buffer[i] == '\033') { /* */ /* VT100 command */ if ((len - i) >= 3) { if (buffer[i+1] == '[') { if (buffer[i+2] == 'A') { /* Up */ history_up(); } else if (buffer[i+2] == 'B') { /* Down */ history_down(); } } i += 2; } } } else { eat = true; } if (eat) inputbuf.push_back(buffer[i]); } } else if (pmode == 1) { ctlbuf.push_back(buffer[i]); if (process_cmd() && ctlbuf.empty()) pmode = 0; } last = buffer[i]; } // Ugly code ahead. if (should_echo && !input_is_updated) { std::string buf; for (i = min_was; i < was_input; i++) { buf.push_back('\b'); } int len = inputbuf.size(); if (len >= was_input) { for (i = min_was; i < len; i++) { buf.push_back(inputbuf[i]); } } else { for (i = len; i < was_input; i++) { buf.push_back(' '); } for (i = len; i < was_input; i++) { buf.push_back('\b'); } } writeclient(buf.c_str()); } } bool telnet_console_connection::process_cmd() { if (ctlbuf.size() < 1) return false; switch (ctlbuf[0]) { case DO: if (ctlbuf.size() < 2) return false; if (ctlbuf[1] == TELOPT_ECHO) { will_echo = true; } ctlbuf.pop_front(); ctlbuf.pop_front(); return true; case SB: if (ctlbuf.size() < 2) return false; if (ctlbuf[1] == TELOPT_NAWS) { if (ctlbuf.size() < 6) return false; for (int i = 0; i < 6; i++) ctlbuf.pop_front(); return true; } break; case SE: ctlbuf.pop_front(); return true; } return false; } bool telnet_console_connection::process_line(const char *in) { conn_timer.restart(); if (authenticate_state < GotAllData) { return authenticate(in); } if (should_echo) writeclient("\r\n"); bool res = console_connection::process_line(in); if (res) show_prompt(); history.push_back(in); history_pos = history.size(); return res; } extern bool partial_match(const char *, const char *); bool telnet_console_connection::tabcomplete() { if (inputbuf.empty()) return false; parser_context ctx(inputbuf.c_str()); node *n; bool ro = false; std::string lasttent; int res = transform(&ctx, g_mrd, node::method, n, lasttent); if (ctx.current_column() != (int)inputbuf.size()) { node::content_type ctype; const char *cmatch; res = n->match_property(node::method, ctx.head().value.c_str(), ctype, cmatch); if (res == 1 && ctype == node::method && !strcmp(cmatch, "show")) { res = transform(&ctx, n, node::info_method, n, lasttent); ro = true; } } /* if not all buffer was consumed, we can't really complete */ if (ctx.current_column() != (int)inputbuf.size()) return false; if (res == END_LINE) { /* grunf */ if (!isspace(inputbuf[inputbuf.size()-1])) { inputbuf.resize(inputbuf.size() - lasttent.size()); inputbuf += n->name(); inputbuf += " "; } } else if (res == OK) { node::content_type ctype; const char *cmatch; res = n->match_property(node::child | (ro ? node::info_method : node::method), ctx.head().value.c_str(), ctype, cmatch); if (res == 0) { return false; } else if (res == 1) { int pos = ctx.current_column() - ctx.head().value.size(); /* if end-of-input */ if (ctx.eat() == 0) { inputbuf.resize(pos); inputbuf += cmatch; inputbuf += " "; } return true; } else { res = CONSISTENCY_ERROR; } } if (res == CONSISTENCY_ERROR) { std::string base; int count = 0; writeclient("\r\n"); for (node::properties::const_iterator i = n->get_properties().begin(); i != n->get_properties().end(); ++i) { if (i->second.is_child() || (i->second.is_method() && i->second.is_readonly() == ro)) { if (partial_match(lasttent.c_str(), i->first.c_str())) { _output.xprintf("%s ", i->first.c_str()); count++; if (base.empty()) base = i->first; else { int cn; int alen = base.size(), blen = i->first.size(); /* least common denominator */ for (cn = 0; cn < alen && cn < blen && base[cn] == i->first[cn]; cn++); if (cn < alen) base.resize(cn); } } } } if (count) { _output.newl(); if (!base.empty() && base != lasttent) { inputbuf.resize(inputbuf.size() - lasttent.size()); inputbuf += base; } } } return true; } void telnet_console_connection::show_prompt() { clearline(); writeclient(prompt.c_str()); } void telnet_console_connection::set_prompt(const char *p) { prompt = p; show_prompt(); } void telnet_console_connection::clearline() { unsigned char op[5]; /* ANSI/VT100 erase line */ op[0] = '\033'; op[1] = '['; op[2] = '2'; op[3] = 'K'; op[4] = '\r'; send(sock.fd(), op, 5, MSG_DONTWAIT); } void telnet_console_connection::cmd(char c, char opt) { char code[3]; code[0] = IAC; code[1] = c; code[2] = opt; send(sock.fd(), code, 3, MSG_DONTWAIT); } void telnet_console_connection::redisplay_input() { writeclient("\r"); writeclient(prompt.c_str()); writeclient(inputbuf.c_str()); input_is_updated = true; } void telnet_console_connection::flushed(const char *str, bool newline) { writeclient(str); if (newline) writeclient("\r\n"); } void telnet_console_connection::history_up() { if (history_pos == 0) return; if (history_pos == (int)history.size()) { tmp_inputbuf = inputbuf; } history_pos--; inputbuf = history[history_pos]; clearline(); redisplay_input(); } void telnet_console_connection::history_down() { if (history_pos >= (int)history.size()) return; history_pos++; if (history_pos == (int)history.size()) { inputbuf = tmp_inputbuf; } else { inputbuf = history[history_pos]; } clearline(); redisplay_input(); } void telnet_console_connection::dump_history(base_stream &out) const { for (std::vector::const_iterator i = history.begin(); i != history.end(); ++i) { out.writeline(i->c_str()); } } void telnet_console_connection::log(bool end) { if (!end) clearline(); else redisplay_input(); } #endif mrd6-0.9.6/src/parser.cpp0000644000175000017500000001715010550053317013656 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * parser.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include enum { _INVALID, _LCURLY, _RCURLY, _LPARENT, _RPARENT, _TERM, _EQUAL, _PLUSEQUAL, _DOT, _COMMA, _TOKEN, _PARTIAL_TOKEN, _STRING, _NEWLINE, _OCOMMENT, _CCOMMENT, _LCOMMENT, }; static const char *_token_names[] = { "end of input", "open-braces", "close-braces", "left-parenthesis", "right-parenthesis", "colon", "equal", "plus-equal", "dot", "comma", "token", "partial token", "quoted string" }; const char *parser_context::token_name(token_type tok) { return _token_names[tok]; } parser_context::parser_context() : m_current_line(1), m_input(0), m_input_pointer(0), m_input_line_start(0), m_partial(false) { } parser_context::parser_context(const char *input, bool partial) : m_current_line(1), m_input(input), m_input_pointer(input), m_input_line_start(input), m_partial(partial) { } parser_context::parser_context(const parser_context &ctx) : m_current(ctx.m_current), m_current_line(ctx.m_current_line), m_input(ctx.m_input), m_input_pointer(ctx.m_input_pointer), m_input_line_start(ctx.m_input_line_start), m_partial(ctx.m_partial) { } parser_context &parser_context::operator = (const parser_context &ctx) { m_current = ctx.m_current; m_current_line = ctx.m_current_line; m_input = ctx.m_input; m_input_pointer = ctx.m_input_pointer; m_input_line_start = ctx.m_input_line_start; m_partial = ctx.m_partial; return *this; } parser_context::symbol::symbol() : line(0), sym(NONE) {} parser_context::symbol::symbol(int l, token_type tok, const std::string &text) : line(l), sym(tok), value(text) {} parser_context::symbol::symbol(const symbol &sym) : line(sym.line), sym(sym.sym), value(sym.value) {} std::string parser_context::current_line() const { const char *ptr = m_input_line_start; while (*ptr && *ptr != '\n') ptr++; if (*ptr == '\n') ptr--; return std::string(m_input_line_start, ptr); } int parser_context::read() { if (m_symq.empty()) { int res, sym; const char *symstart = m_input_pointer; if ((res = read_token(false, &sym, &symstart)) < 1) return res; const char *symend = m_input_pointer; if (sym == _STRING) { symstart++; symend--; } m_current = symbol(m_current_line, (token_type)sym, std::string(symstart, symend)); m_symq.push_back(m_current); } else { m_current = m_symq.front(); } return 1; } int parser_context::eat() { int res; if ((res = read()) < 1) return res; m_symq.pop_front(); return 1; } int parser_context::eat(token_type tok) { return eat(1, tok); } int parser_context::eat(int count, ...) { int res = eat(); if (res < 1) return res; va_list vl; va_start(vl, count); for (int i = 0; i < count; i++) { if (va_arg(vl, int) == m_current.sym) { va_end(vl); return 1; } } va_end(vl); return -1; } int parser_context::read_token(bool strict, int *sym, const char **symstart, bool eat) { int res, readnum; int pointer = 0, prevpointer = 0; int linenumber = m_current_line; int state = 0; const char *currline = m_input_line_start; int commentlevel = 0; while (1) { if ((res = parse_one(m_input_pointer + pointer, state, sym, &readnum)) < 1) { return res; } if (strict) { break; } if (*sym == _OCOMMENT) { int commentlen = readnum; pointer += readnum; commentlevel++; state = 1; while (commentlevel > 0) { if ((res = parse_one(m_input_pointer + pointer, state, sym, &readnum)) < 1) return res; if (*sym == _NEWLINE) { linenumber++; currline = m_input_pointer + pointer + readnum; } else if (*sym == _OCOMMENT) { commentlevel++; } else if (*sym == _CCOMMENT) { commentlevel--; } pointer += readnum; } pointer -= commentlen; state = 0; } else if (*sym == _LCOMMENT) { while (1) { if ((res = parse_one(m_input_pointer + pointer, 1, sym, &readnum)) < 1) return res; if (*sym == _NEWLINE) { linenumber++; currline = m_input_pointer + pointer + readnum; break; } pointer += readnum; } } else if (*sym == _NEWLINE) { linenumber++; currline = m_input_pointer + pointer + readnum; } else if (*sym != -1) { break; } prevpointer = pointer; pointer += readnum; } if (symstart) *symstart = m_input_pointer + pointer; if (eat) { m_current_line = linenumber; m_input_pointer += pointer + readnum; m_input_line_start = currline; } return 1; } static inline bool is_token_char(char c) { return isalnum(c) || c == ':' || c == '/' || c == '-' || c == '_' || c == '.'; } int parser_context::parse_one(const char *input, int state, int *sym, int *readnum) const { if (!input || !*input) return 0; /* comment state */ if (state == 1) { if (input[0] == '\n') { *sym = _NEWLINE; *readnum = 1; } else if (input[0] == '/' && input[1] == '*') { *sym = _OCOMMENT; *readnum = 2; } else if (input[0] == '*' && input[1] == '/') { *sym = _CCOMMENT; *readnum = 2; } else { *sym = -1; int i = 0; while (input[i]) { if (input[i] == '\n') { break; } else if (input[i] == '/' && input[i+1] == '*') { break; } else if (input[i] == '*' &&input[i+1] == '/') { break; } i++; } *readnum = i; } } else { int c = -1, s = -1; if (input[0] == '\n') { c = 1; s = _NEWLINE; } else if (isspace(input[0])) { c = 1; for (; isspace(input[c]); c++); } else if (input[0] == '/' && input[1] == '*') { c = 2; s = _OCOMMENT; } else if (input[0] == '/' && input[1] == '/') { c = 2; s = _LCOMMENT; } else if (input[0] == '*' && input[1] == '/') { c = 2; s = _CCOMMENT; } else if (input[0] == '{') { c = 1; s = _LCURLY; } else if (input[0] == '}') { c = 1; s = _RCURLY; } else if (input[0] == '(') { c = 1; s = _LPARENT; } else if (input[0] == ')') { c = 1; s = _RPARENT; } else if (input[0] == ';') { c = 1; s = _TERM; } else if (input[0] == '=') { c = 1; s = _EQUAL; } else if (input[0] == '+' && input[1] == '=') { c = 2; s = _PLUSEQUAL; } else if (input[0] == '.') { c = 1; s = _DOT; } else if (input[0] == ',') { c = 1; s = _COMMA; } else if (is_token_char(input[0])) { c = 1; for (; is_token_char(input[c]); c++); if (input[c] == '?') { c++; s = _PARTIAL_TOKEN; } else { s = _TOKEN; } } else if (input[0] == '?') { c = 1; s = _PARTIAL_TOKEN; } else if (input[0] == '*') { c = 1; s = _TOKEN; } else if (input[0] == '\"') { c = 1; for (; input[c] && input[c] != '\"'; c++); if (input[c] == '\"') { c++; s = _STRING; } else { c = -1; } } *readnum = c; *sym = s; if (*readnum == -1) return -1; } return 1; } mrd6-0.9.6/src/mrdisc/0000755000175000017500000000000010746353706013147 5ustar hugohugomrd6-0.9.6/src/mrdisc/mrdisc_module.cpp0000644000175000017500000001457610746067643016520 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mrdisc_module.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include "mrdisc_def.h" static const int MRDISC_MAX_INIT_ADV_DELAY = 2000; static const int MRDISC_MAX_RESPONSE_DELAY = 2000; enum { SolicitationCount = 0, AdvertisementCount, MessageCount }; enum { RX = 0, TX, Bad }; static const char *stats_descriptions[] = { "Solicitation", "Advertisement", }; class mrdisc_module : public mrd_module, public node, public icmp_handler { public: mrdisc_module(mrd *, void *); bool check_startup(); void shutdown(); void icmp_message_available(interface *, const in6_addr &, const in6_addr &, icmp6_hdr *, int); void register_send_adv(interface *intf, int maxwhen); void event(int, void *); void send_unsolicited(); void send_termination(interface *); void send_advert(interface *); void send_solicited(int &); int adv_jitter() const; int next_adv_interval() const; inet6_addr all_routers, all_snoopers; timer m_unsolicited; int interface_count; typedef timer1 solicited_timer; typedef std::list solicited_timers; solicited_timers m_solicited; property_def *adv_interval; message_stats_node m_stats; }; module_entry(mrdisc, mrdisc_module); mrdisc_module::mrdisc_module(mrd *m, void *p) : mrd_module(m, p), node(m, "mrdisc"), m_unsolicited("mrdisc unsolicited", this, std::mem_fun(&mrdisc_module::send_unsolicited)), m_stats(this, MessageCount, stats_descriptions) { all_routers = inet6_addr("ff02::2"); all_snoopers = inet6_addr("ff02::6a"); adv_interval = instantiate_property_u("adv-interval", 20000); interface_count = 0; } bool mrdisc_module::check_startup() { if (!adv_interval) return false; if (!m_stats.setup()) return false; m_stats.disable_counter(SolicitationCount, TX); m_stats.disable_counter(AdvertisementCount, RX); if (!node::check_startup()) return false; if (!g_mrd->add_child(this)) return false; g_mrd->icmp().register_handler(MRDISC_ROUTER_SOLICITATION, this); g_mrd->icmp().require_mgroup(all_routers, true); return true; } void mrdisc_module::shutdown() { g_mrd->icmp().register_handler(MRDISC_ROUTER_SOLICITATION, 0); g_mrd->icmp().require_mgroup(all_routers, false); g_mrd->remove_child("msnip"); } void mrdisc_module::icmp_message_available(interface *intf, const in6_addr &src, const in6_addr &dst, icmp6_hdr *hdr, int length) { if (hdr->icmp6_type != MRDISC_ROUTER_SOLICITATION) return; m_stats.counter(SolicitationCount, RX)++; if (!IN6_IS_ADDR_LINKLOCAL(&src) || !(dst == all_routers.address())) { m_stats.counter(SolicitationCount, Bad)++; return; } register_send_adv(intf, MRDISC_MAX_RESPONSE_DELAY); } void mrdisc_module::register_send_adv(interface *intf, int maxwhen) { /* timer is already running? */ for (solicited_timers::const_iterator i = m_solicited.begin(); i != m_solicited.end(); ++i) { if ((*i)->argument() == intf->index()) return; } solicited_timer *tmr = new solicited_timer("mrdisc solicitation timer", this, std::mem_fun(&mrdisc_module::send_solicited), intf->index()); if (tmr) { tmr->start(mrd::get_randu32() % maxwhen); m_solicited.push_back(tmr); } } void mrdisc_module::event(int ev, void *ptr) { if (ev == mrd::InterfaceStateChanged) { interface *intf = (interface *)ptr; if (intf->up()) { register_send_adv(intf, MRDISC_MAX_INIT_ADV_DELAY); if (interface_count == 0) { m_unsolicited.start(next_adv_interval(), false); } interface_count ++; } else { send_termination(intf); if (interface_count == 1) { m_unsolicited.stop(); } interface_count --; } } else { node::event(ev, ptr); } } void mrdisc_module::send_termination(interface *intf) { icmp6_hdr hdr; hdr.icmp6_type = MRDISC_ROUTER_TERMINATION; hdr.icmp6_code = 0; g_mrd->icmp().send_icmp(intf, all_snoopers, &hdr, 4); } void mrdisc_module::send_advert(interface *intf) { icmp6_hdr hdr; hdr.icmp6_type = MRDISC_ROUTER_ADVERTISEMENT; hdr.icmp6_code = adv_interval->get_unsigned() / 1000; const property_def *qi = 0, *rb = 0; if (intf->conf()->is_router_enabled("mld")) { qi = intf->conf()->get_child_property("mld", "query_interval"); rb = intf->conf()->get_child_property("mld", "robustness"); } /* query interval */ hdr.icmp6_maxdelay = htons(qi ? (qi->get_unsigned() / 1000) : 0); /* robustness */ hdr.icmp6_seq = htons(rb ? rb->get_unsigned() : 0); if (g_mrd->icmp().send_icmp(intf, all_snoopers, &hdr, sizeof(hdr))) { m_stats.counter(AdvertisementCount, TX)++; } } void mrdisc_module::send_unsolicited() { mrd::interface_list::const_iterator i; for (i = g_mrd->intflist().begin(); i != g_mrd->intflist().end(); ++i) { if (i->second->linklocals().empty() || !i->second->up()) continue; send_advert(i->second); } m_unsolicited.start(next_adv_interval(), false); } void mrdisc_module::send_solicited(int &index) { for (solicited_timers::iterator i = m_solicited.begin(); i != m_solicited.end(); ++i) { if ((*i)->argument() == index) { interface *intf = g_mrd->get_interface_by_index(index); if (intf) send_advert(intf); delete *i; m_solicited.erase(i); return; } } } int mrdisc_module::adv_jitter() const { return (int)std::floor(adv_interval->get_unsigned() * 0.025 + 0.5); } int mrdisc_module::next_adv_interval() const { int jitter = adv_jitter(); int base = adv_interval->get_unsigned(); return base + (rand() % (2 * jitter)) - jitter; } mrd6-0.9.6/src/mrdisc/mrdisc_def.h0000644000175000017500000000221510550053610015377 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * mrdisc_def.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_mrdisc_def_h_ #define _mrd_mrdisc_def_h_ #include #include #define MRDISC_ROUTER_ADVERTISEMENT 151 #define MRDISC_ROUTER_SOLICITATION 152 #define MRDISC_ROUTER_TERMINATION 153 #endif mrd6-0.9.6/src/no-modules.cpp0000644000175000017500000000162710600074302014437 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * modules.cpp * * Copyright (C) 2007 - Hugo Santos * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include void mrd::add_static_modules() { } mrd6-0.9.6/src/group.cpp0000644000175000017500000005056310550053317013523 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * group.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include enum { static_sources_method_static = 1000, static_sources_method_remove }; static const method_info static_sources_methods[] = { { "static", "Adds a static source entry", static_sources_method_static, false, 0 }, { "remove", "Removes a static source entry", static_sources_method_remove, false, 0 }, { 0 } }; enum { groupconf_method_source_discovery = 1000 }; static const method_info groupconf_methods[] = { { "source-discovery", "Specifies the source discovery origins to use for this group conf.", groupconf_method_source_discovery, false, 0 }, { 0 } }; class _static_sources_node : public node { public: _static_sources_node(node *parent, source_discovery_origin *origin); bool check_startup(); void group_interest_changed(group_node *n, bool include, static_source_discovery *); bool call_method(int, base_stream &, const std::vector &); bool output_info(base_stream &, const std::vector &) const; private: address_set sources; source_discovery_origin *origin; }; _static_sources_node::_static_sources_node(node *parent, source_discovery_origin *o) : node(parent, "sources"), origin(o) {} bool _static_sources_node::check_startup() { if (!node::check_startup()) return false; import_methods(static_sources_methods); return true; } void _static_sources_node::group_interest_changed(group_node *n, bool include, static_source_discovery *origin) { for (address_set::const_iterator i = sources.begin(); i != sources.end(); ++i) { if (include) { n->discovered_source(0, *i, origin); } else { n->lost_source(*i, origin); } } } bool _static_sources_node::call_method(int id, base_stream &out, const std::vector &args) { switch (id) { case static_sources_method_static: case static_sources_method_remove: if (args.empty()) return false; std::vector addrs; for (std::vector::const_iterator i = args.begin(); i != args.end(); ++i) { inet6_addr addr; if (!addr.set(*i)) return false; addrs.push_back(addr); } bool add = (id == static_sources_method_static); groupconf *owner = (groupconf *)parent(); for (std::vector::const_iterator i = addrs.begin(); i != addrs.end(); ++i) { if (add) { if (sources.insert(*i).second) origin->discovered_source(0, owner->id(), *i); } else { if (sources.remove(*i)) origin->lost_source(owner->id(), *i); } } return true; } return node::call_method(id, out, args); } bool _static_sources_node::output_info(base_stream &os, const std::vector &args) const { if (!args.empty()) return false; os.xprintf("Sources: %{addrset}\n", sources); return true; } source_discovery_origin::~source_discovery_origin() { } void source_discovery_origin::discovered_source(int ifindex, const inet6_addr &groupmask, const inet6_addr &source) { g_mrd->discovered_source(ifindex, groupmask, source, this); } void source_discovery_origin::lost_source(const inet6_addr &groupmask, const inet6_addr &source) { g_mrd->lost_source(groupmask, source, this); } void source_discovery_origin::group_interest_changed(group_node *, bool include) { } void source_discovery_origin::groupconf_registered(groupconf *, bool) { } aggr_source_discovery::aggr_source_discovery(int keepalive) : m_keepalive(keepalive), m_gc_timer("source disc gc", this, std::mem_fun(&aggr_source_discovery::gc)) {} bool aggr_source_discovery::check_startup() { m_gc_timer.start(5000, true); return true; } void aggr_source_discovery::discovered_source(int ifindex, const inet6_addr &group, const inet6_addr &src) { /* if it wasn't in the cache or was old*/ if (add_to_cache(m_cache, ifindex, group, src) >= 0) source_discovery_origin::discovered_source(ifindex, group, src); } int aggr_source_discovery::add_to_cache(cache &c, int ifindex, const inet6_addr &group, const inet6_addr &src) { sg_pair p(group, src); cache::iterator i = c.find(p); time_t now = time(0); if (i == c.end()) { c.insert(std::make_pair(p, now)); if (g_mrd->should_log(INTERNAL_FLOW)) { g_mrd->log().xprintf("AggrSourceDiscovery added source" " (%{Addr}, %{Addr}) to cache.\n", src, group); } return 1; } else { if ((now - i->second) > m_keepalive) { if (g_mrd->should_log(INTERNAL_FLOW)) { g_mrd->log().xprintf("AggrSourceDiscovery " "updated source (%{Addr}," "%{Addr}), cache was old.\n", src, group); } /* was in the cache but is old */ source_discovery_origin::discovered_source(ifindex, group, src); return 0; } i->second = now; } return -1; } void aggr_source_discovery::lost_source(const inet6_addr &group, const inet6_addr &src) { cache::iterator i = m_cache.find(sg_pair(group, src)); if (i != m_cache.end()) { m_cache.erase(i); } source_discovery_origin::lost_source(group, src); } void aggr_source_discovery::group_interest_changed(group_node *n, bool include) { if (!n || !include) return; cache::iterator i = m_cache.begin(); while (i != m_cache.end()) { cache::iterator j = i; ++i; if (n->owner()->id() == j->first.first) m_cache.erase(j); } } void aggr_source_discovery::dump_cache(base_stream &out) const { out.writeline("Source cache"); out.inc_level(); dump_cache(out, m_cache); out.dec_level(); } void aggr_source_discovery::dump_cache(base_stream &out, const cache &c) const { time_t now = time(0); for (cache::const_iterator i = c.begin(); i != c.end(); ++i) { out.xprintf("(%{Addr}, %{Addr}) for %{duration}\n", i->first.second, i->first.first, time_duration((now - i->second) * 1000)); } } void aggr_source_discovery::gc() { run_gc(m_cache); } void aggr_source_discovery::run_gc(cache &c) { time_t now = time(0); cache::iterator i = c.begin(); while (i != c.end()) { cache::iterator j = i; ++i; if ((now - j->second) > m_keepalive) { if (g_mrd->should_log(INTERNAL_FLOW)) { g_mrd->log().xprintf("AggrSourceDiscovery " "cleaned source (%{Addr}, %{Addr}).\n", j->first.second, j->first.first); } c.erase(j); } } } data_plane_source_discovery::data_plane_source_discovery() : aggr_source_discovery(30) {} void static_source_discovery::group_interest_changed(group_node *n, bool include) { if (!n) return; groupconf *gc = n->owner()->conf(); while (gc) { if (gc->get_child("sources")) break; gc = (groupconf *)gc->next_similiar_node(); } if (!gc) { return; } _static_sources_node *sources = (_static_sources_node *)gc->get_child("sources"); if (!sources) return; sources->group_interest_changed(n, include, this); } void static_source_discovery::groupconf_registered(groupconf *gc, bool include) { if (include) { _static_sources_node *sources = new _static_sources_node(gc, this); if (!sources || !sources->check_startup()) { delete sources; return; } gc->add_child(sources); } else { node *n = gc->get_child("sources"); if (n) { gc->remove_child("sources"); delete n; } } } source_discovery_sink::~source_discovery_sink() { /* empty */ } void source_discovery_sink::discovered_source(interface *input, const inet6_addr &groupaddr, const inet6_addr &sourceaddr, source_discovery_origin *origin) { group *gr = g_mrd->get_group_by_addr(groupaddr); if (gr) discovered_source(input, gr, sourceaddr, origin); } void source_discovery_sink::discovered_source(interface *input, group *, const inet6_addr &, source_discovery_origin *origin) { /* empty */ } groupconf::groupconf(const inet6_addr &addr) : conf_node(g_mrd->get_child("groups"), addr.as_string().c_str()), prefix(addr) { } groupconf::~groupconf() { clear_childs(); } bool groupconf::check_startup() { if (!conf_node::check_startup()) return false; import_methods(groupconf_methods); return true; } void groupconf::fill_defaults() { srcdisc.push_back("data-plane"); for (properties::iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (i->second.is_child()) { ((groupconf_node *)i->second.get_node())->fill_defaults(); } } } node *groupconf::next_similiar_node() const { return g_mrd->get_similiar_groupconf_node(this); } bool groupconf::call_method(int id, base_stream &out, const std::vector &args) { switch (id) { case groupconf_method_source_discovery: for (std::vector::const_iterator j = srcdisc.begin(); j != srcdisc.end(); ++j) { std::vector::const_iterator k = std::find(args.begin(), args.end(), *j); if (k == args.end()) { source_discovery_origin *origin = g_mrd->get_source_discovery(j->c_str()); if (origin) origin->groupconf_registered(this, false); } } srcdisc = args; for (std::vector::const_iterator j = srcdisc.begin(); j != srcdisc.end(); ++j) { source_discovery_origin *origin = g_mrd->get_source_discovery(j->c_str()); if (origin) origin->groupconf_registered(this, true); } return true; } return conf_node::call_method(id, out, args); } node *groupconf::create_child(const char *name) { node *child = get_child(name); if (child) return child; router *rt = g_mrd->get_router(name); if (rt) child = rt->create_group_configuration(this); if (!child || !child->check_startup()) { delete child; return 0; } add_child(child, false, name); return child; } void groupconf::remove_child_node(node *_n) { delete (groupconf_node *)_n; } void groupconf::set_source_discs(const source_discs &d) { srcdisc = d; } groupconf_node::groupconf_node(groupconf *parent, const char *name) : conf_node(parent, name) {} node *groupconf_node::next_similiar_node() const { node *curr = parent(); while ((curr = curr->next_similiar_node())) { node *child = curr->get_child(name()); if (child) return child; } return 0; } group_interface::group_interface(group *grp, group_node *n, interface *i) : node(grp, i->name()), g_owner(grp), g_node_owner(n), g_intf(i), g_filter_mode(include) { } group_interface::~group_interface() { } void group_interface::shutdown() { g_filter_mode = include; g_include_set.clear(); g_exclude_set.clear(); owner()->trigger_mode_event(this, all_sources, address_set()); } bool group_interface::has_interest_on(const in6_addr &addr) const { if (g_filter_mode == include) return g_include_set.has_addr(addr); else return !g_exclude_set.has_addr(addr); } void group_interface::dump_filter(base_stream &os) const { os.xprintf("%s %{addrset}", (filter_mode() == include ? "Include" : "Exclude"), (filter_mode() == include ? include_set() : exclude_set())); } void group_interface::dump_filter() const { if (should_log(DEBUG)) { base_stream &os = log(); os.write("Filter is now "); dump_filter(os); os.newl(); } } const address_set &group_interface::active_set() const { if (g_filter_mode == include) return include_set(); else return exclude_set(); } bool group_interface::output_info(base_stream &out, const std::vector &args) const { output_info(out, false); return true; } void group_interface::output_info(base_stream &, bool) const { } bool group_interface::should_log(int level) const { return owner_node()->should_log(level) && intf()->should_log(level); } base_stream &group_interface::log() const { return intf()->log().xprintf("(%{Addr}) ", owner()->id()); } group_node::group_node(router *rt) : node(0, rt->name()), g_owner(0), g_owner_router(rt) {} group_node::~group_node() { } void group_node::attached(group *owner) { m_parent = owner; g_owner = owner; } void group_node::dettached() { m_parent = 0; g_owner = 0; } void group_node::subscriptions_changed(const group_interface *, group_interface::event_type, const address_set &) { } void group_node::discovered_source(interface *, const inet6_addr &, source_discovery_origin *) { /* empty */ } void group_node::lost_source(const inet6_addr &, source_discovery_origin *) { } bool group_node::should_log(int level) const { if (owner() && owner()->should_log(level)) return owner_router() && owner_router()->should_log(level); return false; } base_stream &group_node::log() const { return owner_router()->log_router_desc(owner()->log()); } group::group(const inet6_addr &addr, groupconf *conf) : node(g_mrd->get_child("group"), addr.as_string().c_str()), g_addr(addr), g_conf(conf), g_intflist(this, "interfaces") { g_doomed = false; add_child(&g_intflist); } group::~group() { for (group_intfs::iterator j = g_oifs.begin(); j != g_oifs.end(); j++) { delete j->second; } for (properties::iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (is_group_node(i->second)) delete i->second.get_node(); } } bool group::check_startup() { return node::check_startup(); } void group::shutdown() { for (group_intfs::iterator j = g_oifs.begin(); j != g_oifs.end(); j++) { delete j->second; } g_oifs.clear(); properties::iterator i = m_properties.begin(); while (i != m_properties.end()) { properties::iterator j = i; ++i; if (is_group_node(j->second)) dettach_node((group_node *)j->second.get_node()); } } bool group::output_info(base_stream &_out, bool detailed) const { _out.xprintf("Group %{Addr}\n", id()); _out.inc_level(); node::output_info(_out, std::vector()); _out.dec_level(); return true; } bool group::output_info(base_stream &out, const std::vector &args) const { bool detailed = false; for (std::vector::const_iterator i = args.begin(); i != args.end(); ++i) { if (*i == "detail") detailed = true; else return false; } return output_info(out, detailed); } bool group::is_group_node(const property_def &prop) const { return prop.is_child() && prop.get_node() != &g_intflist; } groupconf *group::groupconf_with_sourcedisc() const { groupconf *gc = conf(); while (gc) { if (!gc->srcdisc.empty()) break; gc = (groupconf *)gc->next_similiar_node(); } return gc; } bool group::attach_node(group_node *node) { add_child(node); node->attached(this); broadcast_source_interest_change(groupconf_with_sourcedisc(), node, true); return true; } void group::dettach_node(group_node *node) { group_node *n = (group_node *)get_child(node->name()); if (n && n == node) { broadcast_source_interest_change(groupconf_with_sourcedisc(), node, false); remove_child(node->name()); node->dettached(); } } group_node *group::node_owned_by(const router *rt) const { return (group_node *)get_child(rt->name()); } group_interface *group::local_oif(int index) const { group_intfs::const_iterator i = g_oifs.find(index); if (i != g_oifs.end()) return i->second; return 0; } group_interface *group::local_oif(interface *intf) { group_intfs::const_iterator i = g_oifs.find(intf->index()); if (i != g_oifs.end()) return i->second; group_interface *oif = 0; if (conf()->has_property("group-intf")) { const char *val = conf()->get_property_string("group-intf"); if (val) { properties::iterator i = m_properties.find(val); if (i != m_properties.end()) { group_node *gn = (group_node *)i->second.get_node(); if (gn) oif = gn->instantiate_group_interface(intf); } } } if (!oif) { for (properties::iterator j = m_properties.begin(); !oif && j != m_properties.end(); ++j) { if (is_group_node(j->second)) { group_node *gn = (group_node *)j->second.get_node(); oif = gn->instantiate_group_interface(intf); } } } // if no group node is interested in handling this interface // we instantiate a default group_interface if (!oif) oif = new group_interface(this, 0, intf); if (oif) { if (should_log(VERBOSE)) { log().xprintf("Added %s to interface list.\n", intf->name()); } g_oifs.insert(std::make_pair(intf->index(), oif)); /* XXX if the interface gets renamed.. this will bork */ g_intflist.add_child(oif, false, intf->name()); } return oif; } void group::clear_interface_references(interface *intf) { group_intfs::iterator k = g_oifs.find(intf->index()); /* First let's change the local interest to INCLUDE {} */ if (k != g_oifs.end()) { k->second->shutdown(); g_intflist.remove_child(intf->name()); delete k->second; g_oifs.erase(k); } properties::iterator i = m_properties.begin(); while (i != m_properties.end()) { properties::iterator j = i; ++i; if (!is_group_node(j->second)) continue; group_node *gn = (group_node *)j->second.get_node(); gn->clear_interface_references(intf); } } void group::trigger_mode_event(group_interface *intf, group_interface::event_type event, const address_set &addrs) const { groupconf *conf = groupconf_with_sourcedisc(); for (properties::const_iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (is_group_node(i->second)) { group_node *gnode = (group_node *)i->second.get_node(); gnode->subscriptions_changed(intf, event, addrs); broadcast_source_interest_change(conf, gnode, true); } } } void group::broadcast_source_interest_change(group_node *gnode, bool in) const { broadcast_source_interest_change(groupconf_with_sourcedisc(), gnode, in); } void group::broadcast_source_interest_change(groupconf *conf, group_node *gnode, bool in) const { if (conf) { for (std::vector::const_iterator i = conf->srcdisc.begin(); i != conf->srcdisc.end(); ++i) { source_discovery_origin *origin = g_mrd->get_source_discovery(i->c_str()); if (origin) { origin->group_interest_changed(gnode, in); } } } } void group::discovered_source(interface *input, const inet6_addr &source, source_discovery_origin *origin) const { for (properties::const_iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (is_group_node(i->second)) { group_node *gn = (group_node *)i->second.get_node(); gn->discovered_source(input, source, origin); } } } void group::lost_source(const inet6_addr &source, source_discovery_origin *origin) const { for (properties::const_iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (is_group_node(i->second)) { group_node *gn = (group_node *)i->second.get_node(); gn->lost_source(source, origin); } } } bool group::has_interest_on(const in6_addr &src) const { for (group_intfs::const_iterator i = g_oifs.begin(); i != g_oifs.end(); i++) { if (((group_interface *)i->second)->has_interest_on(src)) return true; } return false; } bool group::has_downstream_interest(const in6_addr &src) const { for (properties::const_iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (!is_group_node(i->second)) continue; group_node *gn = (group_node *)i->second.get_node(); if (gn->has_downstream_interest(src)) return true; } return false; } bool group::someone_lost_interest() { for (properties::const_iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (!is_group_node(i->second)) continue; group_node *grpnode = (group_node *)i->second.get_node(); if (grpnode->has_interest_in_group()) { /* g_mrd->log().info(EXTRADEBUG) << "not removing group as " << grpnode->owner_router()->name() << " still has interest" << endl; */ return false; } } if (!g_doomed) { g_doomed = true; g_mrd->register_task(mrd::make_task(g_mrd, mrd::RemoveGroup, this)); } return true; } base_stream &group::log() const { return node::log().xprintf("(%{Addr}) ", id()); } mrd6-0.9.6/src/log.cpp0000644000175000017500000003143610726013264013150 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * log.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include #include enum { log_method_attach = 1000 }; static const method_info log_methods[] = { { "attach", "Attaches a new log instance", log_method_attach, false, property_def::NEGATE }, { 0 } }; enum { _LOG_INFO = 1, _LOG_WARN = 2, _LOG_FATAL = 4 }; stream_flusher::~stream_flusher() { } base_stream::base_stream() : fl(0), level(0), dec(true) { ptr = 0; buffer[0] = 0; currfmt = 0; } base_stream::base_stream(stream_flusher *flusher) : fl(flusher), level(0), dec(true) { ptr = 0; buffer[0] = 0; currfmt = 0; } base_stream::~base_stream() { } void base_stream::inc_level() { level++; } void base_stream::dec_level() { level--; } static const char _whites[] = " "; /* 16 spaces */ void base_stream::spaces(int count) { while (count > 0) { int c = std::min(count, 16); append_chunk(_whites, c); count -= c; } } void base_stream::append_chunk(const char *str) { if (dec) append_chunk(str, strlen(str)); } base_stream &base_stream::printf(const char *fmt, ...) { if (dec) { va_list vl; va_start(vl, fmt); int k = vsnprintf(req_buffer(192), 192, fmt, vl); if (k > 192) k = 192; commit_change(k); va_end(vl); } return *this; } void base_stream::advance_format() { assert(currfmt != 0); const char *p = currfmt; while (1) { if (currfmt[0] == '\n') { if (currfmt > p) append_chunk(p, currfmt - p); newl(); p = currfmt + 1; } else if (currfmt[0] == '%') { if (currfmt[1] == '%') { if (currfmt > p) append_chunk(p, currfmt - p); append_chunk("%", 1); currfmt++; p = currfmt + 1; } else { break; } } else if (currfmt[0] == '\0') { break; } currfmt++; } if (currfmt > p) append_chunk(p, currfmt - p); } base_stream &base_stream::newl() { if (dec && fl) fl->flushed(str(), true); clear(); return *this; } void base_stream::perror(const char *str) { xprintf("%s: %s.\n", str, strerror(errno)); } void base_stream::set_decision(bool b) { dec = b; } void base_stream::flush() { if (dec && fl) fl->flushed(str(), false); clear(); } void base_stream::ident_start() { int l = level; if (l > 20) l = 20; memset(buffer, ' ', l * 2); ptr = l * 2; } void base_stream::append_chunk(const char *str, int len) { append_chunk(str, len, true); } void base_stream::append_chunk(const char *str, int len, bool first) { if (len <= 0) return; if (dec) { if (first && ptr == 0) { ident_start(); } int x; if ((len + ptr) > (int)(sizeof(buffer)-1)) { x = sizeof(buffer) - ptr - 1; } else { x = len; } strncpy(buffer + ptr, str, x); /* if sizeof(char) = 1 byte.. memcpy(buffer + ptr, str, x);*/ ptr += x; buffer[ptr] = 0; if (x < len) { flush(); append_chunk(str + x, len - x, false); } } } char *base_stream::req_buffer(int n) { return req_buffer(n, true); } char *base_stream::req_buffer(int n, bool first) { if (n > (int)(sizeof(buffer)-1)) return 0; if (first && ptr == 0) ident_start(); /* must flush */ if ((n + ptr) > (int)(sizeof(buffer)-1)) { /* prevent loops */ if (!first) return 0; flush(); return req_buffer(n, false); } return buffer + ptr; } void base_stream::commit_change(int n) { ptr += n; } void base_stream::nprintf(int n, const char *fmt, ...) { va_list vl; va_start(vl, fmt); commit_change(vsnprintf(req_buffer(n), n, fmt, vl)); va_end(vl); } void base_stream::clear() { ptr = 0; buffer[0] = 0; } const char *base_stream::str() const { return buffer; } log_node::log_node(log_base *parent, const char *name, int level) : node(parent, name) { infolevel = instantiate_property_i("infolevel", level); } bool log_node::check_startup() { return node::check_startup() && infolevel != 0; } void log_node::set_level(const char *l) { set_property("infolevel", l); } bool log_node::set_property(const char *name, const char *value) { if (!strcmp(name, "infolevel")) { int level; if (!parse_infolevel(value, level)) return false; /* very stupid'ish */ char buf[32]; snprintf(buf, sizeof(buf), "%i", level); return node::set_property(name, buf); } return node::set_property(name, value); } bool log_node::will_log(int type, int level) const { return type != _LOG_INFO || level <= infolevel->get_integer(); } syslog_log_node::syslog_log_node(log_base *parent, const char *name, int infolevel) : log_node(parent, name, infolevel) { } syslog_log_node::~syslog_log_node() { closelog(); } bool syslog_log_node::check_startup() { if (!log_node::check_startup()) return false; openlog("mrd", LOG_PID, LOG_DAEMON); return true; } static inline int type_as_syslog_priority(int t) { switch (t) { case _LOG_INFO: return LOG_INFO; case _LOG_WARN: return LOG_WARNING; case _LOG_FATAL: return LOG_CRIT; default: return LOG_ERR; } } void syslog_log_node::log(int type, int level, const char *msg, bool) { syslog(type_as_syslog_priority(type), msg); } tb_log_node::tb_log_node(log_base *parent, const char *name, int level) : log_node(parent, name, level) {} const char * tb_log_node::timestamp(char *buffer, size_t length) const { timeval tv; gettimeofday(&tv, NULL); time_t nowt = tv.tv_sec; assert(length >= 1); buffer[0] = '['; size_t l = strftime(buffer + 1, length - 1, "%b %d %T", localtime(&nowt)); assert((l + 1) <= length); snprintf(buffer + 1 + l, length - l - 1, ":%06u]", (uint32_t)tv.tv_usec); return buffer; } file_log_node::file_log_node(log_base *parent, const char *name, int level, const char *filename, bool flush) : tb_log_node(parent, name, level) { _base_filename = filename; _fp = fopen(filename, "a"); _flush = instantiate_property_b("flush", flush); } file_log_node::file_log_node(log_base *parent, const char *name, int level, FILE *param) : tb_log_node(parent, name, level), _fp(param) { _flush = instantiate_property_b("flush", false); } file_log_node::~file_log_node() { if (_fp && _fp != stderr) { fclose(_fp); _fp = 0; } } bool file_log_node::check_startup() { if (!log_node::check_startup()) return false; return _flush != 0 && _fp != 0; } void file_log_node::log(int type, int level, const char *msg, bool newline) { if (!_fp) return; char tmp[64]; fputs(timestamp(tmp, sizeof(tmp)), _fp); fputc(' ', _fp); fputs(msg, _fp); if (newline) fputc('\n', _fp); if (newline && _flush->get_bool()) fflush(_fp); } void file_log_node::event(int ev, void *ptr) { if (ev == log_base::ReloadEvent) { if (_fp != stderr) { fclose(_fp); _fp = fopen(_base_filename.c_str(), "a"); } } else { tb_log_node::event(ev, ptr); } } static void _handle_log_reload(int) { log_base::instance().reload_logs(); } log_base::log_base(node *parent) : node(parent, "log"), _base(this) { _force_stderr = false; } log_base::~log_base() { signal(SIGHUP, SIG_IGN); clear_childs(); } void log_base::remove_child_node(node *n) { delete (log_node *)n; } const char *log_base::description() const { return "Logging facilities"; } bool log_base::check_startup() { if (!node::check_startup()) return false; signal(SIGHUP, _handle_log_reload); import_methods(log_methods); return true; } void log_base::reload_logs() { broadcast_event(ReloadEvent, 0); } bool log_base::would_log(int level) const { /* is any of the child log nodes interested in this level of message? */ for (properties::const_iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (!i->second.is_child()) continue; if (((log_node *)i->second.get_node())->will_log(_LOG_INFO, level)) return true; } return _force_stderr; } log_base &log_base::instance() { return g_mrd->g_rlog; } bool log_base::change_context(int level) { _current = _LOG_INFO; _level = level; bool b = would_log(level); _base.set_decision(b); return b; } base_stream &log_base::current_context() { return _base; } void log_base::force_stderr() { _force_stderr = true; } bool log_base::attach_node(log_node *n) { if (!n || !n->check_startup()) { delete n; return false; } node *was = get_child(n->name()); if (was) { remove_child(was->name()); } add_child(n); return true; } void log_base::dettach_node(log_node *n) { if (n) remove_child(n->name()); } bool log_base::call_method(int id, base_stream &out, const std::vector &args) { switch (id) { case log_method_attach: return attach_node(args); } return node::call_method(id, out, args); } bool log_base::negate_method(int id, base_stream &out, const std::vector &args) { if (id == log_method_attach) { if (args.empty()) return false; log_node *n = (log_node *)get_child(args[0].c_str()); if (n) { remove_child(args[0].c_str()); } return true; } return node::negate_method(id, out, args); } void log_base::flushed(const char *str, bool newline) { for (properties::const_iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (!i->second.is_child()) continue; log_node *n = (log_node *)i->second.get_node(); if (n->will_log(_current, _level)) n->log(_current, _level, str, newline); } if (_force_stderr) { fprintf(stderr, "%s%s", str, newline ? "\n" : ""); } } static inline bool _as_int(const char *v, int &value) { char *end; value = strtol(v, &end, 10); if (*end) return false; return true; } bool log_node::parse_infolevel(const char *v, int &value) { if (!strcmp(v, "all")) { value = EVERYTHING; } else if (!strcmp(v, "internal_flow")) { value = INTERNAL_FLOW; } else if (!strcmp(v, "message_content")) { value = MESSAGE_CONTENT; } else if (!strcmp(v, "message_sig")) { value = MESSAGE_SIG; } else if (!strcmp(v, "message_err")) { value = MESSAGE_ERR; } else if (!strcmp(v, "extradebug")) { value = EXTRADEBUG; } else if (!strcmp(v, "debug")) { value = DEBUG; } else if (!strcmp(v, "verbose")) { value = VERBOSE; } else if (!strcmp(v, "normal")) { value = NORMAL; } else { return _as_int(v, value); } return true; } bool log_base::attach_node(const std::vector &args) { if (args.empty()) return false; log_node *n = 0; int val = 5; if (args[0] == "syslog") { if (args.size() > 1) { if (!log_node::parse_infolevel(args[1].c_str(), val)) return false; } n = new syslog_log_node(this, "syslog", val); } else if (args[0] == "stderr") { if (args.size() > 1) { if (!log_node::parse_infolevel(args[1].c_str(), val)) return false; } n = new file_log_node(this, "stderr", val, stderr); } else if (args.size() > 1) { bool flush = true; if (args.size() > 2) { if (!log_node::parse_infolevel(args[2].c_str(), val)) return false; if (args.size() > 3) { if (args[3] == "no-flush") flush = true; else return false; } } n = new file_log_node(this, args[0].c_str(), val, args[1].c_str(), flush); } if (!n) return false; attach_node(n); return true; } const char *stream_type_format_parameter(bool) { return "b"; } const char *stream_type_format_parameter(int) { return "i"; } const char *stream_type_format_parameter(uint32_t) { return "u"; } const char *stream_type_format_parameter(uint64_t) { return "llu"; } const char *stream_type_format_parameter(const char *) { return "s"; } const char *stream_type_format_parameter(const void *) { return "p"; } void stream_push_formated_type(base_stream &os, bool val) { os.append_chunk(val ? "true" : "false"); } void stream_push_formated_type(base_stream &os, int val) { os.nprintf(32, "%i", val); } void stream_push_formated_type(base_stream &os, uint32_t val) { os.nprintf(32, "%u", val); } void stream_push_formated_type(base_stream &os, uint64_t val) { os.nprintf(64, "%llu", val); } void stream_push_formated_type(base_stream &os, const char *val) { os.append_chunk(val ? val : "(null)"); } void stream_push_formated_type(base_stream &os, const void *val) { os.nprintf(32, "%p", val); } mrd6-0.9.6/src/pim/0000755000175000017500000000000010746353706012453 5ustar hugohugomrd6-0.9.6/src/pim/pim_group.cpp0000644000175000017500000006314610746336636015175 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim_group.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include #include bool pim_group_node::calculate_embedded_rp_addr(const in6_addr &addr, inet6_addr &dst) { if ((addr.s6_addr[1] & 0xf0) == 0x70) { if (addr.s6_addr[3] > 0 && addr.s6_addr[3] <= 64) { inet6_addr embrp(in6addr_any, addr.s6_addr[3]); memcpy(embrp.addr.s6_addr, addr.s6_addr + 4, (addr.s6_addr[3] + 7) / 8); embrp.addr.s6_addr[15] |= addr.s6_addr[2] & 0x0f; if (!embrp.matches(inet6_addr("ff80::/10")) && !embrp.matches(inet6_addr("::/16")) && !embrp.matches(inet6_addr("ff00::/8"))) { dst = embrp; return true; } } } return false; } pim_group_node::pim_group_node(router *rt, const inet6_addr &addr, pim_groupconf_node *conf) : group_node(rt), m_rp_path(this, std::mem_fun(&pim_group_node::rp_path_changed)), m_ssm(false), m_rp_failure_report_timer("rp failure report", this, std::mem_fun(&pim_group_node::report_forward_to_rp_failure), 15000, false) { m_addr = addr; m_rpaddr = in6addr_any; m_refcount = 0; m_wildcard = 0; // support embedded-RP mechanism const in6_addr *raddr = addr.address_p(); bool R_bit = raddr->s6_addr[1] & 0x40; bool P_bit = raddr->s6_addr[1] & 0x20; bool T_bit = raddr->s6_addr[1] & 0x10; if (P_bit && T_bit) { if (R_bit) { calculate_embedded_rp_addr(addr.addr, m_embedded_rpaddr); } else if (raddr->s6_addr[3] == 0) { m_ssm = true; } } m_selfrp = false; m_mfa_inst = 0; m_conf = conf; m_rp_failure_count = 0; m_rp_failure_last_msg = 0; } pim_group_node::~pim_group_node() { } void pim_group_node::shutdown() { while (!m_states.empty()) { source_pair &p = m_states.begin()->second; pim_source_state_base *state = 0; if (p.first) { state = p.first; p.first = 0; } else { state = p.second; p.second = 0; } if (!p.first && !p.second) m_states.erase(m_states.begin()); delete state; } delete m_wildcard; m_wildcard = 0; if (m_mfa_inst) { g_mrd->mfa()->release_group(m_mfa_inst); m_mfa_inst = 0; } if (pim->should_log(DEBUG)) pim->log().xprintf("Removed group state for %{Addr}\n", id()); } bool pim_group_node::output_info(base_stream &ctx, const std::vector &args) const { ctx.writeline("PIM"); ctx.inc_level(); if (!is_ssm()) { ctx.write("Rendezvous-Point: "); if (IN6_IS_ADDR_UNSPECIFIED(&m_rpaddr)) { ctx.write("none"); } else { ctx.xprintf("%{addr} [", m_rpaddr); switch (m_rp_source) { case rps_static: ctx.write("static"); break; case rps_embedded: ctx.write("embedded"); break; case rps_rp_set: ctx.write("rp-set"); break; case rps_join: ctx.write("from join-prune"); break; } if (is_self_rp()) ctx.write(", self"); ctx.write("]"); } ctx.newl(); } ctx.writeline("Sources:"); ctx.inc_level(); if (m_states.empty() && !m_wildcard) ctx.writeline("(None)"); else { if (m_wildcard) m_wildcard->output_info(ctx); for (states::const_iterator i = m_states.begin(); i != m_states.end(); ++i) { if (i->second.first) i->second.first->output_info(ctx); if (i->second.second) i->second.second->output_info(ctx); } } ctx.dec_level(); ctx.dec_level(); return true; } void pim_group_node::set_rp() { rp_source src; inet6_addr newrp = rp_for_group(src); set_rp(newrp, src); } void pim_group_node::set_rp(const inet6_addr &rp, rp_source src) { if (m_ssm) return; if (rp.is_any()) return; if (!(m_rpaddr == rp)) { if (should_log(DEBUG)) { if (IN6_IS_ADDR_UNSPECIFIED(&m_rpaddr)) { if (g_mrd->has_address(rp)) log().writeline("I'm the RP for this group"); else log().xprintf("RP is at %{Addr}\n", rp); } else { log().xprintf("Changing RP from %{addr} to %{Addr}\n", m_rpaddr, rp); } } m_rpaddr = rp; m_rp_source = src; m_selfrp = g_mrd->has_address(rp); /* reset failure info */ m_rp_failure_count = 0; m_rp_failure_last_msg = 0; m_rp_failure_report_timer.stop(); if (m_selfrp) m_rp_path.release(); else m_rp_path.set_destination(rp); if (m_wildcard) m_wildcard->rp_changed(); states::iterator i = m_states.begin(); while (i != m_states.end()) { /* call rp_changed for RPT state first as it doesnt * cause state removal. */ if (i->second.second) i->second.second->rp_changed(); pim_group_source_state *st = i->second.first; /* From STL docs: ``Erasing an element from a map also * does not invalidate any iterators, except, of * course, for iterators that actually point to the * element that is being erased''. */ ++i; if (st) st->rp_changed(); } } } void pim_group_node::rp_path_changed(uint32_t) { if (m_rp_path.valid && should_log(EXTRADEBUG)) { log().writeline("RP path changed"); } } inet6_addr pim_group_node::rp_for_group(rp_source &src) const { if (m_conf) { in6_addr rpaddr; if (m_conf->rp_for_group(id(), rpaddr, src)) return rpaddr; } return in6addr_any; } void pim_group_node::rp_set_changed() { if (m_ssm) return; set_rp(); } bool pim_group_node::attach(group *node, const pim_groupconf_node *entry) { node->attach_node(this); return true; } address_set pim_group_node::source_state_set() const { address_set res; for (states::const_iterator i = m_states.begin(); i != m_states.end(); i++) { res.insert(i->first); } return res; } address_set pim_group_node::local_source_state_set() const { address_set res; for (states::const_iterator i = m_states.begin(); i != m_states.end(); i++) { if (i->second.first && i->second.first->is_source_local()) res.insert(i->first); } return res; } void pim_group_node::subscriptions_changed(const group_interface *gif, group_interface::event_type event, const address_set &sources) { if (gif->filter_mode() == group_interface::include) { if (event == group_interface::added_sources) { for (address_set::const_iterator i = sources.begin(); i != sources.end(); ++i) { /* if source host is joining isn't local */ if (!gif->intf()->in_same_subnet(*i)) { /* join it, possibly creating the (S,G) state * if it doesn't exist */ create_state(*i, false, gif->intf(), true); } } } else if (event == group_interface::removed_sources) { for (address_set::const_iterator i = sources.begin(); i != sources.end(); ++i) { pim_group_source_state *state = get_state(*i); if (state) { state->release_oif(gif->intf(), true); } } } else if (event == group_interface::all_sources) { if (m_wildcard) { m_wildcard->release_oif(gif->intf(), true); } address_set sss = source_state_set(); address_set added, removed; sss.assign_with(gif->include_set(), added, removed); for (address_set::const_iterator i = added.begin(); i != added.end(); ++i) { /* if the joining new source isn't local */ if (!gif->intf()->in_same_subnet(*i)) { /* join it, possibly creating the (S,G) state * if it doesn't exist */ create_state(*i, false, gif->intf(), true); } } for (address_set::const_iterator i = removed.begin(); i != removed.end(); ++i) { pim_group_source_state *state = get_state(*i); if (state) { state->release_oif(gif->intf(), true); } } } } else { /* join the (*,G) in interface gif, creates * the wildcard state if it doesn't exist */ create_wildcard(gif->intf(), true, 0); if (event == group_interface::all_sources) { address_set sss = source_state_set(); sss.union_with(gif->exclude_set()); for (address_set::const_iterator i = sss.begin(); i != sss.end(); ++i) { if (!gif->intf()->in_same_subnet(*i)) { pim_source_state_base *state = create_state(*i, false); if (state) { if (gif->exclude_set().has_addr(*i)) { state->set_local_oif(gif->intf(), false); } else { state->release_oif(gif->intf(), true); } } } } } else if (event == group_interface::added_sources) { for (address_set::const_iterator i = gif->exclude_set().begin(); i != gif->exclude_set().end(); ++i) { if (!gif->intf()->in_same_subnet(*i)) { pim_source_state_base *state = create_state(*i, false); if (state) { // Prune the interface state->set_local_oif(gif->intf(), false); } } } } else { for (address_set::const_iterator i = sources.begin(); i != sources.end(); ++i) { pim_group_source_state *state = get_state(*i); if (state) { state->release_oif(gif->intf(), true); } } } } } bool pim_group_node::check_startup() { m_mfa_inst = g_mrd->mfa()->create_group(pim, id(), this); if (!m_mfa_inst) return 0; m_mfa_inst->activate(true); return group_node::check_startup(); } void pim_group_node::attached(group *grp) { group_node::attached(grp); if (!check_startup()) { if (should_log(WARNING)) log().writeline("Failed to attach PIM group"); grp->dettach_node(this); delete this; return; } m_conf->attach_watcher(this); } void pim_group_node::dettached() { m_conf->dettach_watcher(this); shutdown(); group_node::dettached(); } void pim_group_node::garbage_collect() { /* Refresh KATs based on forwarding activity */ for (states::const_iterator i = m_states.begin(); i != m_states.end(); ++i) { /* check non-RPT state */ if (i->second.first && i->second.first->m_kat_enabled) { i->second.first->update_fw_counters(); } } tval now = tval::now(); pim_intfconf_node *defconf = (pim_intfconf_node *) g_mrd->default_interface_configuration()->get_child("pim"); uint32_t defval = defconf ? defconf->data_timeout() : 210000; /* Timeout states based on KAT */ for (states::const_iterator i = m_states.begin(); i != m_states.end(); ++i) { if (i->second.first && i->second.first->m_kat_enabled) { pim_group_source_state *st = i->second.first; pim_interface *iif = pim->get_interface(st->iif()); uint32_t val = iif ? iif->conf()->data_timeout() : defval; uint32_t diff = now - st->m_kat_last_update; if (val <= diff) { st->m_kat_enabled = false; if (!handle_kat_expired(st)) break; } } } } bool pim_group_node::has_interest_in_group() const { if (!m_states.empty()) { for (states::const_iterator i = m_states.begin(); i != m_states.end(); ++i) { if (i->second.first && i->second.first->state_desired()) return true; if (i->second.second && i->second.second->state_desired()) return true; } } return m_wildcard && m_wildcard->state_desired(); } pim_source_state_base *pim_group_node::create_state(const inet6_addr &addr, bool rpt) { return create_state(addr, rpt, 0, false, 0); } pim_source_state_base *pim_group_node::create_state(const inet6_addr &addr, bool rpt, interface *oif, bool local, uint32_t val) { if (addr.is_any()) return create_wildcard(oif, local, val) ? m_wildcard : 0; states::const_iterator i = m_states.find(addr); pim_source_state_base *state = i != m_states.end() ? (rpt ? i->second.second : (pim_source_state_base *)i->second.first) : 0; bool created = false; if (!state) { if (!rpt) state = create_source_state(addr); else state = create_source_rpt_state(addr); if (!state || !state->check_startup()) { delete state; return 0; } if (!rpt) m_states[addr].first = (pim_group_source_state *)state; else m_states[addr].second = (pim_group_source_rpt_state *)state; if (m_wildcard) state->wildcard_state_existance_changed(true); created = true; } if (oif) { if (local) state->set_local_oif(oif); else state->set_oif(oif, val); } if (created) { state->check_upstream_path(); if (should_log(VERBOSE)) log().xprintf("Created state (%{addr}%s).\n", state->addr(), rpt ? ", RPT" : ""); } return state; } pim_source_state_base *pim_group_node::get_state(const inet6_addr &addr, bool rpt) const { states::const_iterator i = m_states.find(addr); if (i != m_states.end()) return rpt ? i->second.second : (pim_source_state_base *)i->second.first; return 0; } void pim_group_node::remove_state(pim_source_state_base *st) { if (!st) return; bool remove = false; if (m_wildcard == st) { m_wildcard = 0; remove = true; /* safeguard group instance */ m_refcount ++; states::iterator i = m_states.begin(); while (i != m_states.end()) { pim_group_source_state *state = i->second.first; pim_group_source_rpt_state *rptstate = i->second.second; ++i; if (rptstate) rptstate->wildcard_state_existance_changed(false); if (state) state->wildcard_state_existance_changed(false); } m_refcount --; } else { states::iterator i = m_states.begin(); while (i != m_states.end()) { if (i->second.first == st) { i->second.first = 0; remove = true; } else if (i->second.second == st) { i->second.second = 0; remove = true; } if (!i->second.first && !i->second.second) { states::iterator j = i; ++i; m_states.erase(j); } else { ++i; } } } if (remove) { if (should_log(VERBOSE)) { base_stream &os = log().write("Removed "); st->output_name(os); os.writeline(" state"); } delete st; if (m_states.empty() && m_refcount == 0) { owner()->someone_lost_interest(); } } } bool pim_group_node::create_wildcard(interface *oif, bool local, uint32_t val) { if (m_ssm) return false; bool created = false; if (!m_wildcard) { m_wildcard = create_wildcard_state(); if (!m_wildcard) return false; if (!m_wildcard->check_startup()) { delete m_wildcard; m_wildcard = 0; return false; } if (should_log(VERBOSE)) log().writeline("Created state (*, G)."); created = true; } if (oif) { if (local) m_wildcard->set_local_oif(oif); else m_wildcard->set_oif(oif, val); } if (created) { m_wildcard->check_upstream_path(); for (states::iterator i = m_states.begin(); i != m_states.end(); ++i) { if (i->second.first) i->second.first->wildcard_state_existance_changed(true); if (i->second.second) i->second.second->wildcard_state_existance_changed(true); } /* Force cached entries to be re-add */ owner()->broadcast_source_interest_change(this); } return true; } bool pim_group_node::create_wildcard() { return create_wildcard(0, false, 0); } bool pim_group_node::has_wildcard() const { return m_wildcard != 0; } pim_group_wildcard_state *pim_group_node::wildcard() const { return m_wildcard; } void pim_group_node::dr_changed(pim_interface *intf, bool islocal) { if (m_wildcard) m_wildcard->dr_changed(intf, islocal); for (states::iterator i = m_states.begin(); i != m_states.end(); ++i) { if (i->second.first) i->second.first->dr_changed(intf, islocal); if (i->second.second) i->second.second->dr_changed(intf, islocal); } } void pim_group_node::do_register(const in6_addr *from, ip6_hdr *hdr, uint16_t pktlen, bool null) { if (m_ssm) return; if (is_self_rp()) { hdr->ip6_hlim--; /* XXX maybe cache ACL accept value in source state? */ if (!rp_acl_accept_source(hdr->ip6_src)) { int opt = get_conf()->rp_rejected_source_policy(); if (opt == pim_groupconf_node::RPRejRegisterStop) { send_register_stop_to_router(hdr->ip6_src, *from); } else if (opt == pim_groupconf_node::RPRejSilentIgnore) { /* do nothing */ } else if (opt == pim_groupconf_node::RPRejLogIgnore) { if (should_log(DEBUG)) { log().xprintf("Source Register ACL " "rejected message for " "%{addr} from DR %{addr}" "\n", hdr->ip6_src, *from); } } } // always switch to SPT pim_group_source_state *state = (pim_group_source_state *)create_state(hdr->ip6_src, false); if (!state) { return; } /* This source is alive */ state->restart_kat(); /* XXX Send Register-Stops if InheritedOifs are empty too */ if (state->spt() || state->count_oifs() == 0) { state->trigger_register_stop(from); } else if (!rp_acl_accept(*from)) { state->trigger_register_stop(from); } else if (!null && has_wildcard()) { /* draft11 says we should rejoin the normal packet * forwarding flow here, but that doesn't really * follow our model, so let's forward to * inherited_olist(S,G,rpt) here * * inherited_olist(S,G,rpt) = * (joins(*,G) (-) prunes(S,G,rpt)) * (+) (pim_include(*,G) (-) pim_exclude(S,G)) * (-) (lost_assert(*,G) (+) lost_assert(S,G,rpt)) * */ const pim_source_state_base::oifs *inhoifs = wildcard()->get_oifs(); for (pim_source_state_base::oifs::const_iterator i = inhoifs->begin(); i != inhoifs->end(); ++i) { /* the presence of a Intf in inhoifs precludes * that there is joins(*,G) or pim_include(*,G) */ /* checks joins(*,G), pim_include(*,G) and lost_assert(*,G) */ if ((*i)->get_interest() != pim_oif::Include) continue; pim_common_oif *oif = (pim_common_oif *)state->get_oif((*i)->intf()); if (!oif || oif->get_local_interest() == pim_oif::Exclude || state->lost_assert_rpt(oif)) continue; state->forward((*i)->intf(), hdr, pktlen); } } } else { send_register_stop_to_router(hdr->ip6_src, *from); } } void pim_group_node::send_register_stop_to_router(const in6_addr &src, const in6_addr &from) const { if (is_self_rp()) { pim->send_register_stop_to_router(id(), rpaddr(), src, from); } else { pim->send_register_stop_to_router(id(), pim->my_address().address(), src, from); } } bool pim_group_node::rp_acl_accept(const in6_addr &from) const { #if 0 if (m_conf->get_value("rp_acl")->as() != rpa_any) { bool accepted = false; const std::vector acls = m_conf->get_value("rp_acls")->as >(); for (std::vector::const_iterator i = acls.begin(); !accepted && i != acls.end(); i++) { accepted = i->matches(*from); } if ((m_conf->get_value("rp_acl")->as() == rpa_allow && !accepted) || (m_conf->get_value("rp_acl")->as() == rpa_deny && accepted)) { state->trigger_register_stop(from); return; } } #endif /* Always accept for now */ return true; } bool pim_group_node::rp_acl_accept_source(const in6_addr &src) const { return get_conf()->rp_source_acl_accepts(this, src); } void pim_group_node::register_stop(const inet6_addr &rpaddr, const inet6_addr &addr) { if (rpaddr == m_rpaddr) { pim_group_source_state *state = get_state(addr); if (state) state->register_stop(); } else if (should_log(EXTRADEBUG)) { log().xprintf("Register Stop discarded, source is not the RP " "(expected %{addr} got %{Addr})\n", m_rpaddr, rpaddr); } } void pim_group_node::clear_interface_references(interface *intf) { if (m_wildcard) { m_wildcard->grab(); m_wildcard->clear_interface_references(intf); } states::iterator i = m_states.begin(); while (i != m_states.end()) { pim_group_source_state *nonrpt = i->second.first; pim_group_source_rpt_state *rpt = i->second.second; /* safeguard our iterator */ ++i; /* RPT */ if (rpt) { rpt->grab(); rpt->clear_interface_references(intf); } if (nonrpt) { nonrpt->grab(); nonrpt->clear_interface_references(intf); } } /* safeguard the group instance */ m_refcount ++; i = m_states.begin(); while (i != m_states.end()) { pim_group_source_state *nonrpt = i->second.first; pim_group_source_rpt_state *rpt = i->second.second; /* safeguard our iterator */ ++i; if (rpt) rpt->release(); if (nonrpt) nonrpt->release(); } if (m_wildcard) { m_wildcard->release(); } m_refcount --; if (m_states.empty() && !m_wildcard) { owner()->someone_lost_interest(); } } bool pim_group_node::handle_kat_expired(pim_group_source_state *state) { if (should_log(DEBUG)) log().xprintf("state (%{addr}) KAT expired after %{duration}\n", state->addr(), time_duration(state->uptime())); return state->check_interest(); } void pim_group_node::found_new_neighbour(pim_neighbour *neigh) const { if (m_wildcard) m_wildcard->found_new_neighbour(neigh); for (states::const_iterator i = m_states.begin(); i != m_states.end(); ++i) { if (i->second.first) i->second.first->found_new_neighbour(neigh); /* RPT states don't care about new neighbours */ } } void pim_group_node::lost_neighbour(pim_neighbour *neigh) const { for (states::const_iterator i = m_states.begin(); i != m_states.end(); ++i) { if (i->second.first) i->second.first->neighbour_lost(neigh); /* RPT states don't care about lost neighbours */ } if (m_wildcard) m_wildcard->neighbour_lost(neigh); } void pim_group_node::rpt_upstream_changed() { for (states::const_iterator i = m_states.begin(); i != m_states.end(); ++i) { /* update (S,G,rpt) upstream */ if (i->second.second) i->second.second->build_upstream_state(); } } void pim_group_node::rpt_update_upstream() { for (states::const_iterator i = m_states.begin(); i != m_states.end(); ++i) { /* update (S,G,rpt) upstream */ if (i->second.second) i->second.second->update_upstream(); } } void pim_group_node::inherited_oif_changed_state(pim_oif *oif, pim_oif::interest prev) { /* safeguard the integrity of the group */ m_refcount ++; states::iterator i = m_states.begin(); while (i != m_states.end()) { pim_group_source_state *st = i->second.first; ++i; if (st) { /* we set final=false as we don't want this call * to remove the group state while we are iterating */ st->inherited_oif_changed_state(oif, prev); } /* the RPT state has no interest in inherited oifs here */ } m_refcount --; owner()->someone_lost_interest(); } void pim_group_node::forward_to_rp(pim_group_source_state *state, interface *iif, ip6_hdr *hdr, uint16_t len) { if (!has_rp()) return; const inet6_addr &from = pref_source_towards_rp(); if (!has_rp_path() || from.is_any() || from.address() == in6addr_loopback) { failed_to_forward_to_rp("No available unicast path to RP"); return; } g_mrd->opktb->send_offset = 0; pim_register_message *msg = g_mrd->opktb->header(); memset(msg, 0, sizeof(*msg)); memcpy(msg->ip6_header(), hdr, len); msg->construct(false, false); if (pim->send_register(from, rpaddr(), msg, len)) { state->restart_kat(); } else { failed_to_forward_to_rp(0); } } void pim_group_node::failed_to_forward_to_rp(const char *msg) { m_rp_failure_count++; if (m_rp_failure_report_timer.is_running()) { if (msg == m_rp_failure_last_msg) { return; } else { m_rp_failure_report_timer.stop(); } } if (should_log(DEBUG)) { base_stream &os = log(); os.xprintf("Failed to register a multicast packet to RP %{addr}", m_rpaddr); if (msg) os.xprintf(", %s", msg); os.newl(); } m_rp_failure_last_msg = msg; m_rp_failure_report_timer.start(); } void pim_group_node::report_forward_to_rp_failure() { if (!m_rp_failure_count) return; if (should_log(DEBUG)) { base_stream &os = log(); os.xprintf("Failed to register %u multicast packets to RP " "%{addr} in the last 15 seconds", m_rp_failure_count, m_rpaddr); if (m_rp_failure_last_msg) os.xprintf(", %s", m_rp_failure_last_msg); os.newl(); } m_rp_failure_count = 0; m_rp_failure_report_timer.start(); } void pim_group_node::property_changed(node *n, const char *name) { if (!strcmp(name, "rp")) { set_rp(); } } bool pim_group_node::has_interest_on(const in6_addr &src) const { /* Handles local interest */ if (owner()->has_interest_on(src)) return true; /* Handles (*,G) needs */ if (has_wildcard()) { return wildcard()->get_downstream_interest() == pim_oif::Include; } return false; } void pim_group_node::discovered_source(interface *input, const inet6_addr &sourceaddr, source_discovery_origin *origin) { bool same = g_mrd->in_same_subnet(sourceaddr); bool inter = has_interest_on(sourceaddr); if (should_log(MESSAGE_SIG)) { log().xprintf("Discovered Source %{Addr} from %s InSameSubnet(" "%b) HasLocalInterest(%b)%s.\n", sourceaddr, origin ? origin->origin_description() : "(unknown)", same, inter, !same && !inter ? ", state not being created" : ""); } if (!same && !inter) return; /* XXX match input with RPF(S) */ pim_group_source_state *state = (pim_group_source_state *)create_state(sourceaddr, false); if (!state) { /* XXX */ } } bool pim_group_node::has_downstream_interest(const in6_addr &src) const { states::const_iterator i = m_states.find(src); if (i == m_states.end() || !i->second.first) return false; if (i->second.first->is_source_local()) { if (i->second.first->has_downstream_interest(true)) return true; } return false; } pim_group_source_state *pim_group_node::create_source_state(const inet6_addr &addr) { return new pim_group_source_state(this, addr); } pim_group_source_rpt_state *pim_group_node::create_source_rpt_state(const inet6_addr &addr) { return new pim_group_source_rpt_state(this, addr); } pim_group_wildcard_state *pim_group_node::create_wildcard_state() { return new pim_group_wildcard_state(this); } mrd6-0.9.6/src/pim/pim_neighbour.cpp0000644000175000017500000003515210550053317016000 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim_neighbour.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include pim_neighbour_watcher_base::pim_neighbour_watcher_base(mrib_watcher_target *t) : mrib_watcher_base(t) { w_lastintf = 0; w_neigh = 0; } pim_neighbour_watcher_base::~pim_neighbour_watcher_base() { } bool pim_neighbour_watcher_base::check_startup() { return true; } bool pim_neighbour_watcher_base::self_upstream() const { /* if we are the target or the nexthop, we are the upstream */ if (g_mrd->has_address(target()) || g_mrd->has_address(nexthop())) return true; /* if the target is a known neighbour or the nexthop is a known neighbour, * we aren't the upstream */ if (pim->get_neighbour(target()) || pim->get_neighbour(nexthop())) return false; /* if the target is directly attached we are the upstream */ return g_mrd->in_same_subnet(target()); } static inline bool _in6_is_addr_unspecified(const in6_addr &addr) { return IN6_IS_ADDR_UNSPECIFIED(&addr); } bool pim_neighbour_watcher_base::recheck_neighbour() { pim_neighbour *last_neigh = w_neigh; bool self = self_upstream(); w_neigh = 0; if (w_lastintf && !self && !_in6_is_addr_unspecified(nexthop())) { w_neigh = w_lastintf->get_neighbour(nexthop()); } if (!w_neigh || last_neigh != w_neigh) { if (pim->should_log(EXTRADEBUG)) { base_stream &os = pim->log().xprintf("Neighbour for %{addr} " "matched ", target()); if (w_neigh) os.xprintf("%{Addr}", w_neigh->localaddr()); else if (self) os.write("Self"); else os.write("(None)"); os.newl(); } callback(); return true; } return false; } pim_interface *pim_neighbour_watcher_base::tentative_interface() const { return w_lastintf; } void pim_neighbour_watcher_base::entry_changed() { pim_interface *lastintf = w_lastintf; if (pim->should_log(MESSAGE_SIG)) { base_stream &os = pim->log().xprintf("Neighbour for %{addr}", target()); if (intf()) os.xprintf(" using interface %s", intf()->name()); else os.write("has no path/interface"); os.newl(); } w_lastintf = pim->get_interface(intf()); if (!recheck_neighbour() && lastintf != w_lastintf) callback(); } pim_neighbour::pim_neighbour(pim_interface *i, const inet6_addr &t) : n_intf(i), n_addr(t), n_holdtimer("pim neighbour timer", i, std::mem_fun(&pim_interface::neighbour_timed_out), this), n_jp_timer("pim join/prune timer", this, std::mem_fun(&pim_neighbour::handle_jp_timer), i->conf()->joinprune_interval(), true), n_flags(0), npaths(0) { n_present = true; n_propagation_delay = 0; n_override_interval = 0; n_tracking_support = false; } void pim_neighbour::shutdown() { n_holdtimer.stop(); n_jp_timer.stop(); } void pim_neighbour::set_present(bool b) { n_present = b; } bool pim_neighbour::compare_genid(uint32_t genid) const { if (!(n_flags & f_has_genid)) return true; return n_genid == genid; } void pim_neighbour::set_holdtime(uint32_t hold) { n_holdtimer.start_or_update(hold, false); } void pim_neighbour::set_dr_priority(uint32_t prio) { n_flags |= f_has_dr_priority; n_dr_priority = prio; } void pim_neighbour::set_genid(uint32_t gid) { n_flags |= f_has_genid; n_genid = gid; } void pim_neighbour::set_lan_delay(uint16_t progdelay, uint16_t overrinter, bool trackbit) { n_flags |= f_has_lan_delay; n_propagation_delay = progdelay; n_override_interval = overrinter; n_tracking_support = trackbit; } void pim_neighbour::update_from_hello(pim_encoded_unicast_address *addresses, int address_count, pim_encoded_unicast_address *oldaddr, int oldaddr_count, int holdtime) { int i; for (i = 0; i < address_count; i++) { n_secaddrs.insert(addresses[i].addr); } for (i = 0; i < oldaddr_count; i++) { n_secaddrs.insert(oldaddr[i].addr); } set_holdtime(holdtime * 1000); } bool pim_neighbour::has_address(const in6_addr &addr) const { if (n_addr == addr) return true; for (std::set::const_iterator i = n_secaddrs.begin(); i != n_secaddrs.end(); ++i) { if (*i == addr) return true; } return false; } pim_neighbour::upstream_path *pim_neighbour::add_path(pim_source_state_base *state, const inet6_addr &target, bool wc, bool rpt) { if (intf()->should_log(MESSAGE_SIG)) { log().xprintf("add path for %{Addr} with target %{Addr}%s%s\n", state->owner()->id(), target, wc ? " WC" : "", rpt ? " RPT" : ""); } upstream_path *path = new upstream_path(this, state, target, wc, rpt); if (path) { group_state &gst = n_gstates[state->owner()->id()]; gst.joins.push_back(path); npaths++; if (npaths == 1) { n_jp_timer.start(); } } return path; } void pim_neighbour::remove_path(upstream_path *path) { if (intf()->should_log(MESSAGE_SIG)) { log().xprintf("remove path %{Addr}%s%s\n", path->target(), path->wc() ? " WC" : "", path->rpt() ? " RPT" : ""); } std::map::iterator k = n_gstates.find(path->state()->owner()->id()); if (k == n_gstates.end()) return; upstream_jp_state::iterator i = std::find(k->second.joins.begin(), k->second.joins.end(), path); if (i == k->second.joins.end()) { i = std::find(k->second.prunes.begin(), k->second.prunes.end(), path); if (i == k->second.prunes.end()) return; k->second.prunes.erase(i); } else { k->second.joins.erase(i); } npaths--; if (npaths == 0) n_jp_timer.stop(); if (k->second.joins.empty() && k->second.prunes.empty()) { n_gstates.erase(k); } delete path; } static bool add_source_to_jp_message(int mtu, uint32_t &len, pim_encoded_source_address * &aptr, const in6_addr &addr, bool wc, bool rpt) { if ((int)(len + sizeof(pim_encoded_source_address)) > mtu) return false; aptr->construct(addr, wc, rpt); aptr = aptr->next(); len += aptr->length(); return true; } static bool build_block(int mtu, uint32_t &len, int &count, pim_encoded_source_address * &aptr, std::list::const_iterator &i, const std::list::const_iterator &e) { while (i != e) { if ((*i)->is_active()) { if (!(*i)->may_be_overridden()) { if (!add_source_to_jp_message(mtu, len, aptr, (*i)->target(), (*i)->wc(), (*i)->rpt())) return false; count++; } else if ((*i)->neigh()->intf()->should_log(INTERNAL_FLOW)) { pim_source_state_base *st = (*i)->state(); (*i)->neigh()->log().xprintf( "Join/Prune for (%{addr}, %{Addr}%s) was suppressed.\n", st->addr(), st->owner()->id(), st->is_rpt() && !st->is_wildcard() ? ", RPT" : ""); } } ++i; } return true; } base_stream &pim_neighbour::log() const { return pim->log().xprintf("Neighbour(%s, %{addr}) ", intf()->owner()->name(), n_addr.addr); } void pim_neighbour::handle_jp_timer() { pim_joinprune_message *msg; pim_joinprune_group *grp; uint32_t ngrps, len; msg = g_mrd->opktb->header(); grp = msg->groups(); ngrps = 0; len = sizeof(pim_joinprune_message) + sizeof(pim_joinprune_group); uint32_t holdtime = n_intf->conf()->joinprune_holdtime() / 1000; pim_encoded_source_address *addr = grp->addrs(); // int mtu = intf()->owner()->mtu(); int mtu = 1280; for (upstream_state::const_iterator i = n_gstates.begin(); i != n_gstates.end(); i++) { if (!i->second.joins.empty() || !i->second.prunes.empty()) { int sjcount = 0; int spcount = 0; std::list::const_iterator a = i->second.joins.begin(); std::list::const_iterator b = i->second.joins.end(); while (!build_block(mtu, len, sjcount, addr, a, b)) { grp->construct(i->first, sjcount, 0); msg->construct(n_addr, ngrps + (sjcount ? 1 : 0), holdtime); n_intf->send_join_prune(msg); msg = g_mrd->opktb->header(); grp = msg->groups(); addr = grp->addrs(); sjcount = 0; ngrps = 0; len = sizeof(pim_joinprune_message) + sizeof(pim_joinprune_group); } a = i->second.prunes.begin(); b = i->second.prunes.end(); while (!build_block(mtu, len, spcount, addr, a, b)) { grp->construct(i->first, sjcount, spcount); msg->construct(n_addr, ngrps + ((sjcount || spcount) ? 1 : 0), holdtime); n_intf->send_join_prune(msg); msg = g_mrd->opktb->header(); grp = msg->groups(); addr = grp->addrs(); sjcount = 0; spcount = 0; ngrps = 0; len = sizeof(pim_joinprune_message) + sizeof(pim_joinprune_group); } if (sjcount || spcount) { grp->construct(i->first, sjcount, spcount); len += sizeof(pim_joinprune_group); grp = grp->next(); addr = grp->addrs(); ngrps++; } } } if (ngrps) { msg->construct(n_addr, ngrps, holdtime); n_intf->send_join_prune(msg); } } bool pim_neighbour::move_to_joins(upstream_path *path) { upstream_state::iterator k = n_gstates.find(path->state()->owner()->id()); if (k == n_gstates.end()) return false; upstream_jp_state::iterator i = std::find(k->second.prunes.begin(), k->second.prunes.end(), path); if (i == k->second.prunes.end()) { return false; } k->second.prunes.erase(i); k->second.joins.push_back(path); return true; } bool pim_neighbour::move_to_prunes(upstream_path *path) { upstream_state::iterator k = n_gstates.find(path->state()->owner()->id()); if (k == n_gstates.end()) return false; upstream_jp_state::iterator i = std::find(k->second.joins.begin(), k->second.joins.end(), path); if (i == k->second.joins.end()) { return false; } k->second.joins.erase(i); k->second.prunes.push_back(path); return true; } void pim_neighbour::output_info(base_stream &ctx, bool extended) const { ctx.write(localaddr()).write(", "); if (n_holdtimer.is_running()) ctx.write(n_holdtimer.time_left_d()); else ctx.write("n/a"); ctx.newl(); ctx.inc_level(); if (has_dr_priority()) ctx.xprintf("DR-Priority: %u\n", dr_priority()); if (has_lan_delay()) { ctx.xprintf("LAN Propagation Delay: %ums Override Interval %ums\n", propagation_delay(), override_interval()); } if (!n_secaddrs.empty()) { ctx.writeline("Secundary-Addresses:"); ctx.inc_level(); for (std::set::const_iterator k = n_secaddrs.begin(); k != n_secaddrs.end(); k++) ctx.writeline(*k); ctx.dec_level(); } if (extended) { ctx.writeline("Upstream J/P state:"); ctx.inc_level(); for (upstream_state::const_iterator i = n_gstates.begin(); i != n_gstates.end(); i++) { ctx.writeline(i->first); ctx.inc_level(); if (!i->second.joins.empty()) { ctx.writeline("Joins"); ctx.inc_level(); for (upstream_jp_state::const_iterator j = i->second.joins.begin(); j != i->second.joins.end(); j++) { (*j)->output_info(ctx); } ctx.dec_level(); } if (!i->second.prunes.empty()) { ctx.writeline("Prunes"); ctx.inc_level(); for (upstream_jp_state::const_iterator j = i->second.prunes.begin(); j != i->second.prunes.end(); j++) { (*j)->output_info(ctx); } ctx.dec_level(); } ctx.dec_level(); } ctx.dec_level(); } ctx.dec_level(); } pim_neighbour::upstream_path::upstream_path(pim_neighbour *neigh, pim_source_state_base *state, const inet6_addr &targ, bool wc, bool rpt) : p_neigh(neigh), p_state(state), p_target(targ), p_wc(wc), p_rpt(rpt) { p_isjoin = true; p_active = false; } void pim_neighbour::upstream_path::remove(bool sendinvsingle) { if (sendinvsingle) { p_isjoin = !p_isjoin; send_single(true); p_isjoin = !p_isjoin; } p_neigh->remove_path(this); } void pim_neighbour::upstream_path::join(bool permanent) { p_isjoin = true; if (permanent) { if (p_neigh->move_to_joins(this) || !p_active) { send_single(false); } } else { if (p_neigh->move_to_joins(this) && p_active) { send_single(false); } } p_active = permanent; /* reset supression */ p_last_seen = tval(); } void pim_neighbour::upstream_path::prune(bool permanent) { p_isjoin = false; if (permanent) { if (p_neigh->move_to_prunes(this) || !p_active) send_single(false); } else { if (p_neigh->move_to_prunes(this) && p_active) send_single(false); } p_active = permanent; /* reset supression */ p_last_seen = tval(); } void pim_neighbour::upstream_path::send_single(bool now) const { if (!p_neigh->n_present) return; pim_joinprune_message *msg = g_mrd->opktb->header(); uint32_t holdtime = now ? 0 : (p_neigh->intf()->conf()->joinprune_holdtime() / 1000); msg->construct(p_neigh->n_addr, 1, holdtime); pim_joinprune_group *grp = msg->groups(); grp->construct(p_state->owner()->id(), p_isjoin ? 1 : 0, p_isjoin ? 0 : 1); grp->addrs()->construct(p_target, p_wc, p_rpt); p_neigh->intf()->send_join_prune(msg); } void pim_neighbour::upstream_path::refresh_now() const { send_single(false); } void pim_neighbour::upstream_path::update_last_seen(uint32_t holdtime) { if (p_last_seen.secs() != 0 || p_last_seen.usecs() != 0) { uint32_t diff = (tval::now() - p_last_seen); /* still time to go and stored values give more * supression, don't update vals. */ if (diff < p_last_seen_holdtime && ((p_last_seen_holdtime - diff) > holdtime)) { return; } } p_last_seen_holdtime = holdtime; p_last_seen.update_to_now(); } bool pim_neighbour::upstream_path::may_be_overridden() const { if (p_last_seen.secs() == 0 && p_last_seen.usecs() == 0) return false; uint32_t t_joinsuppress = std::min(neigh()->intf()->suppressed_value(), p_last_seen_holdtime); /* diff holds the equivalent of Join Timer */ uint32_t diff = (tval::now() - p_last_seen); return diff < t_joinsuppress; } void pim_neighbour::upstream_path::output_info(base_stream &ctx) const { ctx.xprintf("Target %{Addr}%s%s Owner: (%{addr}, %{Addr}%s)\n", target(), wc() ? " WC" : "", rpt() ? " RPT" : "", state()->addr(), state()->owner()->id(), state()->is_rpt() && !state()->is_wildcard() ? ", RPT" : ""); } mrd6-0.9.6/src/pim/Module.options0000644000175000017500000000011110332456775015307 0ustar hugohugoboolean BSR default on description "Enable PIM Bootstrap Router support" mrd6-0.9.6/src/pim/pim_bsr.cpp0000644000175000017500000006673310746067643014633 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim_bsr.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef PIM_NO_BSR #include #include #include #include #include #include #include #include #include #include #include enum { pim_rp_set_method_static = 9100, }; static const method_info pim_rp_set_methods[] = { { "static", "Adds a static entry to the RP set", pim_rp_set_method_static, false, property_def::NEGATE }, { 0 } }; extern in6_addr pim_all_routers; static const int BSRMinimumTimeBetweenBSMs = 10; static const int BSRInitialCRPAdvCount = 3; static const int BSRInitialCRPAdvPeriod = 3000; pim_bsr::pim_bsr(pim_router *r) : m_bsr_timer("bootstrap timer", this, std::mem_fun(&pim_bsr::handle_bsr_timeout)), m_sz_timer("sz timer", this, std::mem_fun(&pim_bsr::handle_sz_timeout)), m_rp_adv_timer("rp adv timer", this, std::mem_fun(&pim_bsr::handle_rp_adv_timer)), m_rp_set(r) { m_p_enable_bootstrap = r->instantiate_property_b("bootstrap", true); m_p_bsr_candidate = r->instantiate_property_b("bsr-candidate", false); m_p_bsr_priority = r->instantiate_property_u("bsr-priority", 128); m_p_bsr_timeout = r->instantiate_property_u("bsr-timeout", 2 * 60000 + 10000); m_p_bsr_period = r->instantiate_property_u("bsr-period", 60000); m_p_sz_timeout = r->instantiate_property_u("sz-timeout", 10 * (2 * 60000 + 10000)); m_p_rp_candidate = r->instantiate_property_b("rp-candidate", false); m_p_rp_cand_prio = r->instantiate_property_u("rp-cand-priority", 192); m_p_rp_cand_adv_period = r->instantiate_property_u("rp-cand-adv-period", 60000); m_p_rp_cand_holdtime = r->instantiate_property_u("rp-cand-holdtime", 100); m_p_hashmask = r->instantiate_property_u("hashmask", 126); m_rp_set.set_hashmask(126); m_bsr_state = BSRPending; m_bsr_preferred_priority = 128; m_nc_bsr_state = NCNoInfo; m_last_sent_bsm = 0; m_rp_adv_count = 0; } bool pim_bsr::check_startup() { if (!m_rp_set.check_startup()) return false; /* pim-router is registering one property */ if (pim->m_properties.size() < 12) return false; return pim->add_child(&m_rp_set) != 0; } void pim_bsr::leaving() { send_leave_rp_candidate(); send_leave_bootstrap(); } void pim_bsr::shutdown() { m_rp_set.clear(); } base_stream &pim_bsr::log() const { return pim->log().write("BSR, "); } void pim_bsr::acquired_primary_address() { if (m_p_bsr_candidate->get_bool()) m_bsr_timer.start(bsr_rand_override(), false); } void pim_bsr::found_new_neighbour(pim_neighbour *neigh) { sockaddr_in6 addr; memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_addr = neigh->localaddr(); addr.sin6_scope_id = neigh->intf()->owner()->index(); send_bootstrap_message(&addr); } in6_addr pim_bsr::rp_from_rpset(const inet6_addr &grpid) const { return m_rp_set.rp_for(grpid); } static inline const char *_bsr_state_name(pim_bsr::candidate_bsr_state state) { switch (state) { case pim_bsr::BSRPending: return "Pending"; case pim_bsr::BSRCandidate: return "Candidate"; case pim_bsr::BSRElected: return "Elected"; default: return "Unknown"; } } static inline const char *_no_cand_bsr_state_name(int name) { switch (name) { case pim_bsr::NCNoInfo: return "NoInfo"; case pim_bsr::NCAcceptAny: return "AcceptAny"; case pim_bsr::NCAcceptPreferred: return "AcceptPreferred"; default: return "Unknown"; } } void pim_bsr::output_info(base_stream &ctx) const { ctx.xprintf("Bootstrapping: %s", m_p_enable_bootstrap->get_bool() ? "Enabled" : "Disabled"); if (m_p_enable_bootstrap->get_bool()) { if (m_p_bsr_candidate->get_bool()) ctx.write(", BSR-Candidate"); if (m_p_rp_candidate->get_bool()) ctx.write(", RP-Candidate"); } ctx.newl(); if (m_p_enable_bootstrap->get_bool()) { if (m_p_bsr_candidate->get_bool()) { ctx.xprintf("BSR-Priority: %u\n", m_p_bsr_priority->get_unsigned()); } if (m_p_rp_candidate->get_bool()) { ctx.xprintf("RP-Cand-Priority: %u\n", m_p_rp_cand_prio->get_unsigned()); } } bool printpref = true; if (m_p_bsr_candidate->get_bool()) { ctx.xprintf("BSR State: %s", _bsr_state_name(m_bsr_state)); if (!m_bsr_timer.is_running()) ctx.write(" (Not running)"); else if (m_bsr_state != BSRElected) ctx.xprintf(" (for %{duration})", m_bsr_timer.time_left_d()); ctx.newl(); printpref = (m_bsr_state != BSRElected); } else { ctx.xprintf("BSR State: %s\n", _no_cand_bsr_state_name(m_nc_bsr_state)); } if (printpref) { ctx.write("Preferred BSR: "); if (m_bsr_preferred.is_any()) ctx.write("None"); else ctx.write(m_bsr_preferred); ctx.newl(); } m_rp_set.output_info(ctx, std::vector()); } void pim_bsr::handle_bootstrap_message(pim_interface *pif, const sockaddr_in6 *from, const sockaddr_in6 *dst, pim_bootstrap_message *msg, uint16_t len) { if (pim->should_log(MESSAGE_CONTENT)) { base_stream &os = log(); os.inc_level(); _debug_pim_dump(os, *msg, len); os.dec_level(); } if (!m_p_enable_bootstrap->get_bool()) { /* Bootstrapping disabled */ return; } if (!IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr) && (!pif->owner()->in_same_subnet(from->sin6_addr) || !pif->get_neighbour(from->sin6_addr))) { /* Silent drop */ return; } if (dst->sin6_addr == pim_all_routers) { if (!msg->no_forward()) { /* * if (BSM.src_ip_address != RPF_neighbor(BSM.BSR_ip_address)) * silent drop */ pim_neighbour *neigh = pim->get_rpf_neighbour(msg->bsr_address.addr); if (!neigh || !neigh->has_address(from->sin6_addr)) { /* Silent drop */ return; } } else { /* XXX unimplemented */ /* } else if ((any previous BSM for this scope has been accepted) OR (more than BS_Period has elapsed since startup)) { #only accept no-forward BSM if quick refresh on startup drop the Bootstrap message silently } */ } } else if (g_mrd->has_address(dst->sin6_addr)) { /* XXX check for last reception, to see if * this is a quick refresh on startup */ } else { /* Silent drop */ return; } bool ispref = is_bsr_preferred(msg); if (m_p_bsr_candidate->get_bool()) { if (ispref) { /* -> C-BSR state */ switch_bsr_state(BSRCandidate); /* Forward BSM; Store RP-Set; Set Bootstrap Timer to BS_Timeout */ accept_preferred_bsr(&msg->bsr_address.addr, msg->bsr_priority, msg, len); } else { if (m_bsr_state == BSRCandidate && m_bsr_preferred == inet6_addr(msg->bsr_address.addr)) { to_pending_bsr(); } else if (m_bsr_state == BSRElected) { if (!g_mrd->has_address(msg->bsr_address.addr)) im_the_elected_bsr(true); } } } else { if (pim->should_log(INTERNAL_FLOW)) log().xprintf("BSM is%s preferred.\n", ispref ? "" : " not"); if (ispref || (m_nc_bsr_state == NCNoInfo || m_nc_bsr_state == NCAcceptAny)) { /* -> AcceptPreferred state */ change_nc_state(NCAcceptPreferred); /* Forward BSM; Store RP-Set; Set Bootstrap Timer to BS_Timeout */ accept_preferred_bsr(&msg->bsr_address.addr, msg->bsr_priority, msg, len); /* Set SZT to SZ_Timeout */ refresh_sz_timer(); } } } void pim_bsr::change_nc_state(no_cand_bsr_state newstate) { if (m_nc_bsr_state == newstate) return; if (pim->should_log(EXTRADEBUG)) log().xprintf("State changed %s -> %s\n", _no_cand_bsr_state_name(m_nc_bsr_state), _no_cand_bsr_state_name(newstate)); m_nc_bsr_state = newstate; } void pim_bsr::handle_candidate_rp_adv(pim_interface *intf, const sockaddr_in6 *from, pim_candidate_rp_adv_message *msg, uint16_t len) { if (!is_bsr()) return; std::list grps; pim_encoded_group_address *grp = msg->grps(); for (uint8_t i = 0; i < msg->prefixcount; i++, grp++) grps.push_back(inet6_addr(grp->addr, grp->masklen)); m_rp_set.update_entries(msg->rp_addr.addr, msg->priority, ntoh(msg->holdtime), grps); } void pim_bsr::broadcast_rp_set_changed(pim_rp_set *) const { mrd::group_list::const_iterator j = g_mrd->group_table().begin(); for(; j != g_mrd->group_table().end(); ++j) { group_node *node = j->second->node_owned_by(pim); if (node) { ((pim_group_node *)node)->rp_set_changed(); } } } void pim_bsr::handle_bsr_timeout() { if (m_p_bsr_candidate->get_bool()) { switch (m_bsr_state) { case BSRCandidate: switch_bsr_state(BSRPending); m_bsr_timer.start(bsr_rand_override(), false); break; case BSRPending: case BSRElected: im_the_elected_bsr(true); break; } } else { /* -> AcceptAny state */ change_nc_state(NCAcceptAny); /* XXX Refresh RP-Set */ /* Remove BSR State */ reset_preferred_bsr(); } } void pim_bsr::handle_sz_timeout() { if (m_nc_bsr_state == NCAcceptAny) { sz_expired(); } } bool pim_bsr::is_bsr_preferred(const pim_bootstrap_message *msg) const { return is_bsr_preferred(msg->bsr_address.addr, msg->bsr_priority); } bool pim_bsr::is_bsr_preferred(const in6_addr &from, int prio) const { /* * ``A Bootstrap message is also preferred if it is from the * current BSR with a lower weight than the previous BSM it sent, * provided that if the router is a Candidate BSR the current BSR * still has a weight higher or equal than the router itself. In * this case, the "Current Bootstrap Router's BSR Priority" state * must be updated. (For lower weight, see Non-preferred BSM from * Elected BSR case.)'' * * ``The weight of a BSR is defined to be the concatenation in * fixed-precision unsigned arithmetic of the BSR Priority field from * the Bootstrap message and the IP address of the BSR from the * Bootstrap message (with the BSR Priority taking the most- * significant bits and the IP address taking the least significant * bits).'' * * We'll just check the priority first. */ if (!m_p_bsr_candidate->get_bool()) { if (prio == m_bsr_preferred_priority) { return from == m_bsr_preferred || from < m_bsr_preferred; } return prio > m_bsr_preferred_priority; } if (prio < m_bsr_preferred_priority) { if (prio >= (int)m_p_bsr_priority->get_unsigned()) return true; } else if (prio == m_bsr_preferred_priority) { switch (m_bsr_state) { case BSRPending: case BSRElected: return from < pim->my_address(); case BSRCandidate: return from < m_bsr_preferred || from == m_bsr_preferred; } } return prio > m_bsr_preferred_priority; } void pim_bsr::to_pending_bsr() { switch_bsr_state(BSRPending); reset_preferred_bsr(); m_bsr_timer.start_or_update(bsr_rand_override(), false); } void pim_bsr::reset_preferred_bsr() { m_bsr_preferred = in6addr_any; m_bsr_preferred_priority = m_p_bsr_priority->get_unsigned(); has_new_bsr(false); } void pim_bsr::im_the_elected_bsr(bool send) { candidate_bsr_state prevstate = m_bsr_state; if (m_bsr_state != BSRElected) { reset_preferred_bsr(); switch_bsr_state(BSRElected); has_new_bsr(true); } if (prevstate != BSRElected || send) send_bootstrap_message(0); m_bsr_timer.start_or_update(m_p_bsr_period->get_unsigned(), false); } void pim_bsr::send_bootstrap_message(sockaddr_in6 *addr) const { if (!addr) { /* going to send all */ time_t now = time(0); if ((now - m_last_sent_bsm) < BSRMinimumTimeBetweenBSMs) return; } pim_bootstrap_message *msg = g_mrd->opktb->header(); uint16_t fragtag = mrd::get_randu32() & 0xffff; if (m_bsr_state == BSRElected) msg->construct(fragtag, m_rp_set.get_hashmask(), m_p_bsr_priority->get_unsigned(), pim->my_address()); else if (m_bsr_state == BSRCandidate) msg->construct(fragtag, m_rp_set.get_hashmask(), m_bsr_preferred_priority, m_bsr_preferred); else return; uint16_t len = sizeof(pim_bootstrap_message); m_rp_set.build_message(msg, len); if (!addr) pim->send_all_neighbours(msg, len); else pim->sendmsg(0, addr, msg, len); m_last_sent_bsm = time(0); } void pim_bsr::send_leave_bootstrap() const { if (m_bsr_state == BSRElected) { pim_bootstrap_message *msg = g_mrd->opktb->header(); msg->construct(mrd::get_randu32() & 0xffff, m_rp_set.get_hashmask(), 0, pim->my_address()); pim->send_all(msg, sizeof(pim_bootstrap_message)); } } void pim_bsr::send_leave_rp_candidate() const { if (m_p_rp_candidate->get_bool() && m_bsr_state != BSRElected && !m_bsr_preferred.is_any()) { pim_candidate_rp_adv_message *msg = g_mrd->opktb->header(); msg->construct(0, m_p_rp_cand_prio->get_unsigned(), 0, pim->my_address()); sockaddr_in6 addr = m_bsr_preferred.as_sockaddr(); pim->sendmsg(0, &addr, msg, sizeof(pim_candidate_rp_adv_message)); } } void pim_bsr::enable_rp_adv(const inet6_addr &grp, bool enable) { if (m_bsr_state == BSRElected) { std::list entries; entries.push_back(grp); m_rp_set.update_entries(pim->my_address(), m_p_rp_cand_prio->get_unsigned(), enable ? m_p_rp_cand_holdtime->get_unsigned() : 0, entries); } else { /* XXX if was enabled, send_leave_rp_candidate? */ } } uint32_t pim_bsr::bsr_rand_override() const { #if 1 return (uint32_t)((5. + 2 * ::log((double)(1 + m_bsr_preferred_priority - m_p_bsr_priority->get_unsigned())) / ::log(2)) * 1000.); #else return mrd::get_randu32() % 5000; #endif } void pim_bsr::accept_preferred_bsr(const in6_addr *from, int prio, pim_bootstrap_message *msg, uint16_t len) { if (!(m_bsr_preferred == inet6_addr(*from))) { bool was = m_bsr_preferred.is_any(); m_bsr_preferred = *from; if (was && !m_bsr_preferred.is_any() && pim->should_log(NORMAL)) { log().xprintf("Bootstrap Router is at %{Addr}\n", m_bsr_preferred); } has_new_bsr(false); } m_rp_set.store_from_message(*from, msg, len); m_bsr_preferred_priority = prio; pim->send_all_neighbours(msg, len); m_bsr_timer.start_or_update(m_p_bsr_timeout->get_unsigned(), false); } void pim_bsr::refresh_sz_timer() { m_sz_timer.start_or_update(m_p_sz_timeout->get_unsigned(), false); } void pim_bsr::sz_expired() { m_bsr_timer.stop(); reset_preferred_bsr(); // clear state } void pim_bsr::has_new_bsr(bool local) { m_rp_adv_timer.stop(); if (!m_p_rp_candidate->get_bool()) return; if (m_bsr_state == BSRElected || !m_bsr_preferred.is_any()) { m_rp_adv_count = BSRInitialCRPAdvCount; m_rp_adv_timer.start(mrd::get_randu32() % BSRInitialCRPAdvPeriod, true); } } void pim_bsr::handle_rp_adv_timer() { std::list entries = g_mrd->configured_group_set("pim"); std::list::iterator i = entries.begin(); while (i != entries.end()) { std::list::iterator j = i; ++i; groupconf *gc = g_mrd->get_group_configuration(*j); if (gc) { pim_groupconf_node *pimgc = (pim_groupconf_node *)gc->get_child("pim"); if (pimgc && pimgc->get_property_bool("rp_adv")) { continue; } } entries.erase(j); } if (m_rp_adv_count > 0) { m_rp_adv_count --; if (m_rp_adv_count == 0) m_rp_adv_timer.update(m_p_rp_cand_adv_period->get_unsigned(), true); } if (entries.empty()) return; if (m_bsr_state == BSRElected) { m_rp_set.update_entries(pim->my_address(), m_p_rp_cand_prio->get_unsigned(), m_p_rp_cand_holdtime->get_unsigned(), entries); } else { pim_candidate_rp_adv_message *msg = g_mrd->opktb->header(); msg->construct(entries.size(), m_p_rp_cand_prio->get_unsigned(), m_p_rp_cand_holdtime->get_unsigned(), pim->my_address()); pim_encoded_group_address *grp = msg->grps(); for (std::list::iterator i = entries.begin(); i != entries.end(); ++i, grp++) { grp->construct(*i); } sockaddr_in6 addr = m_bsr_preferred.as_sockaddr(); pim->sendmsg(0, &addr, msg, msg->length()); } } void pim_bsr::switch_bsr_state(candidate_bsr_state state) { if (m_bsr_state == state) return; if (pim->should_log(NORMAL)) { log().xprintf("State changed %s -> %s\n", _bsr_state_name(m_bsr_state), _bsr_state_name(state)); } m_bsr_state = state; } pim_rp_set::pim_rp_set(pim_router *parent) : node(parent, "rp_set") { } bool pim_rp_set::check_startup() { if (!node::check_startup()) return false; import_methods(pim_rp_set_methods); return true; } const char *pim_rp_set::description() const { return "RP-Set"; } void pim_rp_set::set_hashmask(uint16_t mask) { m_hashmask = mask; } bool pim_rp_set::call_method(int id, base_stream &os, const std::vector &args) { if (id == pim_rp_set_method_static) { if (args.size() < 2) return false; inet6_addr grpaddr, rpaddr; int prio = 128; if (!grpaddr.set(args[0].c_str()) || !rpaddr.set(args[1].c_str())) return false; if (args.size() > 2) { char *end; prio = strtol(args[2].c_str(), &end, 10); if (*end || prio < 0 || prio > 0xff) return false; } return add_entry(grpaddr, rpaddr, prio, 100, true); } else { return node::call_method(id, os, args); } } bool pim_rp_set::add_entry(const inet6_addr &grp, const inet6_addr &rp, uint8_t prio, uint16_t holdtime, bool _static) { group_set *g = m_db.search(grp); if (!g) { g = new group_set; if (!g) return false; g->prefix = grp; m_db.insert(g); } return g->add_entry(this, rp, prio, holdtime, _static); } bool pim_rp_set::group_set::add_entry(pim_rp_set *rpset, const in6_addr &rpaddr, uint8_t prio, uint16_t holdtime, bool _static) { std::list::iterator i = find(rpaddr); if (i != entries.end()) { if (_static) return false; bool changed = false; entry *ent = *i; if (ent->prio != prio) { entries.erase(i); ent->prio = prio; insert_entry(ent); changed = true; } ent->update_holdtime(holdtime); return changed; } entry *ent = new entry(rpset); ent->owner = this; ent->prio = prio; ent->rpaddr = rpaddr; ent->update_holdtime(holdtime, !_static); insert_entry(ent); if (pim->should_log(DEBUG)) { pim->log().xprintf("RP-Set, added to %{Addr}, RP: %{addr} [prio: %i, holdtime: %i secs]\n", prefix, rpaddr, (int)prio, (int)holdtime); } return true; } bool pim_rp_set::remove_entry(const inet6_addr &grp, const inet6_addr &rp) { group_set *g = m_db.search(grp); if (g) { if (g->release_entry(grp, rp)) { if (g->entries.empty()) { m_db.remove(g); delete g; } return true; } } return false; } void pim_rp_set::update_entries(const inet6_addr &rpaddr, uint8_t prio, uint16_t holdtime, const std::list &grps) { int count = 0; for (std::list::const_iterator i = grps.begin(); i != grps.end(); ++i) { if (holdtime == 0) { if (remove_entry(*i, rpaddr)) count++; } else { if (add_entry(*i, rpaddr, prio, holdtime, false)) count++; } } if (count) { pim->bsr().send_bootstrap_message(0); pim->bsr().broadcast_rp_set_changed(this); } } static inline bool bsm_is_valid_and_has_groups(const in6_addr &from, const pim_bootstrap_message *msg, uint16_t len) { pim_bootstrap_group_def *grp = msg->grps(); bool has_groups = false; for (uint32_t i = sizeof(pim_bootstrap_message); i < len; i += grp->length(), grp = grp->next()) { if ((i + grp->length()) > len) { /* badly formed packet */ if (pim->should_log(MESSAGE_ERR)) pim->bsr().log().xprintf("Received badly formed BSR message " "from %{addr}, dropping.\n", from); return false; } if (grp->fragrp > 0) has_groups = true; } return has_groups; } void pim_rp_set::store_from_message(const in6_addr &from, pim_bootstrap_message *msg, uint16_t len) { /* * ``The router uses the group-to-RP mappings contained in a BSM to * update its local RP-Set. * * This action is skipped for an empty BSM. A BSM is empty if it * contains no group ranges, or if it only contains a single * group range where that group range has the Admin Scope Zone * bit set (a scoped BSM) and an RP count of zero.'' */ /* check if message is empty, and if lengths are OK */ if (!bsm_is_valid_and_has_groups(from, msg, len)) return; pim_bootstrap_group_def *grp = msg->grps(); m_hashmask = msg->hash_masklen; bool changed = false; for (uint32_t i = sizeof(pim_bootstrap_message); i < len; i += grp->length(), grp = grp->next()) { inet6_addr grpaddr(grp->grpaddr.addr, grp->grpaddr.masklen); group_set *g = m_db.search(grpaddr); pim_bootstrap_rp_record *rp = grp->rps(); for (int j = 0; j < grp->fragrp; j++, rp++) { uint16_t holdtime = ntoh(rp->holdtime); if (holdtime == 0) { if (g) { g->release_entry(grpaddr, rp->addr.addr); changed = true; } continue; } if (!g) { g = new group_set; if (!g) continue; g->prefix = grpaddr; m_db.insert(g); changed = true; } entry *ent = 0; std::list::iterator k = g->find(rp->addr.addr); if (k == g->entries.end()) { ent = new entry(this); if (!ent) continue; ent->owner = g; ent->prio = rp->priority; ent->rpaddr = rp->addr.addr; g->insert_entry(ent); changed = true; } else { ent = *k; } ent->update_holdtime(holdtime); } if (g) { std::list::iterator k = g->entries.begin(); while (k != g->entries.end()) { entry *ent = *k; ++k; pim_bootstrap_rp_record *rp = grp->rps(); for (uint8_t j = 0; j < grp->fragrp; j++, rp++) { if (rp->addr.addr == ent->rpaddr) { ent = 0; break; } } if (ent) { g->release_entry(grpaddr, ent->rpaddr); changed = true; } } if (g->entries.empty()) { m_db.remove(g); delete g; } } } if (changed) pim->bsr().broadcast_rp_set_changed(this); } void pim_rp_set::build_message(pim_bootstrap_message *msg, uint16_t &len) const { pim_bootstrap_group_def *grp = msg->grps(); for (db::const_iterator i = m_db.begin(); i != m_db.end(); ++i) { grp->grpaddr.construct(i->prefix); grp->rpcount = grp->fragrp = i->entries.size(); grp->resv = 0; pim_bootstrap_rp_record *rp = grp->rps(); for (std::list::const_iterator j = i->entries.begin(); j != i->entries.end(); ++j) { rp->addr.construct((*j)->rpaddr); rp->holdtime = hton((*j)->holdtime); rp->priority = (*j)->prio; rp->resv = 0; rp++; } len += grp->length(); grp = grp->next(); } } int pim_rp_set::count_entries() const { int count = 0; for (db::const_iterator i = m_db.begin(); i != m_db.end(); ++i) { count += i->entries.size(); } return count; } void pim_rp_set::clear() { db::iterator i; while ((i = m_db.begin()) != m_db.end()) { group_set *g = &(*i); for (std::list::iterator j = g->entries.begin(); j != g->entries.end(); ++j) { delete *j; } m_db.remove(g); delete g; } m_hashmask = pim->bsr().get_default_hashmask(); } void pim_rp_set::handle_entry_timeout(entry * &ent) { group_set *g = ent->owner; std::list::iterator i = g->find_entry(ent); if (i == ent->owner->entries.end()) return; g->entries.erase(i); delete ent; if (g->entries.empty()) { m_db.remove(g); delete g; } pim->bsr().broadcast_rp_set_changed(this); } bool pim_rp_set::output_info(base_stream &ctx, const std::vector &) const { ctx.writeline("RP-Set:"); ctx.inc_level(); for (db::const_iterator i = m_db.begin(); i != m_db.end(); ++i) { if (!i->entries.empty()) { ctx.write(i->prefix).writeline(":"); ctx.inc_level(); for (std::list::const_iterator j = i->entries.begin(); j != i->entries.end(); ++j) { entry *ent = (*j); ctx.xprintf("RP %{addr}", ent->rpaddr); if (ent->timer.is_running()) ctx.xprintf(" for %{duration}", ent->timer.time_left_d()); else ctx.write(", static,"); ctx.xprintf(" prio: %i holdtime %{duration}\n", (int)ent->prio, time_duration(ent->holdtime * 1000)); } ctx.dec_level(); } } if (m_db.empty()) ctx.writeline("(None)"); ctx.dec_level(); return true; } pim_rp_set::entry::entry(pim_rp_set *rpset) : timer("rp set entry", rpset, std::mem_fun(&pim_rp_set::handle_entry_timeout), this) { prio = 0; holdtime = 0; } void pim_rp_set::entry::update_holdtime(uint16_t ht, bool andtimer) { holdtime = ht; if (andtimer) timer.start_or_update(holdtime * 1000, false); } uint8_t pim_rp_set::group_set::greater_prio() const { if (entries.empty()) return 0xff; return (*entries.begin())->prio; } bool pim_rp_set::group_set::has_entry(entry *ent) const { return std::find(entries.begin(), entries.end(), ent) != entries.end(); } bool pim_rp_set::group_set::release_entry(const inet6_addr &grpaddr, const inet6_addr &rpaddr, bool verbose) { std::list::iterator j = find(rpaddr); if (j != entries.end()) { if (!(*j)->timer.is_running()) { /* static */ return false; } if (verbose && pim->should_log(pim->bsr().is_bsr() ? DEBUG : EXTRADEBUG)) { pim->log().xprintf("RP-Set %{Addr}, removed RP: %{Addr} [prio: %i]\n", grpaddr, rpaddr, (int)(*j)->prio); } delete *j; entries.erase(j); return true; } return false; } std::list::iterator pim_rp_set::group_set::find_entry(entry *ent) { return std::find(entries.begin(), entries.end(), ent); } std::list::iterator pim_rp_set::group_set::find(const in6_addr &rp) { for (std::list::iterator i = entries.begin(); i != entries.end(); ++i) { if ((*i)->rpaddr == rp) return i; } return entries.end(); } void pim_rp_set::group_set::insert_entry(entry *ent) { std::list::iterator i = entries.begin(); for (; i != entries.end(); ++i) { if ((*i)->prio > ent->prio) break; } entries.insert(i, ent); } static inline uint32_t _hash_ipv6(const in6_addr *addr) { return ((const uint32_t *)addr)[0] ^ ((const uint32_t *)addr)[1] ^ ((const uint32_t *)addr)[2] ^ ((const uint32_t *)addr)[3]; } static inline uint32_t _one_hash(uint32_t _g, const in6_addr *addr) { uint32_t hash = _hash_ipv6(addr); /* Value(G,M,C(i)) = * (1103515245 * ((1103515245 * (G&M)+12345) XOR C(i)) + 12345) * mod 2^31 */ return ((1103515245UL * ((1103515245UL * _g + 12345) ^ hash) + 12345) & 0x7fffffff); } inet6_addr pim_rp_set::rp_for(const inet6_addr &grp) const { group_set *g = m_db.longest_match(grp); while (g) { if (g->entries.empty()) { g = m_db.get_parent_node(g); continue; } entry *picked = *g->entries.begin(); std::list::iterator i = g->entries.begin(); ++i; in6_addr masked_group = grp.addr; if (m_hashmask < 128) { masked_group.s6_addr[m_hashmask / 8] &= 0xff << (8 - (m_hashmask % 8)); for (int i = (m_hashmask + 7) / 8; i < 16; i++) masked_group.s6_addr[i] = 0; } uint32_t _g = _hash_ipv6(&masked_group); uint32_t best = _one_hash(_g, &picked->rpaddr); for (; i != g->entries.end(); ++i) { entry *potential = *i; if (potential->prio != picked->prio) break; uint32_t hash = _one_hash(_g, &potential->rpaddr); if (hash > best || (hash == best && picked->rpaddr < potential->rpaddr)) { picked = potential; best = hash; } } return picked->rpaddr; } return in6addr_any; } #endif mrd6-0.9.6/src/pim/pim_interface.cpp0000644000175000017500000010054410746067643015772 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim_interface.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include extern in6_addr pim_all_routers; extern sockaddr_in6 pim_all_routers_addr; enum { pim_intf_method_flap = 1000, pim_intf_method_force_timeout }; static const method_info pim_intf_methods[] = { { "flap", 0, pim_intf_method_flap, false, 0 }, { "force-timeout", "Forces the timeout of a neighbor", pim_intf_method_force_timeout, false, 0 }, { 0 } }; enum { AllCount, HelloCount, JoinPruneCount, AssertCount, BootstrapCount, CandRPCount, RegisterCount, RegisterStopCount, MessageCount }; enum { RX = 0, TX, Bad }; static const char *stats_descriptions[] = { "All", "Hello", "Join/Prune", "Assert", "Bootstrap", "Cand-RP", "Register", "RegisterStop", }; pim_interface::pim_interface() : interface_node(pim), m_stats(this, MessageCount, stats_descriptions), hello_timer_id("pim hello timer", this, std::mem_fun(&pim_interface::send_hello)) { intf_state = NOT_READY; gen_id = mrd::get_randu32(); elected_dr = 0; m_landelay_enabled = true; } pim_interface::~pim_interface() { } bool pim_interface::check_startup() { if (!m_stats.setup()) return false; m_stats.disable_counter(AllCount, TX); m_stats.disable_counter(RegisterCount, TX); m_stats.disable_counter(RegisterStopCount, TX); if (!interface_node::check_startup()) return false; import_methods(pim_intf_methods); return true; } void pim_interface::attached(interface *intf) { interface_node::attached(intf); ((conf_node *)conf())->attach_watcher(this); char tmrname[128]; snprintf(tmrname, sizeof(tmrname), "pim hello timer (%s)", owner()->name()); hello_timer_id.name = tmrname; update_hello_interval(conf()->hello_interval()); check_lan_delay(); } void pim_interface::shutdown() { send_hellox(0); neighbours_def n = neighbours; neighbours.clear(); for (neighbours_def::const_iterator j = n.begin(); j != n.end(); ++j) { pim->lost_neighbour(*j); (*j)->shutdown(); delete *j; } ((conf_node *)conf())->dettach_watcher(this); owner()->dettach_node(this); } bool pim_interface::output_info(base_stream &ctx, bool extended) const { if (get_state() == NOT_READY) return false; ctx.writeline("PIM"); ctx.inc_level(); ctx.xprintf("DR Priority: %u\n", conf()->dr_priority()); ctx.xprintf("LAN Propagation Delay: %ums Override Interval: %ums\n", conf()->propagation_delay(), conf()->override_interval()); if (elected_dr) ctx.xprintf("DR: %{Addr}\n", elected_dr->localaddr()); else ctx.writeline("DR: self"); ctx.writeline("Neighbours:"); ctx.inc_level(); if (neighbours.empty()) { ctx.writeline("(None)"); } else { for (neighbours_def::const_iterator j = neighbours.begin(); j != neighbours.end(); j++) { (*j)->output_info(ctx, extended); } } ctx.dec_level(); ctx.dec_level(); return true; } bool pim_interface::output_info(base_stream &out, const std::vector &args) const { bool extended = !args.empty() && args[0] == "extended"; return output_info(out, extended); } void pim_interface::address_added_or_removed(bool added, const inet6_addr &addr) { if (added) { if (addr.is_linklocal()) { if (intf_state != NOT_READY) return; if (!pim->pim_sock.join_mc(owner(), pim_all_routers)) { if (should_log(WARNING)) { log().perror("Failed to join All-PIM-" "Routers multicast group"); } } if (!start_timers()) { if (should_log(WARNING)) { log().writeline("Failed to start required timers"); } } state was = intf_state; intf_state = owner()->globals().empty() ? LOCAL_READY : READY; if (should_log(DEBUG)) { if (was != intf_state) { if (intf_state == LOCAL_READY) { log().writeline("Has link-local address, changed to LOCAL_READY."); } else { log().writeline("Has global address, changed to READY."); } } } pim->interface_state_changed(this, NOT_READY); } else { if (intf_state == LOCAL_READY) { intf_state = READY; if (should_log(DEBUG)) log().writeline("Has global address, changed to READY"); pim->interface_state_changed(this, LOCAL_READY); } } } else { if (addr.is_linklocal()) { if (owner()->linklocals().empty()) { intf_state = NOT_READY; pim->pim_sock.leave_mc(owner(), pim_all_routers); if (should_log(DEBUG)) log().writeline("Lost link-local, changed to NOT_READY"); // stop hello timer pim->interface_state_changed(this, LOCAL_READY); } } else { if (owner()->globals().empty() && intf_state == READY) { intf_state = LOCAL_READY; if (should_log(DEBUG)) log().writeline("Lost global address, changed to LOCAL_READY"); pim->interface_state_changed(this, READY); } if (pim->my_address() == addr) { pim->check_my_address(true); } } } } bool pim_interface::send_local(sockaddr_in6 *dst, pim_message *msg, uint16_t len) const { sockaddr_in6 tmp = (*dst); tmp.sin6_scope_id = owner()->index(); return pim->sendmsg(owner()->localaddr(), &tmp, msg, len); } bool pim_interface::send_all_routers(pim_message *msg, uint16_t len) const { return send_local(&pim_all_routers_addr, msg, len); } bool pim_interface::send_join_prune(pim_joinprune_message *msg) const { if (send_all_routers(msg, msg->length())) { m_stats.counter(JoinPruneCount, TX)++; return true; } return false; } bool pim_interface::send_assert(pim_assert_message *msg) const { if (send_all_routers(msg, sizeof(pim_assert_message))) { m_stats.counter(AssertCount, TX)++; return true; } return false; } bool pim_interface::start_timers() { hello_timer_id.start(true); return true; } void pim_interface::data_available(const sockaddr_in6 *src, const sockaddr_in6 *dst) { pim_message *pimmsg = g_mrd->ipktb->header(); int len = g_mrd->ipktb->rlength; m_stats.counter(AllCount, RX)++; if (pimmsg->type() != pim_msg_register && should_log(MESSAGE_SIG)) { log().xprintf("%s message from %{addr} to %{addr} len %u\n", pimmsg->type_name(), src->sin6_addr, dst->sin6_addr, (uint32_t)len); } if (pimmsg->type() == pim_msg_register) len = sizeof(pim_register_message); if (!(pimmsg->has_valid_checksum(src->sin6_addr, dst->sin6_addr, len) || (pimmsg->type() == pim_msg_register && pimmsg->has_valid_checksum(src->sin6_addr, dst->sin6_addr, g_mrd->ipktb->rlength)))) { m_stats.counter(AllCount, Bad)++; if (should_log(MESSAGE_ERR)) { log().xprintf("Dropping message from %{addr} to %{addr}" " len %u: Bad Checksum\n", src->sin6_addr, dst->sin6_addr, (uint32_t)g_mrd->ipktb->rlength); } return; } if (dst->sin6_addr == pim_all_routers) { if ((int)src->sin6_scope_id != owner()->index()) { if (should_log(MESSAGE_ERR)) { log().xprintf("Dropping message from %{addr} to %{addr}" " len %u: Wrong interface\n", src->sin6_addr, dst->sin6_addr, (uint32_t)g_mrd->ipktb->rlength); } return; } switch (pimmsg->type()) { case pim_msg_hello: handle_hello(src, (pim_hello_message *)g_mrd->ipktb->pheader(), g_mrd->ipktb->rlength); break; case pim_msg_joinprune: handle_joinprune(src, (pim_joinprune_message *)g_mrd->ipktb->pheader(), g_mrd->ipktb->rlength); break; case pim_msg_assert: handle_assert(src, (pim_assert_message *)g_mrd->ipktb->pheader(), g_mrd->ipktb->rlength); break; case pim_msg_bootstrap: handle_bootstrap(src, dst, (pim_bootstrap_message *)g_mrd->ipktb->pheader(), g_mrd->ipktb->rlength); break; default: m_stats.counter(AllCount, Bad)++; } } else { switch (pimmsg->type()) { case pim_msg_register: handle_register(src, dst); break; case pim_msg_register_stop: handle_register_stop(src); break; case pim_msg_bootstrap: handle_bootstrap(src, dst, (pim_bootstrap_message *)g_mrd->ipktb->pheader(), g_mrd->ipktb->rlength); break; case pim_msg_candidate_rp_adv: handle_candidate_rp_adv(src, (pim_candidate_rp_adv_message *)g_mrd->ipktb->pheader(), g_mrd->ipktb->rlength); break; default: m_stats.counter(AllCount, Bad)++; } } } pim_neighbour *pim_interface::get_neighbour(const in6_addr &addr) const { for (neighbours_def::const_iterator i = neighbours.begin(); i != neighbours.end(); i++) { if ((*i)->has_address(addr)) return *i; } return 0; } pim_neighbour *pim_interface::allocate_neighbour(const in6_addr &addr) { pim_neighbour *neigh = new pim_neighbour(this, addr); if (neigh) neighbours.push_back(neigh); return neigh; } void pim_interface::handle_hello(const sockaddr_in6 *from, pim_hello_message *msg, uint16_t len) { m_stats.counter(HelloCount, RX)++; /* rejected by configuration */ if (!conf()->neigh_acl_accepts(from->sin6_addr)) return; uint16_t holdtime = 0; bool has_dr_priority = false; uint32_t dr_priority = 0; bool has_genid = false; uint32_t genid = mrd::get_randu32(); bool has_lan_delay = false; uint16_t propdelay = 0, overrinter = 0; bool trackbit = false; int address_count = 0; pim_encoded_unicast_address *addresses = 0; int old_address_count = 0; pim_encoded_unicast_address *old_addresses = 0; int slen = sizeof(pim_hello_message); pim_hello_option *opt = msg->options(); while (slen < len) { uint16_t optlen = ntoh(opt->length); switch (ntoh(opt->type)) { case pim_hello_option::holdtime: if (optlen == 2) holdtime = ntoh(opt->data16()[0]); break; case pim_hello_option::lan_prune_delay: if (optlen == 4) { has_lan_delay = true; propdelay = ntoh(opt->data16()[0]); overrinter = ntoh(opt->data16()[1]); trackbit = (propdelay & 0x8000) != 0; propdelay &= 0x7fff; } break; case pim_hello_option::dr_priority: if (optlen == 4) { has_dr_priority = true; dr_priority = ntoh(opt->data32()[0]); } break; case pim_hello_option::genid: if (optlen == 4) { has_genid = true; genid = ntoh(opt->data32()[0]); } break; case pim_hello_option::addrlist: if ((optlen % sizeof(pim_encoded_unicast_address)) == 0) { address_count = optlen / sizeof(pim_encoded_unicast_address); addresses = (pim_encoded_unicast_address *)opt->data(); } break; case pim_hello_option::cisco_old_addrlist: if ((optlen % sizeof(pim_encoded_unicast_address)) == 0) { old_address_count = optlen / sizeof(pim_encoded_unicast_address); old_addresses = (pim_encoded_unicast_address *)opt->data(); } break; } slen += sizeof(pim_hello_option) + optlen; opt = opt->next(); } pim_neighbour *neigh; if ((neigh = get_neighbour(from->sin6_addr))) { if (holdtime == 0) { neighbour_timed_out(neigh); return; } if (!neigh->compare_genid(genid)) { if (should_log(NORMAL)) neigh->log().writeline("Had different GenID, forcing timeout."); remove_neighbour(neigh, false); neigh = 0; } } bool is_new = false; if (!neigh) { if (!(neigh = allocate_neighbour(from->sin6_addr))) { if (should_log(DEBUG)) log().writeline("Failed to allocate neighbor state."); return; } is_new = true; } if (!conf()->support_old_cisco_addrlist()) { old_addresses = 0; old_address_count = 0; } neigh->update_from_hello(addresses, address_count, old_addresses, old_address_count, holdtime); if (has_dr_priority) neigh->set_dr_priority(dr_priority); if (has_genid) neigh->set_genid(genid); if (has_lan_delay) neigh->set_lan_delay(propdelay, overrinter, trackbit); if (is_new) found_new_neighbour(neigh); check_lan_delay(); elect_subnet_dr(); } void pim_interface::handle_joinprune(const sockaddr_in6 *_from, pim_joinprune_message *msg, uint16_t len) { m_stats.counter(JoinPruneCount, RX)++; /* Just to be sure */ if (g_mrd->has_address(_from->sin6_addr)) return; if (should_log(MESSAGE_CONTENT)) { base_stream &os = log(); os.inc_level(); _debug_pim_dump(os, *msg); os.dec_level(); } inet6_addr upneigh(msg->upstream_neigh.addr); if (!g_mrd->has_address(upneigh)) { /* Lets monitor the Join/Prunes in the link * to react properly */ handle_external_joinprune(_from, msg, len); return; } pim_neighbour *neigh = pim->get_neighbour(_from->sin6_addr); if (!neigh) { if (should_log(DEBUG)) { log().xprintf("Dropping Join/Prune from %{addr}, not " "a known neighbor.\n", _from->sin6_addr); } m_stats.counter(JoinPruneCount, Bad)++; return; } pim_group_node *node; pim_joinprune_group *grp = msg->groups(); for (uint8_t i = 0; i < msg->ngroups; i++, grp = grp->next()) { inet6_addr groupaddr(grp->maddr.addr, grp->maddr.masklen); groupconf *entry = g_mrd->match_group_configuration(groupaddr); pim_groupconf_node *info = entry ? (pim_groupconf_node *)entry->get_child("pim") : 0; for (pim_jp_g_iterator i = grp->join_begin(); i != grp->join_end(); ++i) { if (i->wc() && i->rpt()) { bool accept_rp = true; if (info) accept_rp = info->get_property_address ("accept_rp").matches(i->address()); if (accept_rp) { address_set prunes; grp->pruned_addrs(prunes); handle_join_wc_rpt(groupaddr, i->address(), prunes, msg->holdtime(), i->rpt()); } else { /// 3.2.2.1.1 } } else if (!i->wc() && !i->rpt()) { handle_join_source(groupaddr, i->address(), msg->holdtime(), i->rpt()); } else { handle_join(groupaddr, i->address(), msg->holdtime(), i->rpt()); } } for (pim_jp_g_iterator i = grp->prune_begin(); i != grp->prune_end(); ++i) { /* we update the node reference on each cycle as * it may have been deleted due to a prune */ node = pim->get_group(groupaddr); if (node == NULL) continue; pim_source_state_base *target = NULL; uint32_t holdtime = 0; if (!i->wc()) { target = node->get_state(i->address(), i->rpt()); holdtime = msg->holdtime(); } else if (i->wc() && i->rpt()) { if (node->rpaddr() == i->address()) target = node->wildcard(); } if (target) target->set_oif(owner(), holdtime, false); } } } void pim_interface::handle_external_joinprune(const sockaddr_in6 *_from, pim_joinprune_message *msg, uint16_t len) { pim_group_node *node; pim_neighbour *upneigh = pim->get_neighbour(msg->upstream_neigh.addr); if (!upneigh) return; pim_joinprune_group *grp = msg->groups(); for (uint8_t i = 0; i < msg->ngroups; i++, grp = grp->next()) { inet6_addr groupaddr(grp->maddr.addr, grp->maddr.masklen); node = pim->get_group(groupaddr); if (!node) continue; for (pim_jp_g_iterator i = grp->join_begin(); i != grp->join_end(); ++i) { if (i->wc() || i->rpt()) continue; pim_group_source_state *state = node->get_state(i->address()); if (state == NULL) continue; if (state->upstream_neighbour() != upneigh) continue; pim_neighbour::upstream_path *path = state->upstream_path(); if (path == NULL) continue; /* A (S,G) that is being currenty joined */ /* If (S,G) is joined and we see a Join, supress our * next one, if sent in the following `override` milisecs */ if (path->is_joined()) path->update_last_seen(msg->holdtime()); } for (pim_jp_g_iterator i = grp->prune_begin(); i != grp->prune_end(); ++i) { if (i->wc() || i->rpt()) continue; pim_group_source_state *state = node->get_state(i->address()); if (state == NULL) continue; if (state->upstream_neighbour() == upneigh && state->upstream_path()) { /* A (S,G) that is being currenty pruned */ /* If (S,G) is joined and we see a Prune, * trigger a Join message upstream */ if (state->upstream_path()->is_joined()) state->upstream_path()->refresh_now(); } } } } struct create_group_pim_intf_context : mrd::create_group_context { create_group_pim_intf_context(); bool from_join; address_set prunedaddrs; uint32_t holdtime; bool rpt, wc; uint8_t *pktbuf; uint16_t pktlen; bool nullreg; }; create_group_pim_intf_context::create_group_pim_intf_context() { from_join = true; holdtime = 0; rpt = wc = false; pktbuf = 0; pktlen = 0; nullreg = false; } void pim_interface::handle_join_wc_rpt(const inet6_addr &grpaddr, const inet6_addr &src, const address_set &pruneaddrs, uint16_t ht, bool rpt) { group *grp = g_mrd->get_group_by_addr(grpaddr); uint32_t holdtime = ht * 1000; if (grp) handle_join_wc_rpt(grp, src, pruneaddrs, holdtime, rpt); else { create_group_pim_intf_context *ctx = new create_group_pim_intf_context; if (!ctx) return; ctx->from_join = true; ctx->groupaddr = grpaddr; ctx->requester = src; ctx->prunedaddrs = pruneaddrs; ctx->holdtime = holdtime; ctx->rpt = rpt; ctx->wc = true; g_mrd->create_group(pim, this, ctx); } } void pim_interface::event(int type, void *ptr) { if (type != mrd::CreatedGroup) { interface_node::event(type, ptr); return; } create_group_pim_intf_context *ctx = (create_group_pim_intf_context *)ptr; if (ctx->from_join) { if (ctx->wc) { handle_join_wc_rpt(ctx->result, ctx->requester, ctx->prunedaddrs, ctx->holdtime, ctx->rpt); } else { handle_join_source(ctx->result, ctx->requester, ctx->holdtime, ctx->rpt); } } else { pim_group_node *node = (pim_group_node *)ctx->result->node_owned_by(pim); if (node) { node->do_register(ctx->requester.address_p(), (ip6_hdr *)ctx->pktbuf, ctx->pktlen, ctx->nullreg); } delete ctx->pktbuf; } delete ctx; } void pim_interface::handle_join_wc_rpt(group *grp, const inet6_addr &src, const address_set &pruneaddrs, uint32_t holdtime, bool rpt) { if (!grp) return; pim_group_node *node = (pim_group_node *)grp->node_owned_by(pim); /// 3.2.2.1.2 if (!node) { /* Either PIM is disabled for this group or we didn't have * enough memory in the past */ return; } if (node->has_rp() && !(node->rpaddr() == src)) { /* * We already have a group instance for G, and the currently * used RP address differs from the requested one, ignore Join. */ return; } bool had = node->has_wildcard(); if (!had) { if (!node->create_wildcard()) { return; } } node->wildcard()->set_oif(owner(), holdtime); if (!had) { rp_source rpsrc; inet6_addr possiblerp = node->rp_for_group(rpsrc); if (!(possiblerp == src)) { if (should_log(DEBUG)) { log().writeline("RP in J/P message is not the" "configured one, ignoring Join/Prune."); return; } } node->set_rp(src, rps_join); /// 3.2.2.1.5 node->wildcard()->check_upstream_path(); } handle_join(node, src, holdtime, rpt); } void pim_interface::handle_join_source(const inet6_addr &grpaddr, const inet6_addr &src, uint32_t holdtime, bool rpt) { group *grp = g_mrd->get_group_by_addr(grpaddr); if (grp) handle_join_source(grp, src, holdtime, rpt); else { create_group_pim_intf_context *ctx = new create_group_pim_intf_context; if (!ctx) return; ctx->from_join = true; ctx->groupaddr = grpaddr; ctx->requester = src; ctx->holdtime = holdtime; ctx->rpt = rpt; ctx->wc = false; g_mrd->create_group(pim, this, ctx); } } void pim_interface::handle_join_source(group *grp, const inet6_addr &src, uint32_t holdtime, bool rpt) { if (!grp) return; pim_group_node *node = (pim_group_node *)grp->node_owned_by(pim); if (!node) return; pim_source_state_base *state = node->get_state(src, rpt); if (!state) { /// 3.2.2.2 /* If we don't have a state for G, as it is a join, downstream * wanted to revert Prune to Join, which doesnt make sense here. */ if (rpt) return; node->create_state(src, rpt, owner(), false, holdtime); // if (pimrouter->mrib().rpf_check(node, src, owner())) // node->create_state(src, owner(), false, holdtime); // else // g_mrd->log().debug() << "Downstream Join failed: RPF check failed." << endl; } handle_join(node, src, holdtime, rpt); } void pim_interface::handle_join(const inet6_addr &grpaddr, const inet6_addr &src, uint32_t holdtime, bool rpt) { group *grp = g_mrd->get_group_by_addr(grpaddr); if (!grp) return; pim_group_node *node = (pim_group_node *)grp->node_owned_by(pim); if (node) handle_join(node, src, holdtime, rpt); } void pim_interface::handle_join(pim_group_node *node, const inet6_addr &src, uint32_t holdtime, bool rpt) { pim_source_state_base *state = node->get_state(src, rpt); if (state) state->set_oif(owner(), holdtime); } void pim_interface::handle_bootstrap(const sockaddr_in6 *src, const sockaddr_in6 *dst, pim_bootstrap_message *msg, uint16_t length) { m_stats.counter(BootstrapCount, RX)++; #ifndef PIM_NO_BSR pim->bsr().handle_bootstrap_message(this, src, dst, msg, length); #endif } void pim_interface::handle_assert(const sockaddr_in6 *from, pim_assert_message *msg, uint16_t length) { m_stats.counter(AssertCount, RX)++; if (should_log(MESSAGE_CONTENT)) { base_stream &os = log(); os.inc_level(); _debug_pim_dump(os, *msg); os.dec_level(); } if (!get_neighbour(from->sin6_addr)) { m_stats.counter(AssertCount, Bad)++; return; } inet6_addr grpaddr(msg->gaddr.addr, msg->gaddr.masklen); pim_group_node *node = pim->get_group(grpaddr); bool rpt = msg->rpt(); uint32_t metric_pref = msg->metric_pref(); uint32_t metric = ntoh(msg->metric); if (node) { if (!IN6_IS_ADDR_UNSPECIFIED(&msg->saddr.addr)) { pim_group_source_state *state = node->get_state(msg->saddr.addr); if (state) { pim_common_oif::assert_state prev = pim_common_oif::AssertNoInfo; bool existed = false; pim_common_oif *oif = (pim_common_oif *)state->get_oif(owner()); if (oif) { prev = oif->current_assert_state(); existed = true; } state->handle_assert(owner(), from->sin6_addr, rpt, metric, metric_pref); /* for some future reason the oif may be released meanwhile */ oif = (pim_common_oif *)state->get_oif(owner()); if (!oif && existed) { /* transitioned somehow */ return; } pim_common_oif::assert_state current = oif ? oif->current_assert_state() : pim_common_oif::AssertNoInfo; if (current != pim_common_oif::AssertNoInfo || current != prev) { /* (S,G) Assert state machine is not NoInfo * or a transition occurred: no (*,G) handling */ return; } } } if (node->has_wildcard()) { node->wildcard()->handle_assert(owner(), from->sin6_addr, rpt, metric, metric_pref); } } } void pim_interface::handle_register(const sockaddr_in6 *src, const sockaddr_in6 *dst) { m_stats.counter(RegisterCount, RX)++; if (!g_mrd->has_address(dst->sin6_addr)) { m_stats.counter(RegisterCount, Bad)++; return; } pim_register_message *msg = g_mrd->ipktb->header(); ip6_hdr *pktbuf = msg->ip6_header(); uint16_t pktlen = g_mrd->ipktb->rlength - sizeof(pim_register_message); if (pktbuf->ip6_src == in6addr_any) { m_stats.counter(RegisterCount, Bad)++; return; } /* Reached Hop Limit */ if (pktbuf->ip6_hlim <= 1) { /* XXX send register-stop? */ return; } pim_group_node *node = pim->get_group(pktbuf->ip6_dst); if (node) { node->do_register(&src->sin6_addr, pktbuf, pktlen, msg->null()); } else { /* XXX if SSM don't create group */ create_group_pim_intf_context *ctx = new create_group_pim_intf_context; if (!ctx) return; ctx->from_join = false; ctx->groupaddr = pktbuf->ip6_dst; ctx->requester = src->sin6_addr; ctx->pktlen = pktlen; ctx->pktbuf = new uint8_t[pktlen]; if (!ctx->pktbuf) { /* XXX drop */ delete ctx; return; } ctx->nullreg = msg->null(); memcpy(ctx->pktbuf, pktbuf, pktlen); g_mrd->create_group(pim, this, ctx); } } void pim_interface::handle_register_stop(const sockaddr_in6 *src) { m_stats.counter(RegisterStopCount, RX)++; pim_register_stop_message *msg = g_mrd->ipktb->header(); pim_group_node *node = pim->get_group(msg->gaddr.addr); if (node) node->register_stop(src->sin6_addr, msg->uaddr.addr); } void pim_interface::handle_candidate_rp_adv(const sockaddr_in6 *from, pim_candidate_rp_adv_message *msg, uint16_t len) { m_stats.counter(CandRPCount, RX)++; #ifndef PIM_NO_BSR pim->bsr().handle_candidate_rp_adv(this, from, msg, len); #endif } void pim_interface::neighbour_timed_out(pim_neighbour * &neigh) { if (should_log(NORMAL)) neigh->log().writeline("Timed out."); remove_neighbour(neigh, true); } void pim_interface::remove_neighbour(pim_neighbour *neigh, bool elect) { for (neighbours_def::iterator i = neighbours.begin(); i != neighbours.end(); i++) { if (*i == neigh) { neighbours.erase(i); if (elect) { check_lan_delay(); elect_subnet_dr(); } neigh->set_present(false); pim->lost_neighbour(neigh); neigh->shutdown(); delete neigh; return; } } } void pim_interface::check_lan_delay() { m_landelay_enabled = true; for (neighbours_def::const_iterator i = neighbours.begin(); m_landelay_enabled && i != neighbours.end(); ++i) m_landelay_enabled = (*i)->has_lan_delay(); if (m_landelay_enabled) { m_propagation_delay = conf()->propagation_delay(); m_override_interval = conf()->override_interval(); for (neighbours_def::const_iterator i = neighbours.begin(); i != neighbours.end(); ++i) { if ((*i)->propagation_delay() > m_propagation_delay) m_propagation_delay = (*i)->propagation_delay(); if ((*i)->override_interval() > m_override_interval) m_override_interval = (*i)->override_interval(); } } else { m_propagation_delay = conf()->propagation_delay(); m_override_interval = conf()->override_interval(); } } bool pim_interface::suppression_enabled() const { if (!lan_delay_enabled()) return true; for (neighbours_def::const_iterator i = neighbours.begin(); i != neighbours.end(); ++i) { if (!(*i)->tracking_support()) return true; } return false; } uint32_t pim_interface::suppressed_value() const { if (!suppression_enabled()) return 0; uint32_t a = (uint32_t)(conf()->joinprune_interval() * 1.1); uint32_t b = (uint32_t)(conf()->joinprune_interval() * 1.4); return a + mrd::get_randu32() % (b-a); } void pim_interface::elect_subnet_dr() { bool mayuseprio = true; for (neighbours_def::const_iterator i = neighbours.begin(); mayuseprio && i != neighbours.end(); ++i) mayuseprio = (*i)->has_dr_priority(); pim_neighbour *bestneigh = 0; // elect DR between known neighbours if (!neighbours.empty()) { bestneigh = *neighbours.begin(); neighbours_def::const_iterator i = neighbours.begin(); i++; for (; i != neighbours.end(); i++) { if (!mayuseprio || (bestneigh->dr_priority() == (*i)->dr_priority())) { if (bestneigh->localaddr() < (*i)->localaddr()) { bestneigh = *i; } } else if (bestneigh->dr_priority() < (*i)->dr_priority()) { bestneigh = *i; } } } // match the elected neighbour against us if (bestneigh) { uint32_t my_dr_prio = conf()->dr_priority(); if (!mayuseprio || (my_dr_prio == bestneigh->dr_priority())) { if (bestneigh->localaddr() < inet6_addr(*owner()->linklocal())) bestneigh = 0; } else { if (my_dr_prio > bestneigh->dr_priority()) bestneigh = 0; } } /* We must set elected_dr before calling dr_changed */ pim_neighbour *old = elected_dr; elected_dr = bestneigh; if (old != bestneigh) { if (bestneigh && !old) { if (should_log(NORMAL)) log().xprintf("No longer the DR, new DR is %{Addr}\n", bestneigh->localaddr()); pim->dr_changed(this, false); } else if (old && !bestneigh) { if (should_log(NORMAL)) log().writeline("Im now the DR"); pim->dr_changed(this, true); } else if (bestneigh) { if (should_log(NORMAL)) log().xprintf("New DR is %{Addr}\n", bestneigh->localaddr()); } } } void pim_interface::found_new_neighbour(pim_neighbour *neigh) { if (should_log(NORMAL)) log().xprintf("New Neighbour at %{Addr}\n", neigh->localaddr()); send_hello(); #ifndef PIM_NO_BSR if (am_dr()) { pim->bsr().found_new_neighbour(neigh); } #endif pim->found_new_neighbour(neigh); } void pim_interface::send_hello() { send_hellox(conf()->holdtime() / 1000); } static inline void _hello_advance_option(int &optlen, pim_hello_option * &opt, int length) { optlen += sizeof(pim_hello_option) + length; opt = opt->next(); } static void _build_addrlist(int optid, pim_hello_option * &opt, const std::set &addrs, int &optlen) { opt->construct(optid, addrs.size() * sizeof(pim_encoded_unicast_address)); pim_encoded_unicast_address *addr = (pim_encoded_unicast_address *)opt->data(); for (std::set::const_iterator i = addrs.begin(); i != addrs.end(); i++, addr++) { addr->construct(*i); } _hello_advance_option(optlen, opt, addrs.size() * sizeof(pim_encoded_unicast_address)); } void pim_interface::send_hellox(uint16_t holdtime) { pim_hello_message *hellomsg = g_mrd->opktb->header(); hellomsg->construct(); pim_hello_option *opt = hellomsg->options(); int optlen = 0; opt->add_uint16(pim_hello_option::holdtime, holdtime); _hello_advance_option(optlen, opt, 2); if (owner()->is_multiaccess()) { opt->add_uint16pair(pim_hello_option::lan_prune_delay, conf()->propagation_delay(), conf()->override_interval()); _hello_advance_option(optlen, opt, 4); } opt->add_uint32(pim_hello_option::genid, gen_id); _hello_advance_option(optlen, opt, 4); opt->add_uint32(pim_hello_option::dr_priority, conf()->dr_priority()); _hello_advance_option(optlen, opt, 4); const std::set &addrs = owner()->globals(); if (!addrs.empty()) { _build_addrlist(pim_hello_option::addrlist, opt, addrs, optlen); if (conf()->support_old_cisco_addrlist()) _build_addrlist(pim_hello_option::cisco_old_addrlist, opt, addrs, optlen); } if (should_log(MESSAGE_SIG)) log().xprintf("Hello message to All-Routers, holdtime = %u.\n", (uint32_t)holdtime); if (send_all_routers(hellomsg, sizeof(pim_hello_message) + optlen)) { m_stats.counter(HelloCount, TX)++; } } bool pim_interface::call_method(int id, base_stream &out, const std::vector &args) { switch (id) { case pim_intf_method_flap: return flap_neighbour(out, args, false); case pim_intf_method_force_timeout: return flap_neighbour(out, args, true); } return interface_node::call_method(id, out, args); } bool pim_interface::flap_neighbour(base_stream &out, const std::vector &args, bool remove) { if (args.empty()) return false; inet6_addr addr; if (!addr.set(args[0])) return false; pim_neighbour *neigh = 0; neighbours_def::iterator i; for (i = neighbours.begin(); i != neighbours.end(); i++) { if ((*i)->has_address(addr)) { neigh = *i; break; } } if (!neigh) { out.writeline("No such neighbour."); } else { if (remove) { neighbour_timed_out(neigh); } else { neighbours.erase(i); pim->lost_neighbour(neigh); neighbours.push_back(neigh); pim->found_new_neighbour(neigh); } } return true; } void pim_interface::property_changed(node *n, const char *name) { if (!strcmp(name, "dr_priority")) { if (conf()) { if (should_log(DEBUG)) log().xprintf("Changed DR-Priority to %u\n", (uint32_t)conf()->dr_priority()); send_hello(); elect_subnet_dr(); } } else if (!strcmp(name, "hello_interval")) { update_hello_interval(conf()->hello_interval()); } } void pim_interface::update_hello_interval(uint32_t value) { if (value == 0) { if (hello_timer_id.is_running() && should_log(DEBUG)) log().writeline("Hello Interval set to 0, entering Passive mode."); hello_timer_id.stop(); } else if (intf_state >= LOCAL_READY) { hello_timer_id.start_or_update(value, true); } else { hello_timer_id.update(value, true); } } uint32_t pim_interface::effective_propagation_delay() const { return m_propagation_delay; } uint32_t pim_interface::effective_override_interval() const { return m_override_interval; } bool pim_interface::lan_delay_enabled() const { return m_landelay_enabled; } mrd6-0.9.6/src/pim/pim_source.cpp0000644000175000017500000012252010746067643015330 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim_source.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include #include /* from pim_oif.cpp */ extern const char *_oif_interest(pim_oif::interest); pim_source_state_base::pim_source_state_base(pim_group_node *owner, const inet6_addr &address) : m_owner(owner), m_addr(address), m_upstream_path(0) { m_creation_time = tval::now(); grab(); m_previous_interest = true; } pim_source_state_base::~pim_source_state_base() { if (m_upstream_path) { m_upstream_path->remove(); m_upstream_path = 0; } for (oifs::iterator i = m_oifs.begin(); i != m_oifs.end(); ++i) { delete *i; } m_oifs.clear(); } bool pim_source_state_base::check_startup() { return true; } bool pim_source_state_base::set_oif(interface *intf, uint32_t timeout, bool join) { if (owner()->should_log(INTERNAL_FLOW)) { log().xprintf("set_oif %s %u %s\n", intf->name(), timeout, join ? " join" : " prune"); } pim_oif *selected = get_oif(intf); if (!selected) { /* Add the interface, to remove it next? No thank you. */ if (!timeout) return true; selected = create_oif(intf); if (!selected) return false; } selected->update(join, timeout); return true; } bool pim_source_state_base::set_local_oif(interface *intf, bool join) { if (owner()->should_log(INTERNAL_FLOW)) { log().xprintf("set_local_oif %s %s\n", intf->name(), join ? " join" : " prune"); } pim_oif *selected = get_oif(intf); if (!selected) { selected = create_oif(intf); if (!selected) return false; } selected->change_local_membership(join ? pim_oif::Include : pim_oif::Exclude); return true; } bool pim_source_state_base::remove_oif(interface *intf) { for (oifs::iterator i = m_oifs.begin(); i != m_oifs.end(); ++i) { if ((*i)->intf() == intf) { pim_oif *oif = *i; m_oifs.erase(i); removing_oif(oif); delete oif; if (owner()->should_log(DEBUG)) log().xprintf("Removed intf %s\n", intf->name()); check_interest(); return true; } } return false; } bool pim_source_state_base::release_oif(interface *intf, bool local) { for (oifs::iterator i = m_oifs.begin(); i != m_oifs.end(); ++i) { if ((*i)->intf() == intf) { if (local) (*i)->change_local_membership(pim_oif::NoInfo); else (*i)->update(true, 0); return true; } } return true; } pim_oif *pim_source_state_base::get_oif(interface *intf) const { for (oifs::const_iterator i = m_oifs.begin(); i != m_oifs.end(); ++i) { if ((*i)->intf() == intf) { return *i; } } return 0; } int pim_source_state_base::count_oifs() const { return m_oifs.size(); } bool pim_source_state_base::join_desired() const { return get_downstream_interest() != pim_oif::NoInfo; } pim_oif::interest pim_source_state_base::get_downstream_interest() const { return get_oif_downstream_interest(false); } pim_oif::interest pim_source_state_base::get_oif_downstream_interest(bool checklocal) const { pim_oif::interest res = pim_oif::NoInfo; for (oifs::const_iterator i = m_oifs.begin(); res != pim_oif::Include && i != m_oifs.end(); ++i) { pim_oif::interest k = (*i)->get_interest(); if (k == pim_oif::Include) { res = pim_oif::Include; } else if (k == pim_oif::Exclude) { if (res != pim_oif::Include) res = pim_oif::Exclude; } else if (checklocal && k == pim_oif::NoInfo) { if ((*i)->get_real_local_interest() == pim_oif::Include) res = pim_oif::Include; else if ((*i)->get_real_local_interest() == pim_oif::Exclude && res != pim_oif::Include) res = pim_oif::Exclude; } } return res; } const in6_addr &pim_source_state_base::join_target() const { return m_addr; } bool pim_source_state_base::check_interest() { bool current_interest = state_desired(); if (current_interest != m_previous_interest) { m_previous_interest = current_interest; if (!current_interest) { int _prev_count = get_refcount(); release(); if (_prev_count == 1) { /* If the previous refcount was 1, * we reached 0 after the release * and the state was removed */ return false; } } else { grab(); } } return true; } bool pim_source_state_base::check_interest_and_update_upstream() { if (!check_interest()) return false; update_upstream(); return true; } void pim_source_state_base::dr_changed(pim_interface *intf, bool islocal) { for (oifs::iterator i = m_oifs.begin(); i != m_oifs.end(); ++i) { if ((*i)->intf() == intf->owner()) (*i)->dr_changed(islocal); } } void pim_source_state_base::clear_interface_references(interface *intf) { if (get_oif(intf)) { remove_oif(intf); } } bool pim_source_state_base::state_desired() const { return join_desired(); } void pim_source_state_base::check_upstream_path() { } void pim_source_state_base::build_upstream_state() { pim_neighbour *neigh = upstream_neighbour(); if (!m_upstream_path || m_upstream_path->neigh() != neigh) { bool had = false; if (m_upstream_path) { m_upstream_path->remove(); m_upstream_path = 0; had = true; if (owner()->should_log(DEBUG)) log().writeline("Removing upstream path, " "possibly changing."); } if (neigh) { update_upstream(); } else if (am_self_upstream()) { if (had && owner()->should_log(DEBUG)) log().writeline("I'm the upstream neighbour."); } else if (had) { if (owner()->should_log(DEBUG)) log().writeline("Lost the upstream neighbour."); } upstream_changed(); } } void pim_source_state_base::update_upstream() { if (join_desired()) { pim_neighbour *neigh = upstream_neighbour(); if (!m_upstream_path && neigh) { m_upstream_path = neigh->add_path(this, join_target(), is_wildcard(), is_rpt()); if (m_upstream_path && owner()->should_log(DEBUG)) { log().xprintf("Upstream neighbor is %{Addr} in" " %s.\n", neigh->localaddr(), neigh->intf()->owner()->name()); } } } else if (m_upstream_path) { m_upstream_path->remove(); m_upstream_path = 0; } if (m_upstream_path) { if (get_downstream_interest() == pim_oif::Include) { m_upstream_path->join(true); } else { m_upstream_path->prune(true); } } } void pim_source_state_base::wildcard_state_existance_changed(bool) { /* empty */ } pim_oif *pim_source_state_base::create_oif(interface *intf) { if (!intf) return 0; pim_oif *oif = create_oif(this, intf); if (oif) { m_oifs.push_back(oif); if (owner()->should_log(DEBUG)) log().xprintf("Added intf %s\n", intf->name()); } return oif; } void pim_source_state_base::output_common_info(base_stream &ctx) const { ctx.inc_level(); ctx.xprintf("Input Interface: %s, ", iif() ? iif()->name() : "(None)"); pim_neighbour *neigh = upstream_neighbour(); ctx.write("Upstream: "); if (is_source_local()) { ctx.write("(Local)"); } else if (neigh) { ctx.write(neigh->localaddr()); if (!m_upstream_path) { ctx.write(", No state"); } } else if (am_self_upstream()) { ctx.write("(Self)"); } else { ctx.write("(None)"); } ctx.newl(); if (!m_oifs.empty()) { ctx.writeline(is_wildcard() ? "Output Interfaces:" : "Immediate Output Interfaces:"); ctx.inc_level(); for (oifs::const_iterator i = m_oifs.begin(); i != m_oifs.end(); i++) { (*i)->output_info(ctx); } ctx.dec_level(); } ctx.dec_level(); } void pim_source_state_base::removing_oif(pim_oif *) { /* empty */ } void pim_source_state_base::upstream_changed() { /* empty */ } void pim_source_state_base::destructor() { owner()->remove_state(this); } pim_source_state_common::pim_source_state_common(pim_group_node *owner, const inet6_addr &addr) : pim_source_state_base(owner, addr), m_iif(0), m_neigh_watcher(this, std::mem_fun(&pim_source_state_common::neighbour_changed), this) { } bool pim_source_state_common::check_startup() { return m_neigh_watcher.check_startup(); } pim_oif *pim_source_state_common::create_oif(pim_source_state_base *state, interface *intf) const { return new pim_common_oif(state, intf); } void pim_source_state_common::assert_wstate_actions1(pim_common_oif *oif) { /* * A1: Send Assert(S,G) * Set Assert Timer to (Assert_Time - Assert_Override_Interval) * Store self as AssertWinner(S,G,I) * Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I) * * for (*,G) is the same, but instead of (S,G) is (*,G) and * rpt_assert_metric(*,G,I) */ if (!oif->pim_intf()) return; oif->change_assert_state(pim_common_oif::WonAssert); send_assert(oif->pim_intf()); oif->restart_assert_timer_minus_override(); oif->store_assert_info(0, path_metric(), path_protocol()); } void pim_source_state_common::assert_lstate_actions2(pim_common_oif *oif, pim_neighbour *winner, uint32_t metric, uint32_t pref) { /* * A2: Store new assert winner as AssertWinner(*,G,I) and assert * winner metric as AssertWinnerMetric(*,G,I). * Set Assert Timer to Assert_Time */ if (!oif->pim_intf()) return; oif->change_assert_state(pim_common_oif::LostAssert); oif->store_assert_info(winner, metric, pref); oif->restart_assert_timer(); } interface *pim_source_state_common::iif() const { return m_iif; } uint32_t pim_source_state_common::path_metric() const { return m_neigh_watcher.prefix_metric(); } uint32_t pim_source_state_common::path_protocol() const { return m_neigh_watcher.prefix_protocol(); } pim_neighbour *pim_source_state_common::upstream_neighbour() const { /* respect Assert Winner precedence */ pim_common_oif *oif = (pim_common_oif *)get_oif(iif()); if (oif && oif->current_assert_state() == pim_common_oif::LostAssert) { return oif->assert_winner(); } return m_neigh_watcher.neigh(); } bool pim_source_state_common::am_self_upstream() const { return m_neigh_watcher.self_upstream(); } const in6_addr &pim_source_state_common::target_destination() const { return join_destination(); } const inet6_addr &pim_source_state_common::target_group() const { return owner()->id(); } void pim_source_state_common::check_upstream_path() { if (is_rpt() && !owner()->has_rp()) { /* for (*,G) and (S,G,rpt) states, the direction (RP address) * might be any, i.e. no RP address is configured. */ m_neigh_watcher.release(); } else { m_neigh_watcher.invalidate(); } } bool pim_source_state_common::could_assert(interface *intf) const { /* default behaviour returns false, i.e. (S,G,rpt) states */ return false; } bool pim_source_state_common::assert_tracking_desired(interface *intf) const { /* default behaviour is false, only (S,G) states implement this */ return false; } static bool _check_assert(const pim_source_state_base *s, interface *intf, const inet6_addr &addr, bool rpt, uint32_t metric, uint32_t pref) { if (s->is_rpt() == rpt) { uint32_t own_pref = s->path_protocol(); uint32_t own_metric = s->path_metric(); if (own_pref == pref) { if (own_metric == metric) { return intf->primary_addr() < addr; } else { return own_metric < metric; } } else { return own_pref < pref; } } else { return rpt; } } bool pim_source_state_common::check_assert(interface *intf, const inet6_addr &addr, bool rpt, uint32_t metric, uint32_t pref) const { if (could_assert(intf)) { return _check_assert(this, intf, addr, rpt, metric, pref); } else if (owner()->has_wildcard() && owner()->wildcard()->could_assert(intf)) { return _check_assert(owner()->wildcard(), intf, addr, rpt, metric, pref); } else { return false; } } extern sockaddr_in6 pim_all_routers_addr; void pim_source_state_common::send_assert(pim_interface *intf) { if (!intf) return; uint32_t own_pref = path_protocol(); uint32_t own_metric = path_metric(); pim_assert_message *msg = g_mrd->opktb->header(); msg->construct(m_owner->id(), join_target(), is_wildcard(), own_pref, own_metric); intf->send_assert(msg); } void pim_source_state_common::send_assert_cancel(pim_interface *intf) { if (!intf) return; pim_assert_message *msg = g_mrd->opktb->header(); msg->construct(m_owner->id(), join_target(), true, 0x7fffffff, 0xffffffff); intf->send_assert(msg); } void pim_source_state_common::found_new_neighbour(pim_neighbour *neigh) { m_neigh_watcher.recheck_neighbour(); } void pim_source_state_common::neighbour_lost(pim_neighbour *neigh) { if (m_upstream_path && m_upstream_path->neigh() == neigh) m_neigh_watcher.recheck_neighbour(); for (oifs::iterator i = m_oifs.begin(); i != m_oifs.end(); ++i) { pim_common_oif *k = (pim_common_oif *)(*i); if (k->assert_winner() == neigh) { /* -> NI state, [Actions A5] */ k->change_assert_state(pim_common_oif::AssertNoInfo); } } } void pim_source_state_common::clear_interface_references(interface *intf) { auto_grab grab(this); pim_source_state_base::clear_interface_references(intf); if (m_iif == intf) { if (m_upstream_path) { m_upstream_path->remove(false); m_upstream_path = 0; } removing_iif(m_iif); m_iif = 0; check_upstream_path(); } } void pim_source_state_common::neighbour_changed(pim_neighbour_watcher_base *) { pim_interface *iif = m_neigh_watcher.tentative_interface(); /* safeguard this source state instance */ auto_grab grab(this); if (!m_iif || !iif || iif->owner() != m_iif) { if (m_iif) { pim_oif *oif = get_oif(m_iif); removing_iif(m_iif); m_iif = 0; if (oif) { /* oif may not exist if it was removed */ oif_changed_state(oif, oif->get_interest()); } } if (iif == 0 && m_oifs.empty() && is_wildcard()) { if (owner()->owner()->someone_lost_interest()) return; } changed_iif(iif ? iif->owner() : 0); } build_upstream_state(); } void pim_source_state_common::changed_iif(interface *intf) { m_iif = intf; } void pim_source_state_common::removing_iif(interface *) { /* empty */ } pim_group_source_state::pim_group_source_state(pim_group_node *grp, const inet6_addr &addr) : pim_source_state_common(grp, addr), m_register_supression_timer("pim register supression timer", this, std::mem_fun(&pim_group_source_state::send_probe)) { m_spt = grp->is_ssm(); m_local = false; m_mfa_inst = 0; m_sent_probe = false; m_assert_state = noinfo; m_kat_enabled = false; m_inherited_oifs = 0; m_fw_counter = 0; m_downstream_activity = false; } pim_group_source_state::~pim_group_source_state() { if (spt()) { pim_group_source_rpt_state *rptstate = owner()->get_rpt_state(addr()); if (rptstate) rptstate->set_local_interest(pim_oif::Include); } if (m_iif) { if (m_mfa_inst) m_mfa_inst->release_iif(m_iif); m_iif = 0; } if (m_mfa_inst) { m_owner->mfa()->release_source_state(m_mfa_inst); m_mfa_inst = 0; } } void pim_group_source_state::changed_iif(interface *intf) { if (!intf) { m_iif = 0; return; } pim_oif *oif = get_oif(intf); if (!oif && m_inherited_oifs) oif = owner()->wildcard()->get_oif(intf); if (oif) update_fib(oif->intf(), -1); m_local = intf->in_same_subnet(m_addr); if (m_local) restart_kat(); m_iif = intf; m_mfa_inst->set_iif(intf); merge_inherited_oifs(); } void pim_group_source_state::removing_iif(interface *intf) { m_mfa_inst->release_iif(intf); } bool pim_group_source_state::check_startup() { if (!pim_source_state_common::check_startup()) return false; /* only create MFIB states for (S,G) states */ m_mfa_inst = owner()->mfa()->create_source_state(addr(), this); if (!m_mfa_inst) { return false; } /* for RP register encapsulation */ if (!owner()->is_ssm()) m_mfa_inst->change_flags(mfa_group_source::f_any_incoming, mfa_group_source::copy_full_packet); return true; } void pim_group_source_state::output_name(base_stream &os) const { os.xprintf("(%{addr})", addr()); } bool pim_group_source_state::output_info(base_stream &ctx) const { base_stream &os = ctx.xprintf("(%{addr})", addr()); if (m_spt) os.write(", SPT"); if (m_kat_enabled) { int32_t diff = tval::now() - m_kat_last_update; if (diff <= 10000) os.write(", Active"); else os.xprintf(", Inactive for %{duration}", time_duration(diff)); } os.xprintf(", Uptime: %{duration}\n", time_duration(uptime())); ctx.inc_level(); if (m_register_supression_timer.is_running()) { ctx.xprintf("Register-Stop%s: %{duration}\n", m_sent_probe ? ", pending" : "", m_register_supression_timer.time_left_d()); } ctx.dec_level(); output_common_info(ctx); if (m_inherited_oifs && !m_inherited_oifs->empty()) { ctx.inc_level(); int count = 0; for (oifs::const_iterator i = m_inherited_oifs->begin(); i != m_inherited_oifs->end(); i++) { if (inherited_includes(*i)) count++; } if (count) { ctx.writeline("Inherited Output Interfaces:"); ctx.inc_level(); for (oifs::const_iterator i = m_inherited_oifs->begin(); i != m_inherited_oifs->end(); ++i) { if (inherited_includes(*i)) (*i)->output_info(ctx); } ctx.dec_level(); } ctx.dec_level(); } return true; } pim_oif::interest pim_group_source_state::get_inherited_oif_downstream_interest() const { pim_oif::interest res = pim_oif::NoInfo; if (m_inherited_oifs) { for (oifs::const_iterator i = m_inherited_oifs->begin(); i != m_inherited_oifs->end(); ++i) { if (inherited_includes(*i)) { pim_oif::interest k = (*i)->get_interest(); if (k == pim_oif::Include) res = pim_oif::Include; else if (k == pim_oif::Exclude && res != pim_oif::Include) res = pim_oif::Exclude; else if (k == pim_oif::NoInfo) { if ((*i)->get_real_local_interest() == pim_oif::Include) res = pim_oif::Include; else if ((*i)->get_real_local_interest() == pim_oif::Exclude && res != pim_oif::Include) res = pim_oif::Exclude; } } } } return res; } bool pim_group_source_state::state_desired() const { /* XXX non-SPT states which time-out are never removed */ /* Don't remove if the state is active and (*,G) includes it */ if (owner()->has_wildcard() && (!spt() || m_kat_enabled)) { if (owner()->wildcard()->get_oif_downstream_interest(true) != pim_oif::NoInfo) return true; } /* Don't remove this state if there is downstream interest in (S,G,rpt) */ pim_group_source_rpt_state *rpt = owner()->get_rpt_state(addr()); if (rpt) { if (rpt->get_oif_downstream_interest(true) != pim_oif::NoInfo) return true; } /* If the source is local keep it around while KAT is running */ if (is_source_local() && m_kat_enabled) return true; return !m_oifs.empty(); } bool pim_group_source_state::join_desired() const { /* If we have inherited interest */ if (get_inherited_oif_downstream_interest() != pim_oif::NoInfo) return true; /* Or if we have immediate interest */ if (get_oif_downstream_interest(false) != pim_oif::NoInfo) return true; return false; } bool pim_group_source_state::inherited_includes(pim_oif *oif) const { /* If it is an ImmediateOif, isn't proper */ if (get_oif(oif->intf())) return false; pim_group_source_rpt_state *rptstate = owner()->get_rpt_state(addr()); /* If there is a (S,G,rpt) ... * ... Even if (S,G,rpt) is in Exclude, if (*,G) is include, * the interface is in the inherited oifs */ if (rptstate && oif->get_local_interest() != pim_oif::Include) { pim_oif *rptoif = rptstate->get_oif(oif->intf()); /* If the InheritedOif is prunned in the (S,G,rpt), not proper */ if (rptoif && rptoif->get_interest() == pim_oif::Exclude) return false; } return ((pim_common_oif *)oif)->current_assert_state() != pim_common_oif::LostAssert; } void pim_group_source_state::inherited_oif_changed_state(pim_oif *oif, pim_oif::interest prev) { pim_oif::interest currint = oif->get_interest(); if (owner()->should_log(INTERNAL_FLOW)) { log().xprintf("inherited_Intf(%s) changed state %s -> %s\n", oif->intf()->name(), _oif_interest(prev), _oif_interest(currint)); } /* if the interface is no longer included in the inherited list * and not in the immediate list, remove it */ if (!inherited_includes(oif) && !get_oif(oif->intf())) { update_fib(oif->intf(), -1); if (owner()->should_log(INTERNAL_FLOW)) { log().xprintf("inherited_Intf(%s) rejected, not " "proper.\n", oif->intf()->name()); } return; } if (currint == pim_oif::Include) { update_fib(oif->intf(), 1); } else /* { pim_oif::Exclude, pim_oif::NoInfo } */ { update_fib(oif->intf(), -1); } check_interest_and_update_upstream(); } void pim_group_source_state::update_fib(interface *intf, int change) { if (owner()->should_log(INTERNAL_FLOW)) { log().xprintf("update_fib(%s) += %i [with iif=%s]\n", intf->name(), change, iif() ? iif()->name() : 0); } if (change == 0 || intf == iif()) return; if (change == -1) m_mfa_inst->release_oif(intf); else if (change == 1) m_mfa_inst->add_oif(intf); } static bool _check_oiflist_interest(const pim_source_state_base::oifs &olist) { for (pim_source_state_base::oifs::const_iterator i = olist.begin(); i != olist.end(); ++i) { if ((*i)->get_interest() == pim_oif::Include) return true; } return false; } void pim_group_source_state::check_downstream_activity() { bool newval = _check_oiflist_interest(m_oifs); if (!newval && m_inherited_oifs) { newval = _check_oiflist_interest(*m_inherited_oifs); } if (newval != m_downstream_activity) { m_downstream_activity = newval; if (owner()->should_log(INTERNAL_FLOW)) log().xprintf("Internal activity changed to %b\n", m_downstream_activity); if (m_downstream_activity) { /* was false */ if (g_mrd->interest_in_active_states()) g_mrd->state_is_active(owner()->owner(), addr(), true); } else { /* is false */ if (g_mrd->interest_in_active_states()) g_mrd->state_is_active(owner()->owner(), addr(), false); } } } const in6_addr &pim_group_source_state::join_destination() const { return addr(); } pim_oif::interest pim_group_source_state::get_downstream_interest() const { if (m_inherited_oifs) { for (oifs::const_iterator i = m_inherited_oifs->begin(); i != m_inherited_oifs->end(); ++i) { if (inherited_includes(*i) && (*i)->get_interest() == pim_oif::Include) return pim_oif::Include; } } return pim_source_state_base::get_downstream_interest(); } void pim_group_source_state::removing_oif(pim_oif *oif) { update_fib(oif->intf(), -1); check_downstream_activity(); /* -> NI state, [Actions A4] */ if (((pim_common_oif *)oif)->current_assert_state() == pim_common_oif::WonAssert) { send_assert_cancel(oif->pim_intf()); } } void pim_group_source_state::upstream_changed() { /* Whenever the upstream for (*,G) or (S,G) changes, * notify the (S,G,rpt) state as join_desired() may change */ pim_group_source_rpt_state *rptstate = owner()->get_rpt_state(addr()); if (rptstate) { rptstate->update_upstream(); } } void pim_group_source_state::update_fw_counters() { uint64_t bytes; m_mfa_inst->get_input_counter(bytes); if (m_fw_counter != bytes) { restart_kat(); } m_fw_counter = bytes; } void pim_group_source_state::set_spt(bool b) { if (m_spt == b) return; if (b) restart_kat(); if (owner()->should_log(DEBUG)) log().xprintf("%sin Source Path Tree (SPT).\n", !b ? "not " : ""); m_spt = b; update_upstream(); update_rpts(); } bool pim_group_source_state::has_downstream_interest(bool includelocal) const { for (oifs::const_iterator i = m_oifs.begin(); i != m_oifs.end(); ++i) { if ((*i)->get_interest(includelocal) == pim_oif::Include) return true; } return false; } void pim_group_source_state::rp_changed() { update_rpts(); /* XXX also do this when register_supression is running */ m_mfa_inst->change_flags(mfa_group_source::f_any_incoming, owner()->has_rp() ? mfa_group_source::copy_full_packet : mfa_group_source::no_action); } bool pim_group_source_state::could_assert(interface *intf) const { /* SPTbit(S,G)==TRUE AND RPF_interface(S) != I */ if (!spt() || !iif() || intf == iif()) return false; /* I in [joins(*,*,RP(G)) + joins(*,G) - prunes(S,G,rpt)] * + [ pim_include(*,G) - pim_exclude(S,G) ] * - lost_assert(*,G) * + joins(S,G) + pim_include(S,G) * * They complicate so much.. * * This is, if I is in InheritedOifs or ImmediateOifs and * not in lost_assert(*,G) */ if (m_inherited_oifs) { for (oifs::const_iterator i = m_inherited_oifs->begin(); i != m_inherited_oifs->end(); ++i) { if ((*i)->intf() == intf) { if (inherited_includes(*i)) { if ((*i)->get_interest() == pim_oif::Include) return true; } break; } } } pim_oif *oif = get_oif(intf); if (oif) { return oif->get_interest() == pim_oif::Include; } return false; } bool pim_group_source_state::assert_tracking_desired(interface *intf) const { if (m_inherited_oifs) { for (oifs::const_iterator i = m_inherited_oifs->begin(); i != m_inherited_oifs->end(); ++i) { if (intf == (*i)->intf()) { if (inherited_includes(*i)) { if ((*i)->get_interest() == pim_oif::Include) return true; } } } } pim_oif *oif = get_oif(intf); if (oif) { if (oif->get_interest() == pim_oif::Include && oif->get_local_interest() == pim_oif::NoInfo) { return true; } if (oif->get_local_interest() == pim_oif::Include) { if (((pim_common_oif *)oif)->current_assert_state() == pim_common_oif::WonAssert) return true; pim_interface *pimintf = pim->get_interface(intf); if (pimintf && pimintf->am_dr()) return true; } } if (iif() == intf && join_desired()) return true; if (!spt() && owner()->has_wildcard() && owner()->wildcard()->iif() == intf && owner()->wildcard()->join_desired()) return true; return false; } void pim_group_source_state::handle_assert(interface *intf, const in6_addr &from, bool rpt, uint32_t metric, uint32_t pref) { /* (S,G) Assert state machine */ pim_common_oif *oif = (pim_common_oif *)get_oif(intf); if (!oif) { return; } /* we can be sure it exists */ pim_interface *pintf = pim->get_interface(intf); pim_neighbour *neigh = pintf->get_neighbour(from); if (oif->current_assert_state() == pim_common_oif::AssertNoInfo) { if (could_assert(intf) && (rpt || check_assert(intf, from, rpt, metric, pref))) { /* -> W state, [Actions A1] */ assert_wstate_actions1(oif); } else if (!rpt && assert_tracking_desired(intf)) { /* -> L state, [Actions A6] * * [Actions A6] is [Actions A2] plus SPTBit(S,G) = TRUE */ assert_lstate_actions2(oif, neigh, metric, pref); if (intf == iif() && m_upstream_path) set_spt(true); } } else if (oif->current_assert_state() == pim_common_oif::WonAssert) { if (check_assert(intf, from, rpt, metric, pref)) { /* [Actions A3] */ send_assert(pintf); oif->restart_assert_timer_minus_override(); } else { /* L state, [Actions A2] */ assert_lstate_actions2(oif, neigh, metric, pref); } } else if (oif->current_assert_state() == pim_common_oif::LostAssert) { if (!check_assert(intf, from, rpt, metric, pref)) { /* L state, [Actions A2] */ assert_lstate_actions2(oif, neigh, metric, pref); } else if (neigh == oif->assert_winner()) { oif->change_assert_state(pim_common_oif::AssertNoInfo); } } } void pim_group_source_state::update_rpts() const { pim_group_source_rpt_state *rptstate; /* * (S,G,rpt) creation follows these rules: * * - S must not be local * - (*,G) must exist (i.e. we only want to prune the RPT tree if it exists) * - SPTbit for S must be TRUE */ if (!is_source_local() && owner()->has_wildcard() && spt()) { /* Merge the interfaces into the RPT state, but in reversed interest */ rptstate = (pim_group_source_rpt_state *)owner()->create_state(addr(), true); if (rptstate) rptstate->set_local_interest(pim_oif::Exclude); } else { rptstate = owner()->get_rpt_state(addr()); if (rptstate) rptstate->set_local_interest(pim_oif::Include); } } void pim_group_source_state::merge_inherited_oifs() { if (m_iif && m_inherited_oifs) { for (oifs::const_iterator i = m_inherited_oifs->begin(); i != m_inherited_oifs->end(); ++i) { inherited_oif_changed_state(*i, pim_oif::NoInfo); } } } void pim_group_source_state::wildcard_state_existance_changed(bool created) { if (created) { update_upstream(); m_inherited_oifs = owner()->wildcard()->get_oifs(); merge_inherited_oifs(); update_rpts(); } else if (m_inherited_oifs) { /* If wildcard state was removed, RPT state is already gone * so we don't need to update_rpts again */ /* release any used inherited oif from the MFIB (S,G) state */ for (oifs::const_iterator i = m_inherited_oifs->begin(); i != m_inherited_oifs->end(); ++i) { interface *intf = (*i)->intf(); if (!get_oif(intf)) update_fib(intf, -1); } m_inherited_oifs = 0; } check_downstream_activity(); } void pim_group_source_state::forward_to_rp(interface *iif, ip6_hdr *hdr, uint16_t len) { if (m_owner->is_ssm() || m_owner->is_self_rp() || m_register_supression_timer.is_running()) return; pim_interface *pi = pim->get_interface(iif); /* if PIM is disabled in this interface or we aren't the DR for Iif, dont register */ if (!pi || !pi->am_dr()) return; m_owner->forward_to_rp(this, iif, hdr, len); } void pim_group_source_state::register_stop() { if (m_iif) { if (!m_register_supression_timer.is_running() && owner()->should_log(EXTRADEBUG)) { log().writeline("Stopped sending Register messages to RP"); } pim_intfconf_node *conf = (pim_intfconf_node *)m_iif->conf()->get_child("pim"); uint32_t regsupr = conf->register_supression_timeout(); uint32_t val = regsupr / 2 + ((mrd::get_randu32() % 100) * regsupr) / 100; uint32_t probe = conf->probe_time(); if (val < probe) val = probe * 2; val -= probe; m_register_supression_timer.start_or_update(val, false); m_sent_probe = false; } } void pim_group_source_state::send_probe() { if (m_owner->is_ssm() || !m_owner->has_rp_path()) { return; } if (m_sent_probe) return; struct { ip6_hdr ip6hdr; pim_message pimhdr; } dummy; memset(&dummy, 0, sizeof(dummy)); dummy.ip6hdr.ip6_vfc = 0x60; dummy.ip6hdr.ip6_src = m_addr; dummy.ip6hdr.ip6_dst = m_owner->id(); dummy.ip6hdr.ip6_plen = 4; dummy.ip6hdr.ip6_nxt = IPPROTO_PIM; dummy.ip6hdr.ip6_hops = 255; dummy.pimhdr.vt = 0; dummy.pimhdr.build_checksum(m_addr, m_owner->id(), sizeof(pim_message)); pim_register_message *msg = g_mrd->opktb->header(); memset(msg, 0, sizeof(*msg)); memcpy(msg->ip6_header(), &dummy, sizeof(dummy)); msg->construct(false, true); pim->send_register_probe(m_owner->pref_source_towards_rp(), m_owner->rpaddr(), msg, sizeof(dummy)); m_sent_probe = true; m_register_supression_timer.start_or_update( m_iif->conf()->get_child_property("pim", "probe-time")->get_unsigned(), false); } void pim_group_source_state::trigger_register_stop(const in6_addr *from) { node *_conf = m_iif ? m_iif->conf() : g_mrd->default_interface_configuration(); pim_intfconf_node *conf = (pim_intfconf_node *)_conf->get_child("pim"); /* assert(conf); */ uint32_t rslim = conf->register_stop_rate_limit(); uint32_t rslen = conf->register_stop_rate_timelen(); bool send = false; if (rslim > 0) { std::map::iterator j = m_register_stop_router_rates.find(*from); if (j == m_register_stop_router_rates.end()) { send = true; m_register_stop_router_rates[*from].count = 0; m_register_stop_router_rates[*from].last = tval::now().round_milisecs(); } else { j->second.count++; if (j->second.count >= rslim) { j->second.count = 0; send = true; } tval now = tval::now(); if ((now.round_milisecs() - j->second.last) >= (((uint64_t)rslen) * 1000)) { j->second.last = now.round_milisecs(); send = true; } } } else { send = true; } if (send) send_register_stop_to_router(from); } void pim_group_source_state::send_register_stop_to_router(const in6_addr *addr) const { owner()->send_register_stop_to_router(m_addr, *addr); } void pim_group_source_state::forward(interface *intf, ip6_hdr *hdr, uint16_t len) { g_mrd->mfa()->forward(intf, hdr, len); } void pim_group_source_state::trigger_assert(interface *intf) { pim_common_oif *oif = (pim_common_oif *)get_oif(intf); if (!oif) return; if (oif->current_assert_state() == pim_common_oif::LostAssert) return; /* -> W state, [Actions A1] */ assert_wstate_actions1(oif); } bool pim_group_source_state::lost_assert_rpt(pim_common_oif *oif) const { if (!owner()->has_wildcard()) { /* No RPT */ return false; } if ((oif->intf() == iif() && spt()) || oif->intf() == owner()->wildcard()->iif()) return false; if (!oif) { /* Same as Assert NoInfo */ return false; } return oif->current_assert_state() == pim_common_oif::LostAssert; } void pim_group_source_state::oif_changed_state(pim_oif *_oif, pim_oif::interest prev) { pim_common_oif *oif = (pim_common_oif *)_oif; /* safeguard this source state instance, remove_oif may * trigger removal */ auto_grab grab(this); if (!oif->has_interest()) { remove_oif(oif->intf()); } else { if (oif->get_interest() == pim_oif::Include) update_fib(oif->intf(), 1); else /* Exclude, NoInfo */ { update_fib(oif->intf(), -1); } /* If im Assert Winner -> NI Info, [Actions A4] */ if (oif->current_assert_state() == pim_common_oif::WonAssert && !could_assert(oif->intf())) { oif->change_assert_state(pim_common_oif::AssertNoInfo, false); send_assert_cancel(oif->pim_intf()); } } check_downstream_activity(); check_interest_and_update_upstream(); } pim_group_source_rpt_state::pim_group_source_rpt_state(pim_group_node *owner, const inet6_addr &addr) : pim_source_state_base(owner, addr) { m_local_interest = pim_oif::Include; } pim_oif *pim_group_source_rpt_state::create_oif(pim_source_state_base *state, interface *intf) const { return new pim_sg_rpt_oif(state, intf); } interface *pim_group_source_rpt_state::iif() const { return owner()->wildcard()->iif(); } uint32_t pim_group_source_rpt_state::path_metric() const { return owner()->wildcard()->path_metric(); } uint32_t pim_group_source_rpt_state::path_protocol() const { return owner()->wildcard()->path_protocol(); } pim_neighbour *pim_group_source_rpt_state::upstream_neighbour() const { /* if I_Am_Assert_Loser(S, G, RPF_interface(RP(G))) * return AssertWinner(S, G, RPF_interface(RP(G))) */ pim_group_source_state *state = owner()->get_state(addr()); if (state) { pim_common_oif *oif = (pim_common_oif *)state->get_oif(iif()); if (oif && oif->current_assert_state() == pim_common_oif::LostAssert) { return oif->assert_winner(); } } /* else return RPF'(*,G) */ return owner()->wildcard()->upstream_neighbour(); } bool pim_group_source_rpt_state::am_self_upstream() const { return owner()->wildcard()->am_self_upstream(); } pim_oif::interest pim_group_source_rpt_state::get_downstream_interest() const { if (m_local_interest == pim_oif::Exclude) return pim_oif::Exclude; return pim_source_state_base::get_downstream_interest(); } void pim_group_source_rpt_state::set_local_interest(pim_oif::interest oifint) { if (oifint != m_local_interest) { m_local_interest = oifint; check_interest_and_update_upstream(); } } bool pim_group_source_rpt_state::join_desired() const { /* * - Upstream neigh for RPT MUST be different than Upstream neigh for * S or else we don't really need to explicitly prune (S,G,rpt), our * upstream will do that for us */ pim_group_source_state *src = owner()->get_state(addr()); if (src) { /* PruneDesired if RPF'(*,G) != RPF'(S,G) */ if (upstream_neighbour() && src->upstream_neighbour() == upstream_neighbour()) return false; } return m_local_interest == pim_oif::Exclude || pim_source_state_base::join_desired(); } bool pim_group_source_rpt_state::state_desired() const { return m_local_interest == pim_oif::Exclude || pim_source_state_base::state_desired(); } void pim_group_source_rpt_state::rp_changed() { check_upstream_path(); } bool pim_group_source_rpt_state::output_info(base_stream &ctx) const { ctx.xprintf("(%{addr}, RPT) Uptime: %{duration}\n", addr(), time_duration(uptime())); output_common_info(ctx); ctx.inc_level(); ctx.xprintf("Local interest: %s\n", m_local_interest == pim_oif::Include ? "Include" : "Exclude"); ctx.dec_level(); return true; } void pim_group_source_rpt_state::oif_changed_state(pim_oif *oif, pim_oif::interest prev) { pim_oif::interest currint = oif->get_interest(); /* safeguard this source state instance, remove_oif * may trigger removal */ grab(); if (currint != pim_oif::Exclude) { if (!oif->has_interest() || currint == pim_oif::Include) remove_oif(oif->intf()); } /* get non-RPT state before check_interest as we may be removed */ pim_group_source_state *state = owner()->get_state(addr()); pim_oif *rptoif = state ? owner()->wildcard()->get_oif(oif->intf()) : 0; check_interest_and_update_upstream(); release(); /* even if we were removed we are safe as this stuff is in the stack */ if (rptoif) state->inherited_oif_changed_state(rptoif, prev); } void pim_group_source_rpt_state::wildcard_state_existance_changed(bool created) { if (!created) owner()->remove_state(this); } void pim_group_source_rpt_state::output_name(base_stream &os) const { os.xprintf("(%{addr}, RPT)", addr()); } pim_group_wildcard_state::pim_group_wildcard_state(pim_group_node *parent) : pim_source_state_common(parent, inet6_addr::any()) {} pim_group_wildcard_state::~pim_group_wildcard_state() { } void pim_group_wildcard_state::output_name(base_stream &os) const { os.write("(*)"); } void pim_group_wildcard_state::build_upstream_state() { pim_source_state_base::build_upstream_state(); owner()->rpt_upstream_changed(); } void pim_group_wildcard_state::rp_changed() { check_upstream_path(); } bool pim_group_wildcard_state::could_assert(interface *intf) const { if (!iif() || intf == iif()) return false; pim_oif *oif = get_oif(intf); if (oif) { return oif->get_interest() == pim_oif::Include; } return false; } void pim_group_wildcard_state::handle_assert(interface *intf, const in6_addr &from, bool rpt, uint32_t metric, uint32_t pref) { /* (*,G) Assert state machine */ pim_common_oif *oif = (pim_common_oif *)get_oif(intf); if (!oif) { return; } /* we can be sure it exists */ pim_interface *pintf = pim->get_interface(intf); pim_neighbour *neigh = pintf->get_neighbour(from); if (oif->current_assert_state() == pim_common_oif::AssertNoInfo) { if (could_assert(intf) && rpt && check_assert(intf, from, rpt, metric, pref)) { /* -> W state, [Actions A1] */ assert_wstate_actions1(oif); } else if (rpt && assert_tracking_desired(intf)) { /* -> L state, [Actions A2] */ assert_lstate_actions2(oif, neigh, metric, pref); } } else if (oif->current_assert_state() == pim_common_oif::WonAssert) { if (check_assert(intf, from, rpt, metric, pref)) { /* [Actions A3] */ send_assert(pintf); oif->restart_assert_timer_minus_override(); } else { /* L state, [Actions A2] */ assert_lstate_actions2(oif, neigh, metric, pref); } } else if (oif->current_assert_state() == pim_common_oif::LostAssert) { if (!check_assert(intf, from, rpt, metric, pref)) { /* L state, [Actions A2] */ assert_lstate_actions2(oif, neigh, metric, pref); } else if (neigh == oif->assert_winner()) { oif->change_assert_state(pim_common_oif::AssertNoInfo); } } } const in6_addr &pim_group_wildcard_state::join_target() const { return owner()->rpaddr(); } const in6_addr &pim_group_wildcard_state::join_destination() const { return owner()->rpaddr(); } bool pim_group_wildcard_state::output_info(base_stream &ctx) const { ctx.xprintf("(*) Uptime: %{duration}\n", time_duration(uptime())); output_common_info(ctx); return true; } void pim_group_wildcard_state::oif_changed_state(pim_oif *_oif, pim_oif::interest prevint) { pim_common_oif *oif = (pim_common_oif *)_oif; auto_grab grab(this); /* this must be called before as the remove_oif will remove oif */ owner()->inherited_oif_changed_state(oif, prevint); if (!oif->has_interest()) { remove_oif(oif->intf()); } else if (oif->get_interest() != pim_oif::Include) { /* If im Assert Winner -> NI state, [Actions A4] */ if (oif->current_assert_state() == pim_common_oif::WonAssert && !could_assert(oif->intf())) { oif->change_assert_state(pim_common_oif::AssertNoInfo, false); send_assert_cancel(oif->pim_intf()); } } check_interest_and_update_upstream(); } bool pim_group_wildcard_state::state_desired() const { return !m_oifs.empty(); } void pim_group_wildcard_state::removing_oif(pim_oif *oif) { owner()->inherited_oif_changed_state(oif, pim_oif::Include); } void pim_group_wildcard_state::upstream_changed() { /* Whenever the upstream for (*,G) or (S,G) changes, * notify the (S,G,rpt) state as join_desired() may change */ owner()->rpt_update_upstream(); } base_stream &pim_source_state_base::log() const { base_stream &os = owner()->log(); output_name(os); return os.write(" "); } mrd6-0.9.6/src/pim/test-cases/0000755000175000017500000000000010746353706014526 5ustar hugohugomrd6-0.9.6/src/pim/test-cases/hashmask.c0000644000175000017500000000070210745632462016466 0ustar hugohugo#include #include #include #include int main(int argc, char **argv) { struct in6_addr in; char buf[64]; int mask, i; inet_pton(AF_INET6, argv[1], &in); mask = atoi(argv[2]); if (mask < 128) { in.s6_addr[mask / 8] &= 0xff << (8 - mask % 8); for (i = (mask + 7) / 8; i < 16; i++) in.s6_addr[i] = 0; } printf("result: %s\n", inet_ntop(AF_INET6, &in, buf, sizeof(buf))); return 0; } mrd6-0.9.6/src/pim/pim_module.cpp0000644000175000017500000000322110550053317015273 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim_module.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include extern pim_router *pim; class pim_module : public mrd_module { public: pim_module(mrd *, void *dlh); bool check_startup(); void shutdown(); }; module_entry(pim, pim_module); pim_module::pim_module(mrd *m, void *dlh) : mrd_module(m, dlh) { } bool pim_module::check_startup() { pim = new pim_router(); if (!pim) return false; if (!g_mrd->register_router(pim)) { delete pim; pim = 0; return false; } return true; } void pim_module::shutdown() { g_mrd->unregister_router(pim); pim->shutdown(); delete pim; pim = 0; } mrd6-0.9.6/src/pim/pim_router.cpp0000644000175000017500000005163410550053317015341 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim_router.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include #include #include #include in6_addr pim_all_routers; sockaddr_in6 pim_all_routers_addr; pim_router *pim = 0; enum { pim_router_method_rpf = 9000, pim_router_method_group_rp, pim_router_method_group_summary, }; static const method_info pim_router_methods[] = { { "rpf-neighbor", "Displays RPF neighbor information", pim_router_method_rpf, true, 0 }, { "group-rp", "Displays the would-be RP for the specific group", pim_router_method_group_rp, true, 0 }, { "group-summary", "Displays a summary of active PIM group state", pim_router_method_group_summary, true, 0 }, { 0 } }; pim_router::pim_router() : router("pim"), pim_sock("pim", this, std::mem_fun(&pim_router::data_available)), m_gc("pim garbage collector", this, std::mem_fun(&pim_router::handle_garbage_collector), 5000, true) #ifndef PIM_NO_BSR , m_bsr(this) #endif { pim_all_routers = inet6_addr("ff02::d").address(); memset(&pim_all_routers_addr, 0, sizeof(pim_all_routers_addr)); pim_all_routers_addr.sin6_family = AF_INET6; pim_all_routers_addr.sin6_addr = pim_all_routers; } pim_router::~pim_router() { } const char *pim_router::description() const { return "Protocol Independent Multicast (PIM) Routing Protocol"; } bool pim_router::output_info(base_stream &ctx, const std::vector &args) const { if (!args.empty()) return false; ctx.writeline("PIM"); ctx.inc_level(); #ifndef PIM_NO_BSR m_bsr.output_info(ctx); #endif ctx.dec_level(); return true; } bool pim_router::check_startup() { if (!router::check_startup()) return false; if (m_properties.size() < 1) return false; #ifndef PIM_NO_BSR if (!bsr().check_startup()) return false; #endif if (!g_mrd->register_source_sink(this, true)) return false; import_methods(pim_router_methods); m_gc.start(); int sock = socket(AF_INET6, SOCK_RAW, IPPROTO_PIM); if (sock < 0) { g_mrd->log().perror("PIM: failed to create PIM socket"); return false; } if (!pim_sock.register_fd(sock)) { close(sock); return false; } if (!pim_sock.enable_mc_loop(false)) return false; pim_sock.set_mcast_hoplimit(1); return true; } base_stream &pim_router::log_router_desc(base_stream &os) const { return os.write("PIM, "); } bool pim_router::call_method(int id, base_stream &out, const std::vector &args) { if (id == pim_router_method_rpf) { if (args.size() != 1) return false; inet6_addr addr; if (!addr.set(args[0].c_str())) return false; pim_neighbour *neigh = get_rpf_neighbour(addr); if (neigh) { neigh->output_info(out, false); } else { out.writeline("No RPF neighbor."); } return true; } else if (id == pim_router_method_group_rp) { if (args.size() != 1) return false; inet6_addr addr; if (!addr.set(args[0].c_str())) return false; pim_groupconf_node *pconf = 0; groupconf *conf = g_mrd->match_group_configuration(addr); while (conf && !pconf) { pconf = (pim_groupconf_node *)conf->get_child("pim"); conf = g_mrd->get_similiar_groupconf_node(conf); } if (!pconf) { out.writeline("No available configuration."); return true; } in6_addr rpaddr; rp_source src; if (pconf->rp_for_group(addr, rpaddr, src)) { out.xprintf("RP: %{addr} [", rpaddr); if (src == rps_static) out.write("static"); else if (src == rps_embedded) out.write("embedded"); else if (src == rps_rp_set) out.write("rp_set"); else out.write("unknown"); out.writeline("]"); } else { out.writeline("No available RP"); } return true; } else if (id == pim_router_method_group_summary) { if (args.size() > 1) return false; inet6_addr mask; if (!args.empty()) { if (!mask.set(args[0].c_str())) return false; } mrd::group_list::const_iterator i = g_mrd->group_table().begin(); for (; i != g_mrd->group_table().end(); ++i) { if (!mask.matches(i->first)) continue; pim_group_node *grp = (pim_group_node *)i->second->node_owned_by(this); if (!grp) continue; out.xprintf("%{Addr}\n", i->first); out.inc_level(); if (grp->wildcard()) { out.xprintf("Wildcard present, RP is at %{addr}\n", grp->rpaddr()); } int count = grp->source_state_set().size(); out.xprintf("Has %i state%s.\n", count, count > 1 ? "s" : ""); int local = grp->local_source_state_set().size(); if (local > 0) { out.xprintf("Of which %i %s local.\n", local, local > 1 ? "are" : "is"); } out.dec_level(); } return true; } return router::call_method(id, out, args); } void pim_router::check_my_address(bool force) { if (!force && !m_my_address.is_any()) return; inet6_addr was = m_my_address; m_my_address = in6addr_any; const mrd::interface_list &intflist = g_mrd->intflist(); for (mrd::interface_list::const_iterator i = intflist.begin(); i != intflist.end(); ++i) { if (!i->second->up()) continue; const std::set &globals = i->second->globals(); for (std::set::const_iterator j = globals.begin(); j != globals.end(); ++j) { if (m_my_address.is_any() || *j < m_my_address) m_my_address = *j; } } if (!(was == m_my_address)) { if (!m_my_address.is_any()) { if (should_log(DEBUG)) log().xprintf("Primary global address is" " %{Addr}.\n", m_my_address); #ifndef PIM_NO_BSR if (was.is_any()) bsr().acquired_primary_address(); #endif } else if (!was.is_any()) { if (should_log(DEBUG)) log().writeline("Lost primary global address."); } } } void pim_router::shutdown() { if (should_log(DEBUG)) log().writeline("Shutdown"); g_mrd->register_source_sink(this, false); #ifndef PIM_NO_BSR bsr().leaving(); #endif mrd::group_list::const_iterator j; for (j = g_mrd->group_table().begin(); j != g_mrd->group_table().end(); ++j) { group_node *node = j->second->node_owned_by(this); if (node) { release_group((pim_group_node *)node); } } const mrd::interface_list &intflist = g_mrd->intflist(); for (mrd::interface_list::const_iterator i = intflist.begin(); i != intflist.end(); ++i) { pim_interface *intf = (pim_interface *)i->second->node_owned_by(this); if (intf) { intf->shutdown(); delete intf; } } #ifndef PIM_NO_BSR bsr().shutdown(); #endif pim_sock.unregister(); router::shutdown(); } void pim_router::handle_garbage_collector() { mrd::group_list::const_iterator i = g_mrd->group_table().begin(); while (i != g_mrd->group_table().end()) { group_node *node = i->second->node_owned_by(this); ++i; if (node) ((pim_group_node *)node)->garbage_collect(); } } void pim_router::interface_state_changed(pim_interface *intf, pim_interface::state) { switch (intf->get_state()) { case pim_interface::READY: check_my_address(false); break; default: break; } } void pim_router::created_group(group *grp) { pim_groupconf_node *ent = (pim_groupconf_node *)grp->conf()->create_child("pim"); if (!ent) return; pim_group_node *node = create_group(grp->id(), grp->conf()); if (node) { node->set_rp(); if (!node->attach(grp, ent)) { if (should_log(WARNING)) { log().xprintf("Failed to attach pim node to " "group %{Addr}\n", grp->id()); } } } else if (should_log(WARNING)) { log().xprintf("Failed to attach pim node to group %{Addr}\n", grp->id()); } } void pim_router::released_group(group *grp) { release_group((pim_group_node *)grp->node_owned_by(this)); } void pim_router::release_group(pim_group_node *node) { if (!node) return; if (node->owner()->node_owned_by(this) != node) return; node->owner()->dettach_node(node); delete node; } void pim_router::add_interface(interface *intf) { if (!intf->conf()->create_child("pim")) return; pim_interface *pimintf = new pim_interface(); if (!pimintf || !pimintf->check_startup()) { delete pimintf; return; } if (!intf->attach_node(pimintf)) { pimintf->shutdown(); delete pimintf; } } void pim_router::remove_interface(interface *intf) { pim_interface *pimintf = (pim_interface *)intf->node_owned_by(this); if (!pimintf) { return; } pimintf->shutdown(); delete pimintf; if (intf->globals().find(m_my_address) != intf->globals().end()) { check_my_address(true); } } intfconf_node *pim_router::create_interface_configuration(intfconf *conf) { return new pim_intfconf_node(conf); } groupconf_node *pim_router::create_group_configuration(groupconf *conf) { return new pim_groupconf_node(conf); } pim_interface *pim_router::get_interface(interface *intf) const { if (!intf) return 0; return (pim_interface *)intf->node_owned_by(this); } pim_interface *pim_router::get_interface(int dev) const { return get_interface(g_mrd->get_interface_by_index(dev)); } pim_group_node *pim_router::create_group(const inet6_addr &addr, node *conf) { pim_group_node *node = get_group(addr); if (!node) { return new pim_group_node(this, addr, (pim_groupconf_node *)conf->get_or_create_child("pim")); } return node; } pim_group_node *pim_router::get_group(const inet6_addr &addr) const { group *grp = g_mrd->get_group_by_addr(addr); if (grp) return (pim_group_node *)grp->node_owned_by(this); return 0; } void pim_router::found_new_neighbour(pim_neighbour *neigh) const { mrd::group_list::const_iterator j = g_mrd->group_table().begin(); for (; j != g_mrd->group_table().end(); ++j) { group_node *node = j->second->node_owned_by(this); if (node) { ((pim_group_node *)node)->found_new_neighbour(neigh); } } } void pim_router::lost_neighbour(pim_neighbour *neigh) const { mrd::group_list::const_iterator j = g_mrd->group_table().begin(); for (; j != g_mrd->group_table().end(); ++j) { group_node *node = j->second->node_owned_by(this); if (node) { ((pim_group_node *)node)->lost_neighbour(neigh); } } } pim_neighbour *pim_router::get_neighbour(const inet6_addr &addr) const { for (mrd::interface_list::const_iterator i = g_mrd->intflist().begin(); i != g_mrd->intflist().end(); ++i) { const interface *intf = i->second; pim_interface *pimintf = (pim_interface *)intf->node_owned_by(this); if (pimintf) { pim_neighbour *neigh = pimintf->get_neighbour(addr); if (neigh) return neigh; } } return 0; } pim_neighbour *pim_router::get_rpf_neighbour(const in6_addr &addr) const { inet6_addr nh; const mrib_def::prefix *p = g_mrd->mrib().resolve_nexthop(addr, inet6_addr::any(), nh); if (p && p->is_valid()) { if (!p->intf) return 0; pim_interface *pintf = get_interface(p->intf); if (pintf) { return pintf->get_neighbour(nh); } } return 0; } void pim_router::data_available(uint32_t) { int recvlen = pim_sock.recvfrom(g_mrd->ipktb->buffer(), g_mrd->ipktb->bufferlen()); if (recvlen < 0) { if (should_log(WARNING)) log().perror("recv failed"); return; } if (recvlen < (int)sizeof(pim_message)) { // discard return; } sockaddr_in6 dst; int index; if (!pim_sock.destination_address(dst, index) || index == 0) { pim_message *pimmsg = g_mrd->ipktb->header(); if (should_log(INTERNAL_FLOW)) { log().xprintf("Dropped %s message from %{addr}, no " "input interface.\n", pimmsg->type_name(), pim_sock.source_address().sin6_addr); } return; } g_mrd->ipktb->rlength = recvlen; g_mrd->ipktb->read_offset = 0; pim_interface *pimintf = get_interface(index); if (!pimintf) { pim_message *pimmsg = g_mrd->ipktb->header(); if (should_log(INTERNAL_FLOW)) { log().xprintf("Dropped %s message from %{addr}, PIM " "interface %i is disabled.\n", pimmsg->type_name(), pim_sock.source_address().sin6_addr, index); } return; } g_mrd->ipktb->source = pimintf->owner(); sockaddr_in6 _recvfrom = pim_sock.source_address(); pimintf->data_available(&_recvfrom, &dst); } std::list pim_router::all_global_addrs() const { std::list addrs; const mrd::interface_list &intflist = g_mrd->intflist(); for (mrd::interface_list::const_iterator i = intflist.begin(); i != intflist.end(); ++i) { if (!i->second->up()) continue; const std::set &gs = i->second->globals(); for (std::set::const_iterator j = gs.begin(); j != gs.end(); ++j) { addrs.push_back(*j); } } return addrs; } bool pim_router::sendmsg(const sockaddr_in6 *src, sockaddr_in6 *dst, pim_message *msg, uint16_t len) const { sockaddr_in6 calc_src; if (!src) { src = &calc_src; if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr)) { interface *intf = g_mrd->get_interface_by_index(dst->sin6_scope_id); if (!intf) return false; calc_src = *intf->localaddr(); } else if (!IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) { if (m_my_address.is_any()) { if (should_log(DEBUG)) { log().xprintf("Failed while sending %s" " to %{addr}, no source " "address.", msg->type_name(), dst->sin6_addr); } return false; } memset(&calc_src, 0, sizeof(sockaddr_in6)); calc_src.sin6_family = AF_INET6; calc_src.sin6_addr = m_my_address; } else { if (should_log(DEBUG)) { log().xprintf("Trying to send %s to %{addr} " "without source address", msg->type_name(), dst->sin6_addr); } return false; } } if (msg->checksum == 0) { msg->build_checksum(src->sin6_addr, dst->sin6_addr, len); } if (msg->type() != pim_msg_register && should_log(MESSAGE_SIG)) { log().xprintf("Sending %s message from %{addr} to %{addr} len " "%u\n", msg->type_name(), src->sin6_addr, dst->sin6_addr, (uint32_t)len); } if (pim_sock.sendto(msg, len, dst, src) < 0) { if (should_log(MESSAGE_ERR)) { log().xprintf("sendmsg to %{addr}%%%i from %{addr}%%%i" " failed: %s\n", dst->sin6_addr, (int)dst->sin6_scope_id, src->sin6_addr, (int)src->sin6_scope_id, strerror(errno)); } return false; } return true; } bool pim_router::send_all(pim_message *msg, uint16_t len, const sockaddr_in6 *from) const { bool res = true; for (mrd::interface_list::const_iterator i = g_mrd->intflist().begin(); i != g_mrd->intflist().end(); ++i) { if (i->second->linklocals().empty()) continue; /* Force the build of the checksum */ msg->checksum = 0; pim_interface *intf = (pim_interface *)i->second->node_owned_by(this); if (intf) { if (intf->get_state() == pim_interface::NOT_READY) continue; if (from) { if (!sendmsg(from, &pim_all_routers_addr, msg, len)) res = false; } else if (!i->second->linklocals().empty()) { if (!sendmsg(i->second->localaddr(), &pim_all_routers_addr, msg, len)) res = false; } } } return res; } bool pim_router::send_all_neighbours(pim_message *msg, uint16_t len, const sockaddr_in6 *from) const { bool res = true; for (mrd::interface_list::const_iterator i = g_mrd->intflist().begin(); i != g_mrd->intflist().end(); ++i) { if (i->second->linklocals().empty()) continue; /* Force the build of the checksum */ msg->checksum = 0; pim_interface *intf = (pim_interface *)i->second->node_owned_by(this); if (intf) { if (intf->get_state() == pim_interface::NOT_READY) continue; if (intf->get_neighbours().empty()) continue; if (from) { if (!sendmsg(from, &pim_all_routers_addr, msg, len)) res = false; } else if (!i->second->linklocals().empty()) { if (!sendmsg(i->second->localaddr(), &pim_all_routers_addr, msg, len)) res = false; } } } return res; } bool pim_router::send_register(const in6_addr &src, const in6_addr &dst, pim_register_message *msg, int payload) const { return send_register_generic(src, dst, msg, payload, 0 /* XXX */); } bool pim_router::send_register_probe(const in6_addr &src, const in6_addr &dst, pim_register_message *msg, int payload) const { return send_register_generic(src, dst, msg, payload, 0 /* XXX */); } bool pim_router::send_register_generic(const in6_addr &src, const in6_addr &dst, pim_register_message *msg, int payload, int statname) const { sockaddr_in6 srcaddr; memset(&srcaddr, 0, sizeof(srcaddr)); srcaddr.sin6_family = AF_INET6; srcaddr.sin6_addr = src; sockaddr_in6 addr; memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_addr = dst; msg->build_checksum(src, dst, sizeof(pim_register_message)); if (sendmsg(&srcaddr, &addr, msg, sizeof(pim_register_message) + payload)) { /* m_stats.counter(statname)++; */ return true; } return false; } void pim_router::send_register_stop_to_router(const inet6_addr &grpid, const in6_addr &from, const in6_addr &src, const in6_addr &to) const { pim_register_stop_message *msg = g_mrd->opktb->header(); msg->construct(grpid, src); sockaddr_in6 myaddr, dst; memset(&myaddr, 0, sizeof(myaddr)); memset(&dst, 0, sizeof(dst)); myaddr.sin6_family = AF_INET6; myaddr.sin6_addr = from; dst.sin6_family = AF_INET6; dst.sin6_addr = to; pim->sendmsg(&myaddr, &dst, msg, sizeof(pim_register_stop_message)); } void pim_router::dr_changed(pim_interface *intf, bool islocal) { mrd::group_list::const_iterator j = g_mrd->group_table().begin(); for (; j != g_mrd->group_table().end(); ++j) { group_node *node = j->second->node_owned_by(this); if (node) { ((pim_group_node *)node)->dr_changed(intf, islocal); } } } void pim_router::event(int event, void *ptr) { if (event != mrd::CreatedGroup) { router::event(event, ptr); return; } mrd::create_group_context *ctx = (mrd::create_group_context *)ptr; if (ctx->result) { pim_group_node *gr = get_group(ctx->groupaddr); if (gr) { /* XXX origin=0, force origin existance? */ source_discovery_origin *origin = g_mrd->get_source_discovery(ctx->origin_name.c_str()); interface *intf = g_mrd->get_interface_by_index(ctx->iif); gr->discovered_source(intf, ctx->requester, origin); } } delete ctx; } void pim_router::discovered_source(interface *input, const inet6_addr &groupaddr, const inet6_addr &sourceaddr, source_discovery_origin *origin) { pim_group_node *gr = get_group(groupaddr); if (gr) { gr->discovered_source(input, sourceaddr, origin); } else { /* only create groups for local sources */ if (!g_mrd->in_same_subnet(sourceaddr)) { if (should_log(MESSAGE_SIG)) { log().xprintf("Not creating Group state for " "(%{Addr}, %{Addr}) as it isn't " "local: source address doesn't " "match any of the router's " "prefixes.\n", sourceaddr, groupaddr); } return; } mrd::create_group_context *ctx = new mrd::create_group_context; if (!ctx) return; ctx->iif = input ? input->index() : 0; ctx->groupaddr = groupaddr; ctx->requester = sourceaddr; if (origin) ctx->origin_name = origin->origin_description(); g_mrd->create_group(this, this, ctx); } } void pim_router::mfa_notify(mfa_group_source *srcstate, const in6_addr &grp, const in6_addr &src, uint32_t flags, mfa_group_source::action act, interface *iif, ip6_hdr *hdr, uint16_t plen, uint16_t flen) { pim_group_node *node = 0; pim_group_source_state *state = 0; if (!srcstate || !srcstate->instowner) { node = get_group(grp); if (!node) return; state = node->get_state(src); if (!state) return; } else { state = (pim_group_source_state *)srcstate->instowner; node = state->owner(); } if (!state->spt()) { if (state->iif() == iif) { state->set_spt(true); } else if (node->has_wildcard() && node->wildcard()->iif() != iif) { /* XXX We will never reach here as in our current * design, iif of (S,G) is always RPF_interface(S) */ state->set_spt(true); } } if (state->iif() == iif && state->is_source_local()) { state->restart_kat(); /* encapsulate locally received packets to the RP */ if (flags & mfa_group_source::f_any_incoming && !node->is_ssm() && !node->is_self_rp()) state->forward_to_rp(iif, hdr, plen); } else if (flags & mfa_group_source::f_wrong_iif) { /* if the incoming interface is a Oif for (S,G) */ state->trigger_assert(iif); } } mrd6-0.9.6/src/pim/pim_def.cpp0000644000175000017500000002606710745634621014572 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim_def.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #define _BIT(x) (1 << (x)) #define _TEST(x, y) (((x) & _BIT(y)) != 0) void pim_message::construct(pim_msg_type t) { vt = 0x20 | t; resv1 = 0; checksum = 0; } const char *pim_message::type_name() const { switch (type()) { case pim_msg_hello: return "HELLO"; case pim_msg_register: return "REGISTER"; case pim_msg_register_stop: return "REGISTER-STOP"; case pim_msg_joinprune: return "JOIN/PRUNE"; case pim_msg_bootstrap: return "BOOTSTRAP"; case pim_msg_assert: return "ASSERT"; case pim_msg_candidate_rp_adv: return "CANDIDATE-RP-ADV"; default: return "UNKNOWN"; } } void pim_message::build_checksum(const in6_addr &src, const in6_addr &dst, int len) { checksum = 0; checksum = ipv6_checksum(IPPROTO_PIM, src, dst, this, len); } bool pim_message::has_valid_checksum(const in6_addr &src, const in6_addr &dst, int len) { uint16_t cksum = checksum; checksum = 0; uint16_t calc = ipv6_checksum(IPPROTO_PIM, src, dst, this, len); checksum = cksum; return checksum == calc; } void pim_hello_option::construct(uint16_t t, uint16_t l) { type = hton(t); length = hton(l); } void pim_hello_option::add_uint16(uint16_t type, uint16_t value) { construct(type, 2); data16()[0] = hton(value); } void pim_hello_option::add_uint16pair(uint16_t type, uint16_t v1, uint16_t v2) { construct(type, 4); data16()[0] = hton(v1); data16()[1] = hton(v2); } void pim_hello_option::add_uint32(uint16_t type, uint32_t value) { construct(type, 4); data32()[0] = hton(value); } pim_hello_option *pim_hello_option::next() const { return (pim_hello_option *)(((uint8_t *)this) + sizeof(*this) + ntoh(length)); } void *pim_hello_option::data() { return (((uint8_t *)this) + sizeof(*this)); } void pim_hello_message::construct() { pim_message::construct(pim_msg_hello); } pim_hello_option *pim_hello_message::options() { return (pim_hello_option *)(((uint8_t *)this) + sizeof(*this)); } void pim_register_message::construct(bool border, bool null) { pim_message::construct(pim_msg_register); uint32_t flags = 0; if (border) flags |= _BIT(31); if (null) flags |= _BIT(30); nb = hton(flags); } ip6_hdr *pim_register_message::ip6_header() { return (ip6_hdr *)(((uint8_t *)this) + sizeof(*this)); } bool pim_register_message::border() const { return _TEST(ntoh(nb), 31); } bool pim_register_message::null() const { return _TEST(ntoh(nb), 30); } void pim_encoded_unicast_address::construct(const in6_addr &ma) { family = pim_addr_ip6; type = 0; addr = ma; } void pim_encoded_group_address::construct(const inet6_addr &ma, bool z, bool b) { family = pim_addr_ip6; type = 0; zb = 0; if (z) zb |= _BIT(0); if (b) zb |= _BIT(7); masklen = ma.prefixlen; addr = ma.address(); } void pim_encoded_group_address::construct(const inet6_addr &ma) { construct(ma, false, false); } bool pim_encoded_group_address::bidir() const { return _TEST(zb, 0); } bool pim_encoded_group_address::zoned() const { return _TEST(zb, 7); } void pim_join_attribute_tlv::construct(bool f, bool b, int type, int len) { fs_type = type; if (f) fs_type |= _BIT(7); if (b) fs_type |= _BIT(6); length = len; } void *pim_join_attribute_tlv::data() const { return ((uint8_t *)this) + 2; } bool pim_join_attribute_tlv::forward() const { return _TEST(fs_type, 7); } bool pim_join_attribute_tlv::bottom() const { return _TEST(fs_type, 6); } void pim_encoded_source_address::construct(const inet6_addr &ma, bool w, bool r) { family = pim_addr_ip6; type = 0; flags = _BIT(2); if (w) flags |= _BIT(1); if (r) flags |= _BIT(0); masklen = ma.prefixlen; addr = ma.address(); } bool pim_encoded_source_address::sparse() const { return _TEST(flags, 2); } bool pim_encoded_source_address::wc() const { return _TEST(flags, 1); } bool pim_encoded_source_address::rpt() const { return _TEST(flags, 0); } int pim_encoded_source_address::length() const { return sizeof(pim_encoded_source_address); } pim_encoded_source_address *pim_encoded_source_address::next() const { return (pim_encoded_source_address *)(((uint8_t *)this) + length()); } void pim_register_stop_message::construct(const inet6_addr &ga, const inet6_addr &sa) { pim_message::construct(pim_msg_register_stop); gaddr.construct(ga); uaddr.construct(sa); } void pim_joinprune_group::construct(const inet6_addr &addr, uint16_t js, uint16_t ps) { maddr.construct(addr); njoins = hton(js); nprunes = hton(ps); } uint16_t pim_joinprune_group::length() const { int total = sizeof(*this); int count = join_count() + prune_count(); pim_encoded_source_address *addr = addrs(); for (int i = 0; i < count; i++, addr = addr->next()) { total += addr->length(); } return total; } address_set &pim_joinprune_group::pruned_addrs(address_set &as) const { pim_encoded_source_address *pa = addrs(); for (int i = 0; i < join_count(); i++, pa = pa->next()); for (uint16_t i = 0; i < prune_count(); i++, pa = pa->next()) { as += pa->addr; } return as; } bool pim_joinprune_group::has_prune_addr(const inet6_addr &addr) const { pim_encoded_source_address *pa = addrs(); for (int i = 0; i < join_count(); i++, pa = pa->next()); for (uint16_t i = 0; i < prune_count(); i++, pa = pa->next()) { if (pa->addr == addr.address()) return true; } return false; } pim_joinprune_group *pim_joinprune_group::next() const { return (pim_joinprune_group *)(((uint8_t *)this) + length()); } void pim_joinprune_message::construct(const inet6_addr &neigh, uint8_t groups, uint16_t time) { pim_message::construct(pim_msg_joinprune); upstream_neigh.construct(neigh); resv1 = 0; ngroups = groups; ht = hton(time); } uint32_t pim_joinprune_message::holdtime() const { return (uint32_t)ntoh(ht) * 1000; } uint16_t pim_joinprune_message::length() const { uint16_t len = sizeof(*this); pim_joinprune_group *grp = groups(); for (uint8_t i = 0; i < ngroups; i++, grp = grp->next()) { len += grp->length(); } return len; } pim_joinprune_group *pim_joinprune_message::groups() const { return (pim_joinprune_group *)(((uint8_t *)this) + sizeof(*this)); } pim_bootstrap_rp_record *pim_bootstrap_group_def::rps() const { return (pim_bootstrap_rp_record *)(((uint8_t *)this) + sizeof(*this)); } uint16_t pim_bootstrap_group_def::length() const { return sizeof(*this) + fragrp * sizeof(pim_bootstrap_rp_record); } pim_bootstrap_group_def *pim_bootstrap_group_def::next() const { return (pim_bootstrap_group_def *)(((uint8_t *)this) + length()); } void pim_bootstrap_message::construct(uint16_t frag, uint8_t ml, uint8_t prio, const in6_addr &addr) { pim_message::construct(pim_msg_bootstrap); fragment = hton(frag); hash_masklen = ml; bsr_priority = prio; bsr_address.construct(addr); } pim_bootstrap_group_def *pim_bootstrap_message::grps() const { return (pim_bootstrap_group_def *)(((uint8_t *)this) + sizeof(*this)); } bool pim_bootstrap_message::no_forward() const { return (resv1 & (1 << 7)) != 0; } void pim_candidate_rp_adv_message::construct(uint8_t pfxct, uint8_t prio, uint16_t ht, const in6_addr &addr) { pim_message::construct(pim_msg_candidate_rp_adv); prefixcount = pfxct; priority = prio; holdtime = hton(ht); rp_addr.construct(addr); } pim_encoded_group_address *pim_candidate_rp_adv_message::grps() const { return (pim_encoded_group_address *)(((uint8_t *)this) + sizeof(*this)); } uint16_t pim_candidate_rp_adv_message::length() const { return sizeof(*this) + prefixcount * sizeof(pim_encoded_group_address); } void pim_assert_message::construct(const inet6_addr &grp, const in6_addr &src, bool _rpt, uint32_t pref, uint32_t met) { pim_message::construct(pim_msg_assert); gaddr.construct(grp); saddr.construct(src); pref &= ~_BIT(31); if (_rpt) pref |= _BIT(31); metpref = hton(pref); metric = hton(met); } bool pim_assert_message::rpt() const { return _TEST(ntoh(metpref), 31); } uint32_t pim_assert_message::metric_pref() const { return ntoh(metpref) & ~_BIT(31); } static void _do_encoded_address(base_stream &os, const char *type, const pim_encoded_source_address &addr) { os.xprintf("%s: %{Addr}", type, inet6_addr(addr.addr, addr.masklen)); if (addr.rpt()) os.write(" RPT"); if (addr.wc()) os.write(" WC"); os.newl(); } void _debug_pim_dump(base_stream &os, const pim_joinprune_message &msg) { os.xprintf("PIM J/P for %{addr} with holdtime %u\n", msg.upstream_neigh.addr, msg.holdtime()); int i; pim_joinprune_group *grp = msg.groups(); os.inc_level(); for (i = 0; i < msg.ngroups; i++, grp = grp->next()) { os.writeline(inet6_addr(grp->maddr.addr, grp->maddr.masklen)); os.inc_level(); for (pim_jp_g_iterator i = grp->join_begin(); i != grp->join_end(); ++i) _do_encoded_address(os, "Join", *i); for (pim_jp_g_iterator i = grp->prune_begin(); i != grp->prune_end(); ++i) _do_encoded_address(os, "Prune", *i); os.dec_level(); } os.dec_level(); } void _debug_pim_dump(base_stream &os, const pim_assert_message &msg) { os.xprintf("PIM Assert for (%{addr}, %{addr})%s Pref %u Metric %u\n", msg.saddr.addr, msg.gaddr.addr, msg.rpt() ? " RPT" : "", msg.metric_pref(), ntoh(msg.metric)); } void _debug_pim_dump(base_stream &os, const pim_bootstrap_message &msg, int len) { os.xprintf("PIM Bootstrap from BSR %{addr}, frag %u, masklen %u, prio %u\n", msg.bsr_address.addr, (uint32_t)ntoh(msg.fragment), (uint32_t)msg.hash_masklen, (uint32_t)msg.bsr_priority); int off = sizeof(pim_bootstrap_message); for (pim_bootstrap_group_def *grp = msg.grps(); off < len; grp = grp->next()) { if ((off + (int)sizeof(pim_bootstrap_group_def)) > len || (off + grp->length()) > len) { os.writeline("Badly formed message."); return; } off += grp->length(); } os.inc_level(); off = sizeof(pim_bootstrap_message); for (pim_bootstrap_group_def *grp = msg.grps(); off < len; grp = grp->next()) { os.writeline(inet6_addr(grp->grpaddr.addr, grp->grpaddr.masklen)); os.inc_level(); pim_bootstrap_rp_record *rp = grp->rps(); for (int j = 0; j < grp->fragrp; j++) { os.xprintf("%{addr}, prio = %i, holdtime = %u\n", rp->addr.addr, (int)rp->priority, (uint32_t)ntoh(rp->holdtime)); rp++; } os.dec_level(); off += grp->length(); } os.dec_level(); } mrd6-0.9.6/src/pim/pim_conf.cpp0000644000175000017500000002541710746336636014765 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim_conf.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include struct pim_property { const char *name; uint32_t value; const char *description; }; static pim_property pim_intf_props[] = { { "hello-interval", 30000, "Sets the interval between PIM Hello messages" }, { "joinprune-interval", 60000, "Sets the interval between PIM Join/Prune periodic messages" }, { "data-timeout", 210000, "Sets the time after which a (S,G) state for an inactive source is deleted" }, { "register-supression-timeout", 60000, "Sets the Register-Stop state expiry time" }, { "probe-time", 5000, "Sets the time before a Register-Stop state expiring in which a probe should be sent" }, { "assert-timeout", 180000, "Sets the PIM Assert timeout" }, { "random-delay-join-timeout", 4500, 0 }, { "dr-priority", 1, "Sets the interface DR priority" }, { "register-stop-rate-limit", 30, 0 }, { "register-stop-rate-timelen", 10000, 0 }, { "propagation-delay", 500, 0 }, { "override-interval", 2500, 0 }, { 0 } }; static struct propval_enum::entry rp_rej_entries[] = { { "register-stop", pim_groupconf_node::RPRejRegisterStop }, { "silent-ignore", pim_groupconf_node::RPRejSilentIgnore }, { "log-ignore", pim_groupconf_node::RPRejLogIgnore }, { 0 } }; enum { pim_intfconf_method_neighbor_acl = 1000, }; static struct method_info pim_intfconf_methods[] = { { "neighbor-acl", "Defines the neighbor access list", pim_intfconf_method_neighbor_acl, false, 0 }, { 0 } }; enum { pim_groupconf_method_rp_source_acl = 1000, }; static struct method_info pim_groupconf_methods[] = { { "rp-source-acl", "Defines the register source access list", pim_groupconf_method_rp_source_acl, false, 0 }, { 0 } }; bool pim_source_filter::accepts(const in6_addr &src) const { for (std::set::const_iterator i = sources.begin(); i != sources.end(); ++i) { if (i->matches(src)) { return !filter_mode; } } /* no match */ return filter_mode; } static bool _parse_filter(pim_source_filter &flt, const std::vector &args) { if (args.empty()) return false; if (args[0] != "accept" && args[0] != "reject") return false; bool new_filter = (args[0] == "reject"); std::vector::const_iterator i = args.begin(); ++i; std::set addrs; for (; i != args.end(); ++i) { inet6_addr addr; if (!addr.set(*i)) return false; addrs.insert(addr); } flt.filter_mode = new_filter; flt.sources = addrs; /* XXX act? */ return true; } pim_intfconf_node::pim_intfconf_node(intfconf *conf) : intfconf_node(conf, "pim") { neigh_acl.filter_mode = true; } bool pim_intfconf_node::check_startup() { if (!base::check_startup()) return false; import_methods(pim_intfconf_methods); return true; } bool pim_intfconf_node::fill_defaults() { for (pim_property *p = pim_intf_props; p->name; p++) instantiate_property_u(p->name, p->value, p->description); instantiate_property_b("cisco-old-addrlist", false); return m_properties.size() == 15; } bool pim_intfconf_node::set_property(const char *key, const char *val) { if (!next_similiar_node()->has_property(key)) return false; if (!strcmp(key, "cisco-old-addrlist")) return set_property_inst(key, property_def::VAL_BOOL, val); return set_property_inst(key, property_def::VAL_UNSIGNED, val); } bool pim_intfconf_node::call_method(int id, base_stream &out, const std::vector &args) { switch (id) { case pim_intfconf_method_neighbor_acl: return _parse_filter(neigh_acl, args); } return intfconf_node::call_method(id, out, args); } uint32_t pim_intfconf_node::hello_interval() const { return get_property_unsigned("hello-interval"); } uint32_t pim_intfconf_node::holdtime() const { return (uint32_t)(hello_interval() * 3.5); } uint32_t pim_intfconf_node::joinprune_interval() const { return get_property_unsigned("joinprune-interval"); } uint32_t pim_intfconf_node::joinprune_holdtime() const { return (uint32_t)(joinprune_interval() * 3.5); } uint32_t pim_intfconf_node::joinprune_supression_timeout() const { return (uint32_t)(joinprune_interval() * 1.25); } uint32_t pim_intfconf_node::data_timeout() const { return get_property_unsigned("data-timeout"); } uint32_t pim_intfconf_node::register_supression_timeout() const { return get_property_unsigned("register-supression-timeout"); } uint32_t pim_intfconf_node::probe_time() const { return get_property_unsigned("probe-time"); } uint32_t pim_intfconf_node::assert_timeout() const { return get_property_unsigned("assert-timeout"); } uint32_t pim_intfconf_node::random_delay_join_timeout() const { return get_property_unsigned("random-delay-join-timeout"); } uint32_t pim_intfconf_node::dr_priority() const { return get_property_unsigned("dr-priority"); } uint32_t pim_intfconf_node::register_stop_rate_limit() const { return get_property_unsigned("register-stop-rate-limit"); } uint32_t pim_intfconf_node::register_stop_rate_timelen() const { return get_property_unsigned("register-stop-rate-timelen"); } uint32_t pim_intfconf_node::propagation_delay() const { return get_property_unsigned("propagation-delay"); } uint32_t pim_intfconf_node::override_interval() const { return get_property_unsigned("override-interval"); } bool pim_intfconf_node::support_old_cisco_addrlist() const { return get_property_bool("cisco-old-addrlist"); } bool pim_intfconf_node::neigh_acl_accepts(const in6_addr &src) const { return neigh_acl.accepts(src); } pim_groupconf_node::pim_groupconf_node(groupconf *conf) : groupconf_node(conf, "pim") { } bool pim_groupconf_node::check_startup() { if (!base::check_startup()) return false; import_methods(pim_groupconf_methods); return true; } bool pim_groupconf_node::fill_defaults() { instantiate_property_a("rp", inet6_addr::any()); instantiate_property_a("accept_rp", inet6_addr(in6addr_any, 0)); instantiate_property_b("rp_adv", false); instantiate_property("rp-rejected-source-policy", new propval_enum(rp_rej_entries)); instantiate_property_b("rp-embedded-auto-source-acl", false); #if 0 m_properties["use_spt"] = always_use; m_properties["rp_acl"] = rpa_any; m_properties["rp_acls"] = std::vector(); std::vector rp_pref; rp_pref.push_back("embedded"); rp_pref.push_back("rp_set"); rp_pref.push_back("static"); m_properties["rp_pref"] = rp_pref; #endif return m_properties.size() == 4; } bool pim_groupconf_node::set_property(const char *key, const char *value) { if (!strcmp(key, "rp")) { if (!strcmp(value, "none")) return set_property_inst("rp", property_def::VAL_ADDRESS, "::/128"); else return set_property_inst("rp", property_def::VAL_ADDRESS, value); } else if (!strcmp(key, "accept_rp")) { if (!strcmp(value, "none")) { return set_property_inst("rp", property_def::VAL_ADDRESS, "::/128"); } else if (strcmp(value, "embedded") == 0) { inet6_addr tmp; if (!pim_group_node::calculate_embedded_rp_addr(((groupconf *)parent())->id(), tmp)) { if (pim->should_log(WARNING)) { pim->log().writeline("Group doesn't follow Embedded-RP specification, " "changing accept_rp to any."); } return false; } /* sigh */ return set_property_inst("accept_rp", property_def::VAL_ADDRESS, tmp.as_string().c_str()); } } else if (!strcmp(key, "rp_adv")) { #ifndef PIM_NO_BSR bool prev = get_property_bool("rp_adv"); if (!set_property_inst("rp_adv", property_def::VAL_BOOL, value)) return false; if (prev != get_property_bool("rp_adv")) pim->bsr().enable_rp_adv(((groupconf *)parent())->id(), !prev); return true; #else return false; #endif } else if (!strcmp(key, "rp-rejected-source-policy")) { if (!has_property("rp-rejected-source-policy")) { if (!instantiate_property("rp-rejected-source-policy", new propval_enum(rp_rej_entries))) return false; } } else if (!strcmp(key, "rp-embedded-auto-source-acl")) { return set_property_inst("rp-embedded-auto-source-acl", property_def::VAL_BOOL, value); } return groupconf_node::set_property(key, value); } bool pim_groupconf_node::call_method(int id, base_stream &out, const std::vector &args) { switch (id) { case pim_groupconf_method_rp_source_acl: return _parse_filter(rp_source_acl, args); } return groupconf_node::call_method(id, out, args); } bool pim_groupconf_node::increment_property(const char *key, const char *value) { #if 0 if (key == "rp_acls") { inet6_addr addr; if (addr.set(value)) { std::vector addrs = get_value("rp_acls")->as >(); addrs.push_back(addr); m_properties["rp_acls"] = addrs; } else { return false; } } else { #endif return false; #if 0 } return true; #endif } bool pim_groupconf_node::rp_for_group(const in6_addr &grpaddr, in6_addr &rpaddr, rp_source &src) const { bool R_bit = grpaddr.s6_addr[1] & 0x40; bool P_bit = grpaddr.s6_addr[1] & 0x20; bool T_bit = grpaddr.s6_addr[1] & 0x10; if (P_bit && T_bit && R_bit) { /* Embedded-RP address */ inet6_addr tmp; pim_group_node::calculate_embedded_rp_addr(grpaddr, tmp); rpaddr = tmp; src = rps_embedded; return true; } rpaddr = get_property_address("rp"); if (!IN6_IS_ADDR_UNSPECIFIED(&rpaddr)) { src = rps_static; return true; } #ifndef PIM_NO_BSR rpaddr = pim->bsr().rp_from_rpset(grpaddr); if (!IN6_IS_ADDR_UNSPECIFIED(&rpaddr)) { src = rps_rp_set; return true; } #endif return false; } int pim_groupconf_node::rp_rejected_source_policy() const { return get_property_integer("rp-rejected-source-policy"); } bool pim_groupconf_node::rp_source_acl_accepts(const pim_group_node *gn, const in6_addr &src) const { if (gn->is_embedded()) { if (get_property_bool("rp-embedded-auto-source-acl")) { return gn->embedded_rp_addr().matches(src); } } return rp_source_acl.accepts(src); } mrd6-0.9.6/src/pim/pim_oif.cpp0000644000175000017500000002454710550053317014601 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * pim_oif.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include #include const char *_oif_interest(pim_oif::interest i) { switch (i) { case pim_oif::NoInfo: return "NoInfo"; case pim_oif::Include: return "Include"; case pim_oif::Exclude: return "Exclude"; } return 0; } pim_oif::pim_oif(pim_source_state_base *state, interface *intf) : m_state(state), m_intf(intf), m_timer("", this, std::mem_fun(&pim_oif::timed_out)), m_pp_timer("", this, std::mem_fun(&pim_oif::pp_timed_out)) { m_timer.name = "pim_oif "; m_timer.name += intf->name(); m_pp_timer.name = "pim oif prune pending "; m_pp_timer.name += intf->name(); m_local = NoInfo; m_jpstate = JPNoInfo; } pim_oif::~pim_oif() { } pim_interface *pim_oif::pim_intf() const { return pim->get_interface(m_intf); } void pim_oif::change_local_membership(interest l) { interest prev = get_interest(); m_local = l; changed_state(prev); } static const char *_state_name(pim_oif::state st) { if (st == pim_oif::JPNoInfo) { return "NoInfo"; } else if (st == pim_oif::Joined) { return "Joined"; } else if (st == pim_oif::PendingPrune) { return "PendingPrune"; } else if (st == pim_oif::Pruned) { return "Pruned"; } else { return "Unknown"; } } bool pim_oif::change_state(state ns) { if (ns == m_jpstate) return false; interest prev = get_interest(); if (m_state->owner()->should_log(EXTRADEBUG)) { log().xprintf("changed J/P State %s -> %s\n", _state_name(m_jpstate), _state_name(ns)); } m_jpstate = ns; if (m_jpstate == JPNoInfo) { m_timer.stop(); m_pp_timer.stop(); } changed_state(prev); return true; } void pim_oif::changed_state(interest prev) { if (prev == get_interest()) return; if (m_state->owner()->should_log(EXTRADEBUG)) { log().xprintf("Changed state %s -> %s\n", _oif_interest(prev), _oif_interest(get_interest())); } m_state->oif_changed_state(this, prev); } bool pim_oif::needs_supressing() const { pim_interface *intf = pim->get_interface(m_state->iif()); if (intf) return intf->get_neighbours().size() > 1; return false; } uint32_t pim_oif::jp_override_interval() const { if (!needs_supressing()) return 0; pim_interface *intf = pim_intf(); if (!intf) return 0; return intf->effective_propagation_delay() + intf->effective_override_interval(); } base_stream &pim_oif::log() const { return m_state->log().xprintf("Intf(%s) ", m_intf->name()); } void pim_oif::update(bool join, uint32_t hold) { if ((join && m_jpstate != Joined) || (!join && m_jpstate != Pruned)) { if (m_state->owner()->should_log(EXTRADEBUG)) { log().xprintf("Updated with %s for %{duration}\n", join ? "join" : "prune", time_duration(hold)); } } if (hold == 0) { change_state(JPNoInfo); return; } inner_update(join, hold); } pim_oif::interest pim_oif::get_local_interest() const { pim_interface *intf = pim_intf(); if (intf && !intf->am_dr()) return NoInfo; return m_local; } pim_oif::interest pim_oif::get_interest() const { return get_internal_interest(get_local_interest()); } pim_oif::interest pim_oif::get_interest(bool includelocal) const { return get_internal_interest(includelocal ? get_local_interest() : pim_oif::NoInfo); } pim_oif::interest pim_oif::get_internal_interest(interest local) const { /* If no local interest, rely entirely in PIM state */ if (local == NoInfo) { /* Both have no interest, so we have no interest */ if (m_jpstate == JPNoInfo) return NoInfo; /* If PIM is in Pruned state, we want to Exclude */ if (m_jpstate == Pruned || ((m_state->is_rpt() && !m_state->is_wildcard()) && (m_jpstate == PendingPrune))) return Exclude; /* all other states (Joined, PendingPrune) reflect Include */ return Include; } else { /* As said above */ if (m_jpstate == Joined || (!(m_state->is_rpt() && !m_state->is_wildcard()) && (m_jpstate == PendingPrune))) return Include; /* If PIM doesn't Include, we rely on the local state */ return local; } } bool pim_oif::has_interest() const { return get_internal_interest(m_local) != NoInfo; } void pim_oif::dr_changed(bool islocal) { if (m_state->owner()->should_log(EVERYTHING)) { log().xprintf("DR-Changed event, interest is %s and i'm %s the RP\n", _oif_interest(get_internal_interest(m_local)), islocal ? "" : "no longer "); } /* we only care about DR events if we have local interest */ if (m_local == NoInfo) return; interest prev = NoInfo; if (!islocal) prev = m_local; changed_state(get_internal_interest(prev)); } void pim_oif::timed_out() { /* (*,G), (S,G) and (S,G,rpt) */ change_state(JPNoInfo); } void pim_oif::output_info(base_stream &ctx) const { base_stream &os = ctx.write(m_intf->name()); pim_interface *intf = pim_intf(); if (m_local != NoInfo) { os.write(", Local"); if (intf && !intf->am_dr()) os.write(" (Not DR)"); } if (m_timer.is_running()) { os.xprintf(", %{duration}", m_timer.time_left_d()); } os.write(", "); switch (get_interest()) { case Include: os.write("Forwarding"); break; case Exclude: os.write("Pruned"); break; default: os.write("NoInfo"); break; } output_extra_info(ctx); os.newl(); } void pim_oif::output_extra_info(base_stream &ctx) const { /* empty */ } pim_common_oif::pim_common_oif(pim_source_state_base *owner, interface *intf) : pim_oif(owner, intf), m_assert_timer("", this, std::mem_fun(&pim_common_oif::assert_timed_out)) { m_assert_timer.name = "pim assert timer "; m_assert_timer.name += intf->name(); m_assert_state = AssertNoInfo; delete_assert_info(); } bool pim_common_oif::has_interest() const { return m_assert_state != AssertNoInfo || pim_oif::has_interest(); } static const char *_assert_state_name(pim_common_oif::assert_state state) { switch (state) { case pim_common_oif::AssertNoInfo: return "NoInfo"; case pim_common_oif::LostAssert: return "LostAssert"; case pim_common_oif::WonAssert: return "WonAssert"; } return "Unknown"; } pim_common_oif::assert_state pim_common_oif::current_assert_state() const { return m_assert_state; } pim_neighbour *pim_common_oif::assert_winner() const { return m_assert_winner; } void pim_common_oif::change_assert_state(pim_common_oif::assert_state newstate, bool propagate) { if (newstate == m_assert_state) return; interest prev = get_interest(); if (m_state->owner()->should_log(EXTRADEBUG)) { log().xprintf("Changed ASSERT state %s -> %s\n", _assert_state_name(m_assert_state), _assert_state_name(newstate)); } m_assert_state = newstate; if (m_assert_state == AssertNoInfo) { delete_assert_info(); } if (propagate) changed_state(prev); } void pim_common_oif::store_assert_info(pim_neighbour *neigh, uint32_t metric, uint32_t pref) { bool notify = m_assert_winner != neigh; m_assert_winner = neigh; m_assert_winner_metric = metric; m_assert_winner_pref = pref; if (notify) { /* force rebuild of upstream path if neighbour changed */ m_state->build_upstream_state(); } } void pim_common_oif::restart_assert_timer() { m_assert_timer.start_or_update(pim_intf()->conf()->assert_timeout(), false); } void pim_common_oif::restart_assert_timer_minus_override() { m_assert_timer.start_or_update(pim_intf()->conf()->assert_timeout() - 3000, false); } void pim_common_oif::pp_timed_out() { change_state(JPNoInfo); } void pim_common_oif::delete_assert_info() { m_assert_winner = 0; m_assert_winner_pref = 0xffffffff; m_assert_winner_metric = 0xffffffff; m_assert_timer.stop(); } void pim_common_oif::assert_timed_out() { if (m_assert_state == LostAssert) { change_assert_state(AssertNoInfo); } else if (m_assert_state == WonAssert) { ((pim_source_state_common *)m_state)->send_assert(pim_intf()); restart_assert_timer_minus_override(); } } void pim_common_oif::inner_update(bool join, uint32_t hold) { if (join) { m_timer.start_or_update(hold, false); change_state(Joined); } else { if (m_jpstate == Joined) { uint32_t jpov = jp_override_interval(); if (jpov > 0) { m_pp_timer.start_or_update(jpov, false); change_state(PendingPrune); } else { m_pp_timer.stop(); change_state(JPNoInfo); } } } } pim_oif::interest pim_common_oif::get_internal_interest(pim_oif::interest local) const { if (m_assert_state == LostAssert) { return Exclude; } return pim_oif::get_internal_interest(local); } void pim_common_oif::output_extra_info(base_stream &ctx) const { if (m_assert_state != AssertNoInfo) { ctx.xprintf(" (%s)", m_assert_state == LostAssert ? "Lost Assert" : "Won Assert"); } } pim_sg_rpt_oif::pim_sg_rpt_oif(pim_source_state_base *owner, interface *intf) : pim_oif(owner, intf) { } void pim_sg_rpt_oif::pp_timed_out() { change_state(Pruned); } void pim_sg_rpt_oif::inner_update(bool join, uint32_t hold) { if (join) { change_state(JPNoInfo); } else { if (m_jpstate == JPNoInfo) { uint32_t jpov = jp_override_interval(); if (jpov > 0) { m_pp_timer.start_or_update(jpov, false); m_timer.start_or_update(hold, false); change_state(PendingPrune); } else { /* Short circuit the PrunePending state * as jpov=0, which means either * neigh_count <= 1 or the value is * really that low */ m_pp_timer.stop(); m_timer.start_or_update(hold, false); change_state(Pruned); } } else if (m_jpstate == PendingPrune || m_jpstate == Pruned) { m_timer.start_or_update(hold, false); } } } mrd6-0.9.6/src/node.cpp0000644000175000017500000006612210600102346013303 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * node.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include enum { node_method_enable = 10, node_method_disable, node_method_no, node_method_show_properties }; /* Default node methods. */ static const method_info node_methods[] = { { "enable", 0, node_method_enable, false, property_def::DEFAULT_VALUE }, { "disable", 0, node_method_disable, false, property_def::DEFAULT_VALUE }, { "no", 0, node_method_no, false, property_def::DEFAULT_VALUE }, { "properties", 0, node_method_show_properties, true, property_def::COMPLETE_M | property_def::DEFAULT_VALUE}, { 0 } }; enum { statistics_node_method_reset_counters = 100, }; static const method_info statistics_node_methods[] = { { "reset-counters", 0, statistics_node_method_reset_counters, false, property_def::COMPLETE_M }, { 0 } }; struct propval_bool : propval { propval_bool(const bool *b) : value(b ? *b : false) {} const void *get_value() const { return &value; } bool set_value(const char *); void output_value(base_stream &) const; bool value; }; struct propval_unsigned : propval { propval_unsigned(const uint32_t *u) : value(u ? *u : 0) {} const void *get_value() const { return &value; } bool set_value(const char *); void output_value(base_stream &) const; uint32_t value; }; struct propval_time_interval : propval_unsigned { propval_time_interval(const uint32_t *u) : propval_unsigned(u) {} bool set_value(const char *); }; struct propval_string : propval { propval_string(const char *); ~propval_string(); const void *get_value() const { return value; } bool set_value(const char *); void output_value(base_stream &) const; char *value; }; struct propval_address : propval { propval_address(const inet6_addr *a) : value(a ? *a : inet6_addr()) {} const void *get_value() const { return &value; } bool set_value(const char *); void output_value(base_stream &) const; inet6_addr value; }; propval::~propval() {} bool propval_bool::set_value(const char *v) { if (!strcmp(v, "yes") || !strcmp(v, "true") || !strcmp(v, "1")) value = true; else if (!strcmp(v, "no") || !strcmp(v, "false") || !strcmp(v, "0")) value = false; else return false; return true; } void propval_bool::output_value(base_stream &os) const { os.write(value); } propval_integer::propval_integer(const int32_t *i) : value(i ? *i : 0) {} const void *propval_integer::get_value() const { return &value; } bool propval_integer::set_value(const char *v) { char *end; int val = strtol(v, &end, 10); if (*end) return false; value = val; return true; } void propval_integer::output_value(base_stream &os) const { os.write(value); } bool propval_unsigned::set_value(const char *v) { char *end; unsigned long val = strtoul(v, &end, 10); if (*end) return false; value = val; return true; } void propval_unsigned::output_value(base_stream &os) const { os.write(value); } bool propval_time_interval::set_value(const char *v) { char *end; unsigned long val = strtoul(v, &end, 10); if (!*end || !strcmp(end, "ms")) value = val; else if (!strcmp(end, "s")) value = val * 1000; else if (!strcmp(end, "m")) value = val * 1000 * 60; else if (!strcmp(end, "h")) value = val * 1000 * 60 * 60; else return false; return true; } propval_string::propval_string(const char *s) : value(s ? strdup(s) : 0) {} propval_string::~propval_string() { if (value) { free(value); value = 0; } } bool propval_string::set_value(const char *v) { char *n = strdup(v); if (!n) return false; if (value) free(value); value = n; return true; } void propval_string::output_value(base_stream &os) const { os.write(value); } bool propval_address::set_value(const char *v) { inet6_addr addr; if (!addr.set(v)) return false; value = addr; return true; } void propval_address::output_value(base_stream &os) const { os.write(value); } propval_enum::propval_enum(entry *ents) : propval_integer(0), entries(ents) {} bool propval_enum::set_value(const char *name) { for (entry *i = entries; i->name; i++) { if (!strcmp(i->name, name)) { value = i->value; return true; } } return false; } void propval_enum::output_value(base_stream &os) const { for (entry *i = entries; i->name; i++) { if (value == i->value) { os.write(i->name); return; } } } event_sink::~event_sink() { } void event_sink::event(int type, void *) { /* empty */ } node::node(node *parent, const char *name) : m_parent(parent), m_name(name) { } node::~node() {} bool node::check_startup() { import_methods(node_methods); return true; } std::string node::full_name() const { if (m_parent && m_parent != g_mrd) { std::string n = m_parent->full_name(); n += "."; n += m_name; return n; } else { return name(); } } node *node::next_similiar_node() const { return 0; } property_def *node::instantiate_property(const char *name, property_def::valtype type, const char *desc, uint32_t flags) { return instantiate_property(name, type, 0, desc, flags); } property_def *node::instantiate_property_b(const char *name, bool def, const char *desc, uint32_t flags) { return instantiate_property(name, property_def::VAL_BOOL, &def, desc, flags); } property_def *node::instantiate_property_i(const char *name, int32_t def, const char *desc, uint32_t flags) { return instantiate_property(name, property_def::VAL_INTEGER, &def, desc, flags); } property_def *node::instantiate_property_u(const char *name, uint32_t def, const char *desc, uint32_t flags) { return instantiate_property(name, property_def::VAL_UNSIGNED, &def, desc, flags); } property_def *node::instantiate_property_t(const char *name, uint32_t def, const char *desc, uint32_t flags) { return instantiate_property(name, property_def::VAL_TIME_INTERVAL, &def, desc, flags); } property_def *node::instantiate_property_s(const char *name, const char *def, const char *desc, uint32_t flags) { return instantiate_property(name, property_def::VAL_STRING, def, desc, flags); } property_def *node::instantiate_property_a(const char *name, const inet6_addr &addr, const char *desc, uint32_t flags) { return instantiate_property(name, property_def::VAL_ADDRESS, &addr, desc, flags); } property_def *node::instantiate_property(const char *name, property_def::valtype type, const void *def, const char *desc, uint32_t flags) { if (m_properties.find(name) != m_properties.end()) return 0; property_def &p = m_properties[name]; if (!p.instantiate(type, def, desc, flags)) { m_properties.erase(m_properties.find(name)); return 0; } return &p; } property_def *node::instantiate_property(const char *name, propval *prop, const char *desc, uint32_t flags) { if (!prop) return 0; if (m_properties.find(name) != m_properties.end()) return 0; property_def &p = m_properties[name]; if (!p.instantiate(prop, desc, flags)) { delete prop; m_properties.erase(m_properties.find(name)); return 0; } return &p; } bool node::has_property(const char *key) const { properties::const_iterator i = m_properties.find(key); return i != m_properties.end() && i->second.is_property(); } bool node::has_child_property(const char *key) const { properties::const_iterator i = m_properties.find(key); return i != m_properties.end(); } property_def *node::get_property(const char *n, bool strict) { properties::iterator i = m_properties.find(n); if (i == m_properties.end()) { if (!strict) { node *next = next_similiar_node(); assert(next != this); if (next) return next->get_property(n, false); } return 0; } return i->second.is_property() ? &i->second : 0; } const property_def *node::get_property(const char *n, bool strict) const { properties::const_iterator i = m_properties.find(n); if (i == m_properties.end()) { if (!strict) { node *next = next_similiar_node(); if (next) return next->get_property(n, false); } return 0; } return i->second.is_property() ? &i->second : 0; } const property_def *node::get_any_property(const char *name) const { properties::const_iterator i = m_properties.find(name); if (i == m_properties.end()) return 0; return &i->second; } bool node::get_property_bool(const char *key) const { return get_property(key)->get_bool(); } int32_t node::get_property_integer(const char *key) const { return get_property(key)->get_integer(); } uint32_t node::get_property_unsigned(const char *key) const { return get_property(key)->get_unsigned(); } const char *node::get_property_string(const char *key) const { return get_property(key)->get_string(); } const inet6_addr &node::get_property_address(const char *key) const { return get_property(key)->get_address(); } const property_def *node::get_child_property(const char *childname, const char *name, bool strict) const { node *child = get_child(childname); if (child) return child->get_property(name, strict); return 0; } bool node::set_property(const char *name, const char *value) { properties::iterator i = m_properties.find(name); if (i == m_properties.end() || !i->second.is_property()) return false; if (i->second.set_value(value)) { propagate_property_changed(this, name); return true; } return false; } void node::propagate_property_changed(node *n, const char *name) { property_changed(n, name); } bool node::remove_property(const char *key, bool force) { properties::iterator k = m_properties.find(key); if (k == m_properties.end() || !k->second.is_property() || (!force && (k->second.is_readonly() || !k->second.is_removable()))) return false; m_properties.erase(k); return true; } bool node::set_property_inst(const char *key, property_def::valtype vt, const char *value) { properties::iterator k = m_properties.find(key); if (k == m_properties.end()) { property_def *prop = instantiate_property(key, vt, 0, property_def::REMOVABLE); if (!prop) return false; if (!prop->set_value(value)) { remove_property(key); return false; } else { propagate_property_changed(this, key); } return true; } if (!k->second.is_property() || k->second.is_readonly()) return false; if (k->second.set_value(value)) { propagate_property_changed(this, key); return true; } return false; } bool node::increment_property(const char *, const char *) { return false; } node *node::get_child(const char *name) const { properties::const_iterator i = m_properties.find(name); if (i == m_properties.end() || !i->second.is_child()) return 0; return i->second.get_node(); } node *node::get_or_create_child(const char *name) { node *child = get_child(name); if (child) return child; return create_child(name); } node *node::create_child(const char *) { return 0; } node *node::add_child(node *child, bool cm, const char *name, const char *desc) { if (!name) name = child->name(); if (!name) return 0; if (m_properties.find(name) != m_properties.end()) return 0; property_def &p = m_properties[name]; if (!p.instantiate(child, cm ? property_def::COMPLETE_M : 0)) { m_properties.erase(m_properties.find(name)); return 0; } if (!desc) desc = child->description(); p.set_description(desc); return child; } void node::remove_child(const char *name) { properties::iterator i = m_properties.find(name); if (i == m_properties.end() || !i->second.is_child()) return; node *n = i->second.get_node(); m_properties.erase(i); remove_child_node(n); } void node::remove_child_node(node *n) { /* empty */ } void node::clear_childs() { properties::iterator i = m_properties.begin(); while (i != m_properties.end()) { properties::iterator j = i; ++i; if (j->second.is_child()) { remove_child(j->first.c_str()); } } } bool node::has_method(const char *name, uint32_t type) const { properties::const_iterator i = m_properties.find(name); if (i != m_properties.end() && i->second.is_method()) { return type == method || ((type == info_method) && i->second.is_readonly()); } return false; } bool node::call_method(int id, base_stream &out, const std::vector &args) { switch (id) { case node_method_enable: case node_method_disable: return enable_several(args, id == node_method_enable); case node_method_no: return exec_negate(out, args); case node_method_show_properties: { std::string fname = m_parent ? full_name() : std::string(); for (properties::const_iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (!fname.empty()) out.write(fname.c_str()).write("."); out.write(i->first.c_str()).write(" = "); if (i->second.is_property()) { i->second.output_value(out); if (i->second.is_readonly()) out.write(" [readonly]"); } else if (i->second.is_child()) { out.xprintf("", i->second.get_node()->name()); } else if (i->second.is_method()) { if (i->second.is_readonly()) out.write("", i->second.get_method_info()->name); } out.newl(); } return true; } } return false; } bool node::negate_method(int id, base_stream &out, const std::vector &args) { return false; } void node::import_methods(const method_info *info) { for (int k = 0; info[k].name; k++) { add_method(&info[k]); } } bool node::add_method(const method_info *info) { if (!info || !info->name) return false; if (m_properties.find(info->name) != m_properties.end()) return false; property_def &p = m_properties[info->name]; if (!p.instantiate(info)) { m_properties.erase(m_properties.find(info->name)); return false; } return true; } void node::remove_method(const char *name) { properties::iterator i = m_properties.find(name); if (i == m_properties.end() || !i->second.is_method()) return; m_properties.erase(i); } bool node::output_info(base_stream &out, const std::vector &args) const { for (properties::const_iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (i->second.is_child()) { i->second.get_node()->output_info(out, args); } } return true; } bool node::enable_several(const std::vector &args, bool enable) { if (args.empty()) return false; const char *ens = enable ? "true" : "false"; node *n = this; int len = args.size() - 1; content_type ctype; const char *cmatch; int cres; for (int i = 0; i < len; i++) { cres = match_property(child, args[i].c_str(), ctype, cmatch); if (cres == 1) { n = n->get_or_create_child(cmatch); if (n) continue; } return false; } cres = n->match_property(child | property | method | info_method, args[len].c_str(), ctype, cmatch); if (cres > 1) return false; if (cres == 0) { if (!n->create_child(args[len].c_str())) return n->set_property(args[len].c_str(), ens); cres = 1; ctype = child; cmatch = args[len].c_str(); } if (ctype == method || ctype == info_method) return false; if (cres == 1) { const char *prop = cmatch; if (ctype == child) { n = n->get_child(cmatch); if (!n) return false; return n->set_property_inst("enabled", property_def::VAL_BOOL, ens); } if (!n->has_property(prop)) { node *p = n; while (1) { p = p->next_similiar_node(); if (!p) return false; else if (p->has_property(prop)) { return n->set_property_inst(prop, property_def::VAL_BOOL, ens); } } } return n->set_property(prop, ens); } return false; } bool node::show(base_stream &out, const std::vector &args) { node *final = this; for (std::vector::const_iterator i = args.begin(); i != args.end(); ++i) { content_type ctype; const char *cmatch = 0; int count = final->match_property(info_method | child | method | property, i->c_str(), ctype, cmatch); if (count == 0) return final->output_info(out, std::vector(i, args.end())); else if (count > 1) { out.writeline("% Inconsistency in input."); return true; } if (ctype == child) { final = final->get_child(cmatch); if (!final) return false; } else if (ctype == info_method) { ++i; std::vector newargs(i, args.end()); properties::const_iterator j = final->m_properties.find(cmatch); if (j == m_properties.end()) return false; int id = j->second.get_method_info()->id; return final->call_method(id, out, newargs); } else { out.writeline("% No such command."); return true; } } return final->output_info(out, std::vector()); } bool node::exec_negate(base_stream &out, const std::vector &args) { if (args.empty()) return false; node *final = this; for (std::vector::const_iterator i = args.begin(); i != args.end(); ++i) { content_type ctype; const char *cmatch = 0; int count = final->match_property(info_method | child | method | property, i->c_str(), ctype, cmatch); if (count == 0) { out.writeline("% No such command."); break; } else if (count > 1) { out.writeline("% Inconsistency in input."); break; } if (ctype == child) { final = final->get_child(cmatch); if (!final) return false; } else if (ctype == method) { ++i; std::vector newargs(i, args.end()); properties::const_iterator j = final->m_properties.find(cmatch); if (j == m_properties.end()) { out.writeline("% No such command."); break; } if (!(j->second.flags() & property_def::NEGATE)) { out.writeline("% No such command."); break; } int id = j->second.get_method_info()->id; return final->negate_method(id, out, newargs); } else if (ctype == property) { ++i; if (i != args.end()) { out.writeline("% Too many arguments."); break; } if (!final->remove_property(cmatch)) { out.writeline("% Failed to remove property."); break; } propagate_property_changed(final, cmatch); } else { out.writeline("% No such command."); break; } } return true; } int node::match_property(uint32_t fl, const char *match, content_type &ctype, const char * &cmatch) const { int cmatchcount = 0; cmatch = 0; size_t mlen = strlen(match); for (properties::const_iterator i = m_properties.begin(); i != m_properties.end(); ++i) { content_type curr = unknown; if (i->second.is_child()) curr = child; else if (i->second.is_property()) curr = property; else if (i->second.is_method()) curr = i->second.is_readonly() ? info_method : method; else continue; if (!(fl & curr)) continue; const char *tent = i->first.c_str(); /* Partial match */ if (strncmp(match, tent, mlen) == 0) { if (mlen == strlen(tent)) { /* complete match, return immediatly */ ctype = curr; cmatch = tent; return 1; } else if (i->second.flags() & property_def::COMPLETE_M) { continue; } ctype = curr; cmatch = tent; if (i->second.flags() & property_def::COMPLETE_M) { return 1; } else if (cmatchcount == 0) { cmatchcount = 1; } else { return 2; } } } return cmatch ? cmatchcount : 0; } void node::broadcast_event(int id, void *param, bool all) { for (properties::const_iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (i->second.is_child()) { i->second.get_node()->event(id, param); if (all) i->second.get_node()->broadcast_event(id, param, true); } } } bool node::should_log(int level) const { if (m_parent == NULL) return false; return m_parent->should_log(level); } base_stream &node::log() const { return m_parent->log(); } property_def::property_def() : m_flags(DEFAULT_VALUE), m_prop_description(0) { u.val = 0; } property_def::~property_def() { if (is_property()) { delete u.val; u.val = 0; } } bool property_def::instantiate(valtype type, const void *def, const char *desc, uint32_t fl) { if (is_instantiated() || (fl & METHOD) || (fl & CHILD)) return false; fl |= PROPERTY; switch (type) { case VAL_BOOL: u.val = new propval_bool((const bool *)def); break; case VAL_INTEGER: u.val = new propval_integer((const int32_t *)def); break; case VAL_UNSIGNED: u.val = new propval_unsigned((const uint32_t *)def); break; case VAL_TIME_INTERVAL: u.val = new propval_time_interval((const uint32_t *)def); break; case VAL_STRING: u.val = new propval_string((const char *)def); if (u.val && !((propval_string *)u.val)->value) { delete u.val; u.val = 0; } break; case VAL_ADDRESS: u.val = new propval_address((const inet6_addr *)def); break; default: return false; } if (!u.val) return false; m_flags |= fl; m_prop_description = desc; return true; } bool property_def::instantiate(node *n, uint32_t fl) { if (is_instantiated() || (fl & ~COMPLETE_M)) return false; m_flags |= CHILD | fl; u.child = n; return true; } bool property_def::instantiate(const method_info *info) { if (!info || is_instantiated() || (info->flags & CHILD) || (info->flags & PROPERTY)) return false; m_flags = METHOD | info->flags | (info->informational ? property_def::READ_ONLY : 0); u.method = info; return true; } bool property_def::instantiate(propval *pval, const char *desc, uint32_t fl) { if (is_instantiated() || (fl & METHOD) || (fl & CHILD)) { delete pval; return false; } u.val = pval; fl |= PROPERTY; m_flags |= fl; m_prop_description = desc; return true; } void property_def::set_description(const char *desc) { m_prop_description = desc; } const char *property_def::description() const { if (is_child() && get_node()->description()) return get_node()->description(); else if (is_method() && get_method_info()->description) return get_method_info()->description; return m_prop_description; } void property_def::set_readonly() { m_flags |= READ_ONLY; } bool property_def::set_value(const char *value, bool b) { if (!is_property()) return false; if (u.val && u.val->set_value(value)) { if (b) m_flags &= ~DEFAULT_VALUE; return true; } return false; } void property_def::output_value(base_stream &os) const { if (!u.val) os.write("(null)"); else u.val->output_value(os); } bool property_def::is_instantiated() const { return is_child() || is_property() || is_method(); } conf_node::conf_node(node *parent, const char *name) : node(parent, name) {} void conf_node::enable(bool v) { set_property_inst("enabled", property_def::VAL_BOOL, v ? "true" : "false"); } void conf_node::propagate_property_changed(node *n, const char *name) { property_changed(n, name); for (properties::const_iterator i = m_properties.begin(); i != m_properties.end(); ++i) { if (i->second.is_child()) { i->second.get_node()->propagate_property_changed(n, name); } } } void conf_node::attach_watcher(node *n) { dettach_watcher(n); m_watchers.push_back(n); } void conf_node::dettach_watcher(node *n) { std::vector::iterator i = std::find(m_watchers.begin(), m_watchers.end(), n); if (i != m_watchers.end()) m_watchers.erase(i); } void conf_node::property_changed(node *n, const char *name) { for (std::vector::const_iterator i = m_watchers.begin(); i != m_watchers.end(); ++i) { (*i)->property_changed(n, name); } } statistics_node::statistics_node(node *parent, int count, const char **descriptions) : node(parent, "stats"), m_count(count), m_descriptions(descriptions) { m_counters = new counter_type[count]; } statistics_node::~statistics_node() { delete [] m_counters; m_counters = 0; } bool statistics_node::check_startup() { if (m_counters) { reset_counters(); } else { return false; } import_methods(statistics_node_methods); return true; } bool statistics_node::setup(const char *description) { if (!check_startup()) return false; return m_parent->add_child(this, false, 0, description) == this; } statistics_node::counter_type &statistics_node::counter(int index) const { return m_counters[index]; } void statistics_node::reset_counters() { for (int i = 0; i < m_count; i++) { m_counters[i] = 0; } } bool statistics_node::call_method(int id, base_stream &out, const std::vector &args) { if (id == statistics_node_method_reset_counters) { if (!args.empty()) return false; reset_counters(); return true; } return node::call_method(id, out, args); } bool statistics_node::output_info(base_stream &out, const std::vector &args) const { if (!args.empty()) return false; for (int i = 0; i < m_count; i++) { out.printf("%s: %llu", m_descriptions[i], m_counters[i]).newl(); } return true; } static const char *_default_type_descriptions[] = { "Received", "Sent", "Bad" }; message_stats_node::message_stats_node(node *parent, int count, const char **descriptions, int typecount, const char **typedesc) : statistics_node(parent, count * typecount, descriptions), m_msgcount(count), m_typecount(typecount), m_typedescriptions(typedesc) { if (!typedesc) { assert(typecount == 3); m_typedescriptions = _default_type_descriptions; } /* we keep a 64 bitset to enable/disable stats, so make sure * we don't access more than that */ assert(count <= (int)(m_enablecounters.size() / m_typecount)); m_enablecounters.set(); } statistics_node::counter_type &message_stats_node::counter(int index, int type) const { return statistics_node::counter(index * m_typecount + type); } void message_stats_node::disable_counter(int index, int type) { m_enablecounters.reset(index * m_typecount + type); } void message_stats_node::print_counter(base_stream &out, int index, int type) const { if (!m_enablecounters.test(index * m_typecount + type)) { out.printf(" %10s", "-"); } else { out.printf(" %10u", (uint32_t)m_counters[index * m_typecount + type]); } } bool message_stats_node::output_info(base_stream &out, const std::vector &args) const { if (!args.empty()) return false; int i; /* 1 + 12 + 1 whites */ out.printf(" "); for (i = 0; i < m_typecount; i++) out.printf(" %10s", m_typedescriptions[i]); out.newl(); out.printf(" "); for (i = 0; i < m_typecount; i++) out.printf("-----------"); out.writeline("-"); for (i = 0; i < m_msgcount; i++) { out.printf(" %12s ", m_descriptions[i]); for (int j = 0; j < m_typecount; j++) print_counter(out, i, j); out.newl(); } return true; } mrd6-0.9.6/src/msnip/0000755000175000017500000000000010746353706013014 5ustar hugohugomrd6-0.9.6/src/msnip/msnip_module.cpp0000644000175000017500000002167510550053317016212 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * msnip_module.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include "msnip_def.h" class msnip_module : public mrd_module, public node, public icmp_handler { public: msnip_module(mrd *, void *); bool check_startup(); void shutdown(); void icmp_message_available(interface *, const in6_addr &, const in6_addr &, icmp6_hdr *, int); void refresh_source(interface *, const in6_addr &, uint16_t); void send_transmit(interface *, const in6_addr &, uint16_t); void send_single_transmit(interface *intf, const in6_addr &source, const in6_addr &grpaddr, bool active, int holdtime); void send_single_mrm(interface *intf, const in6_addr &dst, msnip_mrm *mrm, int count) const; void source_timed_out(in6_addr &); void event(int, void *); bool output_info(base_stream &, const std::vector &) const; inet6_addr all_mld_routers; typedef timer1 source_timer; struct source_record { source_record(msnip_module *, const in6_addr &, interface *); source_timer tmr; interface *intf; uint16_t holdtime; }; typedef std::list sources; sources::iterator get_source(const in6_addr &); sources m_sources; property_def *m_range; message_stats_node m_stats; }; msnip_module::source_record::source_record(msnip_module *parent, const in6_addr &src, interface *_intf) : tmr("msnip source", parent, std::mem_fun(&msnip_module::source_timed_out), src), intf(_intf) {} module_entry(msnip, msnip_module); enum { HISCount, MRMCount, MRMRecTransmitCount, MRMRecHoldCount, MessageCount }; enum { RX = 0, TX, Bad }; static const char *stats_descriptions[] = { "HIS", "MRM", "MRM-Rec-TX", "MRM-Rec-Hold", }; msnip_module::msnip_module(mrd *m, void *p) : mrd_module(m, p), node(m, "msnip"), m_stats(this, MessageCount, stats_descriptions) { all_mld_routers = inet6_addr("ff02::16"); m_range = instantiate_property_a("range", inet6_addr("ff3e::/16")); } bool msnip_module::check_startup() { if (!m_range) return false; if (!m_stats.setup()) return false; m_stats.disable_counter(HISCount, TX); m_stats.disable_counter(MRMCount, RX); m_stats.disable_counter(MRMRecTransmitCount, RX); m_stats.disable_counter(MRMRecHoldCount, RX); if (!node::check_startup()) return false; if (!g_mrd->add_child(this)) return false; g_mrd->register_startup(this); return true; } void msnip_module::shutdown() { g_mrd->interested_in_active_states(this, false); g_mrd->icmp().register_handler(MSNIP_HIS_REPORT, 0); g_mrd->icmp().require_mgroup(all_mld_routers, false); g_mrd->remove_child("msnip"); } void msnip_module::icmp_message_available(interface *intf, const in6_addr &src, const in6_addr &dst, icmp6_hdr *hdr, int length) { if (!(dst == all_mld_routers.address())) return; if (hdr->icmp6_type == MSNIP_HIS_REPORT) { m_stats.counter(HISCount, RX)++; uint16_t holdtime = ntohs(hdr->icmp6_maxdelay) * 1000; if (should_log(MESSAGE_SIG)) log().xprintf("(MSNIP) Received a HIS from %{addr} in " "%s with holdtime %u\n", src, intf->name(), (uint32_t)holdtime); refresh_source(intf, src, holdtime); } } msnip_module::sources::iterator msnip_module::get_source(const in6_addr &src) { /* XXX linear search for now */ for (sources::iterator i = m_sources.begin(); i != m_sources.end(); ++i) { if ((*i)->tmr.argument() == src) { return i; } } return m_sources.end(); } void msnip_module::refresh_source(interface *intf, const in6_addr &src, uint16_t holdtime) { sources::iterator i = get_source(src); if (i != m_sources.end()) { if (holdtime == 0) { delete *i; m_sources.erase(i); } else { int diff = ((int)holdtime) - (*i)->tmr.time_left(); if (diff < 1000) { /* only allow a refresh every second */ return; } (*i)->tmr.update(holdtime, true); send_transmit(intf, src, holdtime); } return; } /* source not found */ if (should_log(DEBUG)) log().xprintf("(MSNIP) new source %{addr} with holdtime %u\n", src, (uint32_t)holdtime); source_record *rec = new source_record(this, src, intf); if (rec) { rec->holdtime = holdtime; rec->tmr.start(holdtime, true); send_transmit(intf, src, holdtime); m_sources.push_back(rec); } } /* local buffer, should share this with rest */ static uint8_t buffer[1500]; void msnip_module::send_transmit(interface *intf, const in6_addr &src, uint16_t holdtime) { /* iterate over groups */ mrd::group_list::const_iterator i = g_mrd->group_table().begin(); msnip_mrm *mrm = (msnip_mrm *)buffer; mrm->icmp6_type = MSNIP_MRM_REPORT; mrm->icmp6_code = 0; /* dst count */ mrm->icmp6_maxdelay = htons(holdtime / 1000); /* holdtime */ mrm->icmp6_seq = 0; int index = 0; int max_index = std::max(255, (int)((1280 - sizeof(msnip_mrm)) / (4 + sizeof(in6_addr)))); for (; i != g_mrd->group_table().end(); ++i) { if (!m_range->get_address().matches(i->first)) continue; if (!i->second->has_downstream_interest(src)) continue; mrm->records[index].rectype = MSNIP_TRANSMIT; memset(mrm->records[index].resv, 0, sizeof(mrm->records[index].resv)); mrm->records[index].address = i->first; index ++; if (index == max_index) { /* flush message, start over */ send_single_mrm(intf, src, mrm, index); index = 0; } } if (index > 0) { send_single_mrm(intf, src, mrm, index); } } void msnip_module::send_single_mrm(interface *intf, const in6_addr &dst, msnip_mrm *mrm, int count) const { int length = sizeof(msnip_mrm) + (4 + sizeof(in6_addr)) * count; mrm->icmp6_code = count; if (g_mrd->icmp().send_icmp(intf, dst, 0, mrm, length)) { if (should_log(MESSAGE_SIG)) { if (count == 1) { log().xprintf( "(MSNIP) Sent MRM to %{addr} with %s\n", dst, mrm->records[0].rectype == MSNIP_TRANSMIT ? "Transmit" : "Hold"); } else { log().xprintf( "(MSNIP) Sent MRM to %{addr} with %i " "records\n", dst, count); } } m_stats.counter(MRMCount, TX)++; for (int i = 0; i < count; i++) { if (mrm->records[i].rectype == MSNIP_TRANSMIT) m_stats.counter(MRMRecTransmitCount, TX)++; else m_stats.counter(MRMRecHoldCount, TX)++; } } } void msnip_module::send_single_transmit(interface *intf, const in6_addr &source, const in6_addr &grpaddr, bool active, int holdtime) { msnip_mrm *mrm = (msnip_mrm *)buffer; mrm->icmp6_type = MSNIP_MRM_REPORT; mrm->icmp6_code = 0; /* dst count */ mrm->icmp6_maxdelay = htons(holdtime / 1000); /* holdtime */ mrm->icmp6_seq = 0; mrm->records[0].rectype = active ? MSNIP_TRANSMIT : MSNIP_HOLD; memset(mrm->records[0].resv, 0, sizeof(mrm->records[0].resv)); mrm->records[0].address = grpaddr; send_single_mrm(intf, source, mrm, 1); } void msnip_module::source_timed_out(in6_addr &src) { sources::iterator i = get_source(src); /* assert(i != m_sources.end()); */ if (should_log(DEBUG)) log().xprintf("(MNSIP) source timed out %{addr}\n", src); delete *i; m_sources.erase(i); } void msnip_module::event(int id, void *ptr) { if (id == mrd::ActiveStateNotification) { mrd::active_state_report *rep = (mrd::active_state_report *)ptr; if (!m_range->get_address().matches(rep->group_instance->id())) return; msnip_module::sources::iterator i = get_source(rep->source_address); if (i != m_sources.end()) { send_single_transmit((*i)->intf, rep->source_address, rep->group_instance->id(), rep->active, (*i)->holdtime); } } else if (id == mrd::StartupEvent) { g_mrd->interested_in_active_states(this, true); g_mrd->icmp().register_handler(MSNIP_HIS_REPORT, this); g_mrd->icmp().require_mgroup(all_mld_routers, true); } else { node::event(id, ptr); } } bool msnip_module::output_info(base_stream &os, const std::vector &args) const { if (!args.empty()) return false; for (sources::const_iterator i = m_sources.begin(); i != m_sources.end(); ++i) { os.xprintf("%{addr} in %s for %{duration}\n", (*i)->tmr.argument(), (*i)->intf->name(), (*i)->tmr.time_left_d()); } return true; } mrd6-0.9.6/src/msnip/msnip_def.h0000644000175000017500000000245610550053610015120 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * msnip_def.h * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #ifndef _mrd_msnip_def_h_ #define _mrd_msnip_def_h_ #include #include #define MSNIP_HIS_REPORT 202 #define MSNIP_MRM_REPORT 203 enum { MSNIP_TRANSMIT = 1, MSNIP_HOLD = 2 }; struct msnip_his : icmp6_hdr { }; struct msnip_mrm : icmp6_hdr { struct { uint8_t rectype; uint8_t resv[3]; in6_addr address; } __attribute((packed)) records[0]; }; #endif mrd6-0.9.6/src/support/0000755000175000017500000000000010746353706013402 5ustar hugohugomrd6-0.9.6/src/support/Module.options0000644000175000017500000000010410333700561016222 0ustar hugohugoboolean POOLING default on description "Enable MRD6's memory pools" mrd6-0.9.6/src/support/ptree.cpp0000644000175000017500000001455110600166750015221 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * ptree.cpp * * Copyright (C) 2007 - Hugo Santos * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include base_ptree::base_ptree() : head(NULL), count(0) {} base_ptree::~base_ptree() { clear(); } bool base_ptree::empty() const { return head == NULL; } uint32_t base_ptree::size() const { return count; } bool base_ptree::remove(ptree_node *node) { if (!head || !node || node->_t_color != ptree_node::BLACK) return false; ptree_node *parent = node->_t_parent; if (node->_t_left && node->_t_right) { /* we are a backbone node, need a replacing white node */ ptree_node *white = _alloc_white(node->_t_bit); if (!white) return false; if (node->_t_left) node->_t_left->_t_parent = white; white->_t_left = node->_t_left; if (node->_t_right) node->_t_right->_t_parent = white; white->_t_right = node->_t_right; _fix_parent(white, node); } else if (node->_t_left) { _fix_parent(node->_t_left, node); } else if (node->_t_right) { _fix_parent(node->_t_right, node); } else { _fix_parent(0, node); } /* remove redundant white nodes */ ptree_node *curr = parent; while (curr && curr->_t_color == ptree_node::WHITE) { parent = curr->_t_parent; if (curr->_t_left && curr->_t_right) break; else if (curr->_t_left) _fix_parent(curr->_t_left, curr); else if (curr->_t_right) _fix_parent(curr->_t_right, curr); else _fix_parent(0, curr); _return_white(curr); curr = parent; } count --; return true; } void base_ptree::clear() { ptree_node *node = 0; /* By removing every black node we also trigger * the removal of all white nodes */ while ((node = _get_first_black())) { remove(node); } } ptree_node *base_ptree::get_parent_node(const ptree_node *n) const { if (!n) return 0; do { n = n->_t_parent; } while (n && n->_t_color == ptree_node::WHITE); return (ptree_node *)n; } base_ptree::iterator::iterator() : curr(NULL), prev(NULL) {} base_ptree::iterator::iterator(ptree_node *c) : curr(c), prev(NULL) {} base_ptree::iterator::iterator(const iterator &i) : curr(i.curr), prev(i.prev) {} ptree_node *base_ptree::iterator::increment() { if (curr == NULL) return NULL; do { ptree_node *_prev = prev; prev = curr; if (_prev && _prev == curr->_t_left) { /* coming from left, go right, or up */ if (curr->_t_right) curr = curr->_t_right; else curr = curr->_t_parent; } else if (_prev && _prev == curr->_t_right) { /* coming from right, go up */ curr = curr->_t_parent; } else if (curr->_t_left) { curr = curr->_t_left; } else if (curr->_t_right) { curr = curr->_t_right; } else { curr = curr->_t_parent; } } while (curr && (prev->_t_parent == curr || curr->_t_color == ptree_node::WHITE)); return curr; } ptree_node *base_ptree::_get_first_black() const { if (head) { if (head->_t_color == ptree_node::BLACK) return head; return iterator(head).increment(); } return NULL; } ptree_node *base_ptree::_a_child_black_node(ptree_node *node) const { if (node->_t_color == ptree_node::BLACK) return node; ptree_node *res = NULL, *ch[2] = { node->_t_left, node->_t_right }; for (int i = 0; i < 2 && res == NULL; i++) { if (ch[i] != NULL) res = _a_child_black_node(ch[i]); } return res; } void base_ptree::_fix_parent(ptree_node *newnode, ptree_node *oldnode) { /* assert(oldnode); */ /* assert(!oldnode->_t_parent || oldnode->_t_parent->_t_right == oldnode || oldnode->_t_parent->_t_left == oldnode); */ if (!oldnode->_t_parent) head = newnode; else if (oldnode->_t_parent->_t_right == oldnode) oldnode->_t_parent->_t_right = newnode; else oldnode->_t_parent->_t_left = newnode; if (newnode) newnode->_t_parent = oldnode->_t_parent; oldnode->_t_parent = newnode; } ptree_node *base_ptree::_alloc_white(int bit) { ptree_node *n = whites.request_obj(); if (n == NULL) return NULL; n->_t_parent = 0; n->_t_left = 0; n->_t_right = 0; n->_t_color = ptree_node::WHITE; n->_t_bit = bit; return n; } void base_ptree::_return_white(ptree_node *n) { whites.return_obj(n); } void base_ptree::dump_internal_tree(base_stream &os) const { dump_internal_tree(os, head, "root"); } void base_ptree::dump_internal_tree_graphviz(base_stream &os) const { os.writeline("graph ptree {"); os.inc_level(); dump_internal_tree_graphviz(os, head, "black"); dump_internal_tree_graphviz(os, head, 0); os.dec_level(); os.writeline("}"); } void base_ptree::dump_internal_tree(base_stream &os, const ptree_node *node, const char *desc) const { if (!node) return; os.xprintf("%s ", desc); if (node->_t_color == ptree_node::BLACK) write_prefix(os, node); else os.write("white"); os.xprintf(" at %i", (int)node->_t_bit); os.newl(); os.inc_level(); dump_internal_tree(os, node->_t_left, "left"); dump_internal_tree(os, node->_t_right, "right"); os.dec_level(); } void base_ptree::dump_internal_tree_graphviz(base_stream &os, const ptree_node *node, const char *color) const { if (!node) return; os.xprintf("\"%p\" ", (const void *)node); if (color) { os.write("[label=\""); if (node->_t_color == ptree_node::BLACK) write_prefix(os, node); else os.xprintf("white %i", (int)node->_t_bit); os.xprintf("\",color=%s];\n", color); dump_internal_tree_graphviz(os, node->_t_left, "red"); dump_internal_tree_graphviz(os, node->_t_right, "green"); } else { os.write("-- {"); if (node->_t_left) os.xprintf("\"%p\"", (const void *)node->_t_left); if (node->_t_right) os.xprintf(" \"%p\"", (const void *)node->_t_right); os.writeline("};"); dump_internal_tree_graphviz(os, node->_t_left, 0); dump_internal_tree_graphviz(os, node->_t_right, 0); } } mrd6-0.9.6/src/support/objpool.cpp0000644000175000017500000000624610550053317015546 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * objpool.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #ifndef SUPPORT_NO_POOLING base_objpool::base_objpool(uint32_t _count, uint32_t _single) : granularity(_count), single(_single), light(0), heavy(0) { assert(granularity > 0); } base_objpool::base_objpool(const base_objpool &pool) : granularity(pool.granularity), single(pool.single), light(0), heavy(0) { } void *base_objpool::generic_request_obj() { if (!light) { /* There are no mem chunks with free objects */ light = _alloc_chunk(granularity); if (!light) return 0; } /* assert(head->head) */ _objhead *h = light->head; light->head = h->next; /* Every node with next=0 is allocated */ h->next = 0; light->free --; if (light->free == 0) { /* move this light mem chunk to heavy mem chunks */ _memchunk *m = dlist_pop_front(light); dlist_push_front(heavy, m); } return &h->_obj; } void base_objpool::base_return_obj(void *obj, _memchunk * &m) { _objhead *h = (_objhead *)(((uint8_t *)obj) - sizeof(_objhead)); assert(h->next == 0); assert(h->parent); m = _find_chunk(h); /* assert(m = _find_chunk(h, prev)); */ h->next = m->head; m->head = h; m->free ++; if (m->free == 1) { /* mem chunk is light again */ dlist_remove(heavy, m); dlist_push_front(light, m); } } base_objpool::_memchunk *base_objpool::_alloc_chunk(uint32_t count) { uint32_t one_size = sizeof(_objhead) + single; uint32_t size = sizeof(_memchunk) + count * one_size; uint8_t *mb = new uint8_t[size]; _memchunk *m = (_memchunk *)mb; if (m) { m->chunk = mb + sizeof(_memchunk); m->endchunk = mb + size; m->prev = m->next = 0; m->head = (_objhead *)m->chunk; m->count = count; m->free = count; uint8_t *p = m->chunk; while (p < m->endchunk) { _objhead *h = (_objhead *)p; p += one_size; h->parent = m; h->next = (_objhead *)p; } } return m; } void base_objpool::_free_chunk(_memchunk *m) { uint8_t *mb = (uint8_t *)m; delete [] mb; } base_objpool::_memchunk *base_objpool::_find_chunk(_objhead *h) { return h->parent; } void base_objpool::_clear_memchunks() { _clear_memchunks(heavy); _clear_memchunks(light); } void base_objpool::_clear_memchunks(_memchunk *m) { while (m) { _memchunk *h = m; m = m->next; _free_chunk(h); } } #endif mrd6-0.9.6/src/timers.cpp0000644000175000017500000002213710746067643013704 0ustar hugohugo/* * Multicast Routing Daemon (MRD) * timers.cpp * * Copyright (C) 2006, 2007 - Hugo Santos * Copyright (C) 2004..2006 - Universidade de Aveiro, IT Aveiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Hugo Santos */ #include #include #include #include #include #include #include #include #include #include #include tval tval::operator + (const tval &tv) const { tval n = *this; n += tv; return n; } int32_t tval::operator - (const tval &tv) const { return (v.tv_sec - tv.v.tv_sec) * 1000 + (v.tv_usec - tv.v.tv_usec) / 1000; } tval tval::diff(const tval &tv) const { tval n; // diff = this - tv // with this = 10:10 and tv = 9:0 -> 1:10 // with this = 11:0 and tv 10:50 -> 0:50 if (v.tv_usec >= tv.v.tv_usec) { n.v.tv_usec = v.tv_usec - tv.v.tv_usec; n.v.tv_sec = v.tv_sec - tv.v.tv_sec; } else { n.v.tv_usec = tv.v.tv_usec - v.tv_usec; n.v.tv_sec = v.tv_sec - (tv.v.tv_sec + 1); } return n; } base_stream &tval::print_to(base_stream &os) const { return os.xprintf("%u:%llu", (uint32_t)v.tv_sec, (uint64_t)v.tv_usec); } timer_base::timer_base(const timer_base &original) : name(original.name), _running(original._running), _repeat(original._repeat), _interval(original._interval), _perturb(original._perturb) { if (_running) { g_mrd->timemgr()->clone_position(&original, this); } _extra = 0; } timer_base::~timer_base() { stop(); } bool timer_base::start(bool immediatly) { return start(_interval, _repeat, immediatly); } bool timer_base::start(uint32_t interval, bool repeat, bool immediatly, uint32_t perturb) { if (!_running) { _running = true; _repeat = repeat; _interval = interval; _perturb = perturb; g_mrd->timemgr()->start_timer(this); if (immediatly) callback(); } return true; } bool timer_base::stop() { if (_running) { g_mrd->timemgr()->stop_timer(this); _running = false; return true; } return false; } void timer_base::update(uint32_t interval, bool repeat, uint32_t perturb) { g_mrd->timemgr()->update_timer(this, interval, repeat, perturb); } uint32_t timer_base::time_left() const { if (!is_running()) return 0; return g_mrd->timemgr()->time_left(this) + _extra; } base_stream &timer_base::print_to(base_stream &os) const { os.xprintf("%s, ", name.c_str()); if (is_running()) { os.xprintf("Running, %{duration}", time_left()); } else { os.write("Stopped"); } return os; } timermgr::timermgr() { /* POSIX love */ clk_tck = sysconf(_SC_CLK_TCK); } timermgr::~timermgr() { } bool timermgr::check_startup() { return true; } void timermgr::shutdown() { } void timermgr::start_timer(timer_base *def) { start_timer(def, def->_interval, def->_perturb); } void timermgr::start_timer(timer_base *def, uint32_t interval, uint32_t perturb) { /* * [begin, 5] [10] [end] * * Case 1, insert 2: * -> [begin, 2] [3] [10] [end] * Case 2, insert 7: * -> [begin, 5] [2] [8] [end] * Case 3, insert 20: * -> [begin, 5] [10] [5] [end] */ uint32_t accum = 0; if (perturb) { int32_t i = interval; i += (mrd::get_randu32() % (2 * perturb) - perturb); interval = std::max(i, 0); } tq_def::iterator i = tq.begin(); /* * Case 1: * accum = 0, i = [begin, 5] * Case 2: * accum = 5, i = [10] * Case 3: * accum = 15, i = [end] */ while (1) { if (i == tq.end() || (accum + (*i)->_target) >= interval) break; accum += (*i)->_target; ++i; } def->_extra = 0; def->_target = interval - accum; /* Case {1, 2} */ if (i != tq.end()) (*i)->_target -= def->_target; if (tq.empty()) { tms tmp; lastclk = times(&tmp); taccum = 0; } tq.insert(i, def); } void timermgr::stop_timer(timer_base *def) { /* * [begin, 2] [5] [10] [end] * * Remove [5]: * -> [begin, 2] [10->15] [end] */ for (tq_def::iterator i = tq.begin(); i != tq.end(); ++i) { if (*i == def) { tq_def::iterator j = i; ++j; if (j != tq.end()) (*j)->_target += (*i)->_target; tq.erase(i); return; } } } void timermgr::update_timer(timer_base *def, uint32_t value, bool repeat, uint32_t perturb) { if (def) { /* uint32_t prev = def->_interval; */ def->_interval = value; def->_repeat = repeat; def->_perturb = perturb; if (def->is_running()) { /* XXX `extra` needs thinking: two updates without being * triggered meanwhile, must increment. but meanwhile * must take into account in start_timer */ /*if (value > prev && *tq.begin() != def) { def->_extra = value - prev; } else {*/ stop_timer(def); start_timer(def); /*}*/ } } } void timermgr::clone_position(const timer_base *def1, timer_base *def2) { for (tq_def::iterator i = tq.begin(); i != tq.end(); ++i) { if (*i == def1) { def2->_target = 0; def2->_extra = def1->_extra; ++i; tq.insert(i, def2); return; } } } void timermgr::handle_timer_event() { assert(!tq.empty()); while (!tq.empty()) { timer_base *h = *tq.begin(); /* Not your time yet baby */ if (h->_target > taccum) return; taccum -= h->_target; tq.erase(tq.begin()); if (h->_extra > 0) { start_timer(h, h->_extra, 0); } else { if (h->_repeat) { start_timer(h); } else { h->_running = false; } h->callback(); } } } void timermgr::update_taccum() { tms tmp; clock_t now = times(&tmp); uint32_t diff = ((now - lastclk) * 1000) / clk_tck; lastclk = now; taccum += diff; } bool timermgr::handle_event() { update_taccum(); handle_timer_event(); return false; } static void _extend(char *buf, int size, int &avail, const char *fmt, ...) { if (avail == 0) return; int ptr = size - avail; if (avail < size) { buf[ptr + 0] = ' '; buf[ptr + 1] = 0; avail--; ptr++; } va_list vl; va_start(vl, fmt); int w = vsnprintf(buf + ptr, avail, fmt, vl); va_end(vl); if ((avail - w) < 0) avail = 0; else avail -= w; } static char *_prettyprint(char *buf, int size, uint32_t interval) { int avail = size; if (interval == 0) { /* we need at least 2 bytes in buf */ strcpy(buf, "0"); return buf; } if (interval < 1000) { _extend(buf, size, avail, "%ims", interval); } else { if (interval % 1000) { interval = (interval / 1000) + 1; } else { interval /= 1000; } if (interval > 86400) { _extend(buf, size, avail, "%ud", interval / 86400); interval %= 86400; } if (interval > 3600) { _extend(buf, size, avail, "%uh", interval / 3600); interval %= 3600; } if (interval > 60) { _extend(buf, size, avail, "%um", interval / 60); interval %= 60; } if (interval) _extend(buf, size, avail, "%us", interval); } return buf; } static void _draw_sep(base_stream &ctx, int n) { char buf[64]; buf[0] = '+'; memset(buf + 1, '-', n+2); buf[n+3] = 0; ctx.xprintf("%s+--------------+------------+----------+\n", buf); } bool timermgr::output_info(base_stream &ctx, bool extended) const { size_t namelen = 20; for (tq_def::const_iterator i = tq.begin(); namelen < 50 && i != tq.end(); ++i) { timer_base *h = *i; if (h->name.size() > namelen) namelen = h->name.size(); } if (namelen > 50) namelen = 50; char fmt[64]; snprintf(fmt, sizeof(fmt), "| %%%is | %%12s | %%10s | %%8s |", namelen); _draw_sep(ctx, namelen); ctx.printf(fmt, "timer name", "time left", "interval", "repeat").newl(); _draw_sep(ctx, namelen); char buf1[64], buf2[64]; for (tq_def::const_iterator i = tq.begin(); i != tq.end(); ++i) { timer_base *h = *i; _prettyprint(buf1, sizeof(buf1), h->time_left()); _prettyprint(buf2, sizeof(buf2), h->_interval); ctx.printf(fmt, h->name.c_str(), buf1, buf2, h->_repeat ? "true" : "false").newl(); } _draw_sep(ctx, namelen); return true; } bool timermgr::time_left(timeval &tv) { if (tq.empty()) return false; update_taccum(); timer_base *h = *tq.begin(); if (taccum > h->_target) { taccum -= h->_target; h->_target = 0; } else { h->_target -= taccum; taccum = 0; } tv.tv_sec = h->_target / 1000; tv.tv_usec = (h->_target % 1000) * 1000; return true; } uint32_t timermgr::time_left(const timer_base *def) const { uint32_t ac = 0; for (tq_def::const_iterator i = tq.begin(); i != tq.end(); ++i) { ac += (*i)->_target; if (def == *i) { break; } } if (taccum > ac) return 0; return ac - taccum; } void stream_push_formated_type(base_stream &os, const time_duration &d) { char *p = os.req_buffer(64); _prettyprint(p, 64, d.value); os.commit_change(strlen(p)); } mrd6-0.9.6/ipkg/0000755000175000017500000000000010746353706012031 5ustar hugohugomrd6-0.9.6/ipkg/mrd6.m6bone.conf0000644000175000017500000000070610351364153014726 0ustar hugohugolog { attach stderr normal; attach default "/var/log/mrd6.log" message_err; } interfaces disable br0; // Should be vlan0 + eth1 but Linux bridge seems broken for multicast interfaces disable eth0; // Interface to internal bridge handle-proper-bridge = true; // use ETH_P_ALL to see all packets on wrt54g // The default configured RP is m6bone's Renater RP. // Change this according to your setup groups ff00::/8 pim rp = 2001:660:3007:300:1::; mrd6-0.9.6/ipkg/preinst0000755000175000017500000000011310262007211013412 0ustar hugohugo#!/bin/sh if [ -x /etc/init.d/S50mrd6 ] then /etc/init.d/S50mrd6 stop fi mrd6-0.9.6/ipkg/control0000644000175000017500000000037110550053753013425 0ustar hugohugoPackage: mrd6 Version: 0.9.5-pre2-1 Architecture: mipsel Maintainer: Sebastien Chaumontet Source: http://fivebits.net/mrd6/ Section: net Priority: optional Depends: kmod-ipv6 Description: IPv6 multicast routing daemon mrd6-0.9.6/ipkg/postinst0000755000175000017500000000004410262007211013614 0ustar hugohugo#!/bin/sh /etc/init.d/S50mrd6 start mrd6-0.9.6/ipkg/README0000644000175000017500000000173310550053753012705 0ustar hugohugo---------------- MRD6 for OpenWrt ---------------- This will generate a 1151K large mrd6 binary for mips. More information on OpenWrt on http://www.openwrt.org/ How to build mrd6 for OpenWRT ----------------------------- # Get OpenWrt-SDK (http://openwrt.org/BuildingPackages) wget http://downloads.openwrt.org/nbd/gcc34/OpenWrt-SDK-Linux-i686-1.tar.bz2 tar xjvf OpenWrt-SDK-Linux-i686-1.tar.bz2 cd OpenWrt-SDK-Linux-i686-1/package/ # untar mrd6 source code here # The directory tree will loks like: # .../OpenWrt-SDK-Linux-i686-1/package/mrd6/src/... # Modify OPENWRTBR in mrd6/ipkg/rules according to your OpenWrt-SDK directory path # From mrd6 directory start: fakeroot ipkg-buildpackage -c the ipk package will be generated in ../ Thanks to --------- Hugo Santos for all porting, sizing down work and of course for writing mrd6. Bernhard Schmidt for some network conectivity. -- Sebastien Chaumontet (snip) July 2005 mrd6-0.9.6/ipkg/rules0000755000175000017500000000265510351367310013105 0ustar hugohugo#!/usr/bin/make -f # Path to OpenWrt-SDK OPENWRTBR ?= /opt/openwrt-sdk ARCH = mipsel TARGET = mrd6 DESTDIR = /tmp/mrd6 # use -static for ipkg FULL_STATIC = yes # compiled modules MODULES = mld pim console # used options OPTIONS = NO_INET6_OPTION MODULE_OPTIONS = -include ipkg/rules.local # ---------- Cross compilation env OPTIMIZE_FOR_CPU=$(ARCH) STAGING_DIR=$(OPENWRTBR)/staging_dir_$(ARCH)$(ARCH_FPU_SUFFIX) # TARGET_CROSS=$(STAGING_DIR)/bin/$(OPTIMIZE_FOR_CPU)-linux- TARGET_CROSS=$(STAGING_DIR)/bin/$(OPTIMIZE_FOR_CPU)-linux-uclibc- CC = $(TARGET_CROSS)gcc CXX = $(TARGET_CROSS)g++ STRIP = $(TARGET_CROSS)strip export TARGET DESTDIR FULL_STATIC CC CXX STRIP CONF_MODULES = $(addprefix --static ,$(MODULES)) CONF_OPTIONS = $(addprefix --option ,$(OPTIONS)) CONF_MODULE_OPTIONS = $(addprefix --module-option ,$(MODULE_OPTIONS)) all: build configure: configure-stamp configure-stamp: config/base.pl \ --prefix /usr \ --optimizations space \ --support-modules no \ $(CONF_OPTIONS) $(CONF_MODULE_OPTIONS) $(CONF_MODULES) touch configure-stamp build: configure $(MAKE) -C src $(MAKE) -C tools/c clean: rm configure-stamp $(MAKE) -C src clean install: build $(MAKE) -C src install $(MAKE) -C tools/c install for f in `find $(DESTDIR) -type f`; do (if file $$f | grep "not stripped" > /dev/null; then ( $(STRIP) --strip-unneeded $$f); fi); done mkdir -p $(DESTDIR)/etc/init.d/ cp -p ipkg/S50mrd6 $(DESTDIR)/etc/init.d/ mrd6-0.9.6/ipkg/S50mrd60000755000175000017500000000116410350016756013111 0ustar hugohugo#!/bin/sh DNAME=mrd6 case $1 in start) /sbin/lsmod | grep ipv6 > /dev/null if [ "$?" != "0" ] then echo -n "IPv6 stack required by mrd6. Loading ipv6 module: " /sbin/insmod ipv6 echo "Done." fi echo -n "Starting IPv6 multicast router (mrd6): " /usr/sbin/$DNAME -f /etc/mrd6.conf -D echo "Done." ;; stop) echo -n "Stopping IPv6 multicast router (mrd6): " killall $DNAME echo "Done." ;; restart) $0 stop $0 start ;; *) echo "Usage: $0 {start | stop | restart}" ;; esac exit 0 mrd6-0.9.6/ipkg/mrd6.base.conf0000644000175000017500000000047010351364153014450 0ustar hugohugolog { attach stderr normal; attach default "/var/log/mrd6.log" message_err; } interfaces disable br0; // Should be vlan0 + eth1 but Linux bridge seems broken for multicast interfaces disable eth0; // Interface to internal bridge handle-proper-bridge = true; // use ETH_P_ALL to see all packets on wrt54g mrd6-0.9.6/config/0000755000175000017500000000000010746353706012344 5ustar hugohugomrd6-0.9.6/config/base.pl0000755000175000017500000002510010600141410013564 0ustar hugohugo#!/usr/bin/perl use strict; my $enable_modules = 1; my $use_opts = 0; my $use_space_opts = 0; my $prefix = '/usr/local'; my @mrd_modules = ( 'mld', 'pim', 'console', 'bgp', 'msnip', 'mrdisc', 'ripng' ); my @modules = ( @mrd_modules, 'linux', 'support' ); my %mrd_modules_desc = ( 'mld' => 'Multicast Listener Discovery v1/v2 (MLD)', 'pim' => 'Protocol Independent Multicast Sparse Mode (PIM-SM)', 'console' => 'Interactive configuration', 'bgp' => 'Border Gateway Protocol with Multicast SAFI (BGP4+)', 'msnip' => 'Multicast Source Notification of Interest Protocol', 'mrdisc' => 'Multicast Router Discovery' ); my %static_modules = ( 'mld' => 1, 'pim' => 1, 'console' => 1, 'bgp' => 0, 'msnip' => 0, 'mrdisc' => 0 ); my %external_modules = ( 'mld' => 0, 'pim' => 0, 'console' => 0, 'bgp' => 1, 'msnip' => 1, 'mrdisc' => 1 ); my @adv_options = ( 'NO_INLINE' ); my %adv_options_desc = ( 'NO_INLINE' => 'Don\'t inline methods, easier debugging' ); my %adv_options_values = ( 'NO_INLINE' => 0 ); use constant FIRST_MENU => 0; use constant STATIC_MODULE_MENU => 1; use constant DYN_MODULE_MENU => 2; use constant ASK_WRITE_CONFIG => 3; use constant ADVANCED_MENU => 4; use constant MODULE_OPTIONS_MENU=> 5; my $state = FIRST_MENU; my $menu_result; my %module_options = (); my $dialog = 'dialog'; sub check_dialog { my $res = system("$dialog --version") or 1; if ($res ne 0) { print "dialog(1) or compatible is required to run the configuration.\n"; exit 1; } } sub run_menu { my ($command) = @_; my $res = 1; pipe(READER, WRITER); my $pid = fork; if ($pid == 0) { close(READER); open(STDERR, ">&WRITER"); exec($command); } if ($pid > 0) { close(WRITER); $menu_result = ; close(READER); waitpid($pid, 0); $res = $?; } return $res; } sub _build_modlist { my ($input) = @_; my $res = '"'; foreach (@mrd_modules) { if ($input->{$_}) { $res .= ', ' if $res ne '"'; $res .= $_; } } if ($res eq '"') { $res .= ''; } return $res . '"'; } sub _boolean_entry { my ($name, $val) = @_; my $res = ' "' . $name . '" '; if ($val) { $res .= '"Enabled"'; } else { $res .= '"Disabled"'; } } sub _is_module_enabled { my $name = shift; return 1 if $name eq 'linux' or $name eq 'support'; return $static_modules{$name} or $external_modules{$name}; } my $choose_msg = 'Choose one of the available options'; sub first_menu { my $command = "$dialog --title \"MRD6 Configuration\" --no-cancel --menu \"$choose_msg\" 0 0 0"; my $res; $command .= ' "Exit Configuration" ""'; $command .= _boolean_entry('Module Loading Support', $enable_modules); $command .= _boolean_entry('Optimizations', $use_opts); $command .= _boolean_entry('Space Optimizations', $use_space_opts) if $use_opts; $command .= " \"Installation Prefix\" \"$prefix\""; $command .= ' "Included modules" ' . _build_modlist(\%static_modules); $command .= ' "External modules" ' . _build_modlist(\%external_modules) if $enable_modules; foreach (@modules) { if (_is_module_enabled $_) { my $ref = $module_options{$_}; if (scalar keys %$ref > 0) { $command .= ' "' . (uc $_) . ' Module Options -->" ""'; } } } $command .= ' "Advanced Options -->" ""'; $res = run_menu $command; if ($menu_result eq 'Exit Configuration') { $state = ASK_WRITE_CONFIG; } elsif ($menu_result eq 'Module Loading Support') { $enable_modules = !$enable_modules; } elsif ($menu_result eq 'Optimizations') { $use_opts = !$use_opts; } elsif ($menu_result eq 'Space Optimizations') { $use_space_opts = !$use_space_opts; } elsif ($menu_result eq 'Installation Prefix') { } elsif ($menu_result eq 'Included modules') { $state = STATIC_MODULE_MENU; } elsif ($menu_result eq 'External modules') { $state = DYN_MODULE_MENU; } elsif ($menu_result eq 'Advanced Options -->') { $state = ADVANCED_MENU; } else { $menu_result =~ s/^([A-Z]+) Module Options -->$/$1/; $menu_result = lc $menu_result; $state = MODULE_OPTIONS_MENU; } } my $pretty_msg = "Select from the available module list"; sub module_menu { my ($input) = @_; my $res; my $command = "$dialog --single-quoted --checklist \"$pretty_msg\" 0 0 0"; foreach (@mrd_modules) { $command .= ' "' . $_ . '" "' . $mrd_modules_desc{$_} . '" '; if ($input->{$_}) { $command .= 'on'; } else { $command .= 'off'; } } $res = run_menu $command; if (!$res) { foreach (@mrd_modules) { $input->{$_} = 0; } foreach (split (/ /, $menu_result)) { $input->{$_} = 1; } } $state = FIRST_MENU; } sub advanced_menu { my $command = "$dialog --single-quoted --title \"Advanced Options\" --checklist \"$choose_msg\" 0 0 0"; foreach (@adv_options) { $command .= ' "' . $_ . '" "' . $adv_options_desc{$_} . '" '; if ($adv_options_values{$_}) { $command .= 'on'; } else { $command .= 'off'; } } if (!run_menu $command) { foreach (@adv_options) { $adv_options_values{$_} = 0; } foreach (split (/ /, $menu_result)) { $adv_options_values{$_} = 1; } } $state = FIRST_MENU; } sub load_module_options { foreach my $mod (@modules) { if (open F, "< src/$mod/Module.options") { while () { if ($_ =~ m/^boolean ([A-Z]+) default (on|off) description "([^"]+)"$/) { $module_options{$mod}{$1}{'description'} = $3; $module_options{$mod}{$1}{'default'} = $2; $module_options{$mod}{$1}{'value'} = $2; } } close F; } } } sub module_options_menu { my $command = "$dialog --single-quoted --title \"" . (uc $menu_result) . " module options\" --checklist \"$choose_msg\" 0 0 0"; my $ref = $module_options{$menu_result}; my @keys = sort keys %$ref; foreach my $key (@keys) { my $col = $ref->{$key}; $command .= ' "' . $key . '" "' . $col->{'description'} . '" ' . $col->{'value'}; } if (!run_menu $command) { foreach (@keys) { $ref->{$_}->{'value'} = 'off'; # $ref->{$_}->{'default'}; } foreach (split (/ /, $menu_result)) { $ref->{$_}->{'value'} = 'on'; } } $state = FIRST_MENU; } sub write_config { if (!open F, '> src/Makefile.options') { print 'Failed to open Makefile.options for writing.', "\n"; exit 1; } print F "PREFIX = $prefix\n"; if (!$enable_modules) { print F "SUPPORT_MODULES = no\n"; } if ($use_opts) { print F "OPTIMIZE = yes\n"; if ($use_space_opts) { print F "SPACE_OPTIMIZE = yes\n"; } } print F "STATIC_MODULES ="; foreach (@mrd_modules) { if ($static_modules{$_}) { print F ' ', (uc $_); } } print F "\nMODULES ="; if ($enable_modules) { foreach (@mrd_modules) { if ($external_modules{$_}) { print F ' ', (uc $_); } } } print F "\n"; print F "\nMODULE_OPTIONS ="; foreach (@modules) { if (_is_module_enabled $_) { my $mod = $_; my $ref = $module_options{$mod}; my @keys = keys %$ref; foreach (@keys) { my $def = $ref->{$_}->{'default'}; if (defined $def and ($ref->{$_}->{'value'} ne $def)) { print F ' ' . (uc $mod) . '_'; if ($def eq 'on') { print F 'NO_'; } print F $_; } } } } print F "\n"; if ($adv_options_values{'NO_INLINE'}) { print F "NO_INLINE = yes\n"; } close F; print 'Wrote the configuration to src/Makefile.options', "\n"; } sub parse_current_config { if (open F, '< src/Makefile.options') { while () { if ($_ =~ m/^([A-Z_]+) = ([0-9A-Za-z_\$\(\)\/\. ]+)$/) { if ($1 eq 'SUPPORT_MODULES') { $enable_modules = $2 eq 'yes'; } elsif ($1 eq 'OPTIMIZE') { $use_opts = $2 eq 'yes'; } elsif ($1 eq 'SPACE_OPTIMIZE') { $use_space_opts = $2 eq 'yes'; } elsif ($1 eq 'NO_INLINE') { $adv_options_values{$1} = $2 eq 'yes'; } elsif ($1 eq 'PREFIX') { $prefix = $2; } elsif ($1 eq 'STATIC_MODULES') { foreach (@mrd_modules) { $static_modules{$_} = 0; } foreach (split (/ /, $2)) { if ($_ =~ m/([A-Z]+)/) { $static_modules{lc $1} = 1; } } } elsif ($1 eq 'EXTERNAL_MODULES') { foreach (@mrd_modules) { $external_modules{$_} = 0; } foreach (split (/ /, $2)) { if ($_ =~ m/([A-Z]+)/) { $external_modules{lc $1} = 1; } } } elsif ($1 eq 'MODULE_OPTIONS') { foreach (split (/ /, $2)) { if ($_ =~ m/([A-Z]+)_([A-Z_]+)/) { my $ref = $module_options{lc $1}; #continue if not defined $ref; my $not = 0; my $name = $2; if ($name =~ m/NO_([A-Z]+)/) { $not = 1; $name = $1; } if ($not) { $ref->{$name}->{'value'} = 'off'; } else { $ref->{$name}->{'value'} = 'on'; } } } } } } close F; } } sub save_config { write_config; } sub ask_write_config { my $res = run_menu "$dialog --title \"MRD6 Configuration\" --yesno \"Write the new configuration to disk?\" 7 50"; save_config if !$res; exit 0; } if (!open F, '< src/Makefile') { print 'Not running the configuration from the proper directory', "\n"; exit 1; } close F; load_module_options; parse_current_config; if (scalar(@ARGV) > 1) { my $count = scalar @ARGV; my $i = 0; # Disable all modules by default foreach (@mrd_modules) { $static_modules{$_} = 0; $external_modules{$_} = 0; } # This piece of code isn't luser-safe while ($i < $count) { if ($ARGV[$i] eq '--prefix') { $prefix = $ARGV[$i + 1]; } elsif ($ARGV[$i] eq '--static') { $static_modules{$ARGV[$i + 1]} = 1; } elsif ($ARGV[$i] eq '--external') { $external_modules{$ARGV[$i + 1]} = 1; } elsif ($ARGV[$i] eq '--optimizations') { $use_opts = 1; if ($ARGV[$i + 1] eq 'none') { $use_opts = 0; } elsif ($ARGV[$i + 1] eq 'space') { $use_space_opts = 1; } else { $use_space_opts = 0; } } elsif ($ARGV[$i] eq '--option') { $adv_options_values{$ARGV[$i + 1]} = 1; } elsif ($ARGV[$i] eq '--module-option') { if ($ARGV[$i + 1] =~ m/([A-Z]+)_([A-Z_]+)/) { my $ref = $module_options{lc $1}; my $not = 0; my $name = $2; if ($name =~ m/NO_([A-Z]+)/) { $not = 1; $name = $1; } if ($not) { $ref->{$name}->{'value'} = 'off'; } else { $ref->{$name}->{'value'} = 'on'; } } } elsif ($ARGV[$i] eq '--support-modules') { $enable_modules = $ARGV[$i + 1] eq 'yes'; } else { print "Bad option ", $ARGV[$i], "\n"; exit 1; } $i += 2; } save_config; exit 0; } check_dialog; while (1) { if ($state == FIRST_MENU) { first_menu; } elsif ($state == STATIC_MODULE_MENU) { module_menu \%static_modules; } elsif ($state == DYN_MODULE_MENU) { module_menu \%external_modules; } elsif ($state == ASK_WRITE_CONFIG) { ask_write_config; } elsif ($state == ADVANCED_MENU) { advanced_menu; } elsif ($state == MODULE_OPTIONS_MENU) { module_options_menu; } } mrd6-0.9.6/COPYING0000644000175000017500000004313110125057715012124 0ustar hugohugo GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. mrd6-0.9.6/Troubleshooter0000644000175000017500000000420010276757744014046 0ustar hugohugoMRD6 Troubleshooter ------------------- Common problems: (1) P: The router doesn't create the group and source state for a directly attached sender. A: mrd6 will only create implicit states (i.e. from receiving data packets) if the source of those packets is local. The router will only assume the source is local _if and only if_ the incoming interface has an address with the same prefix as the source. (2) P: I'm testing a small setup using PIM-SM and the Any Source Multicast (ASM) model. The Designated Router for the source has created a source state for the sender and the downstream router has also created the (*,G) state for the localy joined group but no packets reach the receiver. A: No RP address is configured by default in mrd6, and PIM-SM requires one for ASM to work. You may configure an RP address on both routers using the following configuration: groups { ff00::/8 { pim rp = 2001::1; } } Where ff00::/8 is the group mask which is being configured and 2001::1 is the RP address. Please note that the configured RP must be a PIM-SM router as well (one of your deployed routers would work fine). You may also configure the RP address online via: # mrd6sh groups ff00::/8 pim rp = 2001::1 If your problem doesn't match any of the previous described situations procede with the following. Generic troubleshooter ---------------------- Increase the logging level to 'all' by for instance either by adding to or changing your configuration: log { attach default "mrd.log" all; } You may also attach a live log watcher or change your log level while mrd6 is running: # mrd6sh attach default \"mrd.log\" all to change the log level for the logfile to 'all' or: # mrd6sh console attach-log all to watch the log messages live. After doing so repeat any operations which failed previously and check for any error messages in the logs. These messages should express the possible problems clearly, and if they don't, or you believe the error is wrong or even if you get no related output at all, please contact the developers.