pimd: merge pimd as of 2015-01-19

Welcome pimd to the Quagga daemon zoo!

This is a merge of commit 77ae369 ("pimd: Log ifindex found for an
interface when zebra lib reports a new connected address."), with
the intermediate "reconnect" changes removed (c9adf00...d274381).
d274381 is replaced with b162ab7, which includes some changes.  In
addition, 4 reconnect-related changes and 1 cosmetic one have been
bumped out.

The rebase command used to produce the branch that is merged here is:
  git rebase --onto b162ab7 c9adf00 77ae369

Note that 3 patches had their author rewritten from
    "Anonymous SR#108542 <>" (which is not a valid git author ID)
to: "Savannah SR#108542 <nbahr@atcorp.com>" (which is the e-mail address
                               listed in the associated Savannah ticket)

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
This commit is contained in:
David Lamparter 2015-02-04 07:01:14 +01:00 committed by Donald Sharp
parent 5b282f59b1
commit 12e41d03bd
109 changed files with 23920 additions and 9 deletions

View file

@ -1,12 +1,12 @@
## Process this file with automake to produce Makefile.in.
SUBDIRS = lib @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ \
@ISISD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \
@ISISD@ @PIMD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \
redhat @SOLARIS@ tests
DIST_SUBDIRS = lib zebra bgpd ripd ripngd ospfd ospf6d \
isisd watchquagga vtysh ospfclient doc m4 pkgsrc redhat tests \
solaris
solaris pimd
EXTRA_DIST = aclocal.m4 SERVICES TODO REPORTING-BUGS INSTALL.quagga.txt \
update-autotools \

View file

@ -17,3 +17,4 @@ bgpd 2605/tcp
ospf6d 2606/tcp
ospfapi 2607/tcp
isisd 2608/tcp
pimd 2611/tcp

View file

@ -236,6 +236,8 @@ AC_ARG_ENABLE(watchquagga,
[ --disable-watchquagga do not build watchquagga])
AC_ARG_ENABLE(isisd,
[ --enable-isisd build isisd])
AC_ARG_ENABLE(pimd,
[ --enable-pimd build pimd])
AC_ARG_ENABLE(solaris,
[ --enable-solaris build solaris])
AC_ARG_ENABLE(bgp-announce,
@ -1382,6 +1384,13 @@ case "${enable_isisd}" in
esac
AM_CONDITIONAL(ISISD, test "x$ISISD" = "xisisd")
case "${enable_pimd}" in
"yes") PIMD="pimd";;
"no" ) PIMD="";;
* ) ;;
esac
AM_CONDITIONAL(PIMD, test "x$PIMD" = "xpimd")
# XXX Perhaps auto-enable on Solaris, but that's messy for cross builds.
case "${enable_solaris}" in
"yes") SOLARIS="solaris";;
@ -1404,6 +1413,7 @@ AC_SUBST(OSPFD)
AC_SUBST(OSPF6D)
AC_SUBST(WATCHQUAGGA)
AC_SUBST(ISISD)
AC_SUBST(PIMD)
AC_SUBST(SOLARIS)
AC_SUBST(VTYSH)
AC_SUBST(INCLUDES)
@ -1486,7 +1496,8 @@ dnl sockaddr and netinet checks
dnl ---------------------------
AC_CHECK_TYPES([struct sockaddr, struct sockaddr_in,
struct sockaddr_in6, struct sockaddr_un, struct sockaddr_dl,
socklen_t,
socklen_t, struct vifctl, struct mfcctl, struct sioc_sg_req,
vifi_t, struct sioc_vif_req, struct igmpmsg,
struct ifaliasreq, struct if6_aliasreq, struct in6_aliasreq,
struct nd_opt_adv_interval, struct rt_addrinfo,
struct nd_opt_homeagent_info, struct nd_opt_adv_interval],
@ -1515,6 +1526,45 @@ AC_CHECK_TYPES([struct in_pktinfo],
AC_MSG_ERROR(['IRDP requires in_pktinfo at the moment!'])
fi], [QUAGGA_INCLUDES])
dnl -----------------------
dnl checking for IP_PKTINFO
dnl -----------------------
AC_MSG_CHECKING(for IP_PKTINFO)
AC_TRY_COMPILE([#include <netdb.h>], [
int opt = IP_PKTINFO;
], [
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_IP_PKTINFO, 1, [Have IP_PKTINFO])
], [
AC_MSG_RESULT(no)
])
dnl ---------------------------
dnl checking for IP_RECVDSTADDR
dnl ---------------------------
AC_MSG_CHECKING(for IP_RECVDSTADDR)
AC_TRY_COMPILE([#include <netinet/in.h>], [
int opt = IP_RECVDSTADDR;
], [
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_IP_RECVDSTADDR, 1, [Have IP_RECVDSTADDR])
], [
AC_MSG_RESULT(no)
])
dnl ----------------------
dnl checking for IP_RECVIF
dnl ----------------------
AC_MSG_CHECKING(for IP_RECVIF)
AC_TRY_COMPILE([#include <netinet/in.h>], [
int opt = IP_RECVIF;
], [
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_IP_RECVIF, 1, [Have IP_RECVIF])
], [
AC_MSG_RESULT(no)
])
dnl --------------------------------------
dnl checking for getrusage struct and call
dnl --------------------------------------
@ -1687,6 +1737,7 @@ AC_DEFINE_UNQUOTED(PATH_BGPD_PID, "$quagga_statedir/bgpd.pid",bgpd PID)
AC_DEFINE_UNQUOTED(PATH_OSPFD_PID, "$quagga_statedir/ospfd.pid",ospfd PID)
AC_DEFINE_UNQUOTED(PATH_OSPF6D_PID, "$quagga_statedir/ospf6d.pid",ospf6d PID)
AC_DEFINE_UNQUOTED(PATH_ISISD_PID, "$quagga_statedir/isisd.pid",isisd PID)
AC_DEFINE_UNQUOTED(PATH_PIMD_PID, "$quagga_statedir/pimd.pid",pimd PID)
AC_DEFINE_UNQUOTED(PATH_WATCHQUAGGA_PID, "$quagga_statedir/watchquagga.pid",watchquagga PID)
AC_DEFINE_UNQUOTED(ZEBRA_SERV_PATH, "$quagga_statedir/zserv.api",zebra api socket)
AC_DEFINE_UNQUOTED(ZEBRA_VTYSH_PATH, "$quagga_statedir/zebra.vty",zebra vty socket)
@ -1696,6 +1747,7 @@ AC_DEFINE_UNQUOTED(BGP_VTYSH_PATH, "$quagga_statedir/bgpd.vty",bgpd vty socket)
AC_DEFINE_UNQUOTED(OSPF_VTYSH_PATH, "$quagga_statedir/ospfd.vty",ospfd vty socket)
AC_DEFINE_UNQUOTED(OSPF6_VTYSH_PATH, "$quagga_statedir/ospf6d.vty",ospf6d vty socket)
AC_DEFINE_UNQUOTED(ISIS_VTYSH_PATH, "$quagga_statedir/isisd.vty",isisd vty socket)
AC_DEFINE_UNQUOTED(PIM_VTYSH_PATH, "$quagga_statedir/pimd.vty",pimd vty socket)
AC_DEFINE_UNQUOTED(DAEMON_VTY_DIR, "$quagga_statedir",daemon vty directory)
dnl -------------------------------
@ -1721,6 +1773,7 @@ AC_CONFIG_FILES([Makefile lib/Makefile zebra/Makefile ripd/Makefile
ripngd/Makefile bgpd/Makefile ospfd/Makefile watchquagga/Makefile
ospf6d/Makefile isisd/Makefile vtysh/Makefile
doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile
pimd/Makefile
tests/bgpd.tests/Makefile
tests/libzebra.tests/Makefile
redhat/Makefile

View file

@ -63,6 +63,10 @@ quagga_TEXINFOS = appendix.texi basic.texi bgpd.texi filter.texi \
man_MANS = quagga.1
if PIMD
man_MANS += pimd.8
endif
if BGPD
man_MANS += bgpd.8
endif
@ -105,7 +109,7 @@ endif
EXTRA_DIST = BGP-TypeCode draft-zebra-00.ms draft-zebra-00.txt \
bgpd.8 isisd.8 ospf6d.8 ospfclient.8 ospfd.8 ripd.8 \
ripngd.8 vtysh.1 watchquagga.8 zebra.8 \
ripngd.8 pimd.8 vtysh.1 watchquagga.8 zebra.8 \
mpls/ChangeLog.opaque.txt mpls/cli_summary.txt \
mpls/opaque_lsa.txt mpls/ospfd.conf \
$(figures_sources) $(figures_png) $(figures_txt)

View file

@ -273,6 +273,7 @@ bgpd 2605/tcp # BGPd vty
ospf6d 2606/tcp # OSPF6d vty
ospfapi 2607/tcp # ospfapi
isisd 2608/tcp # ISISd vty
pimd 2611/tcp # PIMd vty
@end example
If you use a FreeBSD newer than 2.2.8, the above entries are already

125
doc/pimd.8 Normal file
View file

@ -0,0 +1,125 @@
.TH PIM 8 "10 December 2008" "Quagga PIM daemon" "Version 0.99.11"
.SH NAME
pimd \- a PIM routing for use with Quagga Routing Suite.
.SH SYNOPSIS
.B pimd
[
.B \-dhvZ
] [
.B \-f
.I config-file
] [
.B \-i
.I pid-file
] [
.B \-z
.I path
] [
.B \-P
.I port-number
] [
.B \-A
.I vty-address
] [
.B \-u
.I user
] [
.B \-g
.I group
]
.SH DESCRIPTION
.B pimd
is a protocol-independent multicast component that works with the
.B Quagga
Routing Suite.
.SH OPTIONS
Options available for the
.B pimd
command:
.TP
\fB\-d\fR, \fB\-\-daemon\fR
Runs in daemon mode, forking and exiting from tty.
.TP
\fB\-f\fR, \fB\-\-config-file \fR\fIconfig-file\fR
Specifies the config file to use for startup. If not specified this
option will likely default to \fB\fI/usr/local/etc/pimd.conf\fR.
.TP
\fB\-g\fR, \fB\-\-group \fR\fIgroup\fR
Specify the group to run as. Default is \fIquagga\fR.
.TP
\fB\-h\fR, \fB\-\-help\fR
A brief message.
.TP
\fB\-i\fR, \fB\-\-pid_file \fR\fIpid-file\fR
When pimd starts its process identifier is written to
\fB\fIpid-file\fR. The init system uses the recorded PID to stop or
restart pimd. The likely default is \fB\fI/var/run/pimd.pid\fR.
.TP
\fB\-z\fR, \fB\-\-socket \fR\fIpath\fR
Specify the socket path for contacting the zebra daemon.
The likely default is \fB\fI/var/run/zserv.api\fR.
.TP
\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR
Specify the port that the pimd VTY will listen on. This defaults to
2611, as specified in \fB\fI/etc/services\fR.
.TP
\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR
Specify the address that the pimd VTY will listen on. Default is all
interfaces.
.TP
\fB\-u\fR, \fB\-\-user \fR\fIuser\fR
Specify the user to run as. Default is \fIquagga\fR.
.TP
\fB\-v\fR, \fB\-\-version\fR
Print the version and exit.
.TP
\fB\-Z\fR, \fB\-\-debug_zclient\fR
Enable logging information for zclient debugging.
.SH FILES
.TP
.BI /usr/local/sbin/pimd
The default location of the
.B pimd
binary.
.TP
.BI /usr/local/etc/pimd.conf
The default location of the
.B pimd
config file.
.TP
.BI /var/run/pimd.pid
The default location of the
.B pimd
pid file.
.TP
.BI /var/run/zserv.api
The default location of the
.B zebra
unix socket file.
.TP
.BI $(PWD)/pimd.log
If the
.B pimd
process is config'd to output logs to a file, then you will find this
file in the directory where you started \fBpimd\fR.
.SH WARNING
This man page is intended to be a quick reference for command line
options.
.SH DIAGNOSTICS
The pimd process may log to standard output, to a VTY, to a log
file, or through syslog to the system logs.
.SH "SEE ALSO"
.BR zebra (8),
.BR vtysh (1)
.SH BUGS
\fBpimd\fR is in early development at the moment and is not ready for
production use.
.B pimd
eats bugs for breakfast. If you have food for the maintainers try
.BI https://github.com/udhos/qpimd
.SH AUTHORS
See
.BI https://github.com/udhos/qpimd
for an accurate list of authors.

View file

@ -2943,6 +2943,7 @@ DEFUN (config_exit,
case KEYCHAIN_NODE:
case MASC_NODE:
case RMAP_NODE:
case PIM_NODE:
case VTY_NODE:
vty->node = CONFIG_NODE;
break;
@ -3000,6 +3001,7 @@ DEFUN (config_end,
case KEYCHAIN_NODE:
case KEYCHAIN_KEY_NODE:
case MASC_NODE:
case PIM_NODE:
case VTY_NODE:
vty_config_unlock (vty);
vty->node = ENABLE_NODE;

View file

@ -89,6 +89,7 @@ enum node_type
OSPF_NODE, /* OSPF protocol mode */
OSPF6_NODE, /* OSPF protocol for IPv6 mode */
ISIS_NODE, /* ISIS protocol mode */
PIM_NODE, /* PIM protocol mode */
MASC_NODE, /* MASC for multicast. */
IRDP_NODE, /* ICMP Router Discovery Protocol mode. */
IP_NODE, /* Static ip route node. */

View file

@ -50,6 +50,7 @@ const char *zlog_proto_names[] =
"RIPNG",
"OSPF6",
"ISIS",
"PIM",
"MASC",
NULL,
};

View file

@ -52,6 +52,7 @@ typedef enum
ZLOG_RIPNG,
ZLOG_OSPF6,
ZLOG_ISIS,
ZLOG_PIM,
ZLOG_MASC
} zlog_proto_t;

View file

@ -416,6 +416,17 @@ DEFUN (show_memory,
}
DEFUN (show_memory_pim,
show_memory_pim_cmd,
"show memory pim",
SHOW_STR
"Memory statistics\n"
"PIM memory\n")
{
show_memory_vty (vty, memory_list_pim);
return CMD_SUCCESS;
}
void
memory_init (void)
{

View file

@ -271,6 +271,21 @@ struct memory_list memory_list_isis[] =
{ -1, NULL },
};
struct memory_list memory_list_pim[] =
{
{ MTYPE_PIM_CHANNEL_OIL, "PIM SSM (S,G) channel OIL" },
{ MTYPE_PIM_INTERFACE, "PIM interface" },
{ MTYPE_PIM_IGMP_JOIN, "PIM interface IGMP static join" },
{ MTYPE_PIM_IGMP_SOCKET, "PIM interface IGMP socket" },
{ MTYPE_PIM_IGMP_GROUP, "PIM interface IGMP group" },
{ MTYPE_PIM_IGMP_GROUP_SOURCE, "PIM interface IGMP source" },
{ MTYPE_PIM_NEIGHBOR, "PIM interface neighbor" },
{ MTYPE_PIM_IFCHANNEL, "PIM interface (S,G) state" },
{ MTYPE_PIM_UPSTREAM, "PIM upstream (S,G) state" },
{ MTYPE_PIM_SSMPINGD, "PIM sspimgd socket" },
{ -1, NULL },
};
struct memory_list memory_list_vtysh[] =
{
{ MTYPE_VTYSH_CONFIG, "Vtysh configuration", },
@ -287,5 +302,6 @@ struct mlist mlists[] __attribute__ ((unused)) = {
{ memory_list_ospf6, "OSPF6" },
{ memory_list_isis, "ISIS" },
{ memory_list_bgp, "BGP" },
{ memory_list_pim, "PIM" },
{ NULL, NULL},
};

View file

@ -51,6 +51,7 @@ ZEBRA_ROUTE_OSPF, ospf, ospfd, 'O', 1, 0, "OSPF"
ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, "OSPFv6"
ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, "IS-IS"
ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, "BGP"
ZEBRA_ROUTE_PIM, pim, pimd, 'P', 1, 0, "PIM"
# HSLS and OLSR both are AFI independent (so: 1, 1), however
# we want to disable for them for general Quagga distribution.
# This at least makes it trivial for users of these protocols
@ -71,6 +72,7 @@ ZEBRA_ROUTE_OSPF, "Open Shortest Path First (OSPFv2)"
ZEBRA_ROUTE_OSPF6, "Open Shortest Path First (IPv6) (OSPFv3)"
ZEBRA_ROUTE_ISIS, "Intermediate System to Intermediate System (IS-IS)"
ZEBRA_ROUTE_BGP, "Border Gateway Protocol (BGP)"
ZEBRA_ROUTE_PIM, "Protocol Independent Multicast (PIM)"
ZEBRA_ROUTE_HSLS, "Hazy-Sighted Link State Protocol (HSLS)"
ZEBRA_ROUTE_OLSR, "Optimised Link State Routing (OLSR)"
ZEBRA_ROUTE_TABLE, "Non-main Kernel Routing Table"

View file

@ -1,6 +1,5 @@
/* Thread management routine header.
* Copyright (C) 1998 Kunihiro Ishiguro
* Portions Copyright (c) 2008 Everton da Silva Marques <everton.marques@gmail.com>
*
* This file is part of GNU Zebra.
*

View file

@ -714,6 +714,7 @@ vty_end_config (struct vty *vty)
case KEYCHAIN_NODE:
case KEYCHAIN_KEY_NODE:
case MASC_NODE:
case PIM_NODE:
case VTY_NODE:
vty_config_unlock (vty);
vty->node = ENABLE_NODE;
@ -1117,6 +1118,7 @@ vty_stop_input (struct vty *vty)
case KEYCHAIN_NODE:
case KEYCHAIN_KEY_NODE:
case MASC_NODE:
case PIM_NODE:
case VTY_NODE:
vty_config_unlock (vty);
vty->node = ENABLE_NODE;

View file

@ -260,7 +260,7 @@ zclient_socket_connect (struct zclient *zclient)
#ifdef HAVE_TCP_ZEBRA
zclient->sock = zclient_socket ();
#else
zclient->sock = zclient_socket_un (zclient_serv_path ? zclient_serv_path : ZEBRA_SERV_PATH);
zclient->sock = zclient_socket_un (zclient_serv_path_get());
#endif
return zclient->sock;
}
@ -1582,6 +1582,11 @@ zclient_event (enum event event, struct zclient *zclient)
}
}
const char *const zclient_serv_path_get()
{
return zclient_serv_path ? zclient_serv_path : ZEBRA_SERV_PATH;
}
void
zclient_serv_path_set (char *path)
{

View file

@ -167,6 +167,7 @@ extern void zclient_free (struct zclient *);
extern int zclient_socket_connect (struct zclient *);
extern void zclient_serv_path_set (char *path);
extern const char *const zclient_serv_path_get (void);
extern int redist_check_instance (struct redist_proto *, u_short);
extern void redist_add_instance (struct redist_proto *, u_short);

16
pimd/.gitignore vendored Normal file
View file

@ -0,0 +1,16 @@
Makefile
Makefile.in
libpim.a
pimd
test_igmpv3_join
tags
TAGS
.deps
*.o
*.lo
*.la
*.libs
.arch-inventory
.arch-ids
*~
*.loT

9
pimd/AUTHORS Normal file
View file

@ -0,0 +1,9 @@
# $QuaggaId: $Format:%an, %ai, %h$ $
# Everton da Silva Marques <everton.marques@gmail.com>
$ more ~/.gitconfig
[user]
name = Everton Marques
email = everton.marques@gmail.com
-x-

178
pimd/CAVEATS Normal file
View file

@ -0,0 +1,178 @@
# $QuaggaId: $Format:%an, %ai, %h$ $
C1 IGMPv3 backward compatibility with IGMPv1 and IGMPv2 is not
implemented. See RFC 3376, 7.3. Multicast Router Behavior. That's
because only Source-Specific Multicast is currently targeted.
C2 IGMPv3 support for forwarding any-source groups is not
implemented. Traffic for groups in mode EXCLUDE {empty} won't be
forwarded. See RFC 3376, 6.3. Source-Specific Forwarding
Rules. That's because only Source-Specific Multicast is currently
targeted.
C3 Load Splitting of IP Multicast Traffic over ECMP is not supported.
See also: RFC 2991
Multipath Issues in Unicast and Multicast Next-Hop Selection
http://www.rfc-editor.org/rfc/rfc2991.txt
C4 IPSec AH authentication is not supported (RFC 4601:
6.3. Authentication Using IPsec).
C5 PIM support is limited to SSM mode as defined in section 4.8.2
(PIM-SSM-Only Routers) of RFC4601. That's because only
Source-Specific Multicast is currently targeted.
C6 PIM implementation currently does not support IPv6. PIM-SSM
requires IGMPv3 for IPv4 and MLDv2 for IPv6. MLDv2 is currently
missing. See also CAVEAT C9.
C7 FIXED (S,G) Assert state machine (RFC 4601, section 4.6.1) is not
implemented. See also TODO T6. See also CAVEAT C10.
C8 It is not possible to disable join suppression in order to
explicitly track the join membership of individual downstream
routers.
- IGMPv3 Explicit Membership Tracking is not supported.
When explicit tracking is enabled on a router, the router can
individually track the Internet Group Management Protocol (IGMP)
membership state of all reporting hosts. This feature allows the
router to achieve minimal leave latencies when hosts leave a
multicast group or channel. Example:
conf t
interface eth0
ip igmp explicit-tracking
C9 Only IPv4 Address Family (number=1) is supported in the PIM Address
Family field.
See also RFC 4601: 5.1. PIM Address Family
See also CAVEAT C6.
See also http://www.iana.org/assignments/address-family-numbers
C10 FIXED Assert metric depends on metric_preference and
route_metric. Those parameters should be fetched from RIB
(zebra). See also pim_rpf.c, pim_rpf_update().
C11 SSM Mapping is not supported
SSM Mapping Overview:
SSM mapping introduces a means for the last hop router to discover
sources sending to groups. When SSM mapping is configured, if a
router receives an IGMPv1 or IGMPv2 membership report for a
particular group G, the router translates this report into one or
more (S, G) channel memberships for the well-known sources
associated with this group.
When the router receives an IGMPv1 or IGMPv2 membership report for
a group G, the router uses SSM mapping to determine one or more
source IP addresses for the group G. SSM mapping then translates
the membership report as an IGMPv3 report INCLUDE (G, [S1, G],
[S2, G]...[Sn, G] and continues as if it had received an IGMPv3
report. The router then sends out PIM joins toward (S1, G) to (Sn,
G) and continues to be joined to these groups as long as it
continues to receive the IGMPv1 or IGMPv2 membership reports and
as long as the SSM mapping for the group remains the same. SSM
mapping, thus, enables you to leverage SSM for video delivery to
legacy STBs that do not support IGMPv3 or for applications that do
not take advantage of the IGMPv3 host stack.
SSM mapping enables the last hop router to determine the source
addresses either by a statically configured table on the router or
by consulting a DNS server. When the statically configured table
is changed, or when the DNS mapping changes, the router will leave
the current sources associated with the joined groups.
C12 FIXED MRIB for incongruent unicast/multicast topologies is not
supported. RPF mechanism currently just looks up the information
in the unicast routing table.
See also:
RFC5110: 2.2.3. Issue: Overlapping Unicast/Multicast Topology
Sometimes, multicast RPF mechanisms first look up the multicast
routing table, or M-RIB ("topology database") with a longest
prefix match algorithm, and if they find any entry (including a
default route), that is used; if no match is found, the unicast
routing table is used instead.
C13 Can't detect change of primary address before the actual change.
Possible approach is to craft old interface address into ip source
address by using raw ip socket.
See also:
RFC 4601: 4.3.1. Sending Hello Messages
Before an interface goes down or changes primary IP address, a
Hello message with a zero HoldTime should be sent immediately
(with the old IP address if the IP address changed).
See also pim_sock_delete().
C14 FIXED Detection of interface primary address changes may fail when
there are multiple addresses.
See also TODO T32.
C15 Changes in interface secondary address list are not immediately
detected.
See also detect_secondary_address_change
See also TODO T31.
C16 AMT Draft (mboned-auto-multicast) is not supported.
AMT = Automatic IP Multicast Without Explicit Tunnels
See also:
Draft
http://tools.ietf.org/html/draft-ietf-mboned-auto-multicast
http://tools.ietf.org/html/draft-ietf-mboned-auto-multicast-09
AMT gateway implementation for Linux
http://cs.utdallas.edu/amt/
AMT for Streaming (IPTV) on Global IP Multicast by Greg Shepherd (Cisco)
http://nznog.miniconf.org/nznog-2008-sysadmin-miniconf-greg-shepherd-iptv.pdf
C17 SNMP / RFC 5060 (PIM MIB) is not supported.
C18 MFC never recovers from removal of static route to source
# route add -host 1.2.3.4 gw 192.168.56.10
Before removal:
quagga-pimd-router# sh ip mroute
Source Group Proto Input iVifI Output oVifI TTL Uptime
1.2.3.4 232.1.2.3 I eth1 3 eth0 2 1 00:00:36
# route del -host 1.2.3.4 gw 192.168.56.10
After removal: sh ip mroute --> empty output
# route add -host 1.2.3.4 gw 192.168.56.10
After the route is restored: sh ip mroute --> never recovers (empty output)
At this point, "no ip pim ssm" on the upstream interface (eth0) crashes pimd:
2014/02/14 16:30:14 PIM: ifmembership_set: (S,G)=(1.2.3.4,232.1.2.3) membership now is NOINFO on interface eth0
2014/02/14 16:30:14 PIM: pim_ifchannel_update_assert_tracking_desired: AssertTrackingDesired(1.2.3.4,232.1.2.3,eth0) changed from 1 to 0
2014/02/14 16:30:14 PIM: pim_zebra.c del_oif: nonexistent protocol mask 2 removed OIF eth0 (vif_index=2, min_ttl=0) from channel (S,G)=(1.2.3.4,232.1.2.3)
2014/02/14 16:30:14 PIM: pim_ifchannel_update_could_assert: CouldAssert(1.2.3.4,232.1.2.3,eth0) changed from 1 to 0
2014/02/14 16:30:14 PIM: pim_ifchannel_update_my_assert_metric: my_assert_metric(1.2.3.4,232.1.2.3,eth0) changed from 0,0,0,10.0.2.15 to 1,4294967295,4294967295,0.0.0.0
2014/02/14 16:30:14 PIM: pim_zebra.c del_oif: nonexistent protocol mask 1 removed OIF eth0 (vif_index=2, min_ttl=0) from channel (S,G)=(1.2.3.4,232.1.2.3)
2014/02/14 16:30:14 PIM: Assertion `!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)' failed in file pim_igmpv3.c, line 412, function igmp_source_delete
C19 Provision to prevent group mode clash
Beware group mode clash. A host/application issuing IGMPv2
any-source joins for a group will disrupt SSM multicast for that
group.
For instance, support for source-specific static igmp WILL FAIL if
there is host/application issuing IGMPv2 any-source joins for the
same group.
The reason is the IGMPv2 any-source join forces qpimd to switch
the group mode to ASM (any-source multicast); however, qpimd is
unable to program ASM groups into the kernel; multicast won't
flow. There could be some provision to prevent such a behavior,
but currently there is none.
-x-

81
pimd/COMMANDS Normal file
View file

@ -0,0 +1,81 @@
# $QuaggaId: $Format:%an, %ai, %h$ $
global configuration commands:
pimd:
ip multicast-routing Enable IP multicast forwarding
ip ssmpingd Enable ssmpingd operation
zebra:
ip mroute Configure static unicast route into MRIB for multicast RPF lookup
interface configuration commands:
pimd:
ip igmp Enable IGMP operation
ip igmp join IGMP join multicast group
ip igmp query-interval <1-1800> IGMP host query interval
ip igmp query-max-response-time <1-25> IGMP max query response (seconds)
ip igmp query-max-response-time-dsec <10-250> IGMP max query response (deciseconds)
ip pim ssm Enable PIM SSM operation
verification commands:
pimd:
show ip igmp interface IGMP interface information
show ip igmp join IGMP static join information
show ip igmp parameters IGMP parameters information
show ip igmp groups IGMP groups information
show ip igmp groups retransmissions IGMP group retransmission
show ip igmp sources IGMP sources information
show ip igmp sources retransmissions IGMP source retransmission
show ip pim address PIM interface address
show ip pim assert PIM interface assert
show ip pim assert-internal PIM interface internal assert state
show ip pim assert-metric PIM interface assert metric
show ip pim assert-winner-metric PIM interface assert winner metric
show ip pim designated-router PIM interface designated router
show ip pim hello PIM interface hello information
show ip pim interface PIM interface information
show ip pim lan-prune-delay PIM neighbors LAN prune delay parameters
show ip pim local-membership PIM interface local-membership
show ip pim jp-override-interval PIM interface J/P override interval
show ip pim join PIM interface join information
show ip pim neighbor PIM neighbor information
show ip pim rpf PIM cached source rpf information
show ip pim secondary PIM neighbor addresses
show ip pim upstream PIM upstream information
show ip pim upstream-join-desired PIM upstream join-desired
show ip pim upstream-rpf PIM upstream source rpf
show ip multicast Multicast global information
show ip mroute IP multicast routing table
show ip mroute count Route and packet count data
show ip rib IP unicast routing table
show ip ssmpingd ssmpingd operation
zebra:
show ip rpf Display RPF information for multicast source
debug commands:
pimd:
clear ip interfaces Reset interfaces
clear ip igmp interfaces Reset IGMP interfaces
clear ip mroute Reset multicast routes
clear ip pim interfaces Reset PIM interfaces
clear ip pim oil Rescan PIM OIL (output interface list)
debug igmp IGMP protocol activity
debug mroute PIM interaction with kernel MFC cache
debug pim PIM protocol activity
debug pim zebra ZEBRA protocol activity
debug ssmpingd ssmpingd activity
show debugging State of each debugging option
test igmp receive report Test reception of IGMPv3 report
test pim receive assert Test reception of PIM assert
test pim receive dump Test reception of PIM packet dump
test pim receive hello Test reception of PIM hello
test pim receive join Test reception of PIM join
test pim receive prune Test reception of PIM prune
test pim receive upcall Test reception of kernel upcall
statistics commands:
pimd:
show memory pim PIM memory statistics
-x-

340
pimd/COPYING Normal file
View file

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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
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.
<signature of Ty Coon>, 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.

86
pimd/DEBUG Normal file
View file

@ -0,0 +1,86 @@
# $QuaggaId: $Format:%an, %ai, %h$ $
DEBUG HINTS
- Check the source is issuing multicast packets with TTL high enough
to reach the recipients.
- Check the multicast packets are not being dropped due to
fragmentation problems.
- Three easy options to test IGMPv3 joins from the receiver host:
1) Configure pimd on the receiver host with "ip igmp join":
interface eth0
ip pim ssm
ip igmp join 239.1.1.1 1.1.1.1
2) Use test_igmpv3_join command-line utility (provided with qpimd):
test_igmpv3_join eth0 239.1.1.1 1.1.1.1
3) User the Stig Venaas' ssmping utility:
ssmping -I eth0 1.1.1.1
To see multicast responses with ssmping, you will need run on
the host 1.1.1.1 either:
a) Stig Venaas' ssmpingd command-line daemon
OR
b) qpimd built-in ssmpingd service:
conf t
ip ssmpingd 1.1.1.1
- Using nepim to generate multicast stream from 1.1.1.1 to 239.1.1.1:
Notices:
a) The host unicast address 1.1.1.1 must be reachable from the
receiver.
b) nepim tool requires the receiver must be started *before* the
sender.
First: Start a receiver for that stream by running:
nepim -q -6 -j 1.1.1.1+239.1.1.1@eth0
(Remember of enabling both "ip pim ssm" and "ip igmp" under eth0.)
Second: Start the sender at host 1.1.1.1.
The following command generates a 100-kbps multicast stream for
channel 1.1.1.1,239.1.1.1 with TTL 10 and 1000-byte payload per UDP
packet (to avoid fragmentation):
nepim -6 -M -b 1.1.1.1 -c 239.1.1.1 -T 10 -W 1000 -r 100k -a 1d
SAMPLE DEBUG COMMANDS
conf t
int eth0
ip pim ssm
test pim receive hello eth0 192.168.0.2 600 10 111 1000 3000 0
test pim receive join eth0 600 192.168.0.1 192.168.0.2 239.1.1.1 1.1.1.1
show ip pim join
INTEROPERABILITY WITH CISCO
! Cisco IP Multicast command reference:
! ftp://ftpeng.cisco.com/ipmulticast/Multicast-Commands
!
ip pim ssm default ! enable SSM mode for groups 232.0.0.0/8
ip multicast-routing
ip pim state-refresh disable
no ip pim dm-fallback
!
interface FastEthernet0
ip pim sparse-mode
ip igmp version 3
-x-

View file

@ -0,0 +1,26 @@
# $QuaggaId: $Format:%an, %ai, %h$ $
#
# The Linux Kernel MFC (Multicast Forwarding Cache)
#
# Check Linux kernel multicast interfaces:
cat /proc/net/dev_mcast
# Check that interface eth0 is forwarding multicast:
cat /proc/sys/net/ipv4/conf/eth0/mc_forwarding
# Check Linux kernel multicast VIFs:
cat /proc/net/ip_mr_vif
Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote
# Check Linux kernel MFC:
# Oifs format = vifi:TTL
cat /proc/net/ip_mr_cache
Group Origin Iif Pkts Bytes Wrong Oifs
# iproute2 can display the MFC:
ip mroute show
(2.2.2.2, 239.2.2.2) Iif: eth1 Oifs: eth0
# -- end-of-file --

76
pimd/Makefile.am Normal file
View file

@ -0,0 +1,76 @@
## Process this file with automake to produce Makefile.in.
## $QuaggaId: $Format:%an, %ai, %h$ $
# qpimd - pimd for quagga
# Copyright (C) 2008 Everton da Silva Marques
#
# qpimd 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,
# or (at your option) any later version.
#
# qpimd 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 qpimd; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 59 Temple Place - Suite
# 330, Boston, MA 02111-1307, USA.
# PIM_DEBUG_BYDEFAULT: Automatically enables all pimd "debug ..." commands
# PIM_ZCLIENT_DEBUG: Support for internal ZEBRA client debugging
# PIM_CHECK_RECV_IFINDEX_SANITY: Compare socket ifindex with recv ifindex
# PIM_REPORT_RECV_IFINDEX_MISMATCH: Report sock/recv ifindex mismatch
# PIM_ENFORCE_LOOPFREE_MFC: Refuse adding looping MFC entries
# PIM_UNEXPECTED_KERNEL_UPCALL: Report unexpected kernel upcall
PIM_DEFS =
#PIM_DEFS += -DPIM_DEBUG_BYDEFAULT
PIM_DEFS += -DPIM_CHECK_RECV_IFINDEX_SANITY
#PIM_DEFS += -DPIM_REPORT_RECV_IFINDEX_MISMATCH
PIM_DEFS += -DPIM_ZCLIENT_DEBUG
PIM_DEFS += -DPIM_ENFORCE_LOOPFREE_MFC
#PIM_DEFS += -DPIM_UNEXPECTED_KERNEL_UPCALL
INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib
DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" $(PIM_DEFS)
INSTALL_SDATA=@INSTALL@ -m 600
LIBS = @LIBS@
AM_CFLAGS = $(PICFLAGS)
AM_LDFLAGS = $(PILDFLAGS)
noinst_LIBRARIES = libpim.a
sbin_PROGRAMS = pimd
bin_PROGRAMS = test_igmpv3_join
libpim_a_SOURCES = \
pimd.c pim_version.c pim_cmd.c pim_signals.c pim_iface.c \
pim_vty.c pim_igmp.c pim_sock.c pim_zebra.c \
pim_igmpv3.c pim_str.c pim_mroute.c pim_util.c pim_time.c \
pim_oil.c pim_zlookup.c pim_pim.c pim_tlv.c pim_neighbor.c \
pim_hello.c pim_ifchannel.c pim_join.c pim_assert.c \
pim_msg.c pim_upstream.c pim_rpf.c pim_rand.c pim_macro.c \
pim_igmp_join.c pim_ssmpingd.c pim_int.c
noinst_HEADERS = \
pimd.h pim_version.h pim_cmd.h pim_signals.h pim_iface.h \
pim_vty.h pim_igmp.h pim_sock.h pim_zebra.h \
pim_igmpv3.h pim_str.h pim_mroute.h pim_util.h pim_time.h \
pim_oil.h pim_zlookup.h pim_pim.h pim_tlv.h pim_neighbor.h \
pim_hello.h pim_ifchannel.h pim_join.h pim_assert.h \
pim_msg.h pim_upstream.h pim_rpf.h pim_rand.h pim_macro.h \
pim_igmp_join.h pim_ssmpingd.h pim_int.h
pimd_SOURCES = \
pim_main.c $(libpim_a_SOURCES)
test_igmpv3_join_SOURCES = \
test_igmpv3_join.c pim_igmp_join.c
pimd_LDADD = ../lib/libzebra.la @LIBCAP@
examplesdir = $(exampledir)
dist_examples_DATA = pimd.conf.sample

164
pimd/README Normal file
View file

@ -0,0 +1,164 @@
#
# $QuaggaId: $Format:%an, %ai, %h$ $
#
INTRODUCTION
qpimd aims to implement a PIM (Protocol Independent Multicast)
daemon for the Quagga Routing Suite.
Initially qpimd targets only PIM SSM (Source-Specific
Multicast) mode as defined in section 4.8.2 (PIM-SSM-Only
Routers) of RFC 4601.
In order to deliver end-to-end multicast routing control
plane, qpimd includes the router-side of IGMPv3 (RFC 3376).
LICENSE
qpimd - pimd for quagga
Copyright (C) 2008 Everton da Silva Marques
qpimd 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,
or (at your option) any later version.
qpimd 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 qpimd; see the file COPYING. If not, write
to the Free Software Foundation, Inc., 59 Temple Place - Suite
330, Boston, MA 02111-1307, USA.
HOME SITE
qpimd lives at:
https://github.com/udhos/qpimd
PLATFORMS
qpimd has been tested with Debian Lenny under Linux 2.6.
REQUIREMENTS
qpimd requires Quagga (0.99.11 or higher from http://www.quagga.net)
The GNU Build System (Autotools) is required to build from
source code repository.
gawk is also needed to build with Autotools. Any other awk
usually won't work.
BUILDING FROM QUAGGA GIT REPOSITORY
1) Get the latest quagga source tree
# git clone git://code.quagga.net/quagga.git quagga
2) Apply qpimd patch into quagga source tree
# patch -p1 -d quagga < pimd-0.153-quagga-git20090623.patch
3) Compile and install quagga
# cd quagga
# ./bootstrap.sh
# ./configure --prefix=/usr/local/quagga --enable-pimd
# make
# make install
BUILDING FROM QUAGGA TARBALL
1) Get the latest quagga tarball
# wget http://www.quagga.net/download/quagga-0.99.13.tar.gz
2) Unpack the quagga tarball
# tar xzf quagga-0.99.13.tar.gz
3) Apply qpimd patch into quagga source tree
# patch -p1 -d quagga-0.99.13 < pimd-0.153-quagga-0.99.13.patch
4) Compile and install quagga
# cd quagga-0.99.13
# ./configure --prefix=/usr/local/quagga --enable-pimd
# make
# make install
USAGE
1) Configure and start the zebra daemon
# cp /usr/local/quagga/etc/zebra.conf.sample /usr/local/quagga/etc/zebra.conf
# vi /usr/local/quagga/etc/zebra.conf
# /usr/local/quagga/sbin/zebra
2) Configure and start the pimd daemon
# cp /usr/local/quagga/etc/pimd.conf.sample /usr/local/quagga/etc/pimd.conf
# vi /usr/local/quagga/etc/pimd.conf
# /usr/local/quagga/sbin/pimd
3) Access pimd vty interface at port TCP 2611
# telnet localhost 2611
CONFIGURATION COMMANDS
See available commands in the file pimd/COMMANDS.
KNOWN CAVEATS
See list of known caveats in the file pimd/CAVEATS.
SUPPORT
Please post comments, questions, patches, bug reports at the
support site:
https://github.com/udhos/qpimd
RELATED WORK
igmprt: An IGMPv3-router implementation
- http://www.loria.fr/~lahmadi/igmpv3-router.html
USC pimd: PIMv2-SM daemon
- http://netweb.usc.edu/pim/pimd (URL broken in 2008-12-23)
- http://packages.debian.org/source/sid/pimd (from Debian)
troglobit pimd: This is the original USC pimd from
http://netweb.usc.edu/pim/. In January 16, 2010 it was revived
with the intention to collect patches floating around in
Debian, Gentoo, Lintrack and other distribution repositories
and to provide a central point of collaboration.
- http://github.com/troglobit/pimd
zpimd: zpimd is not dependent of zebra or any other routing daemon
- ftp://robur.slu.se/pub/Routing/Zebra
- http://sunsite2.icm.edu.pl/pub/unix/routing/zpimd
mrd6: an IPv6 Multicast Router for Linux systems
- http://fivebits.net/proj/mrd6/
MBGP: Implementation of RFC 2858 for Quagga
- git://git.coplanar.net/~balajig/quagga
- http://www.gossamer-threads.com/lists/quagga/dev/18000
REFERENCES
IANA Protocol Independent Multicast (PIM) Parameters
http://www.iana.org/assignments/pim-parameters/pim-parameters.txt
Address Family Numbers
http://www.iana.org/assignments/address-family-numbers
-- END --

426
pimd/TODO Normal file
View file

@ -0,0 +1,426 @@
# $QuaggaId: $Format:%an, %ai, %h$ $
T1 DONE Implement debug command
test pim receive join
T2 DONE Implement debug command
test pim receive prune
T3 DONE Per-interface Downstream (S,G) state machine
(RFC 4601 4.5.3. Receiving (S,G) Join/Prune Messages)
T4 DONE Upstream (S,G) state machine
(RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages)
T5 DONE Verify Data Packet Forwarding Rules
RFC 4601 4.2. Data Packet Forwarding Rules
RFC 4601 4.8.2. PIM-SSM-Only Routers
Additionally, the Packet forwarding rules of Section 4.2 can be
simplified in a PIM-SSM-only router:
iif is the incoming interface of the packet.
oiflist = NULL
if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) {
oiflist = inherited_olist(S,G)
} else if (iif is in inherited_olist(S,G)) {
send Assert(S,G) on iif
}
oiflist = oiflist (-) iif
forward packet on all interfaces in oiflist
Macro:
inherited_olist(S,G) =
joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
T6 DONE Implement (S,G) Assert state machine (RFC 4601, section 4.6.1).
Changes in pim_ifchannel.ifassert_winner should trigger
pim_upstream_update_join_desired().
Depends on TODO T27.
Depends on TODO T33.
See also CAVEAT C7.
See also: RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
Transitions from Joined State
RPF'(S,G) changes due to an Assert
http://www.hep.ucl.ac.uk/~ytl/multi-cast/pim-dm_01.html:
The PIM Assert mechanism is used to shutoff duplicate flows onto
the same multiaccess network. Routers detect this condiction when
they receive an (S,G) packet via a multi-access interface that is
in the (S,G) OIL. This causes the routers to send Assert
Messages.
Note that neighbors will not accept Join/Prune or Assert messages
from a router unless they have first heard a Hello message from that
router. Thus, if a router needs to send a Join/Prune or Assert
message on an interface on which it has not yet sent a Hello message
with the currently configured IP address, then it MUST immediately
send the relevant Hello message without waiting for the Hello Timer
to expire, followed by the Join/Prune or Assert message.
T7 DONE Implement hello option: LAN Prune Delay
T8 DONE Implement J/P_Override_Interval(I)
Depends on TODO T7.
See pim_ifchannel.c, pim_ifchannel_prune(), jp_override_interval.
T9 DONE Detect change in IGMPv3 RPF interface/next-hop for S and update.
channel_oil vif index accordingly ?
Beware accidentaly adding looped MFC entries (IIF=OIF).
T10 DONE React to (S,G) join directed to another upstream address. See
also:
RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
If a router wishes to propagate a Join(S,G) upstream, it must also
watch for messages on its upstream interface from other routers on
that subnet, and these may modify its behavior. If it sees a
Join(S,G) to the correct upstream neighbor, it should suppress its
own Join(S,G). If it sees a Prune(S,G), Prune(S,G,rpt), or
Prune(*,G) to the correct upstream neighbor towards S, it should
be prepared to override that prune by scheduling a Join(S,G) to be
sent almost immediately.
T11 DONE Review protocol modifications for SSM
(RFC 4601 4.8.1. Protocol Modifications for SSM Destination
Addresses)
T12 DONE Review updates of RPF entries.
FIXME pim_upstream.c send_join():
Currently only one upstream state is affected by detection of RPF change.
RPF change should affect all upstream states sharing the RPF cache.
T13 DONE Check that RFC macros using S,G,RPF_interface(S) are actually
implemented with this strategy:
rpf_ifch=find_ifch(up->rpf->interface).
See pim_rpf.c pim_rpf_find_rpf_addr() for a correct example.
$ grep -i macro pimd/*.c
pimd/pim_iface.c: RFC 4601: 4.1.6. State Summarization Macros
pimd/pim_ifchannel.c: RFC 4601: 4.6.5. Assert State Macros
pimd/pim_ifchannel.c: RFC 4601: 4.1.6. State Summarization Macros
pimd/pim_ifchannel.c: RFC 4601: 4.1.6. State Summarization Macros
pimd/pim_ifchannel.c: RFC 4601: 4.6.5. Assert State Macros
pimd/pim_ifchannel.c: Macro:
pimd/pim_rpf.c: RFC 4601: 4.1.6. State Summarization Macros
T14 DONE Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
See pim_mroute.c mroute_msg().
T15 DONE Interface command to statically join (S,G).
interface eth0
ip igmp join-group 239.1.1.1 source 1.1.1.1
T16 DONE RPF'(S,G) lookup is not working for S reachable with default route.
See "RPF'(S,G) not found" in pim_rpf_update() from pim_rpf.c.
Zebra daemon RIB is not reflecting changes in kernel routes
accurately?
T17 DONE Prevent CLI from creating bogus interfaces.
Example:
conf t
interface xxx
T18 Consider reliable pim solution (refresh reduction)
A Reliable Transport Mechanism for PIM
http://tools.ietf.org/wg/pim/draft-ietf-pim-port/
PORT=PIM-Over-Reliable-Transport
T19 DONE Fix self as neighbor
See mailing list post:
http://lists.gnu.org/archive/html/qpimd-users/2009-04/msg00000.html
T20 DONE Fix debug message: "pim_neighbor_update: internal error:
trying to replace same prefix list"
See mailing list post:
http://lists.gnu.org/archive/html/qpimd-users/2009-04/msg00000.html
T21 DONE Clean-up PIM/IGMP interface mismatch debugging
See option PIM_CHECK_RECV_IFINDEX_SANITY in pimd/Makefile.am
See mailing list post:
http://lists.nongnu.org/archive/html/qpimd-users/2009-04/msg00003.html
T22 DONE IGMP must be protected against adding looped MFC entries
created by both source and receiver attached to the same
interface.
T23 DONE libzebra crash after zclient_lookup_nexthop.
See mailing list post:
http://lists.nongnu.org/archive/html/qpimd-users/2009-04/msg00008.html
T24 DONE zserv may return recursive routes:
- nexthop type is set to ZEBRA_NEXTHOP_IPV4
- ifindex is not reported
- calls expecting ifindex (fib_lookup_if_vif_index) are disrupted
See also this mailing list post:
[PATCH 21/21] Link detect and recursive routes
http://www.gossamer-threads.com/lists/quagga/dev/17564
T25 DONE Zclient nexthop lookup missing OSPF route to 1.1.1.1/32
See also:
pim_zlookup.c zclient_lookup_nexthop misses OSPF 1.1.1.1/32
zebra/zebra_vty.c show_ip_route_addr_cmd hits OSPF 1.1.1.1/32
T26 DONE Zebra daemon is marking recursive static route as inactive.
FIXED: zebra daemon was incorrectly marking recursive routes
pointing to kernel routes as inactive:
zebra/zebra_rib.c nexthop_active_ipv4:
-- Original:
else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
-- Fixed:
else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL) ||
match->type == ZEBRA_ROUTE_KERNEL)
Old problem description:
This prevents rib_match_ipv4 from returning its nexthop:
client: pim_zlookup.c zclient_read_nexthop
server: zebra/zserv.c zsend_ipv4_nexthop_lookup_v2 -> rib_match_ipv4
Kernel route is injected into zebra in zebra_rib.c rib_add_ipv4
Examples:
rt_netlink.c:726: rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index, table, metric, 0);
rt_netlink.c:864: rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table, 0, 0);
This patch didn't fix the issue:
[PATCH 21/21] Link detect and recursive routes
http://www.gossamer-threads.com/lists/quagga/dev/17564
See the example below for the route 2.2.2.2.
bash# route add -host 1.1.1.1 gw 127.0.0.1
bash# route add -host 2.2.2.2 gw 1.1.1.1
bash# netstat -nvr
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
2.2.2.2 1.1.1.1 255.255.255.255 UGH 0 0 0 lo
1.1.1.1 127.0.0.1 255.255.255.255 UGH 0 0 0 lo
192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
0.0.0.0 192.168.0.2 0.0.0.0 UG 0 0 0 eth0
bash#
zebra# sh ip route
Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
I - ISIS, B - BGP, > - selected route, * - FIB route
K>* 0.0.0.0/0 via 192.168.0.2, eth0
K>* 1.1.1.1/32 via 127.0.0.1, lo
K * 2.2.2.2/32 via 1.1.1.1, lo inactive
C>* 127.0.0.0/8 is directly connected, lo
C>* 192.168.0.0/24 is directly connected, eth0
quagga-pimd-router# sh ip route 1.1.1.1
Address NextHop Interface Metric Preference
1.1.1.1 127.0.0.1 lo 0 0
quagga-pimd-router#
quagga-pimd-router# sh ip route 2.2.2.2
Address NextHop Interface Metric Preference
2.2.2.2 192.168.0.2 eth0 0 0
quagga-pimd-router#
T27 DONE Implement debug command
test pim receive assert
See also TODO T6: (S,G) Assert state machine.
T28 DONE Bad IPv4 address family=02 in Join/Prune dump
Reported by Andrew Lunn <andrew.lunn@ascom.ch>
# 58-byte pim v2 Join/Prune dump
# ------------------------------
# IPv4 address family=02 is wrong, correct IPv4 address family is 01
# See http://www.iana.org/assignments/address-family-numbers
#
c8XX YY03 : ip src 200.xx.yy.3
e000 000d : ip dst 224.0.0.13
9404 0000 : ip router alert option 148.4.0.0
2300 ab13 : pimv2,type=3 res=00 checksum=ab13
0200 : upstream family=02, encoding=00
c8XX YY08 : upstream 200.xx.yy.8
0001 00d2 : res=00 groups=01 holdtime=00d2
0200 0020 : group family=02, encoding=00, res=00, mask_len=20
ef01 0101 : group address 239.1.1.1
0001 0000 : joined=0001 pruned=0000
0200 0020 : source family=02, encoding=00, res=00, mask_len=20
0101 0101 : source address 1.1.1.1
T29 DONE Reset interface PIM-hello-sent counter when primary address changes
See pim_ifp->pim_ifstat_hello_sent
RFC 4601: 4.3.1. Sending Hello Messages
Thus, if a router needs to send a Join/Prune or Assert message on
an interface on which it has not yet sent a Hello message with the
currently configured IP address, then it MUST immediately send the
relevant Hello message without waiting for the Hello Timer to
expire, followed by the Join/Prune or Assert message.
T30 DONE Run interface DR election when primary address changes
Reported by Andrew Lunn <andrew.lunn@ascom.ch>
See pim_if_dr_election().
T31 If an interface changes one of its secondary IP addresses, a Hello
message with an updated Address_List option and a non-zero
HoldTime should be sent immediately.
See also detect_secondary_address_change
See also CAVEAT C15.
See also RFC 4601: 4.3.1. Sending Hello Messages
T32 FIXED Detection of interface primary address changes may fail when
there are multiple addresses.
See also CAVEAT C14.
pim_find_primary_addr() should return interface primary address
from connected list. Currently it returns the first address.
Zebra daemon "show int" is able to keep the primary address as
first address.
T33 DONE Implement debug command: test pim receive upcall
See also TODO T6: (S,G) Assert state machine.
T34 DONE assert_action_a1
T35 DONE Review macros depending on interface I.
See also: grep ,I\) pimd/*.c
For the case (S,G,I) check if I is either
1) interface attached to this per-interface S,G state (don't think so)
or
2) an arbitrary interface (most probably)
For the arbitrary interface case (2), consider representing
interface ifp as its primary address (struct in_addr ifaddr). The
benefit is in_addr does not need to be dereferenced, so it does
not demand protection against crashes.
T36 DONE React to zebra daemon link-detect up/down notification.
pim_ifp->primary_address is managed by detect_primary_address_change()
depending on to ifp->connected (managed by zebra_interface_address_read()).
T37 DONE Review list of variables which may affect pim_upstream.c
pim_upstream_evaluate_join_desired().
Call pim_upstream_update_join_desired() accordingly.
See the order of invokation:
pim_if_dr_election(ifp);
pim_if_update_join_desired(pim_ifp); /* depends on DR */
pim_if_update_could_assert(ifp); /* depends on DR */
pim_if_update_my_assert_metric(ifp); /* depends on could_assert */
join_desired depends on:
pim_ifp->primary_address
pim_ifp->pim_dr_addr
ch->ifassert_winner_metric
ch->ifassert_winner
ch->local_ifmembership
ch->ifjoin_state
ch->upstream->rpf.source_nexthop.mrib_metric_preference
ch->upstream->rpf.source_nexthop.mrib_route_metric
ch->upstream->rpf.source_nexthop.interface
T38 DONE Detect change in AssertTrackingDesired(S,G,I)
See the order of invokation:
dr_election: none
update_join_desired: depends on DR
update_tracking_desired: depends on DR, join_desired
AssertTrackingDesired(S,G,I) depends on:
pim_ifp->primary_address
pim_ifp->pim_dr_addr
ch->local_ifmembership
ch->ifassert_winner
ch->ifjoin_state
ch->upstream->rpf.source_nexthop.interface
PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags)
T39 DONE AssertTrackingDesired: flags is not matching evaluation
# show ip pim assert-internal
CA: CouldAssert
ECA: Evaluate CouldAssert
ATD: AssertTrackingDesired
eATD: Evaluate AssertTrackingDesired
Interface Address Source Group CA eCA ATD eATD
eth0 192.168.1.100 1.1.1.1 239.1.1.1 no no no yes
#
T40 Lightweight MLDv2
http://tools.ietf.org/html/draft-ietf-mboned-lightweight-igmpv3-mldv2-05
http://www.ietf.org/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt
http://www.ietf.org/html.charters/mboned-charter.html
T41 DONE ssmping support
See also:
http://www.venaas.no/multicast/ssmping/
draft-ietf-mboned-ssmping-07
http://tools.ietf.org/html/draft-ietf-mboned-ssmping-07
Example:
debug ssmpingd
conf t
ip ssmpingd 1.1.1.1
show ip ssmpingd
T42 Static igmp join fails when loading config at boot time
! Wrong behavior seen at boot time:
!
2010/02/22 08:59:00 PIM: igmp_source_forward_start: ignoring request for
looped MFC entry (S,G)=(3.3.3.3,239.3.3.3): igmp_sock=12 oif=eth0 vif_index=2
! Correct behavior seen later:
!
2010/02/22 09:03:16 PIM: igmp_source_forward_start: ignoring request for
looped MFC entry (S,G)=(2.2.2.2,239.2.2.2): igmp_sock=17 oif=lo vif_index=1
! To see the wrong message at boot:
!
debug igmp trace
!
interface lo
ip igmp
ip igmp join 239.2.2.2 2.2.2.2
ip igmp join 239.3.3.3 3.3.3.3
!
! Interfaces indexes:
Interface Address ifi Vif PktsIn PktsOut BytesIn BytesOut
eth0 200.202.112.3 2 2 0 0 0 0
lo 127.0.0.1 1 1 0 0 0 0
T43 PIM Neighbor Reduction
https://datatracker.ietf.org/doc/draft-wijnands-pim-neighbor-reduction/
"In a transit LAN (no directly connected source or receiver), many
of the PIM procedures don't apply. (...) This proposal describes
a procedure to reduce the amount of neighbors established over a
transit LAN."
T44 Single Stream Multicast Fast Reroute (SMFR) Method
https://datatracker.ietf.org/doc/draft-liu-pim-single-stream-multicast-frr/
"This document proposes an IP multicast fast convergence method
based on differentiating primary and backup PIM join."
T45 RFC5384 - The Join Attribute Format
"This document describes a modification of the Join message that
allows a node to associate attributes with a particular tree."
T46 PIM Multi-Topology ID (MT-ID) Join-Attribute
http://tools.ietf.org/html/draft-cai-pim-mtid-00
Depends on T45.
"This draft introduces a new type of PIM Join Attribute used to
encode the identity of the topology PIM uses for RPF."
-x-

33
pimd/TROUBLESHOOTING Normal file
View file

@ -0,0 +1,33 @@
TROUBLESHOOTING
# Check kernel mcast cache
# On Linux:
ip mroute show
! qpimd on last-hop router
! . attached to mcast receiver
! . runnning both PIM-SSM and IGMPv3
!
show ip mroute (kernel mcast programming is correct?)
show ip pim upstream (we joined our upstream?)
show ip pim neighbor (upstream is neighbor?)
show ip pim interface (pim enabled on interfaces?)
show ip multicast (multicast enabled at all?)
show ip rib SRC (unicast route towards source?)
show ip igmp sources (receiver joined on interface?)
show ip igmp interface (igmp enabled on receiver interface?)
! qpimd on intermmediate routers
! . may be attached to mcast source
! . runnning only PIM-SSM, not IGMPv3
!
show ip mroute (kernel mcast programming is correct?)
show ip pim upstream (we joined our upstream?)
show ip pim join (downstream joined us?)
show ip pim neighbor (downstream is neighbor?)
show ip pim interface (pim enabled on interfaces?)
show ip multicast (multicast enabled at all?)
show ip rib SRC (unicast route towards source?)
--EOF--

32
pimd/WHY_SSM Normal file
View file

@ -0,0 +1,32 @@
WHY SSM
Benefis of PIM SSM over PIM SM
------------------------------
- SSM consumes minimum link bandwidth
- SSM simplifies multicast address management (specially important for
inter-domain multicast)
- SSM (S,G) channels easily provide unique per-application addressing
- SSM does not require MSDP between PIM domains
- SSM does not suffer instabilities from traffic-driven SPT switchover
- SSM is not suscetible to DoS attack from unwanted sources
- SSM does not use RP. Some RP issues:
- RP is possible point of failure
- RP demands redundancy management
- RP may require PIM dense mode support for RP election
- RP is possible performance bottleneck
- RP may demand lots of extra management
- SSM can be deployed in an existing PIM SM network (only the last hop
routers need to support IGMPv3)
- SSM is easier to deploy and maintain
PIM-SSM drawbacks
-----------------
- SSM requires IGMPv3 support on both receivers and last-hop routers
- SSM may be memory intensive when managing (S,G) states for
many-to-many multicast distribution
- SSM will keep (S,G) state as long as there are subscriptions from
receivers, even if the source is not actually sending traffic
--EOF--

27
pimd/git-clone-github.sh Executable file
View file

@ -0,0 +1,27 @@
#! /bin/bash
#
# Github Developer Git Checkout
#
# Delete remote branch qpimd: git push origin :qpimd
# (git push origin :refs/heads/branch_to_delete)
# Delete remote tag v0.139: git push origin :v0.139
# (git push origin :refs/tags/tag_to_delete)
# Create remote-tracking branch: git checkout -b pim0.142 origin/pim0.142
# Rename branch qpimd to pim: git branch -m qpimd pim
# Commit changes: git commit -a
# Send changes: git push --all
#
# Recipe to re-sync with Quagga repository:
# git clone https://github.com/udhos/qpimd quagga
# cd quagga
# git checkout master
# git pull http://git.sv.gnu.org/r/quagga.git master
# git checkout -b pim origin/pim
# git rebase master pim
# # Test, then push back into Github repository:
# git push origin :pim ;# delete remote branch pim
# git push --all
#
# $QuaggaId: $Format:%an, %ai, %h$ $
git clone https://github.com/udhos/qpimd

27
pimd/git-clone-savannah.sh Executable file
View file

@ -0,0 +1,27 @@
#! /bin/bash
#
# Savannah Developer Git Checkout
#
# Delete remote branch qpimd: git push origin :qpimd
# (git push origin :refs/heads/branch_to_delete)
# Delete remote tag v0.139: git push origin :v0.139
# (git push origin :refs/tags/tag_to_delete)
# Create remote-tracking branch: git checkout -b pim0.142 origin/pim0.142
# Rename branch qpimd to pim: git branch -m qpimd pim
# Commit changes: git commit -a
# Send changes: git push --all
#
# Recipe to re-sync with Quagga repository:
# git clone ssh://evertonm@git.sv.gnu.org/srv/git/qpimd.git quagga
# cd quagga
# git checkout master
# git pull git://code.quagga.net/quagga.git master
# git checkout -b pim origin/pim
# git rebase master pim
# # Test, then push back into Savannah repository:
# git push origin :pim ;# delete remote branch pim
# git push --all
#
# $QuaggaId: $Format:%an, %ai, %h$ $
git clone ssh://evertonm@git.sv.gnu.org/srv/git/qpimd.git quagga

808
pimd/pim_assert.c Normal file
View file

@ -0,0 +1,808 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "log.h"
#include "prefix.h"
#include "pimd.h"
#include "pim_str.h"
#include "pim_tlv.h"
#include "pim_msg.h"
#include "pim_pim.h"
#include "pim_int.h"
#include "pim_time.h"
#include "pim_iface.h"
#include "pim_hello.h"
#include "pim_macro.h"
#include "pim_assert.h"
#include "pim_ifchannel.h"
static int assert_action_a3(struct pim_ifchannel *ch);
static void assert_action_a2(struct pim_ifchannel *ch,
struct pim_assert_metric winner_metric);
static void assert_action_a6(struct pim_ifchannel *ch,
struct pim_assert_metric winner_metric);
void pim_ifassert_winner_set(struct pim_ifchannel *ch,
enum pim_ifassert_state new_state,
struct in_addr winner,
struct pim_assert_metric winner_metric)
{
int winner_changed = (ch->ifassert_winner.s_addr != winner.s_addr);
int metric_changed = !pim_assert_metric_match(&ch->ifassert_winner_metric,
&winner_metric);
if (PIM_DEBUG_PIM_EVENTS) {
if (ch->ifassert_state != new_state) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_debug("%s: (S,G)=(%s,%s) assert state changed from %s to %s on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str,
pim_ifchannel_ifassert_name(ch->ifassert_state),
pim_ifchannel_ifassert_name(new_state),
ch->interface->name);
}
if (winner_changed) {
char src_str[100];
char grp_str[100];
char was_str[100];
char winner_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
pim_inet4_dump("<was?>", ch->ifassert_winner, was_str, sizeof(was_str));
pim_inet4_dump("<winner?>", winner, winner_str, sizeof(winner_str));
zlog_debug("%s: (S,G)=(%s,%s) assert winner changed from %s to %s on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str,
was_str, winner_str, ch->interface->name);
}
} /* PIM_DEBUG_PIM_EVENTS */
ch->ifassert_state = new_state;
ch->ifassert_winner = winner;
ch->ifassert_winner_metric = winner_metric;
ch->ifassert_creation = pim_time_monotonic_sec();
if (winner_changed || metric_changed) {
pim_upstream_update_join_desired(ch->upstream);
pim_ifchannel_update_could_assert(ch);
pim_ifchannel_update_assert_tracking_desired(ch);
}
}
static void on_trace(const char *label,
struct interface *ifp, struct in_addr src)
{
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
zlog_debug("%s: from %s on %s",
label, src_str, ifp->name);
}
}
static int preferred_assert(const struct pim_ifchannel *ch,
const struct pim_assert_metric *recv_metric)
{
return pim_assert_metric_better(recv_metric,
&ch->ifassert_winner_metric);
}
static int acceptable_assert(const struct pim_assert_metric *my_metric,
const struct pim_assert_metric *recv_metric)
{
return pim_assert_metric_better(recv_metric,
my_metric);
}
static int inferior_assert(const struct pim_assert_metric *my_metric,
const struct pim_assert_metric *recv_metric)
{
return pim_assert_metric_better(my_metric,
recv_metric);
}
static int cancel_assert(const struct pim_assert_metric *recv_metric)
{
return (recv_metric->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX)
&&
(recv_metric->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX);
}
static void if_could_assert_do_a1(const char *caller,
struct pim_ifchannel *ch)
{
if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
if (assert_action_a1(ch)) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: %s: (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
__PRETTY_FUNCTION__, caller,
src_str, grp_str, ch->interface->name);
/* log warning only */
}
}
}
static int dispatch_assert(struct interface *ifp,
struct in_addr source_addr,
struct in_addr group_addr,
struct pim_assert_metric recv_metric)
{
struct pim_ifchannel *ch;
ch = pim_ifchannel_add(ifp, source_addr, group_addr);
if (!ch) {
char source_str[100];
char group_str[100];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
zlog_warn("%s: (S,G)=(%s,%s) failure creating channel on interface %s",
__PRETTY_FUNCTION__,
source_str, group_str, ifp->name);
return -1;
}
switch (ch->ifassert_state) {
case PIM_IFASSERT_NOINFO:
if (recv_metric.rpt_bit_flag) {
/* RPT bit set */
if_could_assert_do_a1(__PRETTY_FUNCTION__, ch);
}
else {
/* RPT bit clear */
if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
if_could_assert_do_a1(__PRETTY_FUNCTION__, ch);
}
else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) {
if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags)) {
assert_action_a6(ch, recv_metric);
}
}
}
break;
case PIM_IFASSERT_I_AM_WINNER:
if (preferred_assert(ch, &recv_metric)) {
assert_action_a2(ch, recv_metric);
}
else {
if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
assert_action_a3(ch);
}
}
break;
case PIM_IFASSERT_I_AM_LOSER:
if (recv_metric.ip_address.s_addr == ch->ifassert_winner.s_addr) {
/* Assert from current winner */
if (cancel_assert(&recv_metric)) {
assert_action_a5(ch);
}
else {
if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
assert_action_a5(ch);
}
else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) {
if (!recv_metric.rpt_bit_flag) {
assert_action_a2(ch, recv_metric);
}
}
}
}
else if (preferred_assert(ch, &recv_metric)) {
assert_action_a2(ch, recv_metric);
}
break;
default:
{
char source_str[100];
char group_str[100];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
__PRETTY_FUNCTION__,
source_str, group_str, ch->ifassert_state, ifp->name);
}
return -2;
}
return 0;
}
int pim_assert_recv(struct interface *ifp,
struct pim_neighbor *neigh,
struct in_addr src_addr,
uint8_t *buf, int buf_size)
{
struct prefix msg_group_addr;
struct prefix msg_source_addr;
struct pim_assert_metric msg_metric;
int offset;
uint8_t *curr;
int curr_size;
on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
curr = buf;
curr_size = buf_size;
/*
Parse assert group addr
*/
offset = pim_parse_addr_group(ifp->name, src_addr,
&msg_group_addr,
curr, curr_size);
if (offset < 1) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s",
__PRETTY_FUNCTION__,
src_str, ifp->name);
return -1;
}
curr += offset;
curr_size -= offset;
/*
Parse assert source addr
*/
offset = pim_parse_addr_ucast(ifp->name, src_addr,
&msg_source_addr,
curr, curr_size);
if (offset < 1) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
__PRETTY_FUNCTION__,
src_str, ifp->name);
return -2;
}
curr += offset;
curr_size -= offset;
if (curr_size != 8) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: preference/metric size is not 8: size=%d from %s on interface %s",
__PRETTY_FUNCTION__,
curr_size,
src_str, ifp->name);
return -3;
}
/*
Parse assert metric preference
*/
msg_metric.metric_preference = pim_read_uint32_host(curr);
msg_metric.rpt_bit_flag = msg_metric.metric_preference & 0x80000000; /* save highest bit */
msg_metric.metric_preference &= ~0x80000000; /* clear highest bit */
curr += 4;
/*
Parse assert route metric
*/
msg_metric.route_metric = pim_read_uint32_host(curr);
if (PIM_DEBUG_PIM_TRACE) {
char neigh_str[100];
char source_str[100];
char group_str[100];
pim_inet4_dump("<neigh?>", src_addr, neigh_str, sizeof(neigh_str));
pim_inet4_dump("<src?>", msg_source_addr.u.prefix4, source_str, sizeof(source_str));
pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4, group_str, sizeof(group_str));
zlog_debug("%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
__PRETTY_FUNCTION__, neigh_str, ifp->name,
source_str, group_str,
msg_metric.metric_preference,
msg_metric.route_metric,
PIM_FORCE_BOOLEAN(msg_metric.rpt_bit_flag));
}
msg_metric.ip_address = src_addr;
return dispatch_assert(ifp,
msg_source_addr.u.prefix4,
msg_group_addr.u.prefix4,
msg_metric);
}
/*
RFC 4601: 4.6.3. Assert Metrics
Assert metrics are defined as:
When comparing assert_metrics, the rpt_bit_flag, metric_preference,
and route_metric field are compared in order, where the first lower
value wins. If all fields are equal, the primary IP address of the
router that sourced the Assert message is used as a tie-breaker,
with the highest IP address winning.
*/
int pim_assert_metric_better(const struct pim_assert_metric *m1,
const struct pim_assert_metric *m2)
{
if (m1->rpt_bit_flag < m2->rpt_bit_flag)
return 1;
if (m1->rpt_bit_flag > m2->rpt_bit_flag)
return 0;
if (m1->metric_preference < m2->metric_preference)
return 1;
if (m1->metric_preference > m2->metric_preference)
return 0;
if (m1->route_metric < m2->route_metric)
return 1;
if (m1->route_metric > m2->route_metric)
return 0;
return ntohl(m1->ip_address.s_addr) > ntohl(m2->ip_address.s_addr);
}
int pim_assert_metric_match(const struct pim_assert_metric *m1,
const struct pim_assert_metric *m2)
{
if (m1->rpt_bit_flag != m2->rpt_bit_flag)
return 0;
if (m1->metric_preference != m2->metric_preference)
return 0;
if (m1->route_metric != m2->route_metric)
return 0;
return m1->ip_address.s_addr == m2->ip_address.s_addr;
}
int pim_assert_build_msg(uint8_t *pim_msg, int buf_size,
struct interface *ifp,
struct in_addr group_addr,
struct in_addr source_addr,
uint32_t metric_preference,
uint32_t route_metric,
uint32_t rpt_bit_flag)
{
uint8_t *buf_pastend = pim_msg + buf_size;
uint8_t *pim_msg_curr;
int pim_msg_size;
int remain;
pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* skip room for pim header */
/* Encode group */
remain = buf_pastend - pim_msg_curr;
pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
remain,
group_addr);
if (!pim_msg_curr) {
char group_str[100];
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
zlog_warn("%s: failure encoding group address %s: space left=%d",
__PRETTY_FUNCTION__, group_str, remain);
return -1;
}
/* Encode source */
remain = buf_pastend - pim_msg_curr;
pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
remain,
source_addr);
if (!pim_msg_curr) {
char source_str[100];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: failure encoding source address %s: space left=%d",
__PRETTY_FUNCTION__, source_str, remain);
return -2;
}
/* Metric preference */
pim_write_uint32(pim_msg_curr, rpt_bit_flag ?
metric_preference | 0x80000000 :
metric_preference);
pim_msg_curr += 4;
/* Route metric */
pim_write_uint32(pim_msg_curr, route_metric);
pim_msg_curr += 4;
/*
Add PIM header
*/
pim_msg_size = pim_msg_curr - pim_msg;
pim_msg_build_header(pim_msg, pim_msg_size,
PIM_MSG_TYPE_ASSERT);
return pim_msg_size;
}
static int pim_assert_do(struct pim_ifchannel *ch,
struct pim_assert_metric metric)
{
struct interface *ifp;
struct pim_interface *pim_ifp;
uint8_t pim_msg[1000];
int pim_msg_size;
ifp = ch->interface;
zassert(ifp);
pim_ifp = ifp->info;
if (!pim_ifp) {
zlog_warn("%s: pim not enabled on interface: %s",
__PRETTY_FUNCTION__, ifp->name);
return -1;
}
pim_msg_size = pim_assert_build_msg(pim_msg, sizeof(pim_msg), ifp,
ch->group_addr, ch->source_addr,
metric.metric_preference,
metric.route_metric,
metric.rpt_bit_flag);
if (pim_msg_size < 1) {
zlog_warn("%s: failure building PIM assert message: msg_size=%d",
__PRETTY_FUNCTION__, pim_msg_size);
return -2;
}
/*
RFC 4601: 4.3.1. Sending Hello Messages
Thus, if a router needs to send a Join/Prune or Assert message on
an interface on which it has not yet sent a Hello message with the
currently configured IP address, then it MUST immediately send the
relevant Hello message without waiting for the Hello Timer to
expire, followed by the Join/Prune or Assert message.
*/
pim_hello_require(ifp);
if (PIM_DEBUG_PIM_TRACE) {
char source_str[100];
char group_str[100];
pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
zlog_debug("%s: to %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
__PRETTY_FUNCTION__,
ifp->name, source_str, group_str,
metric.metric_preference,
metric.route_metric,
PIM_FORCE_BOOLEAN(metric.rpt_bit_flag));
}
if (pim_msg_send(pim_ifp->pim_sock_fd,
qpim_all_pim_routers_addr,
pim_msg,
pim_msg_size,
ifp->name)) {
zlog_warn("%s: could not send PIM message on interface %s",
__PRETTY_FUNCTION__, ifp->name);
return -3;
}
return 0;
}
int pim_assert_send(struct pim_ifchannel *ch)
{
return pim_assert_do(ch, ch->ifassert_my_metric);
}
/*
RFC 4601: 4.6.4. AssertCancel Messages
An AssertCancel(S,G) is an infinite metric assert with the RPT bit
set that names S as the source.
*/
static int pim_assert_cancel(struct pim_ifchannel *ch)
{
struct pim_assert_metric metric;
metric.rpt_bit_flag = 0;
metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX;
metric.route_metric = PIM_ASSERT_ROUTE_METRIC_MAX;
metric.ip_address = ch->source_addr;
return pim_assert_do(ch, metric);
}
static int on_assert_timer(struct thread *t)
{
struct pim_ifchannel *ch;
struct interface *ifp;
zassert(t);
ch = THREAD_ARG(t);
zassert(ch);
ifp = ch->interface;
zassert(ifp);
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_debug("%s: (S,G)=(%s,%s) timer expired on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ifp->name);
}
ch->t_ifassert_timer = 0;
switch (ch->ifassert_state) {
case PIM_IFASSERT_I_AM_WINNER:
zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
assert_action_a3(ch);
break;
case PIM_IFASSERT_I_AM_LOSER:
assert_action_a5(ch);
break;
default:
{
char source_str[100];
char group_str[100];
pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
__PRETTY_FUNCTION__,
source_str, group_str, ch->ifassert_state, ifp->name);
}
}
return 0;
}
static void assert_timer_off(struct pim_ifchannel *ch)
{
struct interface *ifp;
zassert(ch);
ifp = ch->interface;
zassert(ifp);
if (PIM_DEBUG_PIM_TRACE) {
if (ch->t_ifassert_timer) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_debug("%s: (S,G)=(%s,%s) cancelling timer on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ifp->name);
}
}
THREAD_OFF(ch->t_ifassert_timer);
zassert(!ch->t_ifassert_timer);
}
static void pim_assert_timer_set(struct pim_ifchannel *ch,
int interval)
{
struct interface *ifp;
zassert(ch);
ifp = ch->interface;
zassert(ifp);
assert_timer_off(ch);
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_debug("%s: (S,G)=(%s,%s) starting %u sec timer on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, interval, ifp->name);
}
THREAD_TIMER_ON(master, ch->t_ifassert_timer,
on_assert_timer,
ch, interval);
}
static void pim_assert_timer_reset(struct pim_ifchannel *ch)
{
pim_assert_timer_set(ch, PIM_ASSERT_TIME - PIM_ASSERT_OVERRIDE_INTERVAL);
}
/*
RFC 4601: 4.6.1. (S,G) Assert Message State Machine
(S,G) Assert State machine Actions
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).
*/
int assert_action_a1(struct pim_ifchannel *ch)
{
struct interface *ifp = ch->interface;
struct pim_interface *pim_ifp;
zassert(ifp);
pim_ifp = ifp->info;
if (!pim_ifp) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s) multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ifp->name);
return -1; /* must return since pim_ifp is used below */
}
/* Switch to I_AM_WINNER before performing action_a3 below */
pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_WINNER,
pim_ifp->primary_address,
pim_macro_spt_assert_metric(&ch->upstream->rpf,
pim_ifp->primary_address));
zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
if (assert_action_a3(ch)) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s) assert_action_a3 failure on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ifp->name);
/* warning only */
}
zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
return 0;
}
/*
RFC 4601: 4.6.1. (S,G) Assert Message State Machine
(S,G) Assert State machine Actions
A2: Store new assert winner as AssertWinner(S,G,I) and assert
winner metric as AssertWinnerMetric(S,G,I).
Set Assert Timer to Assert_Time.
*/
static void assert_action_a2(struct pim_ifchannel *ch,
struct pim_assert_metric winner_metric)
{
pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_LOSER,
winner_metric.ip_address,
winner_metric);
pim_assert_timer_set(ch, PIM_ASSERT_TIME);
zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
}
/*
RFC 4601: 4.6.1. (S,G) Assert Message State Machine
(S,G) Assert State machine Actions
A3: Send Assert(S,G).
Set Assert Timer to (Assert_Time - Assert_Override_Interval).
*/
static int assert_action_a3(struct pim_ifchannel *ch)
{
zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
pim_assert_timer_reset(ch);
if (pim_assert_send(ch)) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s) failure sending assert on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ch->interface->name);
return -1;
}
zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
return 0;
}
/*
RFC 4601: 4.6.1. (S,G) Assert Message State Machine
(S,G) Assert State machine Actions
A4: Send AssertCancel(S,G).
Delete assert info (AssertWinner(S,G,I) and
AssertWinnerMetric(S,G,I) will then return their default
values).
*/
void assert_action_a4(struct pim_ifchannel *ch)
{
if (pim_assert_cancel(ch)) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: failure sending AssertCancel(%s,%s) on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ch->interface->name);
/* log warning only */
}
assert_action_a5(ch);
zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
}
/*
RFC 4601: 4.6.1. (S,G) Assert Message State Machine
(S,G) Assert State machine Actions
A5: Delete assert info (AssertWinner(S,G,I) and
AssertWinnerMetric(S,G,I) will then return their default values).
*/
void assert_action_a5(struct pim_ifchannel *ch)
{
reset_ifassert_state(ch);
zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
}
/*
RFC 4601: 4.6.1. (S,G) Assert Message State Machine
(S,G) Assert State machine Actions
A6: Store new assert winner as AssertWinner(S,G,I) and assert
winner metric as AssertWinnerMetric(S,G,I).
Set Assert Timer to Assert_Time.
If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true)
set SPTbit(S,G) to TRUE.
*/
static void assert_action_a6(struct pim_ifchannel *ch,
struct pim_assert_metric winner_metric)
{
assert_action_a2(ch, winner_metric);
/*
If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
SPTbit(S,G) to TRUE.
Notice: For PIM SSM, SPTbit(S,G) is already always true.
*/
zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
}

75
pimd/pim_assert.h Normal file
View file

@ -0,0 +1,75 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_ASSERT_H
#define PIM_ASSERT_H
#include <zebra.h>
#include "if.h"
#include "pim_neighbor.h"
#include "pim_ifchannel.h"
/*
RFC 4601: 4.11. Timer Values
Note that for historical reasons, the Assert message lacks a
Holdtime field. Thus, changing the Assert Time from the default
value is not recommended.
*/
#define PIM_ASSERT_OVERRIDE_INTERVAL (3) /* seconds */
#define PIM_ASSERT_TIME (180) /* seconds */
#define PIM_ASSERT_METRIC_PREFERENCE_MAX (0xFFFFFFFF)
#define PIM_ASSERT_ROUTE_METRIC_MAX (0xFFFFFFFF)
void pim_ifassert_winner_set(struct pim_ifchannel *ch,
enum pim_ifassert_state new_state,
struct in_addr winner,
struct pim_assert_metric winner_metric);
int pim_assert_recv(struct interface *ifp,
struct pim_neighbor *neigh,
struct in_addr src_addr,
uint8_t *buf, int buf_size);
int pim_assert_metric_better(const struct pim_assert_metric *m1,
const struct pim_assert_metric *m2);
int pim_assert_metric_match(const struct pim_assert_metric *m1,
const struct pim_assert_metric *m2);
int pim_assert_build_msg(uint8_t *pim_msg, int buf_size,
struct interface *ifp,
struct in_addr group_addr,
struct in_addr source_addr,
uint32_t metric_preference,
uint32_t route_metric,
uint32_t rpt_bit_flag);
int pim_assert_send(struct pim_ifchannel *ch);
int assert_action_a1(struct pim_ifchannel *ch);
void assert_action_a4(struct pim_ifchannel *ch);
void assert_action_a5(struct pim_ifchannel *ch);
#endif /* PIM_ASSERT_H */

4500
pimd/pim_cmd.c Normal file

File diff suppressed because it is too large Load diff

66
pimd/pim_cmd.h Normal file
View file

@ -0,0 +1,66 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_CMD_H
#define PIM_CMD_H
#define PIM_STR "PIM information\n"
#define IGMP_STR "IGMP information\n"
#define IGMP_GROUP_STR "IGMP groups information\n"
#define IGMP_SOURCE_STR "IGMP sources information\n"
#define CONF_SSMPINGD_STR "Enable ssmpingd operation\n"
#define SHOW_SSMPINGD_STR "ssmpingd operation\n"
#define IFACE_PIM_STR "Enable PIM SSM operation\n"
#define IFACE_IGMP_STR "Enable IGMP operation\n"
#define IFACE_IGMP_QUERY_INTERVAL_STR "IGMP host query interval\n"
#define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR "IGMP max query response value (seconds)\n"
#define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR "IGMP max query response value (deciseconds)\n"
#define DEBUG_IGMP_STR "IGMP protocol activity\n"
#define DEBUG_IGMP_EVENTS_STR "IGMP protocol events\n"
#define DEBUG_IGMP_PACKETS_STR "IGMP protocol packets\n"
#define DEBUG_IGMP_TRACE_STR "IGMP internal daemon activity\n"
#define DEBUG_MROUTE_STR "PIM interaction with kernel MFC cache\n"
#define DEBUG_PIM_STR "PIM protocol activity\n"
#define DEBUG_PIM_EVENTS_STR "PIM protocol events\n"
#define DEBUG_PIM_PACKETS_STR "PIM protocol packets\n"
#define DEBUG_PIM_HELLO_PACKETS_STR "PIM Hello protocol packets\n"
#define DEBUG_PIM_J_P_PACKETS_STR "PIM Join/Prune protocol packets\n"
#define DEBUG_PIM_PACKETDUMP_STR "PIM packet dump\n"
#define DEBUG_PIM_PACKETDUMP_SEND_STR "Dump sent packets\n"
#define DEBUG_PIM_PACKETDUMP_RECV_STR "Dump received packets\n"
#define DEBUG_PIM_TRACE_STR "PIM internal daemon activity\n"
#define DEBUG_PIM_ZEBRA_STR "ZEBRA protocol activity\n"
#define DEBUG_SSMPINGD_STR "ssmpingd activity\n"
#define CLEAR_IP_IGMP_STR "IGMP clear commands\n"
#define CLEAR_IP_PIM_STR "PIM clear commands\n"
#define MROUTE_STR "IP multicast routing table\n"
#define RIB_STR "IP unicast routing table\n"
#define PIM_CMD_NO "no"
#define PIM_CMD_IP_MULTICAST_ROUTING "ip multicast-routing"
#define PIM_CMD_IP_IGMP_QUERY_INTERVAL "ip igmp query-interval"
#define PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME "ip igmp query-max-response-time"
#define PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC "ip igmp query-max-response-time-dsec"
void pim_cmd_init(void);
#endif /* PIM_CMD_H */

529
pimd/pim_hello.c Normal file
View file

@ -0,0 +1,529 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "log.h"
#include "pimd.h"
#include "pim_pim.h"
#include "pim_str.h"
#include "pim_tlv.h"
#include "pim_util.h"
#include "pim_hello.h"
#include "pim_iface.h"
#include "pim_neighbor.h"
#include "pim_upstream.h"
static void on_trace(const char *label,
struct interface *ifp, struct in_addr src)
{
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
zlog_debug("%s: from %s on %s",
label, src_str, ifp->name);
}
}
static void tlv_trace_bool(const char *label, const char *tlv_name,
const char *ifname, struct in_addr src_addr,
int isset, int value)
{
if (isset) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello option from %s on interface %s: %s=%d",
label,
src_str, ifname,
tlv_name, value);
}
}
static void tlv_trace_uint16(const char *label, const char *tlv_name,
const char *ifname, struct in_addr src_addr,
int isset, uint16_t value)
{
if (isset) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
label,
src_str, ifname,
tlv_name, value);
}
}
static void tlv_trace_uint32(const char *label, const char *tlv_name,
const char *ifname, struct in_addr src_addr,
int isset, uint32_t value)
{
if (isset) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
label,
src_str, ifname,
tlv_name, value);
}
}
static void tlv_trace_uint32_hex(const char *label, const char *tlv_name,
const char *ifname, struct in_addr src_addr,
int isset, uint32_t value)
{
if (isset) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello option from %s on interface %s: %s=%08x",
label,
src_str, ifname,
tlv_name, value);
}
}
#if 0
static void tlv_trace(const char *label, const char *tlv_name,
const char *ifname, struct in_addr src_addr,
int isset)
{
if (isset) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello option from %s on interface %s: %s",
label,
src_str, ifname,
tlv_name);
}
}
#endif
static void tlv_trace_list(const char *label, const char *tlv_name,
const char *ifname, struct in_addr src_addr,
int isset, struct list *addr_list)
{
if (isset) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
label,
src_str, ifname,
tlv_name,
addr_list ? ((int) listcount(addr_list)) : -1,
(void *) addr_list);
}
}
#define FREE_ADDR_LIST \
if (hello_option_addr_list) { \
list_delete(hello_option_addr_list); \
}
#define FREE_ADDR_LIST_THEN_RETURN(code) \
{ \
FREE_ADDR_LIST \
return (code); \
}
int pim_hello_recv(struct interface *ifp,
struct in_addr src_addr,
uint8_t *tlv_buf, int tlv_buf_size)
{
struct pim_interface *pim_ifp;
struct pim_neighbor *neigh;
uint8_t *tlv_curr;
uint8_t *tlv_pastend;
pim_hello_options hello_options = 0; /* bit array recording options found */
uint16_t hello_option_holdtime = 0;
uint16_t hello_option_propagation_delay = 0;
uint16_t hello_option_override_interval = 0;
uint32_t hello_option_dr_priority = 0;
uint32_t hello_option_generation_id = 0;
struct list *hello_option_addr_list = 0;
on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
pim_ifp = ifp->info;
zassert(pim_ifp);
++pim_ifp->pim_ifstat_hello_recv;
/*
Parse PIM hello TLVs
*/
zassert(tlv_buf_size >= 0);
tlv_curr = tlv_buf;
tlv_pastend = tlv_buf + tlv_buf_size;
while (tlv_curr < tlv_pastend) {
uint16_t option_type;
uint16_t option_len;
int remain = tlv_pastend - tlv_curr;
if (remain < PIM_TLV_MIN_SIZE) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
__PRETTY_FUNCTION__,
remain, PIM_TLV_MIN_SIZE,
src_str, ifp->name);
FREE_ADDR_LIST_THEN_RETURN(-1);
}
option_type = PIM_TLV_GET_TYPE(tlv_curr);
tlv_curr += PIM_TLV_TYPE_SIZE;
option_len = PIM_TLV_GET_LENGTH(tlv_curr);
tlv_curr += PIM_TLV_LENGTH_SIZE;
if ((tlv_curr + option_len) > tlv_pastend) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
__PRETTY_FUNCTION__,
option_type, option_len, tlv_pastend - tlv_curr,
src_str, ifp->name);
FREE_ADDR_LIST_THEN_RETURN(-2);
}
if (PIM_DEBUG_PIM_TRACE || PIM_DEBUG_PIM_HELLO) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
__PRETTY_FUNCTION__,
remain,
option_type, option_len,
src_str, ifp->name);
}
switch (option_type) {
case PIM_MSG_OPTION_TYPE_HOLDTIME:
if (pim_tlv_parse_holdtime(ifp->name, src_addr,
&hello_options,
&hello_option_holdtime,
option_len,
tlv_curr)) {
FREE_ADDR_LIST_THEN_RETURN(-3);
}
break;
case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
if (pim_tlv_parse_lan_prune_delay(ifp->name, src_addr,
&hello_options,
&hello_option_propagation_delay,
&hello_option_override_interval,
option_len,
tlv_curr)) {
FREE_ADDR_LIST_THEN_RETURN(-4);
}
break;
case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
&hello_options,
&hello_option_dr_priority,
option_len,
tlv_curr)) {
FREE_ADDR_LIST_THEN_RETURN(-5);
}
break;
case PIM_MSG_OPTION_TYPE_GENERATION_ID:
if (pim_tlv_parse_generation_id(ifp->name, src_addr,
&hello_options,
&hello_option_generation_id,
option_len,
tlv_curr)) {
FREE_ADDR_LIST_THEN_RETURN(-6);
}
break;
case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
if (pim_tlv_parse_addr_list(ifp->name, src_addr,
&hello_options,
&hello_option_addr_list,
option_len,
tlv_curr)) {
return -7;
}
break;
case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
if (PIM_DEBUG_PIM_TRACE || PIM_DEBUG_PIM_HELLO) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
__PRETTY_FUNCTION__,
option_type, option_len,
src_str, ifp->name);
}
break;
default:
{
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
__PRETTY_FUNCTION__,
option_type, option_len,
src_str, ifp->name);
}
}
tlv_curr += option_len;
}
/*
Check received PIM hello options
*/
if (PIM_DEBUG_PIM_TRACE) {
tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime",
ifp->name, src_addr,
PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME),
hello_option_holdtime);
tlv_trace_uint16(__PRETTY_FUNCTION__, "propagation_delay",
ifp->name, src_addr,
PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
hello_option_propagation_delay);
tlv_trace_uint16(__PRETTY_FUNCTION__, "override_interval",
ifp->name, src_addr,
PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
hello_option_override_interval);
tlv_trace_bool(__PRETTY_FUNCTION__, "can_disable_join_suppression",
ifp->name, src_addr,
PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
tlv_trace_uint32(__PRETTY_FUNCTION__, "dr_priority",
ifp->name, src_addr,
PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_DR_PRIORITY),
hello_option_dr_priority);
tlv_trace_uint32_hex(__PRETTY_FUNCTION__, "generation_id",
ifp->name, src_addr,
PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID),
hello_option_generation_id);
tlv_trace_list(__PRETTY_FUNCTION__, "address_list",
ifp->name, src_addr,
PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_ADDRESS_LIST),
hello_option_addr_list);
}
if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: PIM hello missing holdtime from %s on interface %s",
__PRETTY_FUNCTION__,
src_str, ifp->name);
}
/*
New neighbor?
*/
neigh = pim_neighbor_find(ifp, src_addr);
if (!neigh) {
/* Add as new neighbor */
neigh = pim_neighbor_add(ifp, src_addr,
hello_options,
hello_option_holdtime,
hello_option_propagation_delay,
hello_option_override_interval,
hello_option_dr_priority,
hello_option_generation_id,
hello_option_addr_list);
if (!neigh) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: failure creating PIM neighbor %s on interface %s",
__PRETTY_FUNCTION__,
src_str, ifp->name);
FREE_ADDR_LIST_THEN_RETURN(-8);
}
/* actual addr list has been saved under neighbor */
return 0;
}
/*
Received generation ID ?
*/
if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) {
/* GenID mismatch ? */
if (!PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ||
(hello_option_generation_id != neigh->generation_id)) {
/* GenID changed */
pim_upstream_rpf_genid_changed(neigh->source_addr);
/* GenID mismatch, then replace neighbor */
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
__PRETTY_FUNCTION__,
hello_option_generation_id,
neigh->generation_id,
src_str, ifp->name);
}
pim_upstream_rpf_genid_changed(neigh->source_addr);
pim_neighbor_delete(ifp, neigh, "GenID mismatch");
neigh = pim_neighbor_add(ifp, src_addr,
hello_options,
hello_option_holdtime,
hello_option_propagation_delay,
hello_option_override_interval,
hello_option_dr_priority,
hello_option_generation_id,
hello_option_addr_list);
if (!neigh) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: failure re-creating PIM neighbor %s on interface %s",
__PRETTY_FUNCTION__,
src_str, ifp->name);
FREE_ADDR_LIST_THEN_RETURN(-9);
}
/* actual addr list is saved under neighbor */
return 0;
} /* GenId mismatch: replace neighbor */
} /* GenId received */
/*
Update existing neighbor
*/
pim_neighbor_update(neigh,
hello_options,
hello_option_holdtime,
hello_option_dr_priority,
hello_option_addr_list);
/* actual addr list is saved under neighbor */
return 0;
}
int pim_hello_build_tlv(const char *ifname,
uint8_t *tlv_buf, int tlv_buf_size,
uint16_t holdtime,
uint32_t dr_priority,
uint32_t generation_id,
uint16_t propagation_delay,
uint16_t override_interval,
int can_disable_join_suppression,
struct list *ifconnected)
{
uint8_t *curr = tlv_buf;
uint8_t *pastend = tlv_buf + tlv_buf_size;
uint8_t *tmp;
/*
* Append options
*/
/* Holdtime */
curr = pim_tlv_append_uint16(curr,
pastend,
PIM_MSG_OPTION_TYPE_HOLDTIME,
holdtime);
if (!curr) {
zlog_warn("%s: could not set PIM hello Holdtime option for interface %s",
__PRETTY_FUNCTION__, ifname);
return -1;
}
/* LAN Prune Delay */
tmp = pim_tlv_append_2uint16(curr,
pastend,
PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
propagation_delay,
override_interval);
if (!tmp) {
zlog_warn("%s: could not set PIM LAN Prune Delay option for interface %s",
__PRETTY_FUNCTION__, ifname);
return -1;
}
if (can_disable_join_suppression) {
*((uint8_t*)(curr) + 4) |= 0x80; /* enable T bit */
}
curr = tmp;
/* DR Priority */
curr = pim_tlv_append_uint32(curr,
pastend,
PIM_MSG_OPTION_TYPE_DR_PRIORITY,
dr_priority);
if (!curr) {
zlog_warn("%s: could not set PIM hello DR Priority option for interface %s",
__PRETTY_FUNCTION__, ifname);
return -2;
}
/* Generation ID */
curr = pim_tlv_append_uint32(curr,
pastend,
PIM_MSG_OPTION_TYPE_GENERATION_ID,
generation_id);
if (!curr) {
zlog_warn("%s: could not set PIM hello Generation ID option for interface %s",
__PRETTY_FUNCTION__, ifname);
return -3;
}
/* Secondary Address List */
if (ifconnected) {
curr = pim_tlv_append_addrlist_ucast(curr,
pastend,
ifconnected);
if (!curr) {
zlog_warn("%s: could not set PIM hello Secondary Address List option for interface %s",
__PRETTY_FUNCTION__, ifname);
return -4;
}
}
return curr - tlv_buf;
}
/*
RFC 4601: 4.3.1. Sending Hello Messages
Thus, if a router needs to send a Join/Prune or Assert message on an
interface on which it has not yet sent a Hello message with the
currently configured IP address, then it MUST immediately send the
relevant Hello message without waiting for the Hello Timer to
expire, followed by the Join/Prune or Assert message.
*/
void pim_hello_require(struct interface *ifp)
{
struct pim_interface *pim_ifp;
zassert(ifp);
pim_ifp = ifp->info;
zassert(pim_ifp);
if (pim_ifp->pim_ifstat_hello_sent)
return;
pim_hello_restart_now(ifp); /* Send hello and restart timer */
}

46
pimd/pim_hello.h Normal file
View file

@ -0,0 +1,46 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_HELLO_H
#define PIM_HELLO_H
#include <zebra.h>
#include "if.h"
int pim_hello_recv(struct interface *ifp,
struct in_addr src_addr,
uint8_t *tlv_buf, int tlv_buf_size);
int pim_hello_build_tlv(const char *ifname,
uint8_t *tlv_buf, int tlv_buf_size,
uint16_t holdtime,
uint32_t dr_priority,
uint32_t generation_id,
uint16_t propagation_delay,
uint16_t override_interval,
int can_disable_join_suppression,
struct list *ifconnected);
void pim_hello_require(struct interface *ifp);
#endif /* PIM_HELLO_H */

1229
pimd/pim_iface.c Normal file

File diff suppressed because it is too large Load diff

161
pimd/pim_iface.h Normal file
View file

@ -0,0 +1,161 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_IFACE_H
#define PIM_IFACE_H
#include <zebra.h>
#include "if.h"
#include "vty.h"
#include "pim_igmp.h"
#include "pim_upstream.h"
#define PIM_IF_MASK_PIM (1 << 0)
#define PIM_IF_MASK_IGMP (1 << 1)
#define PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS (1 << 2)
#define PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION (1 << 3)
#define PIM_IF_IS_DELETED(ifp) ((ifp)->ifindex == IFINDEX_INTERNAL)
#define PIM_IF_TEST_PIM(options) (PIM_IF_MASK_PIM & (options))
#define PIM_IF_TEST_IGMP(options) (PIM_IF_MASK_IGMP & (options))
#define PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(options) (PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS & (options))
#define PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) (PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION & (options))
#define PIM_IF_DO_PIM(options) ((options) |= PIM_IF_MASK_PIM)
#define PIM_IF_DO_IGMP(options) ((options) |= PIM_IF_MASK_IGMP)
#define PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(options) ((options) |= PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS)
#define PIM_IF_DO_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) ((options) |= PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION)
#define PIM_IF_DONT_PIM(options) ((options) &= ~PIM_IF_MASK_PIM)
#define PIM_IF_DONT_IGMP(options) ((options) &= ~PIM_IF_MASK_IGMP)
#define PIM_IF_DONT_IGMP_LISTEN_ALLROUTERS(options) ((options) &= ~PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS)
#define PIM_IF_DONT_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) ((options) &= ~PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION)
struct pim_interface {
uint32_t options; /* bit vector */
int mroute_vif_index;
struct in_addr primary_address; /* remember addr to detect change */
int igmp_default_robustness_variable; /* IGMPv3 QRV */
int igmp_default_query_interval; /* IGMPv3 secs between general queries */
int igmp_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs for general queries */
int igmp_specific_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs for specific queries */
struct list *igmp_socket_list; /* list of struct igmp_sock */
struct list *igmp_join_list; /* list of struct igmp_join */
int pim_sock_fd; /* PIM socket file descriptor */
struct thread *t_pim_sock_read; /* thread for reading PIM socket */
int64_t pim_sock_creation; /* timestamp of PIM socket creation */
struct thread *t_pim_hello_timer;
int pim_hello_period;
int pim_default_holdtime;
int pim_triggered_hello_delay;
uint32_t pim_generation_id;
uint16_t pim_propagation_delay_msec; /* config */
uint16_t pim_override_interval_msec; /* config */
struct list *pim_neighbor_list; /* list of struct pim_neighbor */
struct list *pim_ifchannel_list; /* list of struct pim_ifchannel */
/* neighbors without lan_delay */
int pim_number_of_nonlandelay_neighbors;
uint16_t pim_neighbors_highest_propagation_delay_msec;
uint16_t pim_neighbors_highest_override_interval_msec;
/* DR Election */
int64_t pim_dr_election_last; /* timestamp */
int pim_dr_election_count;
int pim_dr_election_changes;
struct in_addr pim_dr_addr;
uint32_t pim_dr_priority; /* config */
int pim_dr_num_nondrpri_neighbors; /* neighbors without dr_pri */
int64_t pim_ifstat_start; /* start timestamp for stats */
uint32_t pim_ifstat_hello_sent;
uint32_t pim_ifstat_hello_sendfail;
uint32_t pim_ifstat_hello_recv;
uint32_t pim_ifstat_hello_recvfail;
};
/*
if default_holdtime is set (>= 0), use it;
otherwise default_holdtime is 3.5 * hello_period
*/
#define PIM_IF_DEFAULT_HOLDTIME(pim_ifp) \
(((pim_ifp)->pim_default_holdtime < 0) ? \
((pim_ifp)->pim_hello_period * 7 / 2) : \
((pim_ifp)->pim_default_holdtime))
void pim_if_init(void);
struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim);
void pim_if_delete(struct interface *ifp);
void pim_if_addr_add(struct connected *ifc);
void pim_if_addr_del(struct connected *ifc, int force_prim_as_any);
void pim_if_addr_add_all(struct interface *ifp);
void pim_if_addr_del_all(struct interface *ifp);
void pim_if_addr_del_all_igmp(struct interface *ifp);
void pim_if_addr_del_all_pim(struct interface *ifp);
int pim_if_add_vif(struct interface *ifp);
int pim_if_del_vif(struct interface *ifp);
void pim_if_add_vif_all(void);
void pim_if_del_vif_all(void);
struct interface *pim_if_find_by_vif_index(int vif_index);
int pim_if_find_vifindex_by_ifindex(int ifindex);
int pim_if_lan_delay_enabled(struct interface *ifp);
uint16_t pim_if_effective_propagation_delay_msec(struct interface *ifp);
uint16_t pim_if_effective_override_interval_msec(struct interface *ifp);
uint16_t pim_if_jp_override_interval_msec(struct interface *ifp);
struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp,
struct in_addr addr);
long pim_if_t_suppressed_msec(struct interface *ifp);
int pim_if_t_override_msec(struct interface *ifp);
struct in_addr pim_find_primary_addr(struct interface *ifp);
int pim_if_igmp_join_add(struct interface *ifp,
struct in_addr group_addr,
struct in_addr source_addr);
int pim_if_igmp_join_del(struct interface *ifp,
struct in_addr group_addr,
struct in_addr source_addr);
void pim_if_update_could_assert(struct interface *ifp);
void pim_if_assert_on_neighbor_down(struct interface *ifp,
struct in_addr neigh_addr);
void pim_if_rpf_interface_changed(struct interface *old_rpf_ifp,
struct pim_upstream *up);
void pim_if_update_join_desired(struct pim_interface *pim_ifp);
void pim_if_update_assert_tracking_desired(struct interface *ifp);
#endif /* PIM_IFACE_H */

893
pimd/pim_ifchannel.c Normal file
View file

@ -0,0 +1,893 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "linklist.h"
#include "thread.h"
#include "memory.h"
#include "pimd.h"
#include "pim_str.h"
#include "pim_iface.h"
#include "pim_ifchannel.h"
#include "pim_zebra.h"
#include "pim_time.h"
#include "pim_msg.h"
#include "pim_pim.h"
#include "pim_join.h"
#include "pim_rpf.h"
#include "pim_macro.h"
void pim_ifchannel_free(struct pim_ifchannel *ch)
{
zassert(!ch->t_ifjoin_expiry_timer);
zassert(!ch->t_ifjoin_prune_pending_timer);
zassert(!ch->t_ifassert_timer);
XFREE(MTYPE_PIM_IFCHANNEL, ch);
}
void pim_ifchannel_delete(struct pim_ifchannel *ch)
{
struct pim_interface *pim_ifp;
pim_ifp = ch->interface->info;
zassert(pim_ifp);
if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) {
pim_upstream_update_join_desired(ch->upstream);
}
pim_upstream_del(ch->upstream);
THREAD_OFF(ch->t_ifjoin_expiry_timer);
THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
THREAD_OFF(ch->t_ifassert_timer);
/*
notice that listnode_delete() can't be moved
into pim_ifchannel_free() because the later is
called by list_delete_all_node()
*/
listnode_delete(pim_ifp->pim_ifchannel_list, ch);
pim_ifchannel_free(ch);
}
#define IFCHANNEL_NOINFO(ch) \
( \
((ch)->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO) \
&& \
((ch)->ifjoin_state == PIM_IFJOIN_NOINFO) \
&& \
((ch)->ifassert_state == PIM_IFASSERT_NOINFO) \
)
static void delete_on_noinfo(struct pim_ifchannel *ch)
{
if (IFCHANNEL_NOINFO(ch)) {
/* In NOINFO state, timers should have been cleared */
zassert(!ch->t_ifjoin_expiry_timer);
zassert(!ch->t_ifjoin_prune_pending_timer);
zassert(!ch->t_ifassert_timer);
pim_ifchannel_delete(ch);
}
}
void pim_ifchannel_ifjoin_switch(const char *caller,
struct pim_ifchannel *ch,
enum pim_ifjoin_state new_state)
{
enum pim_ifjoin_state old_state = ch->ifjoin_state;
if (old_state == new_state) {
zlog_debug("%s calledby %s: non-transition on state %d (%s)",
__PRETTY_FUNCTION__, caller, new_state,
pim_ifchannel_ifjoin_name(new_state));
return;
}
zassert(old_state != new_state);
ch->ifjoin_state = new_state;
/* Transition to/from NOINFO ? */
if (
(old_state == PIM_IFJOIN_NOINFO)
||
(new_state == PIM_IFJOIN_NOINFO)
) {
if (PIM_DEBUG_PIM_EVENTS) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_debug("PIM_IFCHANNEL_%s: (S,G)=(%s,%s) on interface %s",
((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"),
src_str, grp_str, ch->interface->name);
}
/*
Record uptime of state transition to/from NOINFO
*/
ch->ifjoin_creation = pim_time_monotonic_sec();
pim_upstream_update_join_desired(ch->upstream);
pim_ifchannel_update_could_assert(ch);
pim_ifchannel_update_assert_tracking_desired(ch);
}
}
const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state)
{
switch (ifjoin_state) {
case PIM_IFJOIN_NOINFO: return "NOINFO";
case PIM_IFJOIN_JOIN: return "JOIN";
case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP";
}
return "ifjoin_bad_state";
}
const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
{
switch (ifassert_state) {
case PIM_IFASSERT_NOINFO: return "NOINFO";
case PIM_IFASSERT_I_AM_WINNER: return "WINNER";
case PIM_IFASSERT_I_AM_LOSER: return "LOSER";
}
return "ifassert_bad_state";
}
/*
RFC 4601: 4.6.5. Assert State Macros
AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
defaults to Infinity when in the NoInfo state.
*/
void reset_ifassert_state(struct pim_ifchannel *ch)
{
THREAD_OFF(ch->t_ifassert_timer);
pim_ifassert_winner_set(ch,
PIM_IFASSERT_NOINFO,
qpim_inaddr_any,
qpim_infinite_assert_metric);
}
static struct pim_ifchannel *pim_ifchannel_new(struct interface *ifp,
struct in_addr source_addr,
struct in_addr group_addr)
{
struct pim_ifchannel *ch;
struct pim_interface *pim_ifp;
struct pim_upstream *up;
pim_ifp = ifp->info;
zassert(pim_ifp);
up = pim_upstream_add(source_addr, group_addr);
if (!up) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
zlog_err("%s: could not attach upstream (S,G)=(%s,%s) on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ifp->name);
return 0;
}
ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
if (!ch) {
zlog_err("%s: PIM XMALLOC(%zu) failure",
__PRETTY_FUNCTION__, sizeof(*ch));
return 0;
}
ch->flags = 0;
ch->upstream = up;
ch->interface = ifp;
ch->source_addr = source_addr;
ch->group_addr = group_addr;
ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
ch->ifjoin_state = PIM_IFJOIN_NOINFO;
ch->t_ifjoin_expiry_timer = 0;
ch->t_ifjoin_prune_pending_timer = 0;
ch->ifjoin_creation = 0;
/* Assert state */
ch->t_ifassert_timer = 0;
reset_ifassert_state(ch);
if (pim_macro_ch_could_assert_eval(ch))
PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
else
PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
if (pim_macro_assert_tracking_desired_eval(ch))
PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
else
PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
/* Attach to list */
listnode_add(pim_ifp->pim_ifchannel_list, ch);
zassert(IFCHANNEL_NOINFO(ch));
return ch;
}
struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
struct in_addr source_addr,
struct in_addr group_addr)
{
struct pim_interface *pim_ifp;
struct listnode *ch_node;
struct pim_ifchannel *ch;
zassert(ifp);
pim_ifp = ifp->info;
if (!pim_ifp) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str,
ifp->name);
return 0;
}
for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
if (
(source_addr.s_addr == ch->source_addr.s_addr) &&
(group_addr.s_addr == ch->group_addr.s_addr)
) {
return ch;
}
}
return 0;
}
static void ifmembership_set(struct pim_ifchannel *ch,
enum pim_ifmembership membership)
{
if (ch->local_ifmembership == membership)
return;
/* if (PIM_DEBUG_PIM_EVENTS) */ {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_debug("%s: (S,G)=(%s,%s) membership now is %s on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str,
membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO",
ch->interface->name);
}
ch->local_ifmembership = membership;
pim_upstream_update_join_desired(ch->upstream);
pim_ifchannel_update_could_assert(ch);
pim_ifchannel_update_assert_tracking_desired(ch);
}
void pim_ifchannel_membership_clear(struct interface *ifp)
{
struct pim_interface *pim_ifp;
struct listnode *ch_node;
struct pim_ifchannel *ch;
pim_ifp = ifp->info;
zassert(pim_ifp);
for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
}
}
void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
{
struct pim_interface *pim_ifp;
struct listnode *node;
struct listnode *next_node;
struct pim_ifchannel *ch;
pim_ifp = ifp->info;
zassert(pim_ifp);
for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
delete_on_noinfo(ch);
}
}
struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
struct in_addr source_addr,
struct in_addr group_addr)
{
struct pim_ifchannel *ch;
char src_str[100];
char grp_str[100];
ch = pim_ifchannel_find(ifp, source_addr, group_addr);
if (ch)
return ch;
ch = pim_ifchannel_new(ifp, source_addr, group_addr);
if (ch)
return ch;
pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=(%s,%s) on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ifp->name);
return 0;
}
static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
{
pim_forward_stop(ch);
pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
delete_on_noinfo(ch);
}
static int on_ifjoin_expiry_timer(struct thread *t)
{
struct pim_ifchannel *ch;
zassert(t);
ch = THREAD_ARG(t);
zassert(ch);
ch->t_ifjoin_expiry_timer = 0;
zassert(ch->ifjoin_state == PIM_IFJOIN_JOIN);
ifjoin_to_noinfo(ch);
/* ch may have been deleted */
return 0;
}
static void prune_echo(struct interface *ifp,
struct in_addr source_addr,
struct in_addr group_addr)
{
struct pim_interface *pim_ifp;
struct in_addr neigh_dst_addr;
pim_ifp = ifp->info;
zassert(pim_ifp);
neigh_dst_addr = pim_ifp->primary_address;
if (PIM_DEBUG_PIM_EVENTS) {
char source_str[100];
char group_str[100];
char neigh_dst_str[100];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<neigh?>", neigh_dst_addr, neigh_dst_str, sizeof(neigh_dst_str));
zlog_debug("%s: sending PruneEcho(S,G)=(%s,%s) to upstream=%s on interface %s",
__PRETTY_FUNCTION__, source_str, group_str, neigh_dst_str, ifp->name);
}
pim_joinprune_send(ifp, neigh_dst_addr, source_addr, group_addr,
0 /* boolean: send_join=false (prune) */);
}
static int on_ifjoin_prune_pending_timer(struct thread *t)
{
struct pim_ifchannel *ch;
int send_prune_echo; /* boolean */
struct interface *ifp;
struct pim_interface *pim_ifp;
struct in_addr ch_source;
struct in_addr ch_group;
zassert(t);
ch = THREAD_ARG(t);
zassert(ch);
ch->t_ifjoin_prune_pending_timer = 0;
zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING);
/* Send PruneEcho(S,G) ? */
ifp = ch->interface;
pim_ifp = ifp->info;
send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1);
/* Save (S,G) */
ch_source = ch->source_addr;
ch_group = ch->group_addr;
ifjoin_to_noinfo(ch);
/* from here ch may have been deleted */
if (send_prune_echo)
prune_echo(ifp, ch_source, ch_group);
return 0;
}
static void check_recv_upstream(int is_join,
struct interface *recv_ifp,
struct in_addr upstream,
struct in_addr source_addr,
struct in_addr group_addr,
uint8_t source_flags,
int holdtime)
{
struct pim_upstream *up;
/* Upstream (S,G) in Joined state ? */
up = pim_upstream_find(source_addr, group_addr);
if (!up)
return;
if (up->join_state != PIM_UPSTREAM_JOINED)
return;
/* Upstream (S,G) in Joined state */
if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
/* RPF'(S,G) not found */
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s %s: RPF'(%s,%s) not found",
__FILE__, __PRETTY_FUNCTION__,
src_str, grp_str);
return;
}
/* upstream directed to RPF'(S,G) ? */
if (upstream.s_addr != up->rpf.rpf_addr.s_addr) {
char src_str[100];
char grp_str[100];
char up_str[100];
char rpf_str[100];
pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
zlog_warn("%s %s: (S,G)=(%s,%s) upstream=%s not directed to RPF'(S,G)=%s on interface %s",
__FILE__, __PRETTY_FUNCTION__,
src_str, grp_str,
up_str, rpf_str, recv_ifp->name);
return;
}
/* upstream directed to RPF'(S,G) */
if (is_join) {
/* Join(S,G) to RPF'(S,G) */
pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime);
return;
}
/* Prune to RPF'(S,G) */
if (source_flags & PIM_RPT_BIT_MASK) {
if (source_flags & PIM_WILDCARD_BIT_MASK) {
/* Prune(*,G) to RPF'(S,G) */
pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)",
up, up->rpf.rpf_addr);
return;
}
/* Prune(S,G,rpt) to RPF'(S,G) */
pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
up, up->rpf.rpf_addr);
return;
}
/* Prune(S,G) to RPF'(S,G) */
pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up,
up->rpf.rpf_addr);
}
static int nonlocal_upstream(int is_join,
struct interface *recv_ifp,
struct in_addr upstream,
struct in_addr source_addr,
struct in_addr group_addr,
uint8_t source_flags,
uint16_t holdtime)
{
struct pim_interface *recv_pim_ifp;
int is_local; /* boolean */
recv_pim_ifp = recv_ifp->info;
zassert(recv_pim_ifp);
is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
if (PIM_DEBUG_PIM_TRACE) {
char up_str[100];
char src_str[100];
char grp_str[100];
pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: recv %s (S,G)=(%s,%s) to %s upstream=%s on %s",
__PRETTY_FUNCTION__,
is_join ? "join" : "prune",
src_str, grp_str,
is_local ? "local" : "non-local",
up_str, recv_ifp->name);
}
if (is_local)
return 0;
/*
Since recv upstream addr was not directed to our primary
address, check if we should react to it in any way.
*/
check_recv_upstream(is_join, recv_ifp, upstream, source_addr, group_addr,
source_flags, holdtime);
return 1; /* non-local */
}
void pim_ifchannel_join_add(struct interface *ifp,
struct in_addr neigh_addr,
struct in_addr upstream,
struct in_addr source_addr,
struct in_addr group_addr,
uint8_t source_flags,
uint16_t holdtime)
{
struct pim_interface *pim_ifp;
struct pim_ifchannel *ch;
if (nonlocal_upstream(1 /* join */, ifp, upstream,
source_addr, group_addr, source_flags, holdtime)) {
return;
}
ch = pim_ifchannel_add(ifp, source_addr, group_addr);
if (!ch)
return;
/*
RFC 4601: 4.6.1. (S,G) Assert Message State Machine
Transitions from "I am Assert Loser" State
Receive Join(S,G) on Interface I
We receive a Join(S,G) that has the Upstream Neighbor Address
field set to my primary IP address on interface I. The action is
to transition to NoInfo state, delete this (S,G) assert state
(Actions A5 below), and allow the normal PIM Join/Prune mechanisms
to operate.
Notice: The nonlocal_upstream() test above ensures the upstream
address of the join message is our primary address.
*/
if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
char src_str[100];
char grp_str[100];
char neigh_str[100];
pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
zlog_warn("%s: Assert Loser recv Join(%s,%s) from %s on %s",
__PRETTY_FUNCTION__,
src_str, grp_str, neigh_str, ifp->name);
assert_action_a5(ch);
}
pim_ifp = ifp->info;
zassert(pim_ifp);
switch (ch->ifjoin_state) {
case PIM_IFJOIN_NOINFO:
pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
if (pim_macro_chisin_oiflist(ch)) {
pim_forward_start(ch);
}
break;
case PIM_IFJOIN_JOIN:
zassert(!ch->t_ifjoin_prune_pending_timer);
/*
In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
previously received join message with holdtime=0xFFFF.
*/
if (ch->t_ifjoin_expiry_timer) {
unsigned long remain =
thread_timer_remain_second(ch->t_ifjoin_expiry_timer);
if (remain > holdtime) {
/*
RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
Transitions from Join State
The (S,G) downstream state machine on interface I remains in
Join state, and the Expiry Timer (ET) is restarted, set to
maximum of its current value and the HoldTime from the
triggering Join/Prune message.
Conclusion: Do not change the ET if the current value is
higher than the received join holdtime.
*/
return;
}
}
THREAD_OFF(ch->t_ifjoin_expiry_timer);
break;
case PIM_IFJOIN_PRUNE_PENDING:
zassert(!ch->t_ifjoin_expiry_timer);
zassert(ch->t_ifjoin_prune_pending_timer);
THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
break;
}
zassert(!IFCHANNEL_NOINFO(ch));
if (holdtime != 0xFFFF) {
THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
on_ifjoin_expiry_timer,
ch, holdtime);
}
}
void pim_ifchannel_prune(struct interface *ifp,
struct in_addr upstream,
struct in_addr source_addr,
struct in_addr group_addr,
uint8_t source_flags,
uint16_t holdtime)
{
struct pim_ifchannel *ch;
int jp_override_interval_msec;
if (nonlocal_upstream(0 /* prune */, ifp, upstream,
source_addr, group_addr, source_flags, holdtime)) {
return;
}
ch = pim_ifchannel_add(ifp, source_addr, group_addr);
if (!ch)
return;
switch (ch->ifjoin_state) {
case PIM_IFJOIN_NOINFO:
case PIM_IFJOIN_PRUNE_PENDING:
/* nothing to do */
break;
case PIM_IFJOIN_JOIN:
{
struct pim_interface *pim_ifp;
pim_ifp = ifp->info;
zassert(ch->t_ifjoin_expiry_timer);
zassert(!ch->t_ifjoin_prune_pending_timer);
THREAD_OFF(ch->t_ifjoin_expiry_timer);
pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
if (listcount(pim_ifp->pim_neighbor_list) > 1) {
jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
}
else {
jp_override_interval_msec = 0; /* schedule to expire immediately */
/* If we called ifjoin_prune() directly instead, care should
be taken not to use "ch" afterwards since it would be
deleted. */
}
THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
on_ifjoin_prune_pending_timer,
ch, jp_override_interval_msec);
zassert(!ch->t_ifjoin_expiry_timer);
zassert(ch->t_ifjoin_prune_pending_timer);
}
break;
}
}
void pim_ifchannel_local_membership_add(struct interface *ifp,
struct in_addr source_addr,
struct in_addr group_addr)
{
struct pim_ifchannel *ch;
struct pim_interface *pim_ifp;
/* PIM enabled on interface? */
pim_ifp = ifp->info;
if (!pim_ifp)
return;
if (!PIM_IF_TEST_PIM(pim_ifp->options))
return;
ch = pim_ifchannel_add(ifp, source_addr, group_addr);
if (!ch) {
return;
}
ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
zassert(!IFCHANNEL_NOINFO(ch));
}
void pim_ifchannel_local_membership_del(struct interface *ifp,
struct in_addr source_addr,
struct in_addr group_addr)
{
struct pim_ifchannel *ch;
struct pim_interface *pim_ifp;
/* PIM enabled on interface? */
pim_ifp = ifp->info;
if (!pim_ifp)
return;
if (!PIM_IF_TEST_PIM(pim_ifp->options))
return;
ch = pim_ifchannel_find(ifp, source_addr, group_addr);
if (!ch)
return;
ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
delete_on_noinfo(ch);
}
void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
{
int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
if (new_couldassert == old_couldassert)
return;
if (PIM_DEBUG_PIM_EVENTS) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
__PRETTY_FUNCTION__,
src_str, grp_str, ch->interface->name,
old_couldassert, new_couldassert);
}
if (new_couldassert) {
/* CouldAssert(S,G,I) switched from FALSE to TRUE */
PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
}
else {
/* CouldAssert(S,G,I) switched from TRUE to FALSE */
PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
assert_action_a4(ch);
}
}
pim_ifchannel_update_my_assert_metric(ch);
}
/*
my_assert_metric may be affected by:
CouldAssert(S,G)
pim_ifp->primary_address
rpf->source_nexthop.mrib_metric_preference;
rpf->source_nexthop.mrib_route_metric;
*/
void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
{
struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch);
if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
return;
if (PIM_DEBUG_PIM_EVENTS) {
char src_str[100];
char grp_str[100];
char old_addr_str[100];
char new_addr_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str));
pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str));
zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
__PRETTY_FUNCTION__,
src_str, grp_str, ch->interface->name,
ch->ifassert_my_metric.rpt_bit_flag,
ch->ifassert_my_metric.metric_preference,
ch->ifassert_my_metric.route_metric,
old_addr_str,
my_metric_new.rpt_bit_flag,
my_metric_new.metric_preference,
my_metric_new.route_metric,
new_addr_str);
}
ch->ifassert_my_metric = my_metric_new;
if (pim_assert_metric_better(&ch->ifassert_my_metric,
&ch->ifassert_winner_metric)) {
assert_action_a5(ch);
}
}
void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
{
int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
if (new_atd == old_atd)
return;
if (PIM_DEBUG_PIM_EVENTS) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
__PRETTY_FUNCTION__,
src_str, grp_str, ch->interface->name,
old_atd, new_atd);
}
if (new_atd) {
/* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
}
else {
/* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
assert_action_a5(ch);
}
}
}

145
pimd/pim_ifchannel.h Normal file
View file

@ -0,0 +1,145 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_IFCHANNEL_H
#define PIM_IFCHANNEL_H
#include <zebra.h>
#include "if.h"
#include "pim_upstream.h"
enum pim_ifmembership {
PIM_IFMEMBERSHIP_NOINFO,
PIM_IFMEMBERSHIP_INCLUDE
};
enum pim_ifjoin_state {
PIM_IFJOIN_NOINFO,
PIM_IFJOIN_JOIN,
PIM_IFJOIN_PRUNE_PENDING
};
enum pim_ifassert_state {
PIM_IFASSERT_NOINFO,
PIM_IFASSERT_I_AM_WINNER,
PIM_IFASSERT_I_AM_LOSER
};
struct pim_assert_metric {
uint32_t rpt_bit_flag;
uint32_t metric_preference;
uint32_t route_metric;
struct in_addr ip_address; /* neighbor router that sourced the Assert message */
};
/*
Flag to detect change in CouldAssert(S,G,I)
*/
#define PIM_IF_FLAG_MASK_COULD_ASSERT (1 << 0)
#define PIM_IF_FLAG_TEST_COULD_ASSERT(flags) ((flags) & PIM_IF_FLAG_MASK_COULD_ASSERT)
#define PIM_IF_FLAG_SET_COULD_ASSERT(flags) ((flags) |= PIM_IF_FLAG_MASK_COULD_ASSERT)
#define PIM_IF_FLAG_UNSET_COULD_ASSERT(flags) ((flags) &= ~PIM_IF_FLAG_MASK_COULD_ASSERT)
/*
Flag to detect change in AssertTrackingDesired(S,G,I)
*/
#define PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED (1 << 1)
#define PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(flags) ((flags) & PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED)
#define PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(flags) ((flags) |= PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED)
#define PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(flags) ((flags) &= ~PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED)
/*
Per-interface (S,G) state
*/
struct pim_ifchannel {
struct in_addr source_addr; /* (S,G) source key */
struct in_addr group_addr; /* (S,G) group key */
struct interface *interface; /* backpointer to interface */
uint32_t flags;
/* IGMPv3 determined interface has local members for (S,G) ? */
enum pim_ifmembership local_ifmembership;
/* Per-interface (S,G) Join/Prune State (Section 4.1.4 of RFC4601) */
enum pim_ifjoin_state ifjoin_state;
struct thread *t_ifjoin_expiry_timer;
struct thread *t_ifjoin_prune_pending_timer;
int64_t ifjoin_creation; /* Record uptime of ifjoin state */
/* Per-interface (S,G) Assert State (Section 4.6.1 of RFC4601) */
enum pim_ifassert_state ifassert_state;
struct thread *t_ifassert_timer;
struct in_addr ifassert_winner;
struct pim_assert_metric ifassert_winner_metric;
int64_t ifassert_creation; /* Record uptime of ifassert state */
struct pim_assert_metric ifassert_my_metric;
/* Upstream (S,G) state */
struct pim_upstream *upstream;
};
void pim_ifchannel_free(struct pim_ifchannel *ch);
void pim_ifchannel_delete(struct pim_ifchannel *ch);
void pim_ifchannel_membership_clear(struct interface *ifp);
void pim_ifchannel_delete_on_noinfo(struct interface *ifp);
struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
struct in_addr source_addr,
struct in_addr group_addr);
struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
struct in_addr source_addr,
struct in_addr group_addr);
void pim_ifchannel_join_add(struct interface *ifp,
struct in_addr neigh_addr,
struct in_addr upstream,
struct in_addr source_addr,
struct in_addr group_addr,
uint8_t source_flags,
uint16_t holdtime);
void pim_ifchannel_prune(struct interface *ifp,
struct in_addr upstream,
struct in_addr source_addr,
struct in_addr group_addr,
uint8_t source_flags,
uint16_t holdtime);
void pim_ifchannel_local_membership_add(struct interface *ifp,
struct in_addr source_addr,
struct in_addr group_addr);
void pim_ifchannel_local_membership_del(struct interface *ifp,
struct in_addr source_addr,
struct in_addr group_addr);
void pim_ifchannel_ifjoin_switch(const char *caller,
struct pim_ifchannel *ch,
enum pim_ifjoin_state new_state);
const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state);
const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state);
int pim_ifchannel_isin_oiflist(struct pim_ifchannel *ch);
void reset_ifassert_state(struct pim_ifchannel *ch);
void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch);
void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch);
void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch);
#endif /* PIM_IFCHANNEL_H */

1433
pimd/pim_igmp.c Normal file

File diff suppressed because it is too large Load diff

176
pimd/pim_igmp.h Normal file
View file

@ -0,0 +1,176 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_IGMP_H
#define PIM_IGMP_H
#include <netinet/in.h>
#include <zebra.h>
#include "vty.h"
#include "linklist.h"
/*
The following sizes are likely to support
any message sent within local MTU.
*/
#define PIM_IGMP_BUFSIZE_READ (20000)
#define PIM_IGMP_BUFSIZE_WRITE (20000)
#define PIM_IGMP_MEMBERSHIP_QUERY (0x11)
#define PIM_IGMP_V1_MEMBERSHIP_REPORT (0x12)
#define PIM_IGMP_V2_MEMBERSHIP_REPORT (0x16)
#define PIM_IGMP_V2_LEAVE_GROUP (0x17)
#define PIM_IGMP_V3_MEMBERSHIP_REPORT (0x22)
#define IGMP_V3_REPORT_HEADER_SIZE (8)
#define IGMP_V3_GROUP_RECORD_MIN_SIZE (8)
#define IGMP_V3_MSG_MIN_SIZE (IGMP_V3_REPORT_HEADER_SIZE + \
IGMP_V3_GROUP_RECORD_MIN_SIZE)
#define IGMP_V12_MSG_SIZE (8)
#define IGMP_V3_GROUP_RECORD_TYPE_OFFSET (0)
#define IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET (1)
#define IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET (2)
#define IGMP_V3_GROUP_RECORD_GROUP_OFFSET (4)
#define IGMP_V3_GROUP_RECORD_SOURCE_OFFSET (8)
/* RFC 3376: 8.1. Robustness Variable - Default: 2 */
#define IGMP_DEFAULT_ROBUSTNESS_VARIABLE (2)
/* RFC 3376: 8.2. Query Interval - Default: 125 seconds */
#define IGMP_GENERAL_QUERY_INTERVAL (125)
/* RFC 3376: 8.3. Query Response Interval - Default: 100 deciseconds */
#define IGMP_QUERY_MAX_RESPONSE_TIME_DSEC (100)
/* RFC 3376: 8.8. Last Member Query Interval - Default: 10 deciseconds */
#define IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC (10)
struct igmp_join {
struct in_addr group_addr;
struct in_addr source_addr;
int sock_fd;
time_t sock_creation;
};
struct igmp_sock {
int fd;
struct interface *interface;
struct in_addr ifaddr;
time_t sock_creation;
struct thread *t_igmp_read; /* read: IGMP sockets */
struct thread *t_igmp_query_timer; /* timer: issue IGMP general queries */
struct thread *t_other_querier_timer; /* timer: other querier present */
int querier_query_interval; /* QQI */
int querier_robustness_variable; /* QRV */
int startup_query_count;
struct list *igmp_group_list; /* list of struct igmp_group */
};
struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
struct in_addr ifaddr);
struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list,
int fd);
struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
struct in_addr ifaddr,
struct interface *ifp);
void igmp_sock_delete(struct igmp_sock *igmp);
void igmp_sock_free(struct igmp_sock *igmp);
int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len);
void pim_igmp_general_query_on(struct igmp_sock *igmp);
void pim_igmp_general_query_off(struct igmp_sock *igmp);
void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp);
void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp);
#define IGMP_SOURCE_MASK_FORWARDING (1 << 0)
#define IGMP_SOURCE_MASK_DELETE (1 << 1)
#define IGMP_SOURCE_MASK_SEND (1 << 2)
#define IGMP_SOURCE_TEST_FORWARDING(flags) ((flags) & IGMP_SOURCE_MASK_FORWARDING)
#define IGMP_SOURCE_TEST_DELETE(flags) ((flags) & IGMP_SOURCE_MASK_DELETE)
#define IGMP_SOURCE_TEST_SEND(flags) ((flags) & IGMP_SOURCE_MASK_SEND)
#define IGMP_SOURCE_DO_FORWARDING(flags) ((flags) |= IGMP_SOURCE_MASK_FORWARDING)
#define IGMP_SOURCE_DO_DELETE(flags) ((flags) |= IGMP_SOURCE_MASK_DELETE)
#define IGMP_SOURCE_DO_SEND(flags) ((flags) |= IGMP_SOURCE_MASK_SEND)
#define IGMP_SOURCE_DONT_FORWARDING(flags) ((flags) &= ~IGMP_SOURCE_MASK_FORWARDING)
#define IGMP_SOURCE_DONT_DELETE(flags) ((flags) &= ~IGMP_SOURCE_MASK_DELETE)
#define IGMP_SOURCE_DONT_SEND(flags) ((flags) &= ~IGMP_SOURCE_MASK_SEND)
struct igmp_source {
struct in_addr source_addr;
struct thread *t_source_timer;
struct igmp_group *source_group; /* back pointer */
time_t source_creation;
uint32_t source_flags;
struct channel_oil *source_channel_oil;
/*
RFC 3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
*/
int source_query_retransmit_count;
};
struct igmp_group {
/*
RFC 3376: 6.2.2. Definition of Group Timers
The group timer is only used when a group is in EXCLUDE mode and it
represents the time for the *filter-mode* of the group to expire and
switch to INCLUDE mode.
*/
struct thread *t_group_timer;
/* Shared between group-specific and
group-and-source-specific retransmissions */
struct thread *t_group_query_retransmit_timer;
/* Counter exclusive for group-specific retransmissions
(not used by group-and-source-specific retransmissions,
since sources have their counters) */
int group_specific_query_retransmit_count;
struct in_addr group_addr;
int group_filtermode_isexcl; /* 0=INCLUDE, 1=EXCLUDE */
struct list *group_source_list; /* list of struct igmp_source */
time_t group_creation;
struct igmp_sock *group_igmp_sock; /* back pointer */
int64_t last_igmp_v1_report_dsec;
int64_t last_igmp_v2_report_dsec;
};
struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
struct in_addr group_addr,
const char *ifname);
void igmp_group_delete_empty_include(struct igmp_group *group);
void igmp_startup_mode_on(struct igmp_sock *igmp);
void igmp_group_timer_on(struct igmp_group *group,
long interval_msec, const char *ifname);
#endif /* PIM_IGMP_H */

67
pimd/pim_igmp_join.c Normal file
View file

@ -0,0 +1,67 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include "pim_igmp_join.h"
#ifndef SOL_IP
#define SOL_IP IPPROTO_IP
#endif
#ifndef MCAST_JOIN_SOURCE_GROUP
#define MCAST_JOIN_SOURCE_GROUP 46
struct group_source_req
{
uint32_t gsr_interface;
struct sockaddr_storage gsr_group;
struct sockaddr_storage gsr_source;
};
#endif
int pim_igmp_join_source(int fd, int ifindex,
struct in_addr group_addr,
struct in_addr source_addr)
{
struct group_source_req req;
struct sockaddr_in *group_sa = (struct sockaddr_in *) &req.gsr_group;
struct sockaddr_in *source_sa = (struct sockaddr_in *) &req.gsr_source;
memset(group_sa, 0, sizeof(*group_sa));
group_sa->sin_family = AF_INET;
group_sa->sin_addr = group_addr;
group_sa->sin_port = htons(0);
memset(source_sa, 0, sizeof(*source_sa));
source_sa->sin_family = AF_INET;
source_sa->sin_addr = source_addr;
source_sa->sin_port = htons(0);
req.gsr_interface = ifindex;
return setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP,
&req, sizeof(req));
return 0;
}

32
pimd/pim_igmp_join.h Normal file
View file

@ -0,0 +1,32 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_IGMP_JOIN_H
#define PIM_IGMP_JOIN_H
#include <netinet/in.h>
int pim_igmp_join_source(int fd, int ifindex,
struct in_addr group_addr,
struct in_addr source_addr);
#endif /* PIM_IGMP_JOIN_H */

1729
pimd/pim_igmpv3.c Normal file

File diff suppressed because it is too large Load diff

100
pimd/pim_igmpv3.h Normal file
View file

@ -0,0 +1,100 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_IGMPV3_H
#define PIM_IGMPV3_H
#include <zebra.h>
#include "if.h"
#define IGMP_V3_CHECKSUM_OFFSET (2)
#define IGMP_V3_REPORT_NUMGROUPS_OFFSET (6)
#define IGMP_V3_REPORT_GROUPPRECORD_OFFSET (8)
#define IGMP_V3_NUMSOURCES_OFFSET (10)
#define IGMP_V3_SOURCES_OFFSET (12)
/* GMI: Group Membership Interval */
#define PIM_IGMP_GMI_MSEC(qrv,qqi,qri_dsec) ((qrv) * (1000 * (qqi)) + 100 * (qri_dsec))
/* OQPI: Other Querier Present Interval */
#define PIM_IGMP_OQPI_MSEC(qrv,qqi,qri_dsec) ((qrv) * (1000 * (qqi)) + 100 * ((qri_dsec) >> 1))
/* SQI: Startup Query Interval */
#define PIM_IGMP_SQI(qi) (((qi) < 4) ? 1 : ((qi) >> 2))
/* LMQT: Last Member Query Time */
#define PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc) ((lmqc) * (100 * (lmqi_dsec)))
/* OHPI: Older Host Present Interval */
#define PIM_IGMP_OHPI_DSEC(qrv,qqi,qri_dsec) ((qrv) * (10 * (qqi)) + (qri_dsec))
void igmp_group_reset_gmi(struct igmp_group *group);
void igmp_source_reset_gmi(struct igmp_sock *igmp,
struct igmp_group *group,
struct igmp_source *source);
void igmp_source_free(struct igmp_source *source);
void igmp_source_delete(struct igmp_source *source);
void igmp_source_delete_expired(struct list *source_list);
int igmp_group_compat_mode(const struct igmp_sock *igmp,
const struct igmp_group *group);
void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
struct in_addr group_addr,
int num_sources, struct in_addr *sources);
void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
struct in_addr group_addr,
int num_sources, struct in_addr *sources);
void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
struct in_addr group_addr,
int num_sources, struct in_addr *sources);
void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
struct in_addr group_addr,
int num_sources, struct in_addr *sources);
void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
struct in_addr group_addr,
int num_sources, struct in_addr *sources);
void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
struct in_addr group_addr,
int num_sources, struct in_addr *sources);
void igmp_group_timer_lower_to_lmqt(struct igmp_group *group);
void igmp_source_timer_lower_to_lmqt(struct igmp_source *source);
struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
struct in_addr src_addr);
void pim_igmp_send_membership_query(struct igmp_group *group,
int fd,
const char *ifname,
char *query_buf,
int query_buf_size,
int num_sources,
struct in_addr dst_addr,
struct in_addr group_addr,
int query_max_response_time_dsec,
uint8_t s_flag,
uint8_t querier_robustness_variable,
uint16_t querier_query_interval);
#endif /* PIM_IGMPV3_H */

44
pimd/pim_int.c Normal file
View file

@ -0,0 +1,44 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <string.h>
#include <netinet/in.h>
#include "pim_int.h"
uint32_t pim_read_uint32_host(const uint8_t *buf)
{
uint32_t val;
memcpy(&val, buf, sizeof(val));
/* val is in netorder */
val = ntohl(val);
/* val is in hostorder */
return val;
}
void pim_write_uint32(uint8_t *buf, uint32_t val_host)
{
/* val_host is in host order */
val_host = htonl(val_host);
/* val_host is in netorder */
memcpy(buf, &val_host, sizeof(val_host));
}

31
pimd/pim_int.h Normal file
View file

@ -0,0 +1,31 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_INT_H
#define PIM_INT_H
#include <stdint.h>
uint32_t pim_read_uint32_host(const uint8_t *buf);
void pim_write_uint32(uint8_t *buf, uint32_t val_host);
#endif /* PIM_INT_H */

445
pimd/pim_join.c Normal file
View file

@ -0,0 +1,445 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "log.h"
#include "prefix.h"
#include "pimd.h"
#include "pim_str.h"
#include "pim_tlv.h"
#include "pim_msg.h"
#include "pim_pim.h"
#include "pim_join.h"
#include "pim_iface.h"
#include "pim_hello.h"
#include "pim_ifchannel.h"
static void on_trace(const char *label,
struct interface *ifp, struct in_addr src)
{
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
zlog_debug("%s: from %s on %s",
label, src_str, ifp->name);
}
}
static void recv_join(struct interface *ifp,
struct pim_neighbor *neigh,
uint16_t holdtime,
struct in_addr upstream,
struct in_addr group,
struct in_addr source,
uint8_t source_flags)
{
if (PIM_DEBUG_PIM_TRACE) {
char up_str[100];
char src_str[100];
char grp_str[100];
char neigh_str[100];
pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
pim_inet4_dump("<src?>", source, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", group, grp_str, sizeof(grp_str));
pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
zlog_warn("%s: join (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
__PRETTY_FUNCTION__,
src_str, grp_str,
source_flags & PIM_RPT_BIT_MASK,
source_flags & PIM_WILDCARD_BIT_MASK,
up_str, holdtime, neigh_str, ifp->name);
}
/* Restart join expiry timer */
pim_ifchannel_join_add(ifp, neigh->source_addr, upstream,
source, group, source_flags, holdtime);
}
static void recv_prune(struct interface *ifp,
struct pim_neighbor *neigh,
uint16_t holdtime,
struct in_addr upstream,
struct in_addr group,
struct in_addr source,
uint8_t source_flags)
{
if (PIM_DEBUG_PIM_TRACE) {
char up_str[100];
char src_str[100];
char grp_str[100];
char neigh_str[100];
pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
pim_inet4_dump("<src?>", source, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", group, grp_str, sizeof(grp_str));
pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
zlog_warn("%s: prune (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
__PRETTY_FUNCTION__,
src_str, grp_str,
source_flags & PIM_RPT_BIT_MASK,
source_flags & PIM_WILDCARD_BIT_MASK,
up_str, holdtime, neigh_str, ifp->name);
}
pim_ifchannel_prune(ifp, upstream, source, group, source_flags, holdtime);
}
int pim_joinprune_recv(struct interface *ifp,
struct pim_neighbor *neigh,
struct in_addr src_addr,
uint8_t *tlv_buf, int tlv_buf_size)
{
struct prefix msg_upstream_addr;
uint8_t msg_num_groups;
uint16_t msg_holdtime;
int addr_offset;
uint8_t *buf;
uint8_t *pastend;
int remain;
int group;
on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
buf = tlv_buf;
pastend = tlv_buf + tlv_buf_size;
/*
Parse ucast addr
*/
addr_offset = pim_parse_addr_ucast(ifp->name, src_addr,
&msg_upstream_addr,
buf, pastend - buf);
#if 0
zlog_warn("%s: pim_parse_addr_ucast addr_offset=%d",
__PRETTY_FUNCTION__,
addr_offset);
#endif
if (addr_offset < 1) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
__PRETTY_FUNCTION__,
src_str, ifp->name);
return -1;
}
buf += addr_offset;
/*
Check upstream address family
*/
if (msg_upstream_addr.family != AF_INET) {
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
__PRETTY_FUNCTION__,
msg_upstream_addr.family, src_str, ifp->name);
}
return -2;
}
remain = pastend - buf;
if (remain < 4) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
__PRETTY_FUNCTION__,
remain, 4, src_str, ifp->name);
return -4;
}
++buf; /* skip reserved byte */
msg_num_groups = *(const uint8_t *) buf;
++buf;
msg_holdtime = ntohs(*(const uint16_t *) buf);
++buf;
++buf;
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
char upstream_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
upstream_str, sizeof(upstream_str));
zlog_warn("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
__PRETTY_FUNCTION__,
upstream_str, msg_num_groups, msg_holdtime,
src_str, ifp->name);
}
/* Scan groups */
for (group = 0; group < msg_num_groups; ++group) {
struct prefix msg_group_addr;
struct prefix msg_source_addr;
uint8_t msg_source_flags;
uint16_t msg_num_joined_sources;
uint16_t msg_num_pruned_sources;
int source;
addr_offset = pim_parse_addr_group(ifp->name, src_addr,
&msg_group_addr,
buf, pastend - buf);
#if 0
zlog_warn("%s: pim_parse_addr_group addr_offset=%d",
__PRETTY_FUNCTION__,
addr_offset);
#endif
if (addr_offset < 1) {
return -5;
}
buf += addr_offset;
remain = pastend - buf;
if (remain < 4) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
__PRETTY_FUNCTION__,
remain, 4, src_str, ifp->name);
return -6;
}
msg_num_joined_sources = ntohs(*(const uint16_t *) buf);
buf += 2;
msg_num_pruned_sources = ntohs(*(const uint16_t *) buf);
buf += 2;
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
char upstream_str[100];
char group_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
upstream_str, sizeof(upstream_str));
pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4,
group_str, sizeof(group_str));
zlog_warn("%s: join/prune upstream=%s group=%s/%d join_src=%d prune_src=%d from %s on %s",
__PRETTY_FUNCTION__,
upstream_str, group_str, msg_group_addr.prefixlen,
msg_num_joined_sources, msg_num_pruned_sources,
src_str, ifp->name);
}
/* Scan joined sources */
for (source = 0; source < msg_num_joined_sources; ++source) {
addr_offset = pim_parse_addr_source(ifp->name, src_addr,
&msg_source_addr,
&msg_source_flags,
buf, pastend - buf);
#if 0
zlog_warn("%s: pim_parse_addr_source addr_offset=%d",
__PRETTY_FUNCTION__,
addr_offset);
#endif
if (addr_offset < 1) {
return -7;
}
buf += addr_offset;
recv_join(ifp, neigh, msg_holdtime,
msg_upstream_addr.u.prefix4,
msg_group_addr.u.prefix4,
msg_source_addr.u.prefix4,
msg_source_flags);
}
/* Scan pruned sources */
for (source = 0; source < msg_num_pruned_sources; ++source) {
addr_offset = pim_parse_addr_source(ifp->name, src_addr,
&msg_source_addr,
&msg_source_flags,
buf, pastend - buf);
if (addr_offset < 1) {
return -8;
}
buf += addr_offset;
recv_prune(ifp, neigh, msg_holdtime,
msg_upstream_addr.u.prefix4,
msg_group_addr.u.prefix4,
msg_source_addr.u.prefix4,
msg_source_flags);
}
} /* scan groups */
return 0;
}
int pim_joinprune_send(struct interface *ifp,
struct in_addr upstream_addr,
struct in_addr source_addr,
struct in_addr group_addr,
int send_join)
{
struct pim_interface *pim_ifp;
uint8_t pim_msg[1000];
const uint8_t *pastend = pim_msg + sizeof(pim_msg);
uint8_t *pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* room for pim header */
int pim_msg_size;
int remain;
zassert(ifp);
pim_ifp = ifp->info;
if (!pim_ifp) {
zlog_warn("%s: multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
ifp->name);
return -1;
}
if (PIM_DEBUG_PIM_TRACE) {
char source_str[100];
char group_str[100];
char dst_str[100];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
zlog_debug("%s: sending %s(S,G)=(%s,%s) to upstream=%s on interface %s",
__PRETTY_FUNCTION__,
send_join ? "Join" : "Prune",
source_str, group_str, dst_str, ifp->name);
}
if (PIM_INADDR_IS_ANY(upstream_addr)) {
if (PIM_DEBUG_PIM_TRACE) {
char source_str[100];
char group_str[100];
char dst_str[100];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
zlog_debug("%s: %s(S,G)=(%s,%s): upstream=%s is myself on interface %s",
__PRETTY_FUNCTION__,
send_join ? "Join" : "Prune",
source_str, group_str, dst_str, ifp->name);
}
return 0;
}
/*
RFC 4601: 4.3.1. Sending Hello Messages
Thus, if a router needs to send a Join/Prune or Assert message on
an interface on which it has not yet sent a Hello message with the
currently configured IP address, then it MUST immediately send the
relevant Hello message without waiting for the Hello Timer to
expire, followed by the Join/Prune or Assert message.
*/
pim_hello_require(ifp);
/*
Build PIM message
*/
remain = pastend - pim_msg_curr;
pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
remain,
upstream_addr);
if (!pim_msg_curr) {
char dst_str[100];
pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
zlog_warn("%s: failure encoding destination address %s: space left=%d",
__PRETTY_FUNCTION__, dst_str, remain);
return -3;
}
remain = pastend - pim_msg_curr;
if (remain < 4) {
zlog_warn("%s: group will not fit: space left=%d",
__PRETTY_FUNCTION__, remain);
return -4;
}
*pim_msg_curr = 0; /* reserved */
++pim_msg_curr;
*pim_msg_curr = 1; /* number of groups */
++pim_msg_curr;
*((uint16_t *) pim_msg_curr) = htons(PIM_JP_HOLDTIME);
++pim_msg_curr;
++pim_msg_curr;
remain = pastend - pim_msg_curr;
pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
remain,
group_addr);
if (!pim_msg_curr) {
char group_str[100];
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
zlog_warn("%s: failure encoding group address %s: space left=%d",
__PRETTY_FUNCTION__, group_str, remain);
return -5;
}
remain = pastend - pim_msg_curr;
if (remain < 4) {
zlog_warn("%s: sources will not fit: space left=%d",
__PRETTY_FUNCTION__, remain);
return -6;
}
/* number of joined sources */
*((uint16_t *) pim_msg_curr) = htons(send_join ? 1 : 0);
++pim_msg_curr;
++pim_msg_curr;
/* number of pruned sources */
*((uint16_t *) pim_msg_curr) = htons(send_join ? 0 : 1);
++pim_msg_curr;
++pim_msg_curr;
remain = pastend - pim_msg_curr;
pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr,
remain,
source_addr);
if (!pim_msg_curr) {
char source_str[100];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: failure encoding source address %s: space left=%d",
__PRETTY_FUNCTION__, source_str, remain);
return -7;
}
/* Add PIM header */
pim_msg_size = pim_msg_curr - pim_msg;
pim_msg_build_header(pim_msg, pim_msg_size,
PIM_MSG_TYPE_JOIN_PRUNE);
if (pim_msg_send(pim_ifp->pim_sock_fd,
qpim_all_pim_routers_addr,
pim_msg,
pim_msg_size,
ifp->name)) {
zlog_warn("%s: could not send PIM message on interface %s",
__PRETTY_FUNCTION__, ifp->name);
return -8;
}
return 0;
}

43
pimd/pim_join.h Normal file
View file

@ -0,0 +1,43 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_JOIN_H
#define PIM_JOIN_H
#include <zebra.h>
#include "if.h"
#include "pim_neighbor.h"
int pim_joinprune_recv(struct interface *ifp,
struct pim_neighbor *neigh,
struct in_addr src_addr,
uint8_t *tlv_buf, int tlv_buf_size);
int pim_joinprune_send(struct interface *ifp,
struct in_addr upstream_addr,
struct in_addr source_addr,
struct in_addr group_addr,
int send_join);
#endif /* PIM_JOIN_H */

437
pimd/pim_macro.c Normal file
View file

@ -0,0 +1,437 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "log.h"
#include "pim_macro.h"
#include "pimd.h"
#include "pim_str.h"
#include "pim_iface.h"
#include "pim_ifchannel.h"
#define PIM_IFP_I_am_DR(pim_ifp) ((pim_ifp)->pim_dr_addr.s_addr == (pim_ifp)->primary_address.s_addr)
/*
DownstreamJPState(S,G,I) is the per-interface state machine for
receiving (S,G) Join/Prune messages.
DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
*/
static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch)
{
return ch->ifjoin_state != PIM_IFJOIN_NOINFO;
}
/*
The clause "local_receiver_include(S,G,I)" is true if the IGMP/MLD
module or other local membership mechanism has determined that local
members on interface I desire to receive traffic sent specifically
by S to G.
*/
static int local_receiver_include(const struct pim_ifchannel *ch)
{
/* local_receiver_include(S,G,I) ? */
return ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE;
}
/*
RFC 4601: 4.1.6. State Summarization Macros
The set "joins(S,G)" is the set of all interfaces on which the
router has received (S,G) Joins:
joins(S,G) =
{ all interfaces I such that
DownstreamJPState(S,G,I) is either Join or Prune-Pending }
DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
*/
int pim_macro_chisin_joins(const struct pim_ifchannel *ch)
{
return downstream_jpstate_isjoined(ch);
}
/*
RFC 4601: 4.6.5. Assert State Macros
The set "lost_assert(S,G)" is the set of all interfaces on which the
router has received (S,G) joins but has lost an (S,G) assert.
lost_assert(S,G) =
{ all interfaces I such that
lost_assert(S,G,I) == TRUE }
bool lost_assert(S,G,I) {
if ( RPF_interface(S) == I ) {
return FALSE
} else {
return ( AssertWinner(S,G,I) != NULL AND
AssertWinner(S,G,I) != me AND
(AssertWinnerMetric(S,G,I) is better
than spt_assert_metric(S,I) )
}
}
AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
packet that won an Assert.
*/
int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch)
{
struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_assert_metric spt_assert_metric;
ifp = ch->interface;
if (!ifp) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s): null interface",
__PRETTY_FUNCTION__,
src_str, grp_str);
return 0; /* false */
}
/* RPF_interface(S) == I ? */
if (ch->upstream->rpf.source_nexthop.interface == ifp)
return 0; /* false */
pim_ifp = ifp->info;
if (!pim_ifp) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ifp->name);
return 0; /* false */
}
if (PIM_INADDR_IS_ANY(ch->ifassert_winner))
return 0; /* false */
/* AssertWinner(S,G,I) == me ? */
if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
return 0; /* false */
spt_assert_metric = pim_macro_spt_assert_metric(&ch->upstream->rpf,
pim_ifp->primary_address);
return pim_assert_metric_better(&ch->ifassert_winner_metric,
&spt_assert_metric);
}
/*
RFC 4601: 4.1.6. State Summarization Macros
pim_include(S,G) =
{ all interfaces I such that:
( (I_am_DR( I ) AND lost_assert(S,G,I) == FALSE )
OR AssertWinner(S,G,I) == me )
AND local_receiver_include(S,G,I) }
AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
packet that won an Assert.
*/
int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch)
{
struct pim_interface *pim_ifp = ch->interface->info;
if (!pim_ifp) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ch->interface->name);
return 0; /* false */
}
/* local_receiver_include(S,G,I) ? */
if (!local_receiver_include(ch))
return 0; /* false */
/* OR AssertWinner(S,G,I) == me ? */
if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
return 1; /* true */
return (
/* I_am_DR( I ) ? */
PIM_IFP_I_am_DR(pim_ifp)
&&
/* lost_assert(S,G,I) == FALSE ? */
(!pim_macro_ch_lost_assert(ch))
);
}
int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch)
{
if (pim_macro_chisin_joins(ch))
return 1; /* true */
return pim_macro_chisin_pim_include(ch);
}
/*
RFC 4601: 4.6.1. (S,G) Assert Message State Machine
CouldAssert(S,G,I) =
SPTbit(S,G)==TRUE
AND (RPF_interface(S) != I)
AND (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) ) )
CouldAssert(S,G,I) is true for downstream interfaces that would be in
the inherited_olist(S,G) if (S,G) assert information was not taken
into account.
CouldAssert(S,G,I) may be affected by changes in the following:
pim_ifp->primary_address
pim_ifp->pim_dr_addr
ch->ifassert_winner_metric
ch->ifassert_winner
ch->local_ifmembership
ch->ifjoin_state
ch->upstream->rpf.source_nexthop.mrib_metric_preference
ch->upstream->rpf.source_nexthop.mrib_route_metric
ch->upstream->rpf.source_nexthop.interface
*/
int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch)
{
struct interface *ifp;
/* SPTbit(S,G) is always true for PIM-SSM-Only Routers */
ifp = ch->interface;
if (!ifp) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s): null interface",
__PRETTY_FUNCTION__,
src_str, grp_str);
return 0; /* false */
}
/* RPF_interface(S) != I ? */
if (ch->upstream->rpf.source_nexthop.interface == ifp)
return 0; /* false */
/* I in joins(S,G) (+) pim_include(S,G) ? */
return pim_macro_chisin_joins_or_include(ch);
}
/*
RFC 4601: 4.6.3. Assert Metrics
spt_assert_metric(S,I) gives the assert metric we use if we're
sending an assert based on active (S,G) forwarding state:
assert_metric
spt_assert_metric(S,I) {
return {0,MRIB.pref(S),MRIB.metric(S),my_ip_address(I)}
}
*/
struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf,
struct in_addr ifaddr)
{
struct pim_assert_metric metric;
metric.rpt_bit_flag = 0;
metric.metric_preference = rpf->source_nexthop.mrib_metric_preference;
metric.route_metric = rpf->source_nexthop.mrib_route_metric;
metric.ip_address = ifaddr;
return metric;
}
/*
RFC 4601: 4.6.3. Assert Metrics
An assert metric for (S,G) to include in (or compare against) an
Assert message sent on interface I should be computed using the
following pseudocode:
assert_metric my_assert_metric(S,G,I) {
if( CouldAssert(S,G,I) == TRUE ) {
return spt_assert_metric(S,I)
} else if( CouldAssert(*,G,I) == TRUE ) {
return rpt_assert_metric(G,I)
} else {
return infinite_assert_metric()
}
}
*/
struct pim_assert_metric pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch)
{
struct pim_interface *pim_ifp;
pim_ifp = ch->interface->info;
if (pim_ifp) {
if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
return pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address);
}
}
return qpim_infinite_assert_metric;
}
/*
RFC 4601 4.2. Data Packet Forwarding Rules
RFC 4601 4.8.2. PIM-SSM-Only Routers
Macro:
inherited_olist(S,G) =
joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
*/
static int pim_macro_chisin_inherited_olist(const struct pim_ifchannel *ch)
{
if (pim_macro_ch_lost_assert(ch))
return 0; /* false */
return pim_macro_chisin_joins_or_include(ch);
}
/*
RFC 4601 4.2. Data Packet Forwarding Rules
RFC 4601 4.8.2. PIM-SSM-Only Routers
Additionally, the Packet forwarding rules of Section 4.2 can be
simplified in a PIM-SSM-only router:
iif is the incoming interface of the packet.
oiflist = NULL
if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) {
oiflist = inherited_olist(S,G)
} else if (iif is in inherited_olist(S,G)) {
send Assert(S,G) on iif
}
oiflist = oiflist (-) iif
forward packet on all interfaces in oiflist
Macro:
inherited_olist(S,G) =
joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
Note:
- The following test is performed as response to WRONGVIF kernel
upcall:
if (iif is in inherited_olist(S,G)) {
send Assert(S,G) on iif
}
See pim_mroute.c mroute_msg().
*/
int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch)
{
if (ch->upstream->join_state != PIM_UPSTREAM_JOINED) {
/* oiflist is NULL */
return 0; /* false */
}
/* oiflist = oiflist (-) iif */
if (ch->interface == ch->upstream->rpf.source_nexthop.interface)
return 0; /* false */
return pim_macro_chisin_inherited_olist(ch);
}
/*
RFC 4601: 4.6.1. (S,G) Assert Message State Machine
AssertTrackingDesired(S,G,I) =
(I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
(+) ( pim_include(*,G) (-) pim_exclude(S,G) )
(-) lost_assert(*,G)
(+) joins(S,G) ) )
OR (local_receiver_include(S,G,I) == TRUE
AND (I_am_DR(I) OR (AssertWinner(S,G,I) == me)))
OR ((RPF_interface(S) == I) AND (JoinDesired(S,G) == TRUE))
OR ((RPF_interface(RP(G)) == I) AND (JoinDesired(*,G) == TRUE)
AND (SPTbit(S,G) == FALSE))
AssertTrackingDesired(S,G,I) is true on any interface in which an
(S,G) assert might affect our behavior.
*/
int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch)
{
struct pim_interface *pim_ifp;
struct interface *ifp;
ifp = ch->interface;
if (!ifp) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s): null interface",
__PRETTY_FUNCTION__,
src_str, grp_str);
return 0; /* false */
}
pim_ifp = ifp->info;
if (!pim_ifp) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ch->interface->name);
return 0; /* false */
}
/* I in joins(S,G) ? */
if (pim_macro_chisin_joins(ch))
return 1; /* true */
/* local_receiver_include(S,G,I) ? */
if (local_receiver_include(ch)) {
/* I_am_DR(I) ? */
if (PIM_IFP_I_am_DR(pim_ifp))
return 1; /* true */
/* AssertWinner(S,G,I) == me ? */
if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
return 1; /* true */
}
/* RPF_interface(S) == I ? */
if (ch->upstream->rpf.source_nexthop.interface == ifp) {
/* JoinDesired(S,G) ? */
if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags))
return 1; /* true */
}
return 0; /* false */
}

44
pimd/pim_macro.h Normal file
View file

@ -0,0 +1,44 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_MACRO_H
#define PIM_MACRO_H
#include <zebra.h>
#include "if.h"
#include "pim_upstream.h"
#include "pim_ifchannel.h"
int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch);
int pim_macro_chisin_joins(const struct pim_ifchannel *ch);
int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch);
int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch);
int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch);
struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf,
struct in_addr ifaddr);
struct pim_assert_metric pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch);
int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch);
int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch);
#endif /* PIM_MACRO_H */

294
pimd/pim_main.c Normal file
View file

@ -0,0 +1,294 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "log.h"
#include "privs.h"
#include "version.h"
#include <getopt.h>
#include "command.h"
#include "thread.h"
#include <signal.h>
#include "memory.h"
#include "filter.h"
#include "vty.h"
#include "sigevent.h"
#include "version.h"
#include "pimd.h"
#include "pim_version.h"
#include "pim_signals.h"
#include "pim_zebra.h"
#ifdef PIM_ZCLIENT_DEBUG
extern int zclient_debug;
#endif
extern struct host host;
char config_default[] = SYSCONFDIR PIMD_DEFAULT_CONFIG;
struct option longopts[] = {
{ "daemon", no_argument, NULL, 'd'},
{ "config_file", required_argument, NULL, 'f'},
{ "pid_file", required_argument, NULL, 'i'},
{ "vty_addr", required_argument, NULL, 'A'},
{ "vty_port", required_argument, NULL, 'P'},
{ "version", no_argument, NULL, 'v'},
{ "debug_zclient", no_argument, NULL, 'Z'},
{ "help", no_argument, NULL, 'h'},
{ 0 }
};
/* pimd privileges */
zebra_capabilities_t _caps_p [] =
{
ZCAP_NET_ADMIN,
ZCAP_SYS_ADMIN,
ZCAP_NET_RAW,
};
/* pimd privileges to run with */
struct zebra_privs_t pimd_privs =
{
#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP)
.user = QUAGGA_USER,
.group = QUAGGA_GROUP,
#endif
#ifdef VTY_GROUP
.vty_group = VTY_GROUP,
#endif
.caps_p = _caps_p,
.cap_num_p = sizeof(_caps_p)/sizeof(_caps_p[0]),
.cap_num_i = 0
};
char* progname;
const char *pid_file = PATH_PIMD_PID;
static void usage(int status)
{
if (status != 0)
fprintf (stderr, "Try `%s --help' for more information.\n", progname);
else {
printf ("Usage : %s [OPTION...]\n\
Daemon which manages PIM.\n\n\
-d, --daemon Run in daemon mode\n\
-f, --config_file Set configuration file name\n\
-i, --pid_file Set process identifier file name\n\
-z, --socket Set path of zebra socket\n\
-A, --vty_addr Set vty's bind address\n\
-P, --vty_port Set vty's port number\n\
-v, --version Print program version\n\
"
#ifdef PIM_ZCLIENT_DEBUG
"\
-Z, --debug_zclient Enable zclient debugging\n\
"
#endif
"\
-h, --help Display this help and exit\n\
\n\
Report bugs to %s\n", progname, PIMD_BUG_ADDRESS);
}
exit (status);
}
int main(int argc, char** argv, char** envp) {
char *p;
char *vty_addr = NULL;
int vty_port = -1;
int daemon_mode = 0;
char *config_file = NULL;
char *zebra_sock_path = NULL;
struct thread thread;
umask(0027);
progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
zlog_default = openzlog(progname, ZLOG_PIM,
LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
/* this while just reads the options */
while (1) {
int opt;
opt = getopt_long (argc, argv, "df:i:z:A:P:vZh", longopts, 0);
if (opt == EOF)
break;
switch (opt) {
case 0:
break;
case 'd':
daemon_mode = 1;
break;
case 'f':
config_file = optarg;
break;
case 'i':
pid_file = optarg;
break;
case 'z':
zebra_sock_path = optarg;
break;
case 'A':
vty_addr = optarg;
break;
case 'P':
vty_port = atoi (optarg);
break;
case 'v':
printf(PIMD_PROGNAME " version %s\n", PIMD_VERSION);
print_version(QUAGGA_PROGNAME);
exit (0);
break;
#ifdef PIM_ZCLIENT_DEBUG
case 'Z':
zclient_debug = 1;
break;
#endif
case 'h':
usage (0);
break;
default:
usage (1);
break;
}
}
master = thread_master_create();
/*
* Temporarily send zlog to stdout
*/
zlog_default->maxlvl[ZLOG_DEST_STDOUT] = zlog_default->default_lvl;
zlog_notice("Boot logging temporarily directed to stdout - begin");
zlog_notice("Quagga %s " PIMD_PROGNAME " %s starting",
QUAGGA_VERSION, PIMD_VERSION);
/*
* Initializations
*/
zprivs_init (&pimd_privs);
pim_signals_init();
cmd_init(1);
vty_init(master);
memory_init();
access_list_init();
pim_init();
/*
* reset zlog default, then will obey configuration file
*/
zlog_notice("Boot logging temporarily directed to stdout - end");
#if 0
/* this would disable logging to stdout, but config has not been
loaded yet to reconfig the logging output */
zlog_default->maxlvl[ZLOG_DEST_STDOUT] = ZLOG_DISABLED;
#endif
/*
Initialize zclient "update" and "lookup" sockets
*/
pim_zebra_init(zebra_sock_path);
zlog_notice("Loading configuration - begin");
/* Get configuration file. */
vty_read_config(config_file, config_default);
/*
Starting from here zlog_* functions will log according configuration
*/
zlog_notice("Loading configuration - end");
/* Change to the daemon program. */
if (daemon_mode) {
if (daemon(0, 0)) {
zlog_warn("failed to daemonize");
}
}
/* Process ID file creation. */
pid_output(pid_file);
/* Create pimd VTY socket */
if (vty_port < 0)
vty_port = PIMD_VTY_PORT;
vty_serv_sock(vty_addr, vty_port, PIM_VTYSH_PATH);
zlog_notice("Quagga %s " PIMD_PROGNAME " %s starting, VTY interface at port TCP %d",
QUAGGA_VERSION, PIMD_VERSION, vty_port);
#ifdef PIM_DEBUG_BYDEFAULT
zlog_notice("PIM_DEBUG_BYDEFAULT: Enabling all debug commands");
PIM_DO_DEBUG_PIM_EVENTS;
PIM_DO_DEBUG_PIM_PACKETS;
PIM_DO_DEBUG_PIM_TRACE;
PIM_DO_DEBUG_IGMP_EVENTS;
PIM_DO_DEBUG_IGMP_PACKETS;
PIM_DO_DEBUG_IGMP_TRACE;
PIM_DO_DEBUG_ZEBRA;
#endif
#ifdef PIM_ZCLIENT_DEBUG
zlog_notice("PIM_ZCLIENT_DEBUG: zclient debugging is supported, mode is %s (see option -Z)",
zclient_debug ? "ON" : "OFF");
#endif
#ifdef PIM_CHECK_RECV_IFINDEX_SANITY
zlog_notice("PIM_CHECK_RECV_IFINDEX_SANITY: will match sock/recv ifindex");
#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
zlog_notice("PIM_REPORT_RECV_IFINDEX_MISMATCH: will report sock/recv ifindex mismatch");
#endif
#endif
#ifdef PIM_UNEXPECTED_KERNEL_UPCALL
zlog_notice("PIM_UNEXPECTED_KERNEL_UPCALL: report unexpected kernel upcall");
#endif
#ifdef HAVE_CLOCK_MONOTONIC
zlog_notice("HAVE_CLOCK_MONOTONIC");
#else
zlog_notice("!HAVE_CLOCK_MONOTONIC");
#endif
while (thread_fetch(master, &thread))
thread_call(&thread);
zlog_err("%s %s: thread_fetch() returned NULL, exiting",
__FILE__, __PRETTY_FUNCTION__);
/* never reached */
return 0;
}

451
pimd/pim_mroute.c Normal file
View file

@ -0,0 +1,451 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "log.h"
#include "privs.h"
#include "pimd.h"
#include "pim_mroute.h"
#include "pim_str.h"
#include "pim_time.h"
#include "pim_iface.h"
#include "pim_macro.h"
/* GLOBAL VARS */
extern struct zebra_privs_t pimd_privs;
static void mroute_read_on(void);
static int pim_mroute_set(int fd, int enable)
{
int err;
int opt = enable ? MRT_INIT : MRT_DONE;
socklen_t opt_len = sizeof(opt);
err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len);
if (err) {
int e = errno;
zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
__FILE__, __PRETTY_FUNCTION__,
fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e));
errno = e;
return -1;
}
#if 0
zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok",
__FILE__, __PRETTY_FUNCTION__,
fd, opt);
#endif
return 0;
}
int pim_mroute_msg(int fd, const char *buf, int buf_size)
{
struct interface *ifp;
const struct ip *ip_hdr;
const struct igmpmsg *msg;
const char *upcall;
char src_str[100];
char grp_str[100];
ip_hdr = (const struct ip *) buf;
/* kernel upcall must have protocol=0 */
if (ip_hdr->ip_p) {
/* this is not a kernel upcall */
#ifdef PIM_UNEXPECTED_KERNEL_UPCALL
zlog_warn("%s: not a kernel upcall proto=%d msg_size=%d",
__PRETTY_FUNCTION__, ip_hdr->ip_p, buf_size);
#endif
return 0;
}
msg = (const struct igmpmsg *) buf;
switch (msg->im_msgtype) {
case IGMPMSG_NOCACHE: upcall = "NOCACHE"; break;
case IGMPMSG_WRONGVIF: upcall = "WRONGVIF"; break;
case IGMPMSG_WHOLEPKT: upcall = "WHOLEPKT"; break;
default: upcall = "<unknown_upcall?>";
}
ifp = pim_if_find_by_vif_index(msg->im_vif);
pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
if (msg->im_msgtype == IGMPMSG_WRONGVIF) {
struct pim_ifchannel *ch;
struct pim_interface *pim_ifp;
/*
Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
RFC 4601 4.8.2. PIM-SSM-Only Routers
iif is the incoming interface of the packet.
if (iif is in inherited_olist(S,G)) {
send Assert(S,G) on iif
}
*/
if (PIM_DEBUG_PIM_TRACE) {
zlog_debug("%s: WRONGVIF from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
__PRETTY_FUNCTION__,
fd,
src_str,
grp_str,
ifp ? ifp->name : "<ifname?>",
msg->im_vif);
}
if (!ifp) {
zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
__PRETTY_FUNCTION__,
src_str, grp_str, msg->im_vif);
return -1;
}
pim_ifp = ifp->info;
if (!pim_ifp) {
zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ifp->name);
return -2;
}
ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst);
if (!ch) {
zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ifp->name);
return -3;
}
/*
RFC 4601: 4.6.1. (S,G) Assert Message State Machine
Transitions from NoInfo State
An (S,G) data packet arrives on interface I, AND
CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
downstream interface that is in our (S,G) outgoing interface
list. We optimistically assume that we will be the assert
winner for this (S,G), and so we transition to the "I am Assert
Winner" state and perform Actions A1 (below), which will
initiate the assert negotiation for (S,G).
*/
if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ifp->name);
return -4;
}
if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel",
__PRETTY_FUNCTION__,
src_str, grp_str, ifp->name);
return -5;
}
if (assert_action_a1(ch)) {
zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ifp->name);
return -6;
}
return 0;
} /* IGMPMSG_WRONGVIF */
zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
__PRETTY_FUNCTION__,
upcall,
msg->im_msgtype,
ip_hdr->ip_p,
fd,
src_str,
grp_str,
ifp ? ifp->name : "<ifname?>",
msg->im_vif);
return 0;
}
static int mroute_read_msg(int fd)
{
const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg));
char buf[1000];
int rd;
if (((int) sizeof(buf)) < msg_min_size) {
zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d",
__PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size);
return -1;
}
rd = read(fd, buf, sizeof(buf));
if (rd < 0) {
zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
__PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
return -2;
}
if (rd < msg_min_size) {
zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
__PRETTY_FUNCTION__, fd, rd, msg_min_size);
return -3;
}
return pim_mroute_msg(fd, buf, rd);
}
static int mroute_read(struct thread *t)
{
int fd;
int result;
zassert(t);
zassert(!THREAD_ARG(t));
fd = THREAD_FD(t);
zassert(fd == qpim_mroute_socket_fd);
result = mroute_read_msg(fd);
/* Keep reading */
qpim_mroute_socket_reader = 0;
mroute_read_on();
return result;
}
static void mroute_read_on()
{
zassert(!qpim_mroute_socket_reader);
zassert(PIM_MROUTE_IS_ENABLED);
THREAD_READ_ON(master, qpim_mroute_socket_reader,
mroute_read, 0, qpim_mroute_socket_fd);
}
static void mroute_read_off()
{
THREAD_OFF(qpim_mroute_socket_reader);
}
int pim_mroute_socket_enable()
{
int fd;
if (PIM_MROUTE_IS_ENABLED)
return -1;
if ( pimd_privs.change (ZPRIVS_RAISE) )
zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
safe_strerror (errno) );
fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
if ( pimd_privs.change (ZPRIVS_LOWER) )
zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
safe_strerror (errno) );
if (fd < 0) {
zlog_warn("Could not create mroute socket: errno=%d: %s",
errno, safe_strerror(errno));
return -2;
}
if (pim_mroute_set(fd, 1)) {
zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
fd, errno, safe_strerror(errno));
close(fd);
return -3;
}
qpim_mroute_socket_fd = fd;
qpim_mroute_socket_creation = pim_time_monotonic_sec();
mroute_read_on();
zassert(PIM_MROUTE_IS_ENABLED);
return 0;
}
int pim_mroute_socket_disable()
{
if (PIM_MROUTE_IS_DISABLED)
return -1;
if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
qpim_mroute_socket_fd, errno, safe_strerror(errno));
return -2;
}
if (close(qpim_mroute_socket_fd)) {
zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
qpim_mroute_socket_fd, errno, safe_strerror(errno));
return -3;
}
mroute_read_off();
qpim_mroute_socket_fd = -1;
zassert(PIM_MROUTE_IS_DISABLED);
return 0;
}
/*
For each network interface (e.g., physical or a virtual tunnel) that
would be used for multicast forwarding, a corresponding multicast
interface must be added to the kernel.
*/
int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr)
{
struct vifctl vc;
int err;
if (PIM_MROUTE_IS_DISABLED) {
zlog_warn("%s: global multicast is disabled",
__PRETTY_FUNCTION__);
return -1;
}
memset(&vc, 0, sizeof(vc));
vc.vifc_vifi = vif_index;
vc.vifc_flags = 0;
vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
vc.vifc_rate_limit = 0;
memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
#ifdef PIM_DVMRP_TUNNEL
if (vc.vifc_flags & VIFF_TUNNEL) {
memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
}
#endif
err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
if (err) {
char ifaddr_str[100];
int e = errno;
pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s): errno=%d: %s",
__FILE__, __PRETTY_FUNCTION__,
qpim_mroute_socket_fd, vif_index, ifaddr_str,
e, safe_strerror(e));
errno = e;
return -2;
}
return 0;
}
int pim_mroute_del_vif(int vif_index)
{
struct vifctl vc;
int err;
if (PIM_MROUTE_IS_DISABLED) {
zlog_warn("%s: global multicast is disabled",
__PRETTY_FUNCTION__);
return -1;
}
memset(&vc, 0, sizeof(vc));
vc.vifc_vifi = vif_index;
err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
if (err) {
int e = errno;
zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
__FILE__, __PRETTY_FUNCTION__,
qpim_mroute_socket_fd, vif_index,
e, safe_strerror(e));
errno = e;
return -2;
}
return 0;
}
int pim_mroute_add(struct mfcctl *mc)
{
int err;
qpim_mroute_add_last = pim_time_monotonic_sec();
++qpim_mroute_add_events;
if (PIM_MROUTE_IS_DISABLED) {
zlog_warn("%s: global multicast is disabled",
__PRETTY_FUNCTION__);
return -1;
}
err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
mc, sizeof(*mc));
if (err) {
int e = errno;
zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
__FILE__, __PRETTY_FUNCTION__,
qpim_mroute_socket_fd,
e, safe_strerror(e));
errno = e;
return -2;
}
return 0;
}
int pim_mroute_del(struct mfcctl *mc)
{
int err;
qpim_mroute_del_last = pim_time_monotonic_sec();
++qpim_mroute_del_events;
if (PIM_MROUTE_IS_DISABLED) {
zlog_warn("%s: global multicast is disabled",
__PRETTY_FUNCTION__);
return -1;
}
err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, mc, sizeof(*mc));
if (err) {
int e = errno;
zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
__FILE__, __PRETTY_FUNCTION__,
qpim_mroute_socket_fd,
e, safe_strerror(e));
errno = e;
return -2;
}
return 0;
}

173
pimd/pim_mroute.h Normal file
View file

@ -0,0 +1,173 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_MROUTE_H
#define PIM_MROUTE_H
/*
For msghdr.msg_control in Solaris 10
*/
#ifndef _XPG4_2
#define _XPG4_2
#endif
#ifndef __EXTENSIONS__
#define __EXTENSIONS__
#endif
#include <netinet/in.h>
#ifdef HAVE_NETINET_IP_MROUTE_H
#include <netinet/ip_mroute.h>
#endif
#define PIM_MROUTE_MIN_TTL (1)
/*
Below: from <linux/mroute.h>
*/
#ifndef MAXVIFS
#define MAXVIFS (32)
#endif
#ifndef SIOCGETVIFCNT
#define SIOCGETVIFCNT SIOCPROTOPRIVATE /* IP protocol privates */
#define SIOCGETSGCNT (SIOCPROTOPRIVATE+1)
#define SIOCGETRPF (SIOCPROTOPRIVATE+2)
#endif
#ifndef MRT_INIT
#define MRT_BASE 200
#define MRT_INIT (MRT_BASE) /* Activate the kernel mroute code */
#define MRT_DONE (MRT_BASE+1) /* Shutdown the kernel mroute */
#define MRT_ADD_VIF (MRT_BASE+2) /* Add a virtual interface */
#define MRT_DEL_VIF (MRT_BASE+3) /* Delete a virtual interface */
#define MRT_ADD_MFC (MRT_BASE+4) /* Add a multicast forwarding entry */
#define MRT_DEL_MFC (MRT_BASE+5) /* Delete a multicast forwarding entry */
#define MRT_VERSION (MRT_BASE+6) /* Get the kernel multicast version */
#define MRT_ASSERT (MRT_BASE+7) /* Activate PIM assert mode */
#define MRT_PIM (MRT_BASE+8) /* enable PIM code */
#endif
#ifndef HAVE_VIFI_T
typedef unsigned short vifi_t;
#endif
#ifndef HAVE_STRUCT_VIFCTL
struct vifctl {
vifi_t vifc_vifi; /* Index of VIF */
unsigned char vifc_flags; /* VIFF_ flags */
unsigned char vifc_threshold; /* ttl limit */
unsigned int vifc_rate_limit; /* Rate limiter values (NI) */
struct in_addr vifc_lcl_addr; /* Our address */
struct in_addr vifc_rmt_addr; /* IPIP tunnel addr */
};
#endif
#ifndef HAVE_STRUCT_MFCCTL
struct mfcctl {
struct in_addr mfcc_origin; /* Origin of mcast */
struct in_addr mfcc_mcastgrp; /* Group in question */
vifi_t mfcc_parent; /* Where it arrived */
unsigned char mfcc_ttls[MAXVIFS]; /* Where it is going */
unsigned int mfcc_pkt_cnt; /* pkt count for src-grp */
unsigned int mfcc_byte_cnt;
unsigned int mfcc_wrong_if;
int mfcc_expire;
};
#endif
/*
* Group count retrieval for mrouted
*/
/*
struct sioc_sg_req sgreq;
memset(&sgreq, 0, sizeof(sgreq));
memcpy(&sgreq.src, &source_addr, sizeof(sgreq.src));
memcpy(&sgreq.grp, &group_addr, sizeof(sgreq.grp));
ioctl(mrouter_s4, SIOCGETSGCNT, &sgreq);
*/
#ifndef HAVE_STRUCT_SIOC_SG_REQ
struct sioc_sg_req {
struct in_addr src;
struct in_addr grp;
unsigned long pktcnt;
unsigned long bytecnt;
unsigned long wrong_if;
};
#endif
/*
* To get vif packet counts
*/
/*
struct sioc_vif_req vreq;
memset(&vreq, 0, sizeof(vreq));
vreq.vifi = vif_index;
ioctl(mrouter_s4, SIOCGETVIFCNT, &vreq);
*/
#ifndef HAVE_STRUCT_SIOC_VIF_REQ
struct sioc_vif_req {
vifi_t vifi; /* Which iface */
unsigned long icount; /* In packets */
unsigned long ocount; /* Out packets */
unsigned long ibytes; /* In bytes */
unsigned long obytes; /* Out bytes */
};
#endif
/*
* Pseudo messages used by mrouted
*/
#ifndef IGMPMSG_NOCACHE
#define IGMPMSG_NOCACHE 1 /* Kern cache fill request to mrouted */
#define IGMPMSG_WRONGVIF 2 /* For PIM assert processing (unused) */
#define IGMPMSG_WHOLEPKT 3 /* For PIM Register processing */
#endif
#ifndef HAVE_STRUCT_IGMPMSG
struct igmpmsg
{
uint32_t unused1,unused2;
unsigned char im_msgtype; /* What is this */
unsigned char im_mbz; /* Must be zero */
unsigned char im_vif; /* Interface (this ought to be a vifi_t!) */
unsigned char unused3;
struct in_addr im_src,im_dst;
};
#endif
/*
Above: from <linux/mroute.h>
*/
int pim_mroute_socket_enable(void);
int pim_mroute_socket_disable(void);
int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr);
int pim_mroute_del_vif(int vif_index);
int pim_mroute_add(struct mfcctl *mc);
int pim_mroute_del(struct mfcctl *mc);
int pim_mroute_msg(int fd, const char *buf, int buf_size);
#endif /* PIM_MROUTE_H */

106
pimd/pim_msg.c Normal file
View file

@ -0,0 +1,106 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "pimd.h"
#include "pim_pim.h"
#include "pim_msg.h"
#include "pim_util.h"
void pim_msg_build_header(uint8_t *pim_msg, int pim_msg_size,
uint8_t pim_msg_type)
{
uint16_t checksum;
zassert(pim_msg_size >= PIM_PIM_MIN_LEN);
/*
* Write header
*/
*(uint8_t *) PIM_MSG_HDR_OFFSET_VERSION(pim_msg) = (PIM_PROTO_VERSION << 4) | pim_msg_type;
*(uint8_t *) PIM_MSG_HDR_OFFSET_RESERVED(pim_msg) = 0;
/*
* Compute checksum
*/
*(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = 0;
checksum = in_cksum(pim_msg, pim_msg_size);
*(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = checksum;
}
uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf,
int buf_size,
struct in_addr addr)
{
const int ENCODED_IPV4_UCAST_SIZE = 6;
if (buf_size < ENCODED_IPV4_UCAST_SIZE) {
return 0;
}
buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */
buf[1] = '\0'; /* native encoding */
memcpy(buf+2, &addr, sizeof(struct in_addr));
return buf + ENCODED_IPV4_UCAST_SIZE;
}
uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf,
int buf_size,
struct in_addr addr)
{
const int ENCODED_IPV4_GROUP_SIZE = 8;
if (buf_size < ENCODED_IPV4_GROUP_SIZE) {
return 0;
}
buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */
buf[1] = '\0'; /* native encoding */
buf[2] = '\0'; /* reserved */
buf[3] = 32; /* mask len */
memcpy(buf+4, &addr, sizeof(struct in_addr));
return buf + ENCODED_IPV4_GROUP_SIZE;
}
uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf,
int buf_size,
struct in_addr addr)
{
const int ENCODED_IPV4_SOURCE_SIZE = 8;
if (buf_size < ENCODED_IPV4_SOURCE_SIZE) {
return 0;
}
buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */
buf[1] = '\0'; /* native encoding */
buf[2] = 4; /* reserved = 0 | S bit = 1 | W bit = 0 | R bit = 0 */
buf[3] = 32; /* mask len */
memcpy(buf+4, &addr, sizeof(struct in_addr));
return buf + ENCODED_IPV4_SOURCE_SIZE;
}

52
pimd/pim_msg.h Normal file
View file

@ -0,0 +1,52 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_MSG_H
#define PIM_MSG_H
#include <netinet/in.h>
/*
Number Description
---------- ------------------
0 Reserved
1 IP (IP version 4)
2 IP6 (IP version 6)
From:
http://www.iana.org/assignments/address-family-numbers
*/
#define PIM_MSG_ADDRESS_FAMILY_IPV4 (1)
void pim_msg_build_header(uint8_t *pim_msg, int pim_msg_size,
uint8_t pim_msg_type);
uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf,
int buf_size,
struct in_addr addr);
uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf,
int buf_size,
struct in_addr addr);
uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf,
int buf_size,
struct in_addr addr);
#endif /* PIM_MSG_H */

719
pimd/pim_neighbor.c Normal file
View file

@ -0,0 +1,719 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "log.h"
#include "prefix.h"
#include "memory.h"
#include "pimd.h"
#include "pim_neighbor.h"
#include "pim_time.h"
#include "pim_str.h"
#include "pim_iface.h"
#include "pim_pim.h"
#include "pim_upstream.h"
#include "pim_ifchannel.h"
static void dr_election_by_addr(struct interface *ifp)
{
struct pim_interface *pim_ifp;
struct listnode *node;
struct pim_neighbor *neigh;
pim_ifp = ifp->info;
zassert(pim_ifp);
pim_ifp->pim_dr_addr = pim_ifp->primary_address;
if (PIM_DEBUG_PIM_TRACE) {
zlog_debug("%s: on interface %s",
__PRETTY_FUNCTION__,
ifp->name);
}
for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) {
if (ntohl(neigh->source_addr.s_addr) > ntohl(pim_ifp->pim_dr_addr.s_addr)) {
pim_ifp->pim_dr_addr = neigh->source_addr;
}
}
}
static void dr_election_by_pri(struct interface *ifp)
{
struct pim_interface *pim_ifp;
struct listnode *node;
struct pim_neighbor *neigh;
uint32_t dr_pri;
pim_ifp = ifp->info;
zassert(pim_ifp);
pim_ifp->pim_dr_addr = pim_ifp->primary_address;
dr_pri = pim_ifp->pim_dr_priority;
if (PIM_DEBUG_PIM_TRACE) {
zlog_debug("%s: dr pri %u on interface %s",
__PRETTY_FUNCTION__,
dr_pri, ifp->name);
}
for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) {
if (PIM_DEBUG_PIM_TRACE) {
zlog_info("%s: neigh pri %u addr %x if dr addr %x",
__PRETTY_FUNCTION__,
neigh->dr_priority,
ntohl(neigh->source_addr.s_addr),
ntohl(pim_ifp->pim_dr_addr.s_addr));
}
if (
(neigh->dr_priority > dr_pri) ||
(
(neigh->dr_priority == dr_pri) &&
(ntohl(neigh->source_addr.s_addr) > ntohl(pim_ifp->pim_dr_addr.s_addr))
)
) {
pim_ifp->pim_dr_addr = neigh->source_addr;
dr_pri = neigh->dr_priority;
}
}
}
/*
RFC 4601: 4.3.2. DR Election
A router's idea of the current DR on an interface can change when a
PIM Hello message is received, when a neighbor times out, or when a
router's own DR Priority changes.
*/
void pim_if_dr_election(struct interface *ifp)
{
struct pim_interface *pim_ifp = ifp->info;
struct in_addr old_dr_addr;
++pim_ifp->pim_dr_election_count;
old_dr_addr = pim_ifp->pim_dr_addr;
if (pim_ifp->pim_dr_num_nondrpri_neighbors) {
dr_election_by_addr(ifp);
}
else {
dr_election_by_pri(ifp);
}
/* DR changed ? */
if (old_dr_addr.s_addr != pim_ifp->pim_dr_addr.s_addr) {
/* if (PIM_DEBUG_PIM_EVENTS) */ {
char dr_old_str[100];
char dr_new_str[100];
pim_inet4_dump("<old_dr?>", old_dr_addr, dr_old_str, sizeof(dr_old_str));
pim_inet4_dump("<new_dr?>", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str));
zlog_debug("%s: DR was %s now is %s on interface %s",
__PRETTY_FUNCTION__,
dr_old_str, dr_new_str, ifp->name);
}
pim_ifp->pim_dr_election_last = pim_time_monotonic_sec(); /* timestamp */
++pim_ifp->pim_dr_election_changes;
pim_if_update_join_desired(pim_ifp);
pim_if_update_could_assert(ifp);
pim_if_update_assert_tracking_desired(ifp);
}
}
static void update_dr_priority(struct pim_neighbor *neigh,
pim_hello_options hello_options,
uint32_t dr_priority)
{
pim_hello_options will_set_pri; /* boolean */
pim_hello_options bit_flip; /* boolean */
pim_hello_options pri_change; /* boolean */
will_set_pri = PIM_OPTION_IS_SET(hello_options,
PIM_OPTION_MASK_DR_PRIORITY);
bit_flip =
(
will_set_pri !=
PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY)
);
if (bit_flip) {
struct pim_interface *pim_ifp = neigh->interface->info;
/* update num. of neighbors without dr_pri */
if (will_set_pri) {
--pim_ifp->pim_dr_num_nondrpri_neighbors;
}
else {
++pim_ifp->pim_dr_num_nondrpri_neighbors;
}
}
pri_change =
(
bit_flip
||
(neigh->dr_priority != dr_priority)
);
if (will_set_pri) {
neigh->dr_priority = dr_priority;
}
else {
neigh->dr_priority = 0; /* cosmetic unset */
}
if (pri_change) {
/*
RFC 4601: 4.3.2. DR Election
A router's idea of the current DR on an interface can change when a
PIM Hello message is received, when a neighbor times out, or when a
router's own DR Priority changes.
*/
pim_if_dr_election(neigh->interface); // router's own DR Priority changes
}
}
static int on_neighbor_timer(struct thread *t)
{
struct pim_neighbor *neigh;
struct interface *ifp;
char msg[100];
zassert(t);
neigh = THREAD_ARG(t);
zassert(neigh);
ifp = neigh->interface;
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
zlog_debug("Expired %d sec holdtime for neighbor %s on interface %s",
neigh->holdtime, src_str, ifp->name);
}
neigh->t_expire_timer = 0;
snprintf(msg, sizeof(msg), "%d-sec holdtime expired", neigh->holdtime);
pim_neighbor_delete(ifp, neigh, msg);
/*
RFC 4601: 4.3.2. DR Election
A router's idea of the current DR on an interface can change when a
PIM Hello message is received, when a neighbor times out, or when a
router's own DR Priority changes.
*/
pim_if_dr_election(ifp); // neighbor times out
return 0;
}
static void neighbor_timer_off(struct pim_neighbor *neigh)
{
if (PIM_DEBUG_PIM_TRACE) {
if (neigh->t_expire_timer) {
char src_str[100];
pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
zlog_debug("%s: cancelling timer for neighbor %s on %s",
__PRETTY_FUNCTION__,
src_str, neigh->interface->name);
}
}
THREAD_OFF(neigh->t_expire_timer);
zassert(!neigh->t_expire_timer);
}
void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime)
{
neigh->holdtime = holdtime;
neighbor_timer_off(neigh);
/*
0xFFFF is request for no holdtime
*/
if (neigh->holdtime == 0xFFFF) {
return;
}
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
zlog_debug("%s: starting %u sec timer for neighbor %s on %s",
__PRETTY_FUNCTION__,
neigh->holdtime, src_str, neigh->interface->name);
}
THREAD_TIMER_ON(master, neigh->t_expire_timer,
on_neighbor_timer,
neigh, neigh->holdtime);
}
static struct pim_neighbor *pim_neighbor_new(struct interface *ifp,
struct in_addr source_addr,
pim_hello_options hello_options,
uint16_t holdtime,
uint16_t propagation_delay,
uint16_t override_interval,
uint32_t dr_priority,
uint32_t generation_id,
struct list *addr_list)
{
struct pim_interface *pim_ifp;
struct pim_neighbor *neigh;
char src_str[100];
zassert(ifp);
pim_ifp = ifp->info;
zassert(pim_ifp);
neigh = XMALLOC(MTYPE_PIM_NEIGHBOR, sizeof(*neigh));
if (!neigh) {
zlog_err("%s: PIM XMALLOC(%zu) failure",
__PRETTY_FUNCTION__, sizeof(*neigh));
return 0;
}
neigh->creation = pim_time_monotonic_sec();
neigh->source_addr = source_addr;
neigh->hello_options = hello_options;
neigh->propagation_delay_msec = propagation_delay;
neigh->override_interval_msec = override_interval;
neigh->dr_priority = dr_priority;
neigh->generation_id = generation_id;
neigh->prefix_list = addr_list;
neigh->t_expire_timer = 0;
neigh->interface = ifp;
pim_neighbor_timer_reset(neigh, holdtime);
pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
if (PIM_DEBUG_PIM_EVENTS) {
zlog_debug("%s: creating PIM neighbor %s on interface %s",
__PRETTY_FUNCTION__,
src_str, ifp->name);
}
zlog_info("PIM NEIGHBOR UP: neighbor %s on interface %s",
src_str, ifp->name);
if (neigh->propagation_delay_msec > pim_ifp->pim_neighbors_highest_propagation_delay_msec) {
pim_ifp->pim_neighbors_highest_propagation_delay_msec = neigh->propagation_delay_msec;
}
if (neigh->override_interval_msec > pim_ifp->pim_neighbors_highest_override_interval_msec) {
pim_ifp->pim_neighbors_highest_override_interval_msec = neigh->override_interval_msec;
}
if (!PIM_OPTION_IS_SET(neigh->hello_options,
PIM_OPTION_MASK_LAN_PRUNE_DELAY)) {
/* update num. of neighbors without hello option lan_delay */
++pim_ifp->pim_number_of_nonlandelay_neighbors;
}
if (!PIM_OPTION_IS_SET(neigh->hello_options,
PIM_OPTION_MASK_DR_PRIORITY)) {
/* update num. of neighbors without hello option dr_pri */
++pim_ifp->pim_dr_num_nondrpri_neighbors;
}
/*
RFC 4601: 4.3.2. DR Election
A router's idea of the current DR on an interface can change when a
PIM Hello message is received, when a neighbor times out, or when a
router's own DR Priority changes.
*/
pim_if_dr_election(neigh->interface); // new neighbor -- should not trigger dr election...
/*
RFC 4601: 4.3.1. Sending Hello Messages
To allow new or rebooting routers to learn of PIM neighbors quickly,
when a Hello message is received from a new neighbor, or a Hello
message with a new GenID is received from an existing neighbor, a
new Hello message should be sent on this interface after a
randomized delay between 0 and Triggered_Hello_Delay.
*/
pim_hello_restart_triggered(neigh->interface);
return neigh;
}
static void delete_prefix_list(struct pim_neighbor *neigh)
{
if (neigh->prefix_list) {
#ifdef DUMP_PREFIX_LIST
struct listnode *p_node;
struct prefix *p;
char addr_str[10];
int list_size = neigh->prefix_list ? (int) listcount(neigh->prefix_list) : -1;
int i = 0;
for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, p_node, p)) {
pim_inet4_dump("<addr?>", p->u.prefix4, addr_str, sizeof(addr_str));
zlog_debug("%s: DUMP_PREFIX_LIST neigh=%x prefix_list=%x prefix=%x addr=%s [%d/%d]",
__PRETTY_FUNCTION__,
(unsigned) neigh, (unsigned) neigh->prefix_list, (unsigned) p,
addr_str, i, list_size);
++i;
}
#endif
list_delete(neigh->prefix_list);
neigh->prefix_list = 0;
}
}
void pim_neighbor_free(struct pim_neighbor *neigh)
{
zassert(!neigh->t_expire_timer);
delete_prefix_list(neigh);
XFREE(MTYPE_PIM_NEIGHBOR, neigh);
}
struct pim_neighbor *pim_neighbor_find(struct interface *ifp,
struct in_addr source_addr)
{
struct pim_interface *pim_ifp;
struct listnode *node;
struct pim_neighbor *neigh;
pim_ifp = ifp->info;
zassert(pim_ifp);
for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) {
if (source_addr.s_addr == neigh->source_addr.s_addr) {
return neigh;
}
}
return 0;
}
struct pim_neighbor *pim_neighbor_add(struct interface *ifp,
struct in_addr source_addr,
pim_hello_options hello_options,
uint16_t holdtime,
uint16_t propagation_delay,
uint16_t override_interval,
uint32_t dr_priority,
uint32_t generation_id,
struct list *addr_list)
{
struct pim_interface *pim_ifp;
struct pim_neighbor *neigh;
neigh = pim_neighbor_new(ifp, source_addr,
hello_options,
holdtime,
propagation_delay,
override_interval,
dr_priority,
generation_id,
addr_list);
if (!neigh) {
return 0;
}
pim_ifp = ifp->info;
zassert(pim_ifp);
listnode_add(pim_ifp->pim_neighbor_list, neigh);
return neigh;
}
static uint16_t
find_neighbors_next_highest_propagation_delay_msec(struct interface *ifp,
struct pim_neighbor *highest_neigh)
{
struct pim_interface *pim_ifp;
struct listnode *neigh_node;
struct pim_neighbor *neigh;
uint16_t next_highest_delay_msec;
pim_ifp = ifp->info;
zassert(pim_ifp);
next_highest_delay_msec = pim_ifp->pim_propagation_delay_msec;
for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node, neigh)) {
if (neigh == highest_neigh)
continue;
if (neigh->propagation_delay_msec > next_highest_delay_msec)
next_highest_delay_msec = neigh->propagation_delay_msec;
}
return next_highest_delay_msec;
}
static uint16_t
find_neighbors_next_highest_override_interval_msec(struct interface *ifp,
struct pim_neighbor *highest_neigh)
{
struct pim_interface *pim_ifp;
struct listnode *neigh_node;
struct pim_neighbor *neigh;
uint16_t next_highest_interval_msec;
pim_ifp = ifp->info;
zassert(pim_ifp);
next_highest_interval_msec = pim_ifp->pim_override_interval_msec;
for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node, neigh)) {
if (neigh == highest_neigh)
continue;
if (neigh->override_interval_msec > next_highest_interval_msec)
next_highest_interval_msec = neigh->override_interval_msec;
}
return next_highest_interval_msec;
}
void pim_neighbor_delete(struct interface *ifp,
struct pim_neighbor *neigh,
const char *delete_message)
{
struct pim_interface *pim_ifp;
char src_str[100];
pim_ifp = ifp->info;
zassert(pim_ifp);
pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
zlog_info("PIM NEIGHBOR DOWN: neighbor %s on interface %s: %s",
src_str, ifp->name, delete_message);
neighbor_timer_off(neigh);
pim_if_assert_on_neighbor_down(ifp, neigh->source_addr);
if (!PIM_OPTION_IS_SET(neigh->hello_options,
PIM_OPTION_MASK_LAN_PRUNE_DELAY)) {
/* update num. of neighbors without hello option lan_delay */
--pim_ifp->pim_number_of_nonlandelay_neighbors;
}
if (!PIM_OPTION_IS_SET(neigh->hello_options,
PIM_OPTION_MASK_DR_PRIORITY)) {
/* update num. of neighbors without dr_pri */
--pim_ifp->pim_dr_num_nondrpri_neighbors;
}
zassert(neigh->propagation_delay_msec <= pim_ifp->pim_neighbors_highest_propagation_delay_msec);
zassert(neigh->override_interval_msec <= pim_ifp->pim_neighbors_highest_override_interval_msec);
if (pim_if_lan_delay_enabled(ifp)) {
/* will delete a neighbor with highest propagation delay? */
if (neigh->propagation_delay_msec == pim_ifp->pim_neighbors_highest_propagation_delay_msec) {
/* then find the next highest propagation delay */
pim_ifp->pim_neighbors_highest_propagation_delay_msec =
find_neighbors_next_highest_propagation_delay_msec(ifp, neigh);
}
/* will delete a neighbor with highest override interval? */
if (neigh->override_interval_msec == pim_ifp->pim_neighbors_highest_override_interval_msec) {
/* then find the next highest propagation delay */
pim_ifp->pim_neighbors_highest_override_interval_msec =
find_neighbors_next_highest_override_interval_msec(ifp, neigh);
}
}
if (PIM_DEBUG_PIM_TRACE) {
zlog_debug("%s: deleting PIM neighbor %s on interface %s",
__PRETTY_FUNCTION__,
src_str, ifp->name);
}
listnode_delete(pim_ifp->pim_neighbor_list, neigh);
pim_neighbor_free(neigh);
}
void pim_neighbor_delete_all(struct interface *ifp,
const char *delete_message)
{
struct pim_interface *pim_ifp;
struct listnode *neigh_node;
struct listnode *neigh_nextnode;
struct pim_neighbor *neigh;
pim_ifp = ifp->info;
zassert(pim_ifp);
for (ALL_LIST_ELEMENTS(pim_ifp->pim_neighbor_list, neigh_node,
neigh_nextnode, neigh)) {
pim_neighbor_delete(ifp, neigh, delete_message);
}
}
struct prefix *pim_neighbor_find_secondary(struct pim_neighbor *neigh,
struct in_addr addr)
{
struct listnode *node;
struct prefix *p;
if (!neigh->prefix_list)
return 0;
for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, node, p)) {
if (p->family == AF_INET) {
if (addr.s_addr == p->u.prefix4.s_addr) {
return p;
}
}
}
return 0;
}
/*
RFC 4601: 4.3.4. Maintaining Secondary Address Lists
All the advertised secondary addresses in received Hello messages
must be checked against those previously advertised by all other
PIM neighbors on that interface. If there is a conflict and the
same secondary address was previously advertised by another
neighbor, then only the most recently received mapping MUST be
maintained, and an error message SHOULD be logged to the
administrator in a rate-limited manner.
*/
static void delete_from_neigh_addr(struct interface *ifp,
struct list *addr_list,
struct in_addr neigh_addr)
{
struct listnode *addr_node;
struct prefix *addr;
struct pim_interface *pim_ifp;
pim_ifp = ifp->info;
zassert(pim_ifp);
zassert(addr_list);
/*
Scan secondary address list
*/
for (ALL_LIST_ELEMENTS_RO(addr_list, addr_node,
addr)) {
struct listnode *neigh_node;
struct pim_neighbor *neigh;
if (addr->family != AF_INET)
continue;
/*
Scan neighbors
*/
for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node,
neigh)) {
{
struct prefix *p = pim_neighbor_find_secondary(neigh, addr->u.prefix4);
if (p) {
char addr_str[100];
char this_neigh_str[100];
char other_neigh_str[100];
pim_inet4_dump("<addr?>", addr->u.prefix4, addr_str, sizeof(addr_str));
pim_inet4_dump("<neigh1?>", neigh_addr, this_neigh_str, sizeof(this_neigh_str));
pim_inet4_dump("<neigh2?>", neigh->source_addr, other_neigh_str, sizeof(other_neigh_str));
zlog_info("secondary addr %s recvd from neigh %s deleted from neigh %s on %s",
addr_str, this_neigh_str, other_neigh_str, ifp->name);
listnode_delete(neigh->prefix_list, p);
prefix_free(p);
}
}
} /* scan neighbors */
} /* scan addr list */
}
void pim_neighbor_update(struct pim_neighbor *neigh,
pim_hello_options hello_options,
uint16_t holdtime,
uint32_t dr_priority,
struct list *addr_list)
{
struct pim_interface *pim_ifp = neigh->interface->info;
/* Received holdtime ? */
if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
pim_neighbor_timer_reset(neigh, holdtime);
}
else {
pim_neighbor_timer_reset(neigh, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
}
#ifdef DUMP_PREFIX_LIST
zlog_debug("%s: DUMP_PREFIX_LIST old_prefix_list=%x old_size=%d new_prefix_list=%x new_size=%d",
__PRETTY_FUNCTION__,
(unsigned) neigh->prefix_list,
neigh->prefix_list ? (int) listcount(neigh->prefix_list) : -1,
(unsigned) addr_list,
addr_list ? (int) listcount(addr_list) : -1);
#endif
if (neigh->prefix_list == addr_list) {
if (addr_list) {
zlog_err("%s: internal error: trying to replace same prefix list=%p",
__PRETTY_FUNCTION__, (void *) addr_list);
}
}
else {
/* Delete existing secondary address list */
delete_prefix_list(neigh);
}
if (addr_list) {
delete_from_neigh_addr(neigh->interface, addr_list, neigh->source_addr);
}
/* Replace secondary address list */
neigh->prefix_list = addr_list;
update_dr_priority(neigh,
hello_options,
dr_priority);
/*
Copy flags
*/
neigh->hello_options = hello_options;
}

74
pimd/pim_neighbor.h Normal file
View file

@ -0,0 +1,74 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_NEIGHBOR_H
#define PIM_NEIGHBOR_H
#include <zebra.h>
#include "if.h"
#include "linklist.h"
#include "pim_tlv.h"
struct pim_neighbor {
int64_t creation; /* timestamp of creation */
struct in_addr source_addr;
pim_hello_options hello_options;
uint16_t holdtime;
uint16_t propagation_delay_msec;
uint16_t override_interval_msec;
uint32_t dr_priority;
uint32_t generation_id;
struct list *prefix_list; /* list of struct prefix */
struct thread *t_expire_timer;
struct interface *interface;
};
void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime);
void pim_neighbor_free(struct pim_neighbor *neigh);
struct pim_neighbor *pim_neighbor_find(struct interface *ifp,
struct in_addr source_addr);
struct pim_neighbor *pim_neighbor_add(struct interface *ifp,
struct in_addr source_addr,
pim_hello_options hello_options,
uint16_t holdtime,
uint16_t propagation_delay,
uint16_t override_interval,
uint32_t dr_priority,
uint32_t generation_id,
struct list *addr_list);
void pim_neighbor_delete(struct interface *ifp,
struct pim_neighbor *neigh,
const char *delete_message);
void pim_neighbor_delete_all(struct interface *ifp,
const char *delete_message);
void pim_neighbor_update(struct pim_neighbor *neigh,
pim_hello_options hello_options,
uint16_t holdtime,
uint32_t dr_priority,
struct list *addr_list);
struct prefix *pim_neighbor_find_secondary(struct pim_neighbor *neigh,
struct in_addr addr);
void pim_if_dr_election(struct interface *ifp);
#endif /* PIM_NEIGHBOR_H */

140
pimd/pim_oil.c Normal file
View file

@ -0,0 +1,140 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "log.h"
#include "memory.h"
#include "linklist.h"
#include "pimd.h"
#include "pim_oil.h"
#include "pim_str.h"
#include "pim_iface.h"
void pim_channel_oil_free(struct channel_oil *c_oil)
{
XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
}
static void pim_channel_oil_delete(struct channel_oil *c_oil)
{
/*
notice that listnode_delete() can't be moved
into pim_channel_oil_free() because the later is
called by list_delete_all_node()
*/
listnode_delete(qpim_channel_oil_list, c_oil);
pim_channel_oil_free(c_oil);
}
static struct channel_oil *channel_oil_new(struct in_addr group_addr,
struct in_addr source_addr,
int input_vif_index)
{
struct channel_oil *c_oil;
struct interface *ifp_in;
ifp_in = pim_if_find_by_vif_index(input_vif_index);
if (!ifp_in) {
/* warning only */
char group_str[100];
char source_str[100];
pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
__PRETTY_FUNCTION__,
source_str, group_str, input_vif_index);
}
c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
if (!c_oil) {
zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
return 0;
}
c_oil->oil.mfcc_mcastgrp = group_addr;
c_oil->oil.mfcc_origin = source_addr;
c_oil->oil.mfcc_parent = input_vif_index;
c_oil->oil_ref_count = 1;
zassert(c_oil->oil_size == 0);
return c_oil;
}
static struct channel_oil *pim_add_channel_oil(struct in_addr group_addr,
struct in_addr source_addr,
int input_vif_index)
{
struct channel_oil *c_oil;
c_oil = channel_oil_new(group_addr, source_addr, input_vif_index);
if (!c_oil) {
zlog_warn("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
return 0;
}
listnode_add(qpim_channel_oil_list, c_oil);
return c_oil;
}
static struct channel_oil *pim_find_channel_oil(struct in_addr group_addr,
struct in_addr source_addr)
{
struct listnode *node;
struct channel_oil *c_oil;
for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
if ((group_addr.s_addr == c_oil->oil.mfcc_mcastgrp.s_addr) &&
(source_addr.s_addr == c_oil->oil.mfcc_origin.s_addr))
return c_oil;
}
return 0;
}
struct channel_oil *pim_channel_oil_add(struct in_addr group_addr,
struct in_addr source_addr,
int input_vif_index)
{
struct channel_oil *c_oil;
c_oil = pim_find_channel_oil(group_addr, source_addr);
if (c_oil) {
++c_oil->oil_ref_count;
return c_oil;
}
return pim_add_channel_oil(group_addr, source_addr, input_vif_index);
}
void pim_channel_oil_del(struct channel_oil *c_oil)
{
--c_oil->oil_ref_count;
if (c_oil->oil_ref_count < 1) {
pim_channel_oil_delete(c_oil);
}
}

53
pimd/pim_oil.h Normal file
View file

@ -0,0 +1,53 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_OIL_H
#define PIM_OIL_H
#include "pim_mroute.h"
#define PIM_OIF_FLAG_PROTO_IGMP (1 << 0) /* bitmask 1 */
#define PIM_OIF_FLAG_PROTO_PIM (1 << 1) /* bitmask 2 */
#define PIM_OIF_FLAG_PROTO_ANY (3) /* bitmask (1 | 2) */
/*
qpim_channel_oil_list holds a list of struct channel_oil.
Each channel_oil.oil is used to control an (S,G) entry in the Kernel
Multicast Forwarding Cache.
*/
struct channel_oil {
struct mfcctl oil;
int oil_size;
int oil_ref_count;
time_t oif_creation[MAXVIFS];
uint32_t oif_flags[MAXVIFS];
};
void pim_channel_oil_free(struct channel_oil *c_oil);
struct channel_oil *pim_channel_oil_add(struct in_addr group_addr,
struct in_addr source_addr,
int input_vif_index);
void pim_channel_oil_del(struct channel_oil *c_oil);
#endif /* PIM_OIL_H */

742
pimd/pim_pim.c Normal file
View file

@ -0,0 +1,742 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "log.h"
#include "thread.h"
#include "memory.h"
#include "pimd.h"
#include "pim_pim.h"
#include "pim_time.h"
#include "pim_iface.h"
#include "pim_sock.h"
#include "pim_str.h"
#include "pim_util.h"
#include "pim_tlv.h"
#include "pim_neighbor.h"
#include "pim_hello.h"
#include "pim_join.h"
#include "pim_assert.h"
#include "pim_msg.h"
#include "pim_rand.h"
static int on_pim_hello_send(struct thread *t);
static int pim_hello_send(struct interface *ifp,
uint16_t holdtime);
static void sock_close(struct interface *ifp)
{
struct pim_interface *pim_ifp = ifp->info;
if (PIM_DEBUG_PIM_TRACE) {
if (pim_ifp->t_pim_sock_read) {
zlog_debug("Cancelling READ event for PIM socket fd=%d on interface %s",
pim_ifp->pim_sock_fd,
ifp->name);
}
}
THREAD_OFF(pim_ifp->t_pim_sock_read);
if (PIM_DEBUG_PIM_TRACE) {
if (pim_ifp->t_pim_hello_timer) {
zlog_debug("Cancelling PIM hello timer for interface %s",
ifp->name);
}
}
THREAD_OFF(pim_ifp->t_pim_hello_timer);
if (PIM_DEBUG_PIM_TRACE) {
zlog_debug("Deleting PIM socket fd=%d on interface %s",
pim_ifp->pim_sock_fd, ifp->name);
}
if (close(pim_ifp->pim_sock_fd)) {
zlog_warn("Failure closing PIM socket fd=%d on interface %s: errno=%d: %s",
pim_ifp->pim_sock_fd, ifp->name,
errno, safe_strerror(errno));
}
pim_ifp->pim_sock_fd = -1;
pim_ifp->pim_sock_creation = 0;
zassert(pim_ifp->pim_sock_fd < 0);
zassert(!pim_ifp->t_pim_sock_read);
zassert(!pim_ifp->t_pim_hello_timer);
zassert(!pim_ifp->pim_sock_creation);
}
void pim_sock_delete(struct interface *ifp, const char *delete_message)
{
zlog_info("PIM INTERFACE DOWN: on interface %s: %s",
ifp->name, delete_message);
if (!ifp->info) {
zlog_err("%s: %s: but PIM not enabled on interface %s (!)",
__PRETTY_FUNCTION__, delete_message, ifp->name);
return;
}
/*
RFC 4601: 4.3.1. Sending Hello Messages
Before an interface goes down or changes primary IP address, a Hello
message with a zero HoldTime should be sent immediately (with the
old IP address if the IP address changed).
*/
pim_hello_send(ifp, 0 /* zero-sec holdtime */);
pim_neighbor_delete_all(ifp, delete_message);
sock_close(ifp);
}
int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len)
{
struct ip *ip_hdr;
size_t ip_hlen; /* ip header length in bytes */
char src_str[100];
char dst_str[100];
uint8_t *pim_msg;
int pim_msg_len;
uint8_t pim_version;
uint8_t pim_type;
uint16_t pim_checksum; /* received checksum */
uint16_t checksum; /* computed checksum */
struct pim_neighbor *neigh;
if (!ifp->info) {
zlog_warn("%s: PIM not enabled on interface %s",
__PRETTY_FUNCTION__, ifp->name);
return -1;
}
if (len < sizeof(*ip_hdr)) {
zlog_warn("PIM packet size=%zu shorter than minimum=%zu",
len, sizeof(*ip_hdr));
return -1;
}
ip_hdr = (struct ip *) buf;
pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
pim_inet4_dump("<dst?>", ip_hdr->ip_dst, dst_str, sizeof(dst_str));
ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
if (PIM_DEBUG_PIM_PACKETS) {
zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
src_str, dst_str, ifp->name, len, ip_hlen, ip_hdr->ip_p);
}
if (ip_hdr->ip_p != PIM_IP_PROTO_PIM) {
zlog_warn("IP packet protocol=%d is not PIM=%d",
ip_hdr->ip_p, PIM_IP_PROTO_PIM);
return -1;
}
if (ip_hlen < PIM_IP_HEADER_MIN_LEN) {
zlog_warn("IP packet header size=%zu shorter than minimum=%d",
ip_hlen, PIM_IP_HEADER_MIN_LEN);
return -1;
}
if (ip_hlen > PIM_IP_HEADER_MAX_LEN) {
zlog_warn("IP packet header size=%zu greater than maximum=%d",
ip_hlen, PIM_IP_HEADER_MAX_LEN);
return -1;
}
pim_msg = buf + ip_hlen;
pim_msg_len = len - ip_hlen;
if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_len);
}
if (pim_msg_len < PIM_PIM_MIN_LEN) {
zlog_warn("PIM message size=%d shorter than minimum=%d",
pim_msg_len, PIM_PIM_MIN_LEN);
return -1;
}
pim_version = PIM_MSG_HDR_GET_VERSION(pim_msg);
pim_type = PIM_MSG_HDR_GET_TYPE(pim_msg);
if (pim_version != PIM_PROTO_VERSION) {
zlog_warn("Ignoring PIM pkt from %s with unsupported version: %d",
ifp->name, pim_version);
return -1;
}
/* save received checksum */
pim_checksum = PIM_MSG_HDR_GET_CHECKSUM(pim_msg);
/* for computing checksum */
*(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = 0;
checksum = in_cksum(pim_msg, pim_msg_len);
if (checksum != pim_checksum) {
zlog_warn("Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
ifp->name, pim_checksum, checksum);
return -1;
}
if (PIM_DEBUG_PIM_PACKETS) {
zlog_debug("Recv PIM packet from %s to %s on %s: ttl=%d pim_version=%d pim_type=%d pim_msg_size=%d checksum=%x",
src_str, dst_str, ifp->name, ip_hdr->ip_ttl,
pim_version, pim_type, pim_msg_len, checksum);
}
if (pim_type == PIM_MSG_TYPE_HELLO) {
int result = pim_hello_recv(ifp,
ip_hdr->ip_src,
pim_msg + PIM_MSG_HEADER_LEN,
pim_msg_len - PIM_MSG_HEADER_LEN);
if (!result) {
pim_if_dr_election(ifp); /* PIM Hello message is received */
}
return result;
}
neigh = pim_neighbor_find(ifp, ip_hdr->ip_src);
if (!neigh) {
zlog_warn("%s %s: non-hello PIM message type=%d from non-neighbor %s on %s",
__FILE__, __PRETTY_FUNCTION__,
pim_type, src_str, ifp->name);
return -1;
}
switch (pim_type) {
case PIM_MSG_TYPE_JOIN_PRUNE:
return pim_joinprune_recv(ifp, neigh,
ip_hdr->ip_src,
pim_msg + PIM_MSG_HEADER_LEN,
pim_msg_len - PIM_MSG_HEADER_LEN);
case PIM_MSG_TYPE_ASSERT:
return pim_assert_recv(ifp, neigh,
ip_hdr->ip_src,
pim_msg + PIM_MSG_HEADER_LEN,
pim_msg_len - PIM_MSG_HEADER_LEN);
default:
zlog_warn("%s %s: unsupported PIM message type=%d from %s on %s",
__FILE__, __PRETTY_FUNCTION__,
pim_type, src_str, ifp->name);
}
return -1;
}
static void pim_sock_read_on(struct interface *ifp);
static int pim_sock_read(struct thread *t)
{
struct interface *ifp;
struct pim_interface *pim_ifp;
int fd;
struct sockaddr_in from;
struct sockaddr_in to;
socklen_t fromlen = sizeof(from);
socklen_t tolen = sizeof(to);
uint8_t buf[PIM_PIM_BUFSIZE_READ];
int len;
int ifindex = -1;
int result = -1; /* defaults to bad */
zassert(t);
ifp = THREAD_ARG(t);
zassert(ifp);
fd = THREAD_FD(t);
pim_ifp = ifp->info;
zassert(pim_ifp);
zassert(fd == pim_ifp->pim_sock_fd);
len = pim_socket_recvfromto(fd, buf, sizeof(buf),
&from, &fromlen,
&to, &tolen,
&ifindex);
if (len < 0) {
zlog_warn("Failure receiving IP PIM packet on fd=%d: errno=%d: %s",
fd, errno, safe_strerror(errno));
goto done;
}
if (PIM_DEBUG_PIM_PACKETS) {
char from_str[100];
char to_str[100];
if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
sprintf(from_str, "<from?>");
if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
sprintf(to_str, "<to?>");
zlog_debug("Recv IP PIM pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
len, from_str, to_str, fd, ifindex, ifp->ifindex);
}
if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
pim_pkt_dump(__PRETTY_FUNCTION__, buf, len);
}
#ifdef PIM_CHECK_RECV_IFINDEX_SANITY
/* ifindex sanity check */
if (ifindex != (int) ifp->ifindex) {
char from_str[100];
char to_str[100];
struct interface *recv_ifp;
if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
sprintf(from_str, "<from?>");
if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
sprintf(to_str, "<to?>");
recv_ifp = if_lookup_by_index(ifindex);
if (recv_ifp) {
zassert(ifindex == (int) recv_ifp->ifindex);
}
#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
zlog_warn("Interface mismatch: recv PIM pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
from_str, to_str, fd,
ifindex, recv_ifp ? recv_ifp->name : "<if-notfound>",
ifp->ifindex, ifp->name);
#endif
goto done;
}
#endif
int fail = pim_pim_packet(ifp, buf, len);
if (fail) {
zlog_warn("%s: pim_pim_packet() return=%d",
__PRETTY_FUNCTION__, fail);
goto done;
}
result = 0; /* good */
done:
pim_sock_read_on(ifp);
if (result) {
++pim_ifp->pim_ifstat_hello_recvfail;
}
return result;
}
static void pim_sock_read_on(struct interface *ifp)
{
struct pim_interface *pim_ifp;
zassert(ifp);
zassert(ifp->info);
pim_ifp = ifp->info;
if (PIM_DEBUG_PIM_TRACE) {
zlog_debug("Scheduling READ event on PIM socket fd=%d",
pim_ifp->pim_sock_fd);
}
pim_ifp->t_pim_sock_read = 0;
zassert(!pim_ifp->t_pim_sock_read);
THREAD_READ_ON(master, pim_ifp->t_pim_sock_read, pim_sock_read, ifp,
pim_ifp->pim_sock_fd);
}
static int pim_sock_open(struct in_addr ifaddr, int ifindex)
{
int fd;
fd = pim_socket_mcast(IPPROTO_PIM, ifaddr, 0 /* loop=false */);
if (fd < 0)
return -1;
if (pim_socket_join(fd, qpim_all_pim_routers_addr, ifaddr, ifindex)) {
return -2;
}
return fd;
}
void pim_ifstat_reset(struct interface *ifp)
{
struct pim_interface *pim_ifp;
zassert(ifp);
pim_ifp = ifp->info;
if (!pim_ifp) {
return;
}
pim_ifp->pim_ifstat_start = pim_time_monotonic_sec();
pim_ifp->pim_ifstat_hello_sent = 0;
pim_ifp->pim_ifstat_hello_sendfail = 0;
pim_ifp->pim_ifstat_hello_recv = 0;
pim_ifp->pim_ifstat_hello_recvfail = 0;
}
void pim_sock_reset(struct interface *ifp)
{
struct pim_interface *pim_ifp;
zassert(ifp);
zassert(ifp->info);
pim_ifp = ifp->info;
pim_ifp->primary_address = pim_find_primary_addr(ifp);
pim_ifp->pim_sock_fd = -1;
pim_ifp->pim_sock_creation = 0;
pim_ifp->t_pim_sock_read = 0;
pim_ifp->t_pim_hello_timer = 0;
pim_ifp->pim_hello_period = PIM_DEFAULT_HELLO_PERIOD;
pim_ifp->pim_default_holdtime = -1; /* unset: means 3.5 * pim_hello_period */
pim_ifp->pim_triggered_hello_delay = PIM_DEFAULT_TRIGGERED_HELLO_DELAY;
pim_ifp->pim_dr_priority = PIM_DEFAULT_DR_PRIORITY;
pim_ifp->pim_propagation_delay_msec = PIM_DEFAULT_PROPAGATION_DELAY_MSEC;
pim_ifp->pim_override_interval_msec = PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC;
if (PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION) {
PIM_IF_DO_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options);
}
else {
PIM_IF_DONT_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options);
}
/* neighbors without lan_delay */
pim_ifp->pim_number_of_nonlandelay_neighbors = 0;
pim_ifp->pim_neighbors_highest_propagation_delay_msec = 0;
pim_ifp->pim_neighbors_highest_override_interval_msec = 0;
/* DR Election */
pim_ifp->pim_dr_election_last = 0; /* timestamp */
pim_ifp->pim_dr_election_count = 0;
pim_ifp->pim_dr_election_changes = 0;
pim_ifp->pim_dr_num_nondrpri_neighbors = 0; /* neighbors without dr_pri */
pim_ifp->pim_dr_addr = pim_ifp->primary_address;
pim_ifstat_reset(ifp);
}
int pim_msg_send(int fd,
struct in_addr dst,
uint8_t *pim_msg,
int pim_msg_size,
const char *ifname)
{
ssize_t sent;
struct sockaddr_in to;
socklen_t tolen;
if (PIM_DEBUG_PIM_PACKETS) {
char dst_str[100];
pim_inet4_dump("<dst?>", dst, dst_str, sizeof(dst_str));
zlog_debug("%s: to %s on %s: msg_size=%d checksum=%x",
__PRETTY_FUNCTION__,
dst_str, ifname, pim_msg_size,
*(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg));
}
#if 0
memset(&to, 0, sizeof(to));
#endif
to.sin_family = AF_INET;
to.sin_addr = dst;
#if 0
to.sin_port = htons(0);
#endif
tolen = sizeof(to);
if (PIM_DEBUG_PIM_PACKETDUMP_SEND) {
pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_size);
}
sent = sendto(fd, pim_msg, pim_msg_size, MSG_DONTWAIT, &to, tolen);
if (sent != (ssize_t) pim_msg_size) {
int e = errno;
char dst_str[100];
pim_inet4_dump("<dst?>", dst, dst_str, sizeof(dst_str));
if (sent < 0) {
zlog_warn("%s: sendto() failure to %s on %s: fd=%d msg_size=%d: errno=%d: %s",
__PRETTY_FUNCTION__,
dst_str, ifname, fd, pim_msg_size,
e, safe_strerror(e));
}
else {
zlog_warn("%s: sendto() partial to %s on %s: fd=%d msg_size=%d: sent=%zd",
__PRETTY_FUNCTION__,
dst_str, ifname, fd,
pim_msg_size, sent);
}
return -1;
}
return 0;
}
static int hello_send(struct interface *ifp,
uint16_t holdtime)
{
uint8_t pim_msg[PIM_PIM_BUFSIZE_WRITE];
struct pim_interface *pim_ifp;
int pim_tlv_size;
int pim_msg_size;
pim_ifp = ifp->info;
if (PIM_DEBUG_PIM_PACKETS || PIM_DEBUG_PIM_HELLO) {
char dst_str[100];
pim_inet4_dump("<dst?>", qpim_all_pim_routers_addr, dst_str, sizeof(dst_str));
zlog_debug("%s: to %s on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%d",
__PRETTY_FUNCTION__,
dst_str, ifp->name,
holdtime,
pim_ifp->pim_propagation_delay_msec, pim_ifp->pim_override_interval_msec,
PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options),
pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id,
listcount(ifp->connected));
}
pim_tlv_size = pim_hello_build_tlv(ifp->name,
pim_msg + PIM_PIM_MIN_LEN,
sizeof(pim_msg) - PIM_PIM_MIN_LEN,
holdtime,
pim_ifp->pim_dr_priority,
pim_ifp->pim_generation_id,
pim_ifp->pim_propagation_delay_msec,
pim_ifp->pim_override_interval_msec,
PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options),
ifp->connected);
if (pim_tlv_size < 0) {
return -1;
}
pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN;
zassert(pim_msg_size >= PIM_PIM_MIN_LEN);
zassert(pim_msg_size <= PIM_PIM_BUFSIZE_WRITE);
pim_msg_build_header(pim_msg, pim_msg_size,
PIM_MSG_TYPE_HELLO);
if (pim_msg_send(pim_ifp->pim_sock_fd,
qpim_all_pim_routers_addr,
pim_msg,
pim_msg_size,
ifp->name)) {
zlog_warn("%s: could not send PIM message on interface %s",
__PRETTY_FUNCTION__, ifp->name);
return -2;
}
return 0;
}
static int pim_hello_send(struct interface *ifp,
uint16_t holdtime)
{
struct pim_interface *pim_ifp;
zassert(ifp);
pim_ifp = ifp->info;
zassert(pim_ifp);
if (hello_send(ifp, holdtime)) {
++pim_ifp->pim_ifstat_hello_sendfail;
zlog_warn("Could not send PIM hello on interface %s",
ifp->name);
return -1;
}
++pim_ifp->pim_ifstat_hello_sent;
return 0;
}
static void hello_resched(struct interface *ifp)
{
struct pim_interface *pim_ifp;
zassert(ifp);
pim_ifp = ifp->info;
zassert(pim_ifp);
if (PIM_DEBUG_PIM_TRACE) {
zlog_debug("Rescheduling %d sec hello on interface %s",
pim_ifp->pim_hello_period, ifp->name);
}
THREAD_OFF(pim_ifp->t_pim_hello_timer);
THREAD_TIMER_ON(master, pim_ifp->t_pim_hello_timer,
on_pim_hello_send,
ifp, pim_ifp->pim_hello_period);
}
/*
Periodic hello timer
*/
static int on_pim_hello_send(struct thread *t)
{
struct pim_interface *pim_ifp;
struct interface *ifp;
zassert(t);
ifp = THREAD_ARG(t);
zassert(ifp);
pim_ifp = ifp->info;
/*
* Schedule next hello
*/
pim_ifp->t_pim_hello_timer = 0;
hello_resched(ifp);
/*
* Send hello
*/
return pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
}
/*
RFC 4601: 4.3.1. Sending Hello Messages
Thus, if a router needs to send a Join/Prune or Assert message on an
interface on which it has not yet sent a Hello message with the
currently configured IP address, then it MUST immediately send the
relevant Hello message without waiting for the Hello Timer to
expire, followed by the Join/Prune or Assert message.
*/
void pim_hello_restart_now(struct interface *ifp)
{
struct pim_interface *pim_ifp;
zassert(ifp);
pim_ifp = ifp->info;
zassert(pim_ifp);
/*
* Reset next hello timer
*/
hello_resched(ifp);
/*
* Immediately send hello
*/
pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
}
/*
RFC 4601: 4.3.1. Sending Hello Messages
To allow new or rebooting routers to learn of PIM neighbors quickly,
when a Hello message is received from a new neighbor, or a Hello
message with a new GenID is received from an existing neighbor, a
new Hello message should be sent on this interface after a
randomized delay between 0 and Triggered_Hello_Delay.
*/
void pim_hello_restart_triggered(struct interface *ifp)
{
struct pim_interface *pim_ifp;
int triggered_hello_delay_msec;
int random_msec;
zassert(ifp);
pim_ifp = ifp->info;
zassert(pim_ifp);
triggered_hello_delay_msec = 1000 * pim_ifp->pim_triggered_hello_delay;
if (pim_ifp->t_pim_hello_timer) {
long remain_msec = pim_time_timer_remain_msec(pim_ifp->t_pim_hello_timer);
if (remain_msec <= triggered_hello_delay_msec) {
/* Rescheduling hello would increase the delay, then it's faster
to just wait for the scheduled periodic hello. */
return;
}
THREAD_OFF(pim_ifp->t_pim_hello_timer);
pim_ifp->t_pim_hello_timer = 0;
}
zassert(!pim_ifp->t_pim_hello_timer);
random_msec = pim_rand_next(0, triggered_hello_delay_msec);
if (PIM_DEBUG_PIM_EVENTS) {
zlog_debug("Scheduling %d msec triggered hello on interface %s",
random_msec, ifp->name);
}
THREAD_TIMER_MSEC_ON(master, pim_ifp->t_pim_hello_timer,
on_pim_hello_send,
ifp, random_msec);
}
int pim_sock_add(struct interface *ifp)
{
struct pim_interface *pim_ifp;
struct in_addr ifaddr;
pim_ifp = ifp->info;
zassert(pim_ifp);
if (pim_ifp->pim_sock_fd >= 0) {
zlog_warn("Can't recreate existing PIM socket fd=%d for interface %s",
pim_ifp->pim_sock_fd, ifp->name);
return -1;
}
ifaddr = pim_ifp->primary_address;
pim_ifp->pim_sock_fd = pim_sock_open(ifaddr, ifp->ifindex);
if (pim_ifp->pim_sock_fd < 0) {
zlog_warn("Could not open PIM socket on interface %s",
ifp->name);
return -2;
}
pim_ifp->t_pim_sock_read = 0;
pim_ifp->pim_sock_creation = pim_time_monotonic_sec();
pim_ifp->pim_generation_id = pim_rand() & (int64_t) 0xFFFFFFFF;
zlog_info("PIM INTERFACE UP: on interface %s ifindex=%d",
ifp->name, ifp->ifindex);
/*
* Start receiving PIM messages
*/
pim_sock_read_on(ifp);
/*
* Start sending PIM hello's
*/
pim_hello_restart_triggered(ifp);
return 0;
}

71
pimd/pim_pim.h Normal file
View file

@ -0,0 +1,71 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_PIM_H
#define PIM_PIM_H
#include <zebra.h>
#include "if.h"
#define PIM_PIM_BUFSIZE_READ (20000)
#define PIM_PIM_BUFSIZE_WRITE (20000)
#define PIM_NEXTHOP_IFINDEX_TAB_SIZE (20)
#define PIM_DEFAULT_HELLO_PERIOD (30) /* seconds, RFC 4601: 4.11 */
#define PIM_DEFAULT_TRIGGERED_HELLO_DELAY (5) /* seconds, RFC 4601: 4.11 */
#define PIM_DEFAULT_DR_PRIORITY (1) /* RFC 4601: 4.3.1 */
#define PIM_DEFAULT_PROPAGATION_DELAY_MSEC (500) /* RFC 4601: 4.11. Timer Values */
#define PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC (2500) /* RFC 4601: 4.11. Timer Values */
#define PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION (0) /* boolean */
#define PIM_DEFAULT_T_PERIODIC (60) /* RFC 4601: 4.11. Timer Values */
#define PIM_MSG_TYPE_HELLO (0)
#define PIM_MSG_TYPE_JOIN_PRUNE (3)
#define PIM_MSG_TYPE_ASSERT (5)
#define PIM_MSG_HDR_OFFSET_VERSION(pim_msg) (pim_msg)
#define PIM_MSG_HDR_OFFSET_TYPE(pim_msg) (pim_msg)
#define PIM_MSG_HDR_OFFSET_RESERVED(pim_msg) (((char *)(pim_msg)) + 1)
#define PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) (((char *)(pim_msg)) + 2)
#define PIM_MSG_HDR_GET_VERSION(pim_msg) ((*(uint8_t*) PIM_MSG_HDR_OFFSET_VERSION(pim_msg)) >> 4)
#define PIM_MSG_HDR_GET_TYPE(pim_msg) ((*(uint8_t*) PIM_MSG_HDR_OFFSET_TYPE(pim_msg)) & 0xF)
#define PIM_MSG_HDR_GET_CHECKSUM(pim_msg) (*(uint16_t*) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg))
void pim_ifstat_reset(struct interface *ifp);
void pim_sock_reset(struct interface *ifp);
int pim_sock_add(struct interface *ifp);
void pim_sock_delete(struct interface *ifp, const char *delete_message);
void pim_hello_restart_now(struct interface *ifp);
void pim_hello_restart_triggered(struct interface *ifp);
int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len);
int pim_msg_send(int fd,
struct in_addr dst,
uint8_t *pim_msg,
int pim_msg_size,
const char *ifname);
#endif /* PIM_PIM_H */

60
pimd/pim_rand.c Normal file
View file

@ -0,0 +1,60 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include "pim_rand.h"
#include "pim_time.h"
/* Quick and dirty random number generator from NUMERICAL RECIPES IN C:
THE ART OF SCIENTIFIC COMPUTING (ISBN 0-521-43108-5). */
/* BEWARE: '_qseed_' is assigned! */
#define QRANDOM(_qseed_) ((_qseed_) = (((_qseed_) * 1664525L) + 1013904223L))
static long qpim_rand_seed;
void pim_rand_init()
{
qpim_rand_seed = pim_time_monotonic_sec() ^ getpid();
}
long pim_rand()
{
return QRANDOM(qpim_rand_seed);
}
int pim_rand_next(int min, int max)
{
long rand;
assert(min <= max);
/* FIXME better random generator ? */
rand = QRANDOM(qpim_rand_seed);
if (rand < 0)
rand = -rand;
rand = rand % (1 + max - min) + min;
assert(rand >= min);
assert(rand <= max);
return rand;
}

30
pimd/pim_rand.h Normal file
View file

@ -0,0 +1,30 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_RAND_H
#define PIM_RAND_H
void pim_rand_init(void);
long pim_rand(void);
int pim_rand_next(int min, int max);
#endif /* PIM_RAND_H */

260
pimd/pim_rpf.c Normal file
View file

@ -0,0 +1,260 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "log.h"
#include "prefix.h"
#include "memory.h"
#include "pimd.h"
#include "pim_rpf.h"
#include "pim_pim.h"
#include "pim_str.h"
#include "pim_iface.h"
#include "pim_zlookup.h"
#include "pim_ifchannel.h"
static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up);
int pim_nexthop_lookup(struct pim_nexthop *nexthop,
struct in_addr addr)
{
struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE];
int num_ifindex;
struct interface *ifp;
int first_ifindex;
num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab,
PIM_NEXTHOP_IFINDEX_TAB_SIZE,
addr, PIM_NEXTHOP_LOOKUP_MAX);
if (num_ifindex < 1) {
char addr_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_warn("%s %s: could not find nexthop ifindex for address %s",
__FILE__, __PRETTY_FUNCTION__,
addr_str);
return -1;
}
first_ifindex = nexthop_tab[0].ifindex;
if (num_ifindex > 1) {
char addr_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_debug("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)",
__FILE__, __PRETTY_FUNCTION__,
num_ifindex, addr_str, first_ifindex);
/* debug warning only, do not return */
}
ifp = if_lookup_by_index(first_ifindex);
if (!ifp) {
char addr_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_warn("%s %s: could not find interface for ifindex %d (address %s)",
__FILE__, __PRETTY_FUNCTION__,
first_ifindex, addr_str);
return -2;
}
if (!ifp->info) {
char addr_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_warn("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)",
__PRETTY_FUNCTION__,
ifp->name, first_ifindex, addr_str);
/* debug warning only, do not return */
}
if (PIM_DEBUG_PIM_TRACE) {
char nexthop_str[100];
char addr_str[100];
pim_inet4_dump("<nexthop?>", nexthop_tab[0].nexthop_addr, nexthop_str, sizeof(nexthop_str));
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_debug("%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d",
__FILE__, __PRETTY_FUNCTION__,
nexthop_str, addr_str,
ifp->name, first_ifindex,
nexthop_tab[0].route_metric,
nexthop_tab[0].protocol_distance);
}
/* update nextop data */
nexthop->interface = ifp;
nexthop->mrib_nexthop_addr = nexthop_tab[0].nexthop_addr;
nexthop->mrib_metric_preference = nexthop_tab[0].protocol_distance;
nexthop->mrib_route_metric = nexthop_tab[0].route_metric;
return 0;
}
static int nexthop_mismatch(const struct pim_nexthop *nh1,
const struct pim_nexthop *nh2)
{
return (nh1->interface != nh2->interface)
||
(nh1->mrib_nexthop_addr.s_addr != nh2->mrib_nexthop_addr.s_addr)
||
(nh1->mrib_metric_preference != nh2->mrib_metric_preference)
||
(nh1->mrib_route_metric != nh2->mrib_route_metric);
}
enum pim_rpf_result pim_rpf_update(struct pim_upstream *up,
struct in_addr *old_rpf_addr)
{
struct in_addr save_rpf_addr;
struct pim_nexthop save_nexthop;
struct pim_rpf *rpf = &up->rpf;
save_nexthop = rpf->source_nexthop; /* detect change in pim_nexthop */
save_rpf_addr = rpf->rpf_addr; /* detect change in RPF'(S,G) */
if (pim_nexthop_lookup(&rpf->source_nexthop,
up->source_addr)) {
return PIM_RPF_FAILURE;
}
rpf->rpf_addr = pim_rpf_find_rpf_addr(up);
if (PIM_INADDR_IS_ANY(rpf->rpf_addr)) {
/* RPF'(S,G) not found */
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s %s: RPF'(%s,%s) not found: won't send join upstream",
__FILE__, __PRETTY_FUNCTION__,
src_str, grp_str);
/* warning only */
}
/* detect change in pim_nexthop */
if (nexthop_mismatch(&rpf->source_nexthop, &save_nexthop)) {
/* if (PIM_DEBUG_PIM_EVENTS) */ {
char src_str[100];
char grp_str[100];
char nhaddr_str[100];
pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
pim_inet4_dump("<addr?>", rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str));
zlog_debug("%s %s: (S,G)=(%s,%s) source nexthop now is: interface=%s address=%s pref=%d metric=%d",
__FILE__, __PRETTY_FUNCTION__,
src_str, grp_str,
rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>",
nhaddr_str,
rpf->source_nexthop.mrib_metric_preference,
rpf->source_nexthop.mrib_route_metric);
/* warning only */
}
pim_upstream_update_join_desired(up);
pim_upstream_update_could_assert(up);
pim_upstream_update_my_assert_metric(up);
}
/* detect change in RPF_interface(S) */
if (save_nexthop.interface != rpf->source_nexthop.interface) {
/* if (PIM_DEBUG_PIM_EVENTS) */ {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
zlog_debug("%s %s: (S,G)=(%s,%s) RPF_interface(S) changed from %s to %s",
__FILE__, __PRETTY_FUNCTION__,
src_str, grp_str,
save_nexthop.interface ? save_nexthop.interface->name : "<oldif?>",
rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<newif?>");
/* warning only */
}
pim_upstream_rpf_interface_changed(up, save_nexthop.interface);
}
/* detect change in RPF'(S,G) */
if (save_rpf_addr.s_addr != rpf->rpf_addr.s_addr) {
/* return old rpf to caller ? */
if (old_rpf_addr)
*old_rpf_addr = save_rpf_addr;
return PIM_RPF_CHANGED;
}
return PIM_RPF_OK;
}
/*
RFC 4601: 4.1.6. State Summarization Macros
neighbor RPF'(S,G) {
if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
return AssertWinner(S, G, RPF_interface(S) )
} else {
return NBR( RPF_interface(S), MRIB.next_hop( S ) )
}
}
RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
packets should be coming and to which joins should be sent on the RP
tree and SPT, respectively.
*/
static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up)
{
struct pim_ifchannel *rpf_ch;
struct pim_neighbor *neigh;
struct in_addr rpf_addr;
if (!up->rpf.source_nexthop.interface) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: missing RPF interface for upstream (S,G)=(%s,%s)",
__PRETTY_FUNCTION__,
src_str, grp_str);
rpf_addr.s_addr = PIM_NET_INADDR_ANY;
return rpf_addr;
}
rpf_ch = pim_ifchannel_find(up->rpf.source_nexthop.interface,
up->source_addr, up->group_addr);
if (rpf_ch) {
if (rpf_ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
return rpf_ch->ifassert_winner;
}
}
/* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
neigh = pim_if_find_neighbor(up->rpf.source_nexthop.interface,
up->rpf.source_nexthop.mrib_nexthop_addr);
if (neigh)
rpf_addr = neigh->source_addr;
else
rpf_addr.s_addr = PIM_NET_INADDR_ANY;
return rpf_addr;
}

36
pimd/pim_rpf.h Normal file
View file

@ -0,0 +1,36 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_RPF_H
#define PIM_RPF_H
#include <zebra.h>
#include "pim_upstream.h"
#include "pim_neighbor.h"
int pim_nexthop_lookup(struct pim_nexthop *nexthop,
struct in_addr addr);
enum pim_rpf_result pim_rpf_update(struct pim_upstream *up,
struct in_addr *old_rpf_addr);
#endif /* PIM_RPF_H */

86
pimd/pim_signals.c Normal file
View file

@ -0,0 +1,86 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <signal.h>
#include <zebra.h>
#include "sigevent.h"
#include "memory.h"
#include "log.h"
#include "pim_signals.h"
#include "pimd.h"
/*
* Signal handlers
*/
static void pim_sighup()
{
zlog_debug ("SIGHUP received, ignoring");
}
static void pim_sigint()
{
zlog_notice("Terminating on signal SIGINT");
pim_terminate();
exit(1);
}
static void pim_sigterm()
{
zlog_notice("Terminating on signal SIGTERM");
pim_terminate();
exit(1);
}
static void pim_sigusr1()
{
zlog_debug ("SIGUSR1 received");
zlog_rotate (NULL);
}
static struct quagga_signal_t pimd_signals[] =
{
{
.signal = SIGHUP,
.handler = &pim_sighup,
},
{
.signal = SIGUSR1,
.handler = &pim_sigusr1,
},
{
.signal = SIGINT,
.handler = &pim_sigint,
},
{
.signal = SIGTERM,
.handler = &pim_sigterm,
},
};
void pim_signals_init()
{
signal_init(master, array_size(pimd_signals), pimd_signals);
}

28
pimd/pim_signals.h Normal file
View file

@ -0,0 +1,28 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_SIGNALS_H
#define PIM_SIGNALS_H
void pim_signals_init(void);
#endif /* PIM_SIGNALS_H */

389
pimd/pim_sock.c Normal file
View file

@ -0,0 +1,389 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include "pim_mroute.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/igmp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#include <zebra.h>
#include "log.h"
#include "privs.h"
#include "pimd.h"
#include "pim_sock.h"
#include "pim_str.h"
#include "pim_igmp_join.h"
/* GLOBAL VARS */
extern struct zebra_privs_t pimd_privs;
int pim_socket_raw(int protocol)
{
int fd;
if ( pimd_privs.change (ZPRIVS_RAISE) )
zlog_err ("pim_sockek_raw: could not raise privs, %s",
safe_strerror (errno) );
fd = socket(AF_INET, SOCK_RAW, protocol);
if ( pimd_privs.change (ZPRIVS_LOWER) )
zlog_err ("pim_socket_raw: could not lower privs, %s",
safe_strerror (errno) );
if (fd < 0) {
zlog_warn("Could not create raw socket: errno=%d: %s",
errno, safe_strerror(errno));
return PIM_SOCK_ERR_SOCKET;
}
return fd;
}
int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop)
{
int fd;
fd = pim_socket_raw(protocol);
if (fd < 0) {
zlog_warn("Could not create multicast socket: errno=%d: %s",
errno, safe_strerror(errno));
return PIM_SOCK_ERR_SOCKET;
}
/* Needed to obtain destination address from recvmsg() */
{
#if defined(HAVE_IP_PKTINFO)
/* Linux IP_PKTINFO */
int opt = 1;
if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) {
zlog_warn("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
fd, errno, safe_strerror(errno));
}
#elif defined(HAVE_IP_RECVDSTADDR)
/* BSD IP_RECVDSTADDR */
int opt = 1;
if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
fd, errno, safe_strerror(errno));
}
#else
zlog_err("%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
__FILE__, __PRETTY_FUNCTION__);
close(fd);
return PIM_SOCK_ERR_DSTADDR;
#endif
}
/* Set router alert (RFC 2113) for all IGMP messages (RFC 3376 4. Message Formats)*/
if (protocol == IPPROTO_IGMP) {
char ra[4];
ra[0] = 148;
ra[1] = 4;
ra[2] = 0;
ra[3] = 0;
if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) {
zlog_warn("Could not set Router Alert Option on socket fd=%d: errno=%d: %s",
fd, errno, safe_strerror(errno));
close(fd);
return PIM_SOCK_ERR_RA;
}
}
{
int reuse = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
(void *) &reuse, sizeof(reuse))) {
zlog_warn("Could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
fd, errno, safe_strerror(errno));
close(fd);
return PIM_SOCK_ERR_REUSE;
}
}
{
const int MTTL = 1;
int ttl = MTTL;
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
(void *) &ttl, sizeof(ttl))) {
zlog_warn("Could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
MTTL, fd, errno, safe_strerror(errno));
close(fd);
return PIM_SOCK_ERR_TTL;
}
}
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
(void *) &loop, sizeof(loop))) {
zlog_warn("Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
loop ? "enable" : "disable",
fd, errno, safe_strerror(errno));
close(fd);
return PIM_SOCK_ERR_LOOP;
}
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
(void *) &ifaddr, sizeof(ifaddr))) {
zlog_warn("Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
fd, errno, safe_strerror(errno));
close(fd);
return PIM_SOCK_ERR_IFACE;
}
{
long flags;
flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) {
zlog_warn("Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
fd, errno, safe_strerror(errno));
close(fd);
return PIM_SOCK_ERR_NONBLOCK_GETFL;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
zlog_warn("Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
fd, errno, safe_strerror(errno));
close(fd);
return PIM_SOCK_ERR_NONBLOCK_SETFL;
}
}
return fd;
}
int pim_socket_join(int fd, struct in_addr group,
struct in_addr ifaddr, int ifindex)
{
int ret;
#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
struct ip_mreqn opt;
#else
struct ip_mreq opt;
#endif
opt.imr_multiaddr = group;
#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
opt.imr_address = ifaddr;
opt.imr_ifindex = ifindex;
#else
opt.imr_interface = ifaddr;
#endif
ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));
if (ret) {
char group_str[100];
char ifaddr_str[100];
if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str)))
sprintf(group_str, "<group?>");
if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str)))
sprintf(ifaddr_str, "<ifaddr?>");
zlog_err("Failure socket joining fd=%d group %s on interface address %s: errno=%d: %s",
fd, group_str, ifaddr_str, errno, safe_strerror(errno));
return ret;
}
if (PIM_DEBUG_TRACE) {
char group_str[100];
char ifaddr_str[100];
if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str)))
sprintf(group_str, "<group?>");
if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str)))
sprintf(ifaddr_str, "<ifaddr?>");
zlog_debug("Socket fd=%d joined group %s on interface address %s",
fd, group_str, ifaddr_str);
}
return ret;
}
int pim_socket_join_source(int fd, int ifindex,
struct in_addr group_addr,
struct in_addr source_addr,
const char *ifname)
{
if (pim_igmp_join_source(fd, ifindex, group_addr, source_addr)) {
int e = errno;
char group_str[100];
char source_str[100];
pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s",
__PRETTY_FUNCTION__,
fd, group_str, source_str, ifindex, ifname,
e, safe_strerror(e));
return -1;
}
return 0;
}
int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
struct sockaddr_in *from, socklen_t *fromlen,
struct sockaddr_in *to, socklen_t *tolen,
int *ifindex)
{
struct msghdr msgh;
struct cmsghdr *cmsg;
struct iovec iov;
char cbuf[1000];
int err;
/*
* IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
* Use getsockname() to get sin_port.
*/
if (to) {
struct sockaddr_in si;
socklen_t si_len = sizeof(si);
((struct sockaddr_in *) to)->sin_family = AF_INET;
if (pim_socket_getsockname(fd, (struct sockaddr *) &si, &si_len)) {
((struct sockaddr_in *) to)->sin_port = ntohs(0);
((struct sockaddr_in *) to)->sin_addr.s_addr = ntohl(0);
}
else {
((struct sockaddr_in *) to)->sin_port = si.sin_port;
((struct sockaddr_in *) to)->sin_addr = si.sin_addr;
}
if (tolen)
*tolen = sizeof(si);
}
memset(&msgh, 0, sizeof(struct msghdr));
iov.iov_base = buf;
iov.iov_len = len;
msgh.msg_control = cbuf;
msgh.msg_controllen = sizeof(cbuf);
msgh.msg_name = from;
msgh.msg_namelen = fromlen ? *fromlen : 0;
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
msgh.msg_flags = 0;
err = recvmsg(fd, &msgh, 0);
if (err < 0)
return err;
if (fromlen)
*fromlen = msgh.msg_namelen;
for (cmsg = CMSG_FIRSTHDR(&msgh);
cmsg != NULL;
cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
#ifdef HAVE_IP_PKTINFO
if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
if (to)
((struct sockaddr_in *) to)->sin_addr = i->ipi_addr;
if (tolen)
*tolen = sizeof(struct sockaddr_in);
if (ifindex)
*ifindex = i->ipi_ifindex;
if (to && PIM_DEBUG_PACKETS) {
char to_str[100];
pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str));
zlog_debug("%s: HAVE_IP_PKTINFO to=%s,%d",
__PRETTY_FUNCTION__,
to_str, ntohs(to->sin_port));
}
break;
}
#endif
#ifdef HAVE_IP_RECVDSTADDR
if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
if (to)
((struct sockaddr_in *) to)->sin_addr = *i;
if (tolen)
*tolen = sizeof(struct sockaddr_in);
if (to && PIM_DEBUG_PACKETS) {
char to_str[100];
pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str));
zlog_debug("%s: HAVE_IP_RECVDSTADDR to=%s,%d",
__PRETTY_FUNCTION__,
to_str, ntohs(to->sin_port));
}
break;
}
#endif
#if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
if (cmsg->cmsg_type == IP_RECVIF)
if (ifindex)
*ifindex = CMSG_IFINDEX(cmsg);
#endif
} /* for (cmsg) */
return err; /* len */
}
int pim_socket_mcastloop_get(int fd)
{
int loop;
socklen_t loop_len = sizeof(loop);
if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
&loop, &loop_len)) {
int e = errno;
zlog_warn("Could not get Multicast Loopback Option on socket fd=%d: errno=%d: %s",
fd, errno, safe_strerror(errno));
errno = e;
return PIM_SOCK_ERR_LOOP;
}
return loop;
}
int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen)
{
if (getsockname(fd, name, namelen)) {
int e = errno;
zlog_warn("Could not get Socket Name for socket fd=%d: errno=%d: %s",
fd, errno, safe_strerror(errno));
errno = e;
return PIM_SOCK_ERR_NAME;
}
return PIM_SOCK_ERR_NONE;
}

57
pimd/pim_sock.h Normal file
View file

@ -0,0 +1,57 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_SOCK_H
#define PIM_SOCK_H
#include <netinet/in.h>
#define PIM_SOCK_ERR_NONE (0) /* No error */
#define PIM_SOCK_ERR_SOCKET (-1) /* socket() */
#define PIM_SOCK_ERR_RA (-2) /* Router Alert option */
#define PIM_SOCK_ERR_REUSE (-3) /* Reuse option */
#define PIM_SOCK_ERR_TTL (-4) /* TTL option */
#define PIM_SOCK_ERR_LOOP (-5) /* Loopback option */
#define PIM_SOCK_ERR_IFACE (-6) /* Outgoing interface option */
#define PIM_SOCK_ERR_DSTADDR (-7) /* Outgoing interface option */
#define PIM_SOCK_ERR_NONBLOCK_GETFL (-8) /* Get O_NONBLOCK */
#define PIM_SOCK_ERR_NONBLOCK_SETFL (-9) /* Set O_NONBLOCK */
#define PIM_SOCK_ERR_NAME (-10) /* Socket name (getsockname) */
int pim_socket_raw(int protocol);
int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop);
int pim_socket_join(int fd, struct in_addr group,
struct in_addr ifaddr, int ifindex);
int pim_socket_join_source(int fd, int ifindex,
struct in_addr group_addr,
struct in_addr source_addr,
const char *ifname);
int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
struct sockaddr_in *from, socklen_t *fromlen,
struct sockaddr_in *to, socklen_t *tolen,
int *ifindex);
int pim_socket_mcastloop_get(int fd);
int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen);
#endif /* PIM_SOCK_H */

448
pimd/pim_ssmpingd.c Normal file
View file

@ -0,0 +1,448 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "if.h"
#include "log.h"
#include "memory.h"
#include "pim_ssmpingd.h"
#include "pim_time.h"
#include "pim_sock.h"
#include "pim_str.h"
#include "pimd.h"
static const char * const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234";
enum {
PIM_SSMPINGD_REQUEST = 'Q',
PIM_SSMPINGD_REPLY = 'A'
};
static void ssmpingd_read_on(struct ssmpingd_sock *ss);
void pim_ssmpingd_init()
{
int result;
zassert(!qpim_ssmpingd_list);
result = inet_pton(AF_INET, PIM_SSMPINGD_REPLY_GROUP, &qpim_ssmpingd_group_addr);
zassert(result > 0);
}
void pim_ssmpingd_destroy()
{
if (qpim_ssmpingd_list) {
list_free(qpim_ssmpingd_list);
qpim_ssmpingd_list = 0;
}
}
static struct ssmpingd_sock *ssmpingd_find(struct in_addr source_addr)
{
struct listnode *node;
struct ssmpingd_sock *ss;
if (!qpim_ssmpingd_list)
return 0;
for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss))
if (source_addr.s_addr == ss->source_addr.s_addr)
return ss;
return 0;
}
static void ssmpingd_free(struct ssmpingd_sock *ss)
{
XFREE(MTYPE_PIM_SSMPINGD, ss);
}
static int ssmpingd_socket(struct in_addr addr, int port, int mttl)
{
struct sockaddr_in sockaddr;
int fd;
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (fd < 0) {
zlog_err("%s: could not create socket: errno=%d: %s",
__PRETTY_FUNCTION__, errno, safe_strerror(errno));
return -1;
}
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr = addr;
sockaddr.sin_port = htons(port);
if (bind(fd, &sockaddr, sizeof(sockaddr))) {
char addr_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%zu) failure: errno=%d: %s",
__PRETTY_FUNCTION__,
fd, addr_str, port, sizeof(sockaddr),
errno, safe_strerror(errno));
close(fd);
return -1;
}
/* Needed to obtain destination address from recvmsg() */
{
#if defined(HAVE_IP_PKTINFO)
/* Linux IP_PKTINFO */
int opt = 1;
if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) {
zlog_warn("%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
__PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
}
#elif defined(HAVE_IP_RECVDSTADDR)
/* BSD IP_RECVDSTADDR */
int opt = 1;
if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
zlog_warn("%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
__PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
}
#else
zlog_err("%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
__FILE__, __PRETTY_FUNCTION__);
close(fd);
return -1;
#endif
}
{
int reuse = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
(void *) &reuse, sizeof(reuse))) {
zlog_warn("%s: could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
__PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
close(fd);
return -1;
}
}
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
(void *) &mttl, sizeof(mttl))) {
zlog_warn("%s: could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
__PRETTY_FUNCTION__, mttl, fd, errno, safe_strerror(errno));
close(fd);
return -1;
}
{
int loop = 0;
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
(void *) &loop, sizeof(loop))) {
zlog_warn("%s: could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
__PRETTY_FUNCTION__,
loop ? "enable" : "disable",
fd, errno, safe_strerror(errno));
close(fd);
return PIM_SOCK_ERR_LOOP;
}
}
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
(void *) &addr, sizeof(addr))) {
zlog_warn("%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
__PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
close(fd);
return -1;
}
{
long flags;
flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) {
zlog_warn("%s: could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
__PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
close(fd);
return -1;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
zlog_warn("%s: could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
__PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
close(fd);
return -1;
}
}
return fd;
}
static void ssmpingd_delete(struct ssmpingd_sock *ss)
{
zassert(ss);
zassert(qpim_ssmpingd_list);
THREAD_OFF(ss->t_sock_read);
if (close(ss->sock_fd)) {
int e = errno;
char source_str[100];
pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
zlog_warn("%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s",
__PRETTY_FUNCTION__,
ss->sock_fd, source_str, e, safe_strerror(e));
/* warning only */
}
listnode_delete(qpim_ssmpingd_list, ss);
ssmpingd_free(ss);
}
static void ssmpingd_sendto(struct ssmpingd_sock *ss,
const uint8_t *buf,
int len,
struct sockaddr_in to)
{
socklen_t tolen = sizeof(to);
int sent;
sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT, &to, tolen);
if (sent != len) {
int e = errno;
char to_str[100];
pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
if (sent < 0) {
zlog_warn("%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s",
__PRETTY_FUNCTION__,
to_str, ntohs(to.sin_port), ss->sock_fd, len,
e, safe_strerror(e));
}
else {
zlog_warn("%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d",
__PRETTY_FUNCTION__,
to_str, ntohs(to.sin_port), ss->sock_fd,
len, sent);
}
}
}
static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
{
struct interface *ifp;
struct sockaddr_in from;
struct sockaddr_in to;
socklen_t fromlen = sizeof(from);
socklen_t tolen = sizeof(to);
int ifindex = -1;
uint8_t buf[1000];
int len;
++ss->requests;
len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf),
&from, &fromlen,
&to, &tolen,
&ifindex);
if (len < 0) {
char source_str[100];
pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
zlog_warn("%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s",
__PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, safe_strerror(errno));
return -1;
}
ifp = if_lookup_by_index(ifindex);
if (buf[0] != PIM_SSMPINGD_REQUEST) {
char source_str[100];
char from_str[100];
char to_str[100];
pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
zlog_warn("%s: bad ssmping type=%d from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s",
__PRETTY_FUNCTION__,
buf[0],
from_str, ntohs(from.sin_port),
to_str, ntohs(to.sin_port),
ifp ? ifp->name : "<iface?>",
ifindex, ss->sock_fd,
source_str);
return 0;
}
if (PIM_DEBUG_SSMPINGD) {
char source_str[100];
char from_str[100];
char to_str[100];
pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
zlog_debug("%s: recv ssmping from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s",
__PRETTY_FUNCTION__,
from_str, ntohs(from.sin_port),
to_str, ntohs(to.sin_port),
ifp ? ifp->name : "<iface?>",
ifindex, ss->sock_fd,
source_str);
}
buf[0] = PIM_SSMPINGD_REPLY;
/* unicast reply */
ssmpingd_sendto(ss, buf, len, from);
/* multicast reply */
from.sin_addr = qpim_ssmpingd_group_addr;
ssmpingd_sendto(ss, buf, len, from);
return 0;
}
static int ssmpingd_sock_read(struct thread *t)
{
struct ssmpingd_sock *ss;
int sock_fd;
int result;
zassert(t);
ss = THREAD_ARG(t);
zassert(ss);
sock_fd = THREAD_FD(t);
zassert(sock_fd == ss->sock_fd);
result = ssmpingd_read_msg(ss);
/* Keep reading */
ss->t_sock_read = 0;
ssmpingd_read_on(ss);
return result;
}
static void ssmpingd_read_on(struct ssmpingd_sock *ss)
{
zassert(!ss->t_sock_read);
THREAD_READ_ON(master, ss->t_sock_read,
ssmpingd_sock_read, ss, ss->sock_fd);
}
static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr)
{
struct ssmpingd_sock *ss;
int sock_fd;
if (!qpim_ssmpingd_list) {
qpim_ssmpingd_list = list_new();
if (!qpim_ssmpingd_list) {
zlog_err("%s %s: failure: qpim_ssmpingd_list=list_new()",
__FILE__, __PRETTY_FUNCTION__);
return 0;
}
qpim_ssmpingd_list->del = (void (*)(void *)) ssmpingd_free;
}
sock_fd = ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64);
if (sock_fd < 0) {
char source_str[100];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: ssmpingd_socket() failure for source %s",
__PRETTY_FUNCTION__, source_str);
return 0;
}
ss = XMALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
if (!ss) {
char source_str[100];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_err("%s: XMALLOC(%zu) failure for ssmpingd source %s",
__PRETTY_FUNCTION__,
sizeof(*ss), source_str);
close(sock_fd);
return 0;
}
ss->sock_fd = sock_fd;
ss->t_sock_read = 0;
ss->source_addr = source_addr;
ss->creation = pim_time_monotonic_sec();
ss->requests = 0;
listnode_add(qpim_ssmpingd_list, ss);
ssmpingd_read_on(ss);
return ss;
}
int pim_ssmpingd_start(struct in_addr source_addr)
{
struct ssmpingd_sock *ss;
ss = ssmpingd_find(source_addr);
if (ss) {
/* silently ignore request to recreate entry */
return 0;
}
{
char source_str[100];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_info("%s: starting ssmpingd for source %s",
__PRETTY_FUNCTION__, source_str);
}
ss = ssmpingd_new(source_addr);
if (!ss) {
char source_str[100];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: ssmpingd_new() failure for source %s",
__PRETTY_FUNCTION__, source_str);
return -1;
}
return 0;
}
int pim_ssmpingd_stop(struct in_addr source_addr)
{
struct ssmpingd_sock *ss;
ss = ssmpingd_find(source_addr);
if (!ss) {
char source_str[100];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_warn("%s: could not find ssmpingd for source %s",
__PRETTY_FUNCTION__, source_str);
return -1;
}
{
char source_str[100];
pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
zlog_info("%s: stopping ssmpingd for source %s",
__PRETTY_FUNCTION__, source_str);
}
ssmpingd_delete(ss);
return 0;
}

45
pimd/pim_ssmpingd.h Normal file
View file

@ -0,0 +1,45 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_SSMPINGD_H
#define PIM_SSMPINGD_H
#include <zebra.h>
#include "if.h"
#include "pim_iface.h"
struct ssmpingd_sock {
int sock_fd; /* socket */
struct thread *t_sock_read; /* thread for reading socket */
struct in_addr source_addr; /* source address */
int64_t creation; /* timestamp of socket creation */
int64_t requests; /* counter */
};
void pim_ssmpingd_init(void);
void pim_ssmpingd_destroy(void);
int pim_ssmpingd_start(struct in_addr source_addr);
int pim_ssmpingd_stop(struct in_addr source_addr);
#endif /* PIM_SSMPINGD_H */

46
pimd/pim_str.c Normal file
View file

@ -0,0 +1,46 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <zebra.h>
#include "log.h"
#include "pim_str.h"
void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size)
{
int save_errno = errno;
if (!inet_ntop(AF_INET, &addr, buf, buf_size)) {
int e = errno;
zlog_warn("pim_inet4_dump: inet_ntop(AF_INET,buf_size=%d): errno=%d: %s",
buf_size, e, safe_strerror(e));
if (onfail)
snprintf(buf, buf_size, "%s", onfail);
}
errno = save_errno;
}

32
pimd/pim_str.h Normal file
View file

@ -0,0 +1,32 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_STR_H
#define PIM_STR_H
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size);
#endif

165
pimd/pim_time.c Normal file
View file

@ -0,0 +1,165 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <zebra.h>
#include "log.h"
#include "thread.h"
#include "pim_time.h"
static int gettime_monotonic(struct timeval *tv)
{
int result;
result = gettimeofday(tv, 0);
if (result) {
zlog_err("%s: gettimeofday() failure: errno=%d: %s",
__PRETTY_FUNCTION__,
errno, safe_strerror(errno));
}
return result;
}
/*
pim_time_monotonic_sec():
number of seconds since some unspecified starting point
*/
int64_t pim_time_monotonic_sec()
{
struct timeval now_tv;
if (gettime_monotonic(&now_tv)) {
zlog_err("%s: gettime_monotonic() failure: errno=%d: %s",
__PRETTY_FUNCTION__,
errno, safe_strerror(errno));
return -1;
}
return now_tv.tv_sec;
}
/*
pim_time_monotonic_dsec():
number of deciseconds since some unspecified starting point
*/
int64_t pim_time_monotonic_dsec()
{
struct timeval now_tv;
int64_t now_dsec;
if (gettime_monotonic(&now_tv)) {
zlog_err("%s: gettime_monotonic() failure: errno=%d: %s",
__PRETTY_FUNCTION__,
errno, safe_strerror(errno));
return -1;
}
now_dsec = ((int64_t) now_tv.tv_sec) * 10 + ((int64_t) now_tv.tv_usec) / 100000;
return now_dsec;
}
int pim_time_mmss(char *buf, int buf_size, long sec)
{
long mm;
int wr;
zassert(buf_size >= 5);
mm = sec / 60;
sec %= 60;
wr = snprintf(buf, buf_size, "%02ld:%02ld", mm, sec);
return wr != 8;
}
static int pim_time_hhmmss(char *buf, int buf_size, long sec)
{
long hh;
long mm;
int wr;
zassert(buf_size >= 8);
hh = sec / 3600;
sec %= 3600;
mm = sec / 60;
sec %= 60;
wr = snprintf(buf, buf_size, "%02ld:%02ld:%02ld", hh, mm, sec);
return wr != 8;
}
void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t_timer)
{
if (t_timer) {
pim_time_mmss(buf, buf_size,
thread_timer_remain_second(t_timer));
}
else {
snprintf(buf, buf_size, "--:--");
}
}
void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t_timer)
{
if (t_timer) {
pim_time_hhmmss(buf, buf_size,
thread_timer_remain_second(t_timer));
}
else {
snprintf(buf, buf_size, "--:--:--");
}
}
void pim_time_uptime(char *buf, int buf_size, int64_t uptime_sec)
{
zassert(buf_size >= 8);
pim_time_hhmmss(buf, buf_size, uptime_sec);
}
void pim_time_uptime_begin(char *buf, int buf_size, int64_t now, int64_t begin)
{
if (begin > 0)
pim_time_uptime(buf, buf_size, now - begin);
else
snprintf(buf, buf_size, "--:--:--");
}
long pim_time_timer_remain_msec(struct thread *t_timer)
{
/* FIXME: Actually fetch msec resolution from thread */
/* no timer thread running means timer has expired: return 0 */
return t_timer ?
1000 * thread_timer_remain_second(t_timer) :
0;
}

40
pimd/pim_time.h Normal file
View file

@ -0,0 +1,40 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_TIME_H
#define PIM_TIME_H
#include <stdint.h>
#include <zebra.h>
#include "thread.h"
int64_t pim_time_monotonic_sec(void);
int64_t pim_time_monotonic_dsec(void);
int pim_time_mmss(char *buf, int buf_size, long sec);
void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t);
void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t);
void pim_time_uptime(char *buf, int buf_size, int64_t uptime_sec);
void pim_time_uptime_begin(char *buf, int buf_size, int64_t now, int64_t begin);
long pim_time_timer_remain_msec(struct thread *t_timer);
#endif /* PIM_TIME_H */

721
pimd/pim_tlv.c Normal file
View file

@ -0,0 +1,721 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "log.h"
#include "prefix.h"
#include "pimd.h"
#include "pim_int.h"
#include "pim_tlv.h"
#include "pim_str.h"
#include "pim_msg.h"
uint8_t *pim_tlv_append_uint16(uint8_t *buf,
const uint8_t *buf_pastend,
uint16_t option_type,
uint16_t option_value)
{
uint16_t option_len = 2;
if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) {
zlog_warn("%s: buffer overflow: left=%zd needed=%d",
__PRETTY_FUNCTION__,
buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len));
return 0;
}
*(uint16_t *) buf = htons(option_type);
buf += 2;
*(uint16_t *) buf = htons(option_len);
buf += 2;
*(uint16_t *) buf = htons(option_value);
buf += option_len;
return buf;
}
uint8_t *pim_tlv_append_2uint16(uint8_t *buf,
const uint8_t *buf_pastend,
uint16_t option_type,
uint16_t option_value1,
uint16_t option_value2)
{
uint16_t option_len = 4;
if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) {
zlog_warn("%s: buffer overflow: left=%zd needed=%d",
__PRETTY_FUNCTION__,
buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len));
return 0;
}
*(uint16_t *) buf = htons(option_type);
buf += 2;
*(uint16_t *) buf = htons(option_len);
buf += 2;
*(uint16_t *) buf = htons(option_value1);
buf += 2;
*(uint16_t *) buf = htons(option_value2);
buf += 2;
return buf;
}
uint8_t *pim_tlv_append_uint32(uint8_t *buf,
const uint8_t *buf_pastend,
uint16_t option_type,
uint32_t option_value)
{
uint16_t option_len = 4;
if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) {
zlog_warn("%s: buffer overflow: left=%zd needed=%d",
__PRETTY_FUNCTION__,
buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len));
return 0;
}
*(uint16_t *) buf = htons(option_type);
buf += 2;
*(uint16_t *) buf = htons(option_len);
buf += 2;
pim_write_uint32(buf, option_value);
buf += option_len;
return buf;
}
#define ucast_ipv4_encoding_len (2 + sizeof(struct in_addr))
uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf,
const uint8_t *buf_pastend,
struct list *ifconnected)
{
struct listnode *node;
uint16_t option_len = 0;
uint8_t *curr;
node = listhead(ifconnected);
/* Empty address list ? */
if (!node) {
return buf;
}
/* Skip first address (primary) */
node = listnextnode(node);
/* Scan secondary address list */
curr = buf + 4; /* skip T and L */
for (; node; node = listnextnode(node)) {
struct connected *ifc = listgetdata(node);
struct prefix *p = ifc->address;
if (p->family != AF_INET)
continue;
if ((curr + ucast_ipv4_encoding_len) > buf_pastend) {
zlog_warn("%s: buffer overflow: left=%zd needed=%zu",
__PRETTY_FUNCTION__,
buf_pastend - curr, ucast_ipv4_encoding_len);
return 0;
}
/* Write encoded unicast IPv4 address */
*(uint8_t *) curr = PIM_MSG_ADDRESS_FAMILY_IPV4; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
++curr;
*(uint8_t *) curr = 0; /* ucast IPv4 native encoding type (RFC 4601: 4.9.1) */
++curr;
memcpy(curr, &p->u.prefix4, sizeof(struct in_addr));
curr += sizeof(struct in_addr);
option_len += ucast_ipv4_encoding_len;
}
if (PIM_DEBUG_PIM_TRACE) {
zlog_warn("%s: number of encoded secondary unicast IPv4 addresses: %zu",
__PRETTY_FUNCTION__,
option_len / ucast_ipv4_encoding_len);
}
if (option_len < 1) {
/* Empty secondary unicast IPv4 address list */
return buf;
}
/*
* Write T and L
*/
*(uint16_t *) buf = htons(PIM_MSG_OPTION_TYPE_ADDRESS_LIST);
*(uint16_t *) (buf + 2) = htons(option_len);
return curr;
}
static int check_tlv_length(const char *label, const char *tlv_name,
const char *ifname, struct in_addr src_addr,
int correct_len, int option_len)
{
if (option_len != correct_len) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: PIM hello %s TLV with incorrect value size=%d correct=%d from %s on interface %s",
label, tlv_name,
option_len, correct_len,
src_str, ifname);
return -1;
}
return 0;
}
static void check_tlv_redefinition_uint16(const char *label, const char *tlv_name,
const char *ifname, struct in_addr src_addr,
pim_hello_options options,
pim_hello_options opt_mask,
uint16_t new, uint16_t old)
{
if (PIM_OPTION_IS_SET(options, opt_mask)) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s",
label, tlv_name,
new, old,
src_str, ifname);
}
}
static void check_tlv_redefinition_uint32(const char *label, const char *tlv_name,
const char *ifname, struct in_addr src_addr,
pim_hello_options options,
pim_hello_options opt_mask,
uint32_t new, uint32_t old)
{
if (PIM_OPTION_IS_SET(options, opt_mask)) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s",
label, tlv_name,
new, old,
src_str, ifname);
}
}
static void check_tlv_redefinition_uint32_hex(const char *label, const char *tlv_name,
const char *ifname, struct in_addr src_addr,
pim_hello_options options,
pim_hello_options opt_mask,
uint32_t new, uint32_t old)
{
if (PIM_OPTION_IS_SET(options, opt_mask)) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: PIM hello TLV redefined %s=%08x old=%08x from %s on interface %s",
label, tlv_name,
new, old,
src_str, ifname);
}
}
int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr,
pim_hello_options *hello_options,
uint16_t *hello_option_holdtime,
uint16_t option_len,
const uint8_t *tlv_curr)
{
const char *label = "holdtime";
if (check_tlv_length(__PRETTY_FUNCTION__, label,
ifname, src_addr,
sizeof(uint16_t), option_len)) {
return -1;
}
check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, label,
ifname, src_addr,
*hello_options, PIM_OPTION_MASK_HOLDTIME,
PIM_TLV_GET_HOLDTIME(tlv_curr),
*hello_option_holdtime);
PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_HOLDTIME);
*hello_option_holdtime = PIM_TLV_GET_HOLDTIME(tlv_curr);
return 0;
}
int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr,
pim_hello_options *hello_options,
uint16_t *hello_option_propagation_delay,
uint16_t *hello_option_override_interval,
uint16_t option_len,
const uint8_t *tlv_curr)
{
if (check_tlv_length(__PRETTY_FUNCTION__, "lan_prune_delay",
ifname, src_addr,
sizeof(uint32_t), option_len)) {
return -1;
}
check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, "propagation_delay",
ifname, src_addr,
*hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY,
PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr),
*hello_option_propagation_delay);
PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY);
*hello_option_propagation_delay = PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr);
if (PIM_TLV_GET_CAN_DISABLE_JOIN_SUPPRESSION(tlv_curr)) {
PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION);
}
else {
PIM_OPTION_UNSET(*hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION);
}
++tlv_curr;
++tlv_curr;
*hello_option_override_interval = PIM_TLV_GET_OVERRIDE_INTERVAL(tlv_curr);
return 0;
}
int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr,
pim_hello_options *hello_options,
uint32_t *hello_option_dr_priority,
uint16_t option_len,
const uint8_t *tlv_curr)
{
const char *label = "dr_priority";
if (check_tlv_length(__PRETTY_FUNCTION__, label,
ifname, src_addr,
sizeof(uint32_t), option_len)) {
return -1;
}
check_tlv_redefinition_uint32(__PRETTY_FUNCTION__, label,
ifname, src_addr,
*hello_options, PIM_OPTION_MASK_DR_PRIORITY,
PIM_TLV_GET_DR_PRIORITY(tlv_curr),
*hello_option_dr_priority);
PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_DR_PRIORITY);
*hello_option_dr_priority = PIM_TLV_GET_DR_PRIORITY(tlv_curr);
return 0;
}
int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr,
pim_hello_options *hello_options,
uint32_t *hello_option_generation_id,
uint16_t option_len,
const uint8_t *tlv_curr)
{
const char *label = "generation_id";
if (check_tlv_length(__PRETTY_FUNCTION__, label,
ifname, src_addr,
sizeof(uint32_t), option_len)) {
return -1;
}
check_tlv_redefinition_uint32_hex(__PRETTY_FUNCTION__, label,
ifname, src_addr,
*hello_options, PIM_OPTION_MASK_GENERATION_ID,
PIM_TLV_GET_GENERATION_ID(tlv_curr),
*hello_option_generation_id);
PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_GENERATION_ID);
*hello_option_generation_id = PIM_TLV_GET_GENERATION_ID(tlv_curr);
return 0;
}
int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr,
struct prefix *p,
const uint8_t *buf,
int buf_size)
{
const int ucast_encoding_min_len = 3; /* 1 family + 1 type + 1 addr */
const uint8_t *addr;
const uint8_t *pastend;
int family;
int type;
if (buf_size < ucast_encoding_min_len) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: unicast address encoding overflow: left=%d needed=%d from %s on %s",
__PRETTY_FUNCTION__,
buf_size, ucast_encoding_min_len,
src_str, ifname);
return -1;
}
addr = buf;
pastend = buf + buf_size;
family = *addr++;
type = *addr++;
switch (family) {
case PIM_MSG_ADDRESS_FAMILY_IPV4:
if (type) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: unknown unicast address encoding type=%d from %s on %s",
__PRETTY_FUNCTION__,
type, src_str, ifname);
return -2;
}
if ((addr + sizeof(struct in_addr)) > pastend) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: IPv4 unicast address overflow: left=%zd needed=%zu from %s on %s",
__PRETTY_FUNCTION__,
pastend - addr, sizeof(struct in_addr),
src_str, ifname);
return -3;
}
p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
memcpy(&p->u.prefix4, addr, sizeof(struct in_addr));
addr += sizeof(struct in_addr);
break;
default:
{
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: unknown unicast address encoding family=%d from %s on %s",
__PRETTY_FUNCTION__,
family, src_str, ifname);
return -4;
}
}
return addr - buf;
}
int pim_parse_addr_group(const char *ifname, struct in_addr src_addr,
struct prefix *p,
const uint8_t *buf,
int buf_size)
{
const int grp_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */
const uint8_t *addr;
const uint8_t *pastend;
int family;
int type;
int mask_len;
if (buf_size < grp_encoding_min_len) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: group address encoding overflow: left=%d needed=%d from %s on %s",
__PRETTY_FUNCTION__,
buf_size, grp_encoding_min_len,
src_str, ifname);
return -1;
}
addr = buf;
pastend = buf + buf_size;
family = *addr++;
type = *addr++;
//++addr;
++addr; /* skip b_reserved_z fields */
mask_len = *addr++;
switch (family) {
case PIM_MSG_ADDRESS_FAMILY_IPV4:
if (type) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: unknown group address encoding type=%d from %s on %s",
__PRETTY_FUNCTION__,
type, src_str, ifname);
return -2;
}
if ((addr + sizeof(struct in_addr)) > pastend) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: IPv4 group address overflow: left=%zd needed=%zu from %s on %s",
__PRETTY_FUNCTION__,
pastend - addr, sizeof(struct in_addr),
src_str, ifname);
return -3;
}
p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
memcpy(&p->u.prefix4, addr, sizeof(struct in_addr));
p->prefixlen = mask_len;
addr += sizeof(struct in_addr);
break;
default:
{
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: unknown group address encoding family=%d from %s on %s",
__PRETTY_FUNCTION__,
family, src_str, ifname);
return -4;
}
}
return addr - buf;
}
int pim_parse_addr_source(const char *ifname,
struct in_addr src_addr,
struct prefix *p,
uint8_t *flags,
const uint8_t *buf,
int buf_size)
{
const int src_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */
const uint8_t *addr;
const uint8_t *pastend;
int family;
int type;
int mask_len;
if (buf_size < src_encoding_min_len) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: source address encoding overflow: left=%d needed=%d from %s on %s",
__PRETTY_FUNCTION__,
buf_size, src_encoding_min_len,
src_str, ifname);
return -1;
}
addr = buf;
pastend = buf + buf_size;
family = *addr++;
type = *addr++;
*flags = *addr++;
mask_len = *addr++;
switch (family) {
case PIM_MSG_ADDRESS_FAMILY_IPV4:
if (type) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: unknown source address encoding type=%d from %s on %s: %02x%02x%02x%02x%02x%02x%02x%02x",
__PRETTY_FUNCTION__,
type, src_str, ifname,
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
return -2;
}
if ((addr + sizeof(struct in_addr)) > pastend) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: IPv4 source address overflow: left=%zd needed=%zu from %s on %s",
__PRETTY_FUNCTION__,
pastend - addr, sizeof(struct in_addr),
src_str, ifname);
return -3;
}
p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
memcpy(&p->u.prefix4, addr, sizeof(struct in_addr));
p->prefixlen = mask_len;
/*
RFC 4601: 4.9.1 Encoded Source and Group Address Formats
Encoded-Source Address
The mask length MUST be equal to the mask length in bits for
the given Address Family and Encoding Type (32 for IPv4 native
and 128 for IPv6 native). A router SHOULD ignore any messages
received with any other mask length.
*/
if (p->prefixlen != 32) {
char src_str[100];
pim_inet4_dump("<src?>", p->u.prefix4, src_str, sizeof(src_str));
zlog_warn("%s: IPv4 bad source address mask: %s/%d",
__PRETTY_FUNCTION__, src_str, p->prefixlen);
return -4;
}
addr += sizeof(struct in_addr);
break;
default:
{
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: unknown source address encoding family=%d from %s on %s: %02x%02x%02x%02x%02x%02x%02x%02x",
__PRETTY_FUNCTION__,
family, src_str, ifname,
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
return -5;
}
}
return addr - buf;
}
#define FREE_ADDR_LIST(hello_option_addr_list) \
{ \
if (hello_option_addr_list) { \
list_delete(hello_option_addr_list); \
hello_option_addr_list = 0; \
} \
}
int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr,
pim_hello_options *hello_options,
struct list **hello_option_addr_list,
uint16_t option_len,
const uint8_t *tlv_curr)
{
const uint8_t *addr;
const uint8_t *pastend;
zassert(hello_option_addr_list);
/*
Scan addr list
*/
addr = tlv_curr;
pastend = tlv_curr + option_len;
while (addr < pastend) {
struct prefix tmp;
int addr_offset;
/*
Parse ucast addr
*/
addr_offset = pim_parse_addr_ucast(ifname, src_addr, &tmp,
addr, pastend - addr);
if (addr_offset < 1) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
__PRETTY_FUNCTION__,
src_str, ifname);
FREE_ADDR_LIST(*hello_option_addr_list);
return -1;
}
addr += addr_offset;
/*
Debug
*/
if (PIM_DEBUG_PIM_TRACE) {
switch (tmp.family) {
case AF_INET:
{
char addr_str[100];
char src_str[100];
pim_inet4_dump("<addr?>", tmp.u.prefix4, addr_str, sizeof(addr_str));
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello TLV option: list_old_size=%d IPv4 address %s from %s on %s",
__PRETTY_FUNCTION__,
*hello_option_addr_list ?
((int) listcount(*hello_option_addr_list)) : -1,
addr_str, src_str, ifname);
}
break;
default:
{
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_debug("%s: PIM hello TLV option: list_old_size=%d UNKNOWN address family from %s on %s",
__PRETTY_FUNCTION__,
*hello_option_addr_list ?
((int) listcount(*hello_option_addr_list)) : -1,
src_str, ifname);
}
}
}
/*
Exclude neighbor's primary address if incorrectly included in
the secondary address list
*/
if (tmp.family == AF_INET) {
if (tmp.u.prefix4.s_addr == src_addr.s_addr) {
char src_str[100];
pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
zlog_warn("%s: ignoring primary address in secondary list from %s on %s",
__PRETTY_FUNCTION__,
src_str, ifname);
continue;
}
}
/*
Allocate list if needed
*/
if (!*hello_option_addr_list) {
*hello_option_addr_list = list_new();
if (!*hello_option_addr_list) {
zlog_err("%s %s: failure: hello_option_addr_list=list_new()",
__FILE__, __PRETTY_FUNCTION__);
return -2;
}
(*hello_option_addr_list)->del = (void (*)(void *)) prefix_free;
}
/*
Attach addr to list
*/
{
struct prefix *p;
p = prefix_new();
if (!p) {
zlog_err("%s %s: failure: prefix_new()",
__FILE__, __PRETTY_FUNCTION__);
FREE_ADDR_LIST(*hello_option_addr_list);
return -3;
}
p->family = tmp.family;
p->u.prefix4 = tmp.u.prefix4;
listnode_add(*hello_option_addr_list, p);
}
} /* while (addr < pastend) */
/*
Mark hello option
*/
PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_ADDRESS_LIST);
return 0;
}

133
pimd/pim_tlv.h Normal file
View file

@ -0,0 +1,133 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_TLV_H
#define PIM_TLV_H
#include <zebra.h>
#include "config.h"
#include "if.h"
#include "linklist.h"
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif /* HAVE_INTTYPES_H */
#define PIM_MSG_OPTION_TYPE_HOLDTIME (1)
#define PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY (2)
#define PIM_MSG_OPTION_TYPE_DR_PRIORITY (19)
#define PIM_MSG_OPTION_TYPE_GENERATION_ID (20)
#define PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH (21)
#define PIM_MSG_OPTION_TYPE_ADDRESS_LIST (24)
typedef uint32_t pim_hello_options;
#define PIM_OPTION_MASK_HOLDTIME (1 << 0) /* recv holdtime */
#define PIM_OPTION_MASK_LAN_PRUNE_DELAY (1 << 1) /* recv lan_prune_delay */
#define PIM_OPTION_MASK_DR_PRIORITY (1 << 2) /* recv dr_priority */
#define PIM_OPTION_MASK_GENERATION_ID (1 << 3) /* recv generation_id */
#define PIM_OPTION_MASK_ADDRESS_LIST (1 << 4) /* recv secondary address list */
#define PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION (1 << 5) /* T bit value (valid if recv lan_prune_delay) */
#define PIM_RPT_BIT_MASK (1 << 0)
#define PIM_WILDCARD_BIT_MASK (1 << 1)
#define PIM_OPTION_SET(options, option_mask) ((options) |= (option_mask))
#define PIM_OPTION_UNSET(options, option_mask) ((options) &= ~(option_mask))
#define PIM_OPTION_IS_SET(options, option_mask) ((options) & (option_mask))
#define PIM_TLV_GET_UINT16(buf) ntohs(*(const uint16_t *)(buf))
#define PIM_TLV_GET_UINT32(buf) ntohl(*(const uint32_t *)(buf))
#define PIM_TLV_GET_TYPE(buf) PIM_TLV_GET_UINT16(buf)
#define PIM_TLV_GET_LENGTH(buf) PIM_TLV_GET_UINT16(buf)
#define PIM_TLV_GET_HOLDTIME(buf) PIM_TLV_GET_UINT16(buf)
#define PIM_TLV_GET_PROPAGATION_DELAY(buf) (PIM_TLV_GET_UINT16(buf) & 0x7FFF)
#define PIM_TLV_GET_OVERRIDE_INTERVAL(buf) PIM_TLV_GET_UINT16(buf)
#define PIM_TLV_GET_CAN_DISABLE_JOIN_SUPPRESSION(buf) ((*(const uint8_t *)(buf)) & 0x80)
#define PIM_TLV_GET_DR_PRIORITY(buf) PIM_TLV_GET_UINT32(buf)
#define PIM_TLV_GET_GENERATION_ID(buf) PIM_TLV_GET_UINT32(buf)
#define PIM_TLV_TYPE_SIZE (2)
#define PIM_TLV_LENGTH_SIZE (2)
#define PIM_TLV_MIN_SIZE (PIM_TLV_TYPE_SIZE + PIM_TLV_LENGTH_SIZE)
#define PIM_TLV_OPTION_SIZE(option_len) (PIM_TLV_MIN_SIZE + (option_len))
uint8_t *pim_tlv_append_uint16(uint8_t *buf,
const uint8_t *buf_pastend,
uint16_t option_type,
uint16_t option_value);
uint8_t *pim_tlv_append_2uint16(uint8_t *buf,
const uint8_t *buf_pastend,
uint16_t option_type,
uint16_t option_value1,
uint16_t option_value2);
uint8_t *pim_tlv_append_uint32(uint8_t *buf,
const uint8_t *buf_pastend,
uint16_t option_type,
uint32_t option_value);
uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf,
const uint8_t *buf_pastend,
struct list *ifconnected);
int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr,
pim_hello_options *hello_options,
uint16_t *hello_option_holdtime,
uint16_t option_len,
const uint8_t *tlv_curr);
int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr,
pim_hello_options *hello_options,
uint16_t *hello_option_propagation_delay,
uint16_t *hello_option_override_interval,
uint16_t option_len,
const uint8_t *tlv_curr);
int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr,
pim_hello_options *hello_options,
uint32_t *hello_option_dr_priority,
uint16_t option_len,
const uint8_t *tlv_curr);
int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr,
pim_hello_options *hello_options,
uint32_t *hello_option_generation_id,
uint16_t option_len,
const uint8_t *tlv_curr);
int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr,
pim_hello_options *hello_options,
struct list **hello_option_addr_list,
uint16_t option_len,
const uint8_t *tlv_curr);
int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr,
struct prefix *p,
const uint8_t *buf,
int buf_size);
int pim_parse_addr_group(const char *ifname, struct in_addr src_addr,
struct prefix *p,
const uint8_t *buf,
int buf_size);
int pim_parse_addr_source(const char *ifname,
struct in_addr src_addr,
struct prefix *p,
uint8_t *flags,
const uint8_t *buf,
int buf_size);
#endif /* PIM_TLV_H */

683
pimd/pim_upstream.c Normal file
View file

@ -0,0 +1,683 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "zebra/rib.h"
#include "log.h"
#include "zclient.h"
#include "memory.h"
#include "thread.h"
#include "linklist.h"
#include "pimd.h"
#include "pim_pim.h"
#include "pim_str.h"
#include "pim_time.h"
#include "pim_iface.h"
#include "pim_join.h"
#include "pim_zlookup.h"
#include "pim_upstream.h"
#include "pim_ifchannel.h"
#include "pim_neighbor.h"
#include "pim_rpf.h"
#include "pim_zebra.h"
#include "pim_oil.h"
#include "pim_macro.h"
static void join_timer_start(struct pim_upstream *up);
static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
void pim_upstream_free(struct pim_upstream *up)
{
XFREE(MTYPE_PIM_UPSTREAM, up);
}
static void upstream_channel_oil_detach(struct pim_upstream *up)
{
if (up->channel_oil) {
pim_channel_oil_del(up->channel_oil);
up->channel_oil = 0;
}
}
void pim_upstream_delete(struct pim_upstream *up)
{
THREAD_OFF(up->t_join_timer);
upstream_channel_oil_detach(up);
/*
notice that listnode_delete() can't be moved
into pim_upstream_free() because the later is
called by list_delete_all_node()
*/
listnode_delete(qpim_upstream_list, up);
pim_upstream_free(up);
}
static void send_join(struct pim_upstream *up)
{
zassert(up->join_state == PIM_UPSTREAM_JOINED);
if (PIM_DEBUG_PIM_TRACE) {
if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
char src_str[100];
char grp_str[100];
char rpf_str[100];
pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
zlog_warn("%s: can't send join upstream: RPF'(%s,%s)=%s",
__PRETTY_FUNCTION__,
src_str, grp_str, rpf_str);
/* warning only */
}
}
/* send Join(S,G) to the current upstream neighbor */
pim_joinprune_send(up->rpf.source_nexthop.interface,
up->rpf.rpf_addr,
up->source_addr,
up->group_addr,
1 /* join */);
}
static int on_join_timer(struct thread *t)
{
struct pim_upstream *up;
zassert(t);
up = THREAD_ARG(t);
zassert(up);
send_join(up);
up->t_join_timer = 0;
join_timer_start(up);
return 0;
}
static void join_timer_start(struct pim_upstream *up)
{
if (PIM_DEBUG_PIM_EVENTS) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
zlog_debug("%s: starting %d sec timer for upstream (S,G)=(%s,%s)",
__PRETTY_FUNCTION__,
qpim_t_periodic,
src_str, grp_str);
}
zassert(!up->t_join_timer);
THREAD_TIMER_ON(master, up->t_join_timer,
on_join_timer,
up, qpim_t_periodic);
}
void pim_upstream_join_timer_restart(struct pim_upstream *up)
{
THREAD_OFF(up->t_join_timer);
join_timer_start(up);
}
static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
int interval_msec)
{
if (PIM_DEBUG_PIM_EVENTS) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
zlog_debug("%s: restarting %d msec timer for upstream (S,G)=(%s,%s)",
__PRETTY_FUNCTION__,
interval_msec,
src_str, grp_str);
}
THREAD_OFF(up->t_join_timer);
THREAD_TIMER_MSEC_ON(master, up->t_join_timer,
on_join_timer,
up, interval_msec);
}
void pim_upstream_join_suppress(struct pim_upstream *up,
struct in_addr rpf_addr,
int holdtime)
{
long t_joinsuppress_msec;
long join_timer_remain_msec;
t_joinsuppress_msec = MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface),
1000 * holdtime);
join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
char grp_str[100];
char rpf_str[100];
pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
zlog_debug("%s %s: detected Join(%s,%s) to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
__FILE__, __PRETTY_FUNCTION__,
src_str, grp_str,
rpf_str,
join_timer_remain_msec, t_joinsuppress_msec);
}
if (join_timer_remain_msec < t_joinsuppress_msec) {
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
zlog_debug("%s %s: suppressing Join(S,G)=(%s,%s) for %ld msec",
__FILE__, __PRETTY_FUNCTION__,
src_str, grp_str, t_joinsuppress_msec);
}
pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
}
}
void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
struct pim_upstream *up,
struct in_addr rpf_addr)
{
long join_timer_remain_msec;
int t_override_msec;
join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface);
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
char grp_str[100];
char rpf_str[100];
pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
zlog_debug("%s: to RPF'(%s,%s)=%s: join_timer=%ld msec t_override=%d msec",
debug_label,
src_str, grp_str, rpf_str,
join_timer_remain_msec, t_override_msec);
}
if (join_timer_remain_msec > t_override_msec) {
if (PIM_DEBUG_PIM_TRACE) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
zlog_debug("%s: decreasing (S,G)=(%s,%s) join timer to t_override=%d msec",
debug_label,
src_str, grp_str,
t_override_msec);
}
pim_upstream_join_timer_restart_msec(up, t_override_msec);
}
}
static void forward_on(struct pim_upstream *up)
{
struct listnode *ifnode;
struct listnode *ifnextnode;
struct listnode *chnode;
struct listnode *chnextnode;
struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_ifchannel *ch;
/* scan all interfaces */
for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
pim_ifp = ifp->info;
if (!pim_ifp)
continue;
/* scan per-interface (S,G) state */
for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
if (ch->upstream != up)
continue;
if (pim_macro_chisin_oiflist(ch))
pim_forward_start(ch);
} /* scan iface channel list */
} /* scan iflist */
}
static void forward_off(struct pim_upstream *up)
{
struct listnode *ifnode;
struct listnode *ifnextnode;
struct listnode *chnode;
struct listnode *chnextnode;
struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_ifchannel *ch;
/* scan all interfaces */
for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
pim_ifp = ifp->info;
if (!pim_ifp)
continue;
/* scan per-interface (S,G) state */
for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
if (ch->upstream != up)
continue;
pim_forward_stop(ch);
} /* scan iface channel list */
} /* scan iflist */
}
static void pim_upstream_switch(struct pim_upstream *up,
enum pim_upstream_state new_state)
{
enum pim_upstream_state old_state = up->join_state;
zassert(old_state != new_state);
up->join_state = new_state;
up->state_transition = pim_time_monotonic_sec();
if (PIM_DEBUG_PIM_EVENTS) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
zlog_debug("%s: PIM_UPSTREAM_%s: (S,G)=(%s,%s)",
__PRETTY_FUNCTION__,
((new_state == PIM_UPSTREAM_JOINED) ? "JOINED" : "NOTJOINED"),
src_str, grp_str);
}
pim_upstream_update_assert_tracking_desired(up);
if (new_state == PIM_UPSTREAM_JOINED) {
forward_on(up);
send_join(up);
join_timer_start(up);
}
else {
forward_off(up);
pim_joinprune_send(up->rpf.source_nexthop.interface,
up->rpf.rpf_addr,
up->source_addr,
up->group_addr,
0 /* prune */);
zassert(up->t_join_timer);
THREAD_OFF(up->t_join_timer);
}
}
static struct pim_upstream *pim_upstream_new(struct in_addr source_addr,
struct in_addr group_addr)
{
struct pim_upstream *up;
up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
if (!up) {
zlog_err("%s: PIM XMALLOC(%zu) failure",
__PRETTY_FUNCTION__, sizeof(*up));
return 0;
}
up->source_addr = source_addr;
up->group_addr = group_addr;
up->flags = 0;
up->ref_count = 1;
up->t_join_timer = 0;
up->join_state = 0;
up->state_transition = pim_time_monotonic_sec();
up->channel_oil = 0;
up->rpf.source_nexthop.interface = 0;
up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference;
up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric;
up->rpf.rpf_addr.s_addr = PIM_NET_INADDR_ANY;
pim_rpf_update(up, 0);
listnode_add(qpim_upstream_list, up);
return up;
}
struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
struct in_addr group_addr)
{
struct listnode *up_node;
struct pim_upstream *up;
for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
if (
(source_addr.s_addr == up->source_addr.s_addr) &&
(group_addr.s_addr == up->group_addr.s_addr)
) {
return up;
}
}
return 0;
}
struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
struct in_addr group_addr)
{
struct pim_upstream *up;
up = pim_upstream_find(source_addr, group_addr);
if (up) {
++up->ref_count;
}
else {
up = pim_upstream_new(source_addr, group_addr);
}
return up;
}
void pim_upstream_del(struct pim_upstream *up)
{
--up->ref_count;
if (up->ref_count < 1) {
pim_upstream_delete(up);
}
}
/*
Evaluate JoinDesired(S,G):
JoinDesired(S,G) is true if there is a downstream (S,G) interface I
in the set:
inherited_olist(S,G) =
joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
JoinDesired(S,G) may be affected by changes in the following:
pim_ifp->primary_address
pim_ifp->pim_dr_addr
ch->ifassert_winner_metric
ch->ifassert_winner
ch->local_ifmembership
ch->ifjoin_state
ch->upstream->rpf.source_nexthop.mrib_metric_preference
ch->upstream->rpf.source_nexthop.mrib_route_metric
ch->upstream->rpf.source_nexthop.interface
See also pim_upstream_update_join_desired() below.
*/
int pim_upstream_evaluate_join_desired(struct pim_upstream *up)
{
struct listnode *ifnode;
struct listnode *ifnextnode;
struct listnode *chnode;
struct listnode *chnextnode;
struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_ifchannel *ch;
/* scan all interfaces */
for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
pim_ifp = ifp->info;
if (!pim_ifp)
continue;
/* scan per-interface (S,G) state */
for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
if (ch->upstream != up)
continue;
if (pim_macro_ch_lost_assert(ch))
continue; /* keep searching */
if (pim_macro_chisin_joins_or_include(ch))
return 1; /* true */
} /* scan iface channel list */
} /* scan iflist */
return 0; /* false */
}
/*
See also pim_upstream_evaluate_join_desired() above.
*/
void pim_upstream_update_join_desired(struct pim_upstream *up)
{
int was_join_desired; /* boolean */
int is_join_desired; /* boolean */
was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags);
is_join_desired = pim_upstream_evaluate_join_desired(up);
if (is_join_desired)
PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags);
else
PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
/* switched from false to true */
if (is_join_desired && !was_join_desired) {
zassert(up->join_state == PIM_UPSTREAM_NOTJOINED);
pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
return;
}
/* switched from true to false */
if (!is_join_desired && was_join_desired) {
zassert(up->join_state == PIM_UPSTREAM_JOINED);
pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED);
return;
}
}
/*
RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
Transitions from Joined State
RPF'(S,G) GenID changes
The upstream (S,G) state machine remains in Joined state. If the
Join Timer is set to expire in more than t_override seconds, reset
it so that it expires after t_override seconds.
*/
void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
{
struct listnode *up_node;
struct listnode *up_nextnode;
struct pim_upstream *up;
/*
Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
*/
for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
if (PIM_DEBUG_PIM_TRACE) {
char neigh_str[100];
char src_str[100];
char grp_str[100];
char rpf_addr_str[100];
pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
zlog_debug("%s: matching neigh=%s against upstream (S,G)=(%s,%s) joined=%d rpf_addr=%s",
__PRETTY_FUNCTION__,
neigh_str, src_str, grp_str,
up->join_state == PIM_UPSTREAM_JOINED,
rpf_addr_str);
}
/* consider only (S,G) upstream in Joined state */
if (up->join_state != PIM_UPSTREAM_JOINED)
continue;
/* match RPF'(S,G)=neigh_addr */
if (up->rpf.rpf_addr.s_addr != neigh_addr.s_addr)
continue;
pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
up, neigh_addr);
}
}
void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
struct interface *old_rpf_ifp)
{
struct listnode *ifnode;
struct listnode *ifnextnode;
struct interface *ifp;
/* scan all interfaces */
for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
struct listnode *chnode;
struct listnode *chnextnode;
struct pim_ifchannel *ch;
struct pim_interface *pim_ifp;
pim_ifp = ifp->info;
if (!pim_ifp)
continue;
/* search all ifchannels */
for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
if (ch->upstream != up)
continue;
if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
if (
/* RPF_interface(S) was NOT I */
(old_rpf_ifp == ch->interface)
&&
/* RPF_interface(S) stopped being I */
(ch->upstream->rpf.source_nexthop.interface != ch->interface)
) {
assert_action_a5(ch);
}
} /* PIM_IFASSERT_I_AM_LOSER */
pim_ifchannel_update_assert_tracking_desired(ch);
}
}
}
void pim_upstream_update_could_assert(struct pim_upstream *up)
{
struct listnode *ifnode;
struct listnode *ifnextnode;
struct listnode *chnode;
struct listnode *chnextnode;
struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_ifchannel *ch;
/* scan all interfaces */
for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
pim_ifp = ifp->info;
if (!pim_ifp)
continue;
/* scan per-interface (S,G) state */
for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
if (ch->upstream != up)
continue;
pim_ifchannel_update_could_assert(ch);
} /* scan iface channel list */
} /* scan iflist */
}
void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
{
struct listnode *ifnode;
struct listnode *ifnextnode;
struct listnode *chnode;
struct listnode *chnextnode;
struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_ifchannel *ch;
/* scan all interfaces */
for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
pim_ifp = ifp->info;
if (!pim_ifp)
continue;
/* scan per-interface (S,G) state */
for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
if (ch->upstream != up)
continue;
pim_ifchannel_update_my_assert_metric(ch);
} /* scan iface channel list */
} /* scan iflist */
}
static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
{
struct listnode *ifnode;
struct listnode *ifnextnode;
struct listnode *chnode;
struct listnode *chnextnode;
struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_ifchannel *ch;
/* scan all interfaces */
for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
pim_ifp = ifp->info;
if (!pim_ifp)
continue;
/* scan per-interface (S,G) state */
for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
if (ch->upstream != up)
continue;
pim_ifchannel_update_assert_tracking_desired(ch);
} /* scan iface channel list */
} /* scan iflist */
}

122
pimd/pim_upstream.h Normal file
View file

@ -0,0 +1,122 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_UPSTREAM_H
#define PIM_UPSTREAM_H
#include <zebra.h>
#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED (1 << 0)
#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED (2 << 0)
#define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
#define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
/*
RFC 4601:
Metric Preference
Preference value assigned to the unicast routing protocol that
provided the route to the multicast source or Rendezvous-Point.
Metric
The unicast routing table metric associated with the route used to
reach the multicast source or Rendezvous-Point. The metric is in
units applicable to the unicast routing protocol used.
*/
struct pim_nexthop {
struct interface *interface; /* RPF_interface(S) */
struct in_addr mrib_nexthop_addr; /* MRIB.next_hop(S) */
uint32_t mrib_metric_preference; /* MRIB.pref(S) */
uint32_t mrib_route_metric; /* MRIB.metric(S) */
};
struct pim_rpf {
struct pim_nexthop source_nexthop;
struct in_addr rpf_addr; /* RPF'(S,G) */
};
enum pim_rpf_result {
PIM_RPF_OK = 0,
PIM_RPF_CHANGED,
PIM_RPF_FAILURE
};
enum pim_upstream_state {
PIM_UPSTREAM_NOTJOINED,
PIM_UPSTREAM_JOINED
};
/*
Upstream (S,G) channel in Joined state
(S,G) in the "Not Joined" state is not represented
See RFC 4601: 4.5.7. Sending (S,G) Join/Prune Message
*/
struct pim_upstream {
struct in_addr source_addr; /* (S,G) source key */
struct in_addr group_addr; /* (S,G) group key */
uint32_t flags;
struct channel_oil *channel_oil;
enum pim_upstream_state join_state;
int ref_count;
struct pim_rpf rpf;
struct thread *t_join_timer;
int64_t state_transition; /* Record current state uptime */
};
void pim_upstream_free(struct pim_upstream *up);
void pim_upstream_delete(struct pim_upstream *up);
struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
struct in_addr group_addr);
struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
struct in_addr group_addr);
void pim_upstream_del(struct pim_upstream *up);
int pim_upstream_evaluate_join_desired(struct pim_upstream *up);
void pim_upstream_update_join_desired(struct pim_upstream *up);
void pim_upstream_join_suppress(struct pim_upstream *up,
struct in_addr rpf_addr,
int holdtime);
void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
struct pim_upstream *up,
struct in_addr rpf_addr);
void pim_upstream_join_timer_restart(struct pim_upstream *up);
void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr);
void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
struct interface *old_rpf_ifp);
void pim_upstream_update_could_assert(struct pim_upstream *up);
void pim_upstream_update_my_assert_metric(struct pim_upstream *up);
#endif /* PIM_UPSTREAM_H */

122
pimd/pim_util.c Normal file
View file

@ -0,0 +1,122 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "log.h"
#include "pim_util.h"
/*
RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
If QQIC < 128, QQI = QQIC
If QQIC >= 128, QQI = (mant | 0x10) << (exp + 3)
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|1| exp | mant |
+-+-+-+-+-+-+-+-+
Since exp=0..7 then (exp+3)=3..10, then QQI has
one of the following bit patterns:
exp=0: QQI = 0000.0000.1MMM.M000
exp=1: QQI = 0000.0001.MMMM.0000
...
exp=6: QQI = 001M.MMM0.0000.0000
exp=7: QQI = 01MM.MM00.0000.0000
--------- ---------
0x4 0x0 0x0 0x0
*/
uint8_t igmp_msg_encode16to8(uint16_t value)
{
uint8_t code;
if (value < 128) {
code = value;
}
else {
uint16_t mask = 0x4000;
uint8_t exp;
uint16_t mant;
for (exp = 7; exp > 0; --exp) {
if (mask & value)
break;
mask >>= 1;
}
mant = 0x000F & (value >> (exp + 3));
code = ((uint8_t) 1 << 7) | ((uint8_t) exp << 4) | (uint8_t) mant;
}
return code;
}
/*
RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
If QQIC < 128, QQI = QQIC
If QQIC >= 128, QQI = (mant | 0x10) << (exp + 3)
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|1| exp | mant |
+-+-+-+-+-+-+-+-+
*/
uint16_t igmp_msg_decode8to16(uint8_t code)
{
uint16_t value;
if (code < 128) {
value = code;
}
else {
uint16_t mant = (code & 0x0F);
uint8_t exp = (code & 0x70) >> 4;
value = (mant | 0x10) << (exp + 3);
}
return value;
}
void pim_pkt_dump(const char *label, const uint8_t *buf, int size)
{
char dump_buf[1000];
int i = 0;
int j = 0;
for (; i < size; ++i, j += 2) {
int left = sizeof(dump_buf) - j;
if (left < 4) {
if (left > 1) {
strcat(dump_buf + j, "!"); /* mark as truncated */
}
break;
}
snprintf(dump_buf + j, left, "%02x", buf[i]);
}
zlog_debug("%s: pkt dump size=%d: %s",
label,
size,
dump_buf);
}

37
pimd/pim_util.h Normal file
View file

@ -0,0 +1,37 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_UTIL_H
#define PIM_UTIL_H
#include <stdint.h>
#include <zebra.h>
#include "checksum.h"
uint8_t igmp_msg_encode16to8(uint16_t value);
uint16_t igmp_msg_decode8to16(uint8_t code);
void pim_pkt_dump(const char *label, const uint8_t *buf, int size);
#endif /* PIM_UTIL_H */

25
pimd/pim_version.c Normal file
View file

@ -0,0 +1,25 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include "pim_version.h"
const char * const PIMD_VERSION = PIMD_VERSION_STR;

30
pimd/pim_version.h Normal file
View file

@ -0,0 +1,30 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_VERSION_H
#define PIM_VERSION_H
#define PIMD_VERSION_STR "0.166"
const char * const PIMD_VERSION;
#endif /* PIM_VERSION_H */

178
pimd/pim_vty.c Normal file
View file

@ -0,0 +1,178 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "if.h"
#include "linklist.h"
#include "pimd.h"
#include "pim_vty.h"
#include "pim_iface.h"
#include "pim_cmd.h"
#include "pim_str.h"
#include "pim_ssmpingd.h"
int pim_debug_config_write(struct vty *vty)
{
int writes = 0;
if (PIM_DEBUG_IGMP_EVENTS) {
vty_out(vty, "debug igmp events%s", VTY_NEWLINE);
++writes;
}
if (PIM_DEBUG_IGMP_PACKETS) {
vty_out(vty, "debug igmp packets%s", VTY_NEWLINE);
++writes;
}
if (PIM_DEBUG_IGMP_TRACE) {
vty_out(vty, "debug igmp trace%s", VTY_NEWLINE);
++writes;
}
if (PIM_DEBUG_MROUTE) {
vty_out(vty, "debug mroute%s", VTY_NEWLINE);
++writes;
}
if (PIM_DEBUG_PIM_EVENTS) {
vty_out(vty, "debug pim events%s", VTY_NEWLINE);
++writes;
}
if (PIM_DEBUG_PIM_PACKETS) {
vty_out(vty, "debug pim packets%s", VTY_NEWLINE);
++writes;
}
if (PIM_DEBUG_PIM_PACKETDUMP_SEND) {
vty_out(vty, "debug pim packet-dump send%s", VTY_NEWLINE);
++writes;
}
if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
vty_out(vty, "debug pim packet-dump receive%s", VTY_NEWLINE);
++writes;
}
if (PIM_DEBUG_PIM_TRACE) {
vty_out(vty, "debug pim trace%s", VTY_NEWLINE);
++writes;
}
if (PIM_DEBUG_ZEBRA) {
vty_out(vty, "debug pim zebra%s", VTY_NEWLINE);
++writes;
}
if (PIM_DEBUG_SSMPINGD) {
vty_out(vty, "debug ssmpingd%s", VTY_NEWLINE);
++writes;
}
return writes;
}
int pim_global_config_write(struct vty *vty)
{
int writes = 0;
if (PIM_MROUTE_IS_ENABLED) {
vty_out(vty, "%s%s", PIM_CMD_IP_MULTICAST_ROUTING, VTY_NEWLINE);
++writes;
}
if (qpim_ssmpingd_list) {
struct listnode *node;
struct ssmpingd_sock *ss;
vty_out(vty, "!%s", VTY_NEWLINE);
++writes;
for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) {
char source_str[100];
pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
vty_out(vty, "ip ssmpingd %s%s", source_str, VTY_NEWLINE);
++writes;
}
}
return writes;
}
int pim_interface_config_write(struct vty *vty)
{
int writes = 0;
struct listnode *node;
struct interface *ifp;
for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
/* IF name */
vty_out(vty, "interface %s%s", ifp->name, VTY_NEWLINE);
++writes;
if (ifp->info) {
struct pim_interface *pim_ifp = ifp->info;
/* IF ip pim ssm */
if (PIM_IF_TEST_PIM(pim_ifp->options)) {
vty_out(vty, " ip pim ssm%s", VTY_NEWLINE);
++writes;
}
/* IF ip igmp */
if (PIM_IF_TEST_IGMP(pim_ifp->options)) {
vty_out(vty, " ip igmp%s", VTY_NEWLINE);
++writes;
}
/* IF ip igmp query-interval */
vty_out(vty, " %s %d%s",
PIM_CMD_IP_IGMP_QUERY_INTERVAL,
pim_ifp->igmp_default_query_interval,
VTY_NEWLINE);
++writes;
/* IF ip igmp query-max-response-time */
vty_out(vty, " %s %d%s",
PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC,
pim_ifp->igmp_query_max_response_time_dsec,
VTY_NEWLINE);
++writes;
/* IF ip igmp join */
if (pim_ifp->igmp_join_list) {
struct listnode *node;
struct igmp_join *ij;
for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_join_list, node, ij)) {
char group_str[100];
char source_str[100];
pim_inet4_dump("<grp?>", ij->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<src?>", ij->source_addr, source_str, sizeof(source_str));
vty_out(vty, " ip igmp join %s %s%s",
group_str, source_str,
VTY_NEWLINE);
++writes;
}
}
}
vty_out(vty, "!%s", VTY_NEWLINE);
++writes;
}
return writes;
}

32
pimd/pim_vty.h Normal file
View file

@ -0,0 +1,32 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_VTY_H
#define PIM_VTY_H
#include "vty.h"
int pim_debug_config_write(struct vty *vty);
int pim_global_config_write(struct vty *vty);
int pim_interface_config_write(struct vty *vty);
#endif /* PIM_VTY_H */

1312
pimd/pim_zebra.c Normal file

File diff suppressed because it is too large Load diff

42
pimd/pim_zebra.h Normal file
View file

@ -0,0 +1,42 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_ZEBRA_H
#define PIM_ZEBRA_H
#include "pim_igmp.h"
#include "pim_ifchannel.h"
void pim_zebra_init(char *zebra_sock_path);
void pim_scan_oil(void);
void igmp_anysource_forward_start(struct igmp_group *group);
void igmp_anysource_forward_stop(struct igmp_group *group);
void igmp_source_forward_start(struct igmp_source *source);
void igmp_source_forward_stop(struct igmp_source *source);
void pim_forward_start(struct pim_ifchannel *ch);
void pim_forward_stop(struct pim_ifchannel *ch);
#endif /* PIM_ZEBRA_H */

457
pimd/pim_zlookup.c Normal file
View file

@ -0,0 +1,457 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "zebra/rib.h"
#include "log.h"
#include "prefix.h"
#include "zclient.h"
#include "stream.h"
#include "network.h"
#include "thread.h"
#include "pimd.h"
#include "pim_pim.h"
#include "pim_str.h"
#include "pim_zlookup.h"
extern int zclient_debug;
static void zclient_lookup_sched(struct zclient *zlookup, int delay);
/* Connect to zebra for nexthop lookup. */
static int zclient_lookup_connect(struct thread *t)
{
struct zclient *zlookup;
zlookup = THREAD_ARG(t);
zlookup->t_connect = NULL;
if (zlookup->sock >= 0) {
return 0;
}
if (zclient_socket_connect(zlookup) < 0) {
++zlookup->fail;
zlog_warn("%s: failure connecting zclient socket: failures=%d",
__PRETTY_FUNCTION__, zlookup->fail);
}
else {
zlookup->fail = 0; /* reset counter on connection */
}
zassert(!zlookup->t_connect);
if (zlookup->sock < 0) {
/* Since last connect failed, retry within 10 secs */
zclient_lookup_sched(zlookup, 10);
return -1;
}
return 0;
}
/* Schedule connection with delay. */
static void zclient_lookup_sched(struct zclient *zlookup, int delay)
{
zassert(!zlookup->t_connect);
THREAD_TIMER_ON(master, zlookup->t_connect,
zclient_lookup_connect,
zlookup, delay);
zlog_notice("%s: zclient lookup connection scheduled for %d seconds",
__PRETTY_FUNCTION__, delay);
}
/* Schedule connection for now. */
static void zclient_lookup_sched_now(struct zclient *zlookup)
{
zassert(!zlookup->t_connect);
zlookup->t_connect = thread_add_event(master, zclient_lookup_connect,
zlookup, 0);
zlog_notice("%s: zclient lookup immediate connection scheduled",
__PRETTY_FUNCTION__);
}
/* Schedule reconnection, if needed. */
static void zclient_lookup_reconnect(struct zclient *zlookup)
{
if (zlookup->t_connect) {
return;
}
zclient_lookup_sched_now(zlookup);
}
static void zclient_lookup_failed(struct zclient *zlookup)
{
if (zlookup->sock >= 0) {
if (close(zlookup->sock)) {
zlog_warn("%s: closing fd=%d: errno=%d %s", __func__, zlookup->sock,
errno, safe_strerror(errno));
}
zlookup->sock = -1;
}
zclient_lookup_reconnect(zlookup);
}
struct zclient *zclient_lookup_new()
{
struct zclient *zlookup;
zlookup = zclient_new();
if (!zlookup) {
zlog_err("%s: zclient_new() failure",
__PRETTY_FUNCTION__);
return 0;
}
zlookup->sock = -1;
zlookup->ibuf = stream_new(ZEBRA_MAX_PACKET_SIZ);
zlookup->obuf = stream_new(ZEBRA_MAX_PACKET_SIZ);
zlookup->t_connect = 0;
zclient_lookup_sched_now(zlookup);
zlog_notice("%s: zclient lookup socket initialized",
__PRETTY_FUNCTION__);
return zlookup;
}
static int zclient_read_nexthop(struct zclient *zlookup,
struct pim_zlookup_nexthop nexthop_tab[],
const int tab_size,
struct in_addr addr)
{
int num_ifindex = 0;
struct stream *s;
const uint16_t MIN_LEN = 14; /* getc=1 getc=1 getw=2 getipv4=4 getc=1 getl=4 getc=1 */
uint16_t length, len;
u_char marker;
u_char version;
uint16_t command;
int nbytes;
struct in_addr raddr;
uint8_t distance;
uint32_t metric;
int nexthop_num;
int i;
if (PIM_DEBUG_ZEBRA) {
char addr_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_debug("%s: addr=%s",
__PRETTY_FUNCTION__,
addr_str);
}
s = zlookup->ibuf;
stream_reset(s);
nbytes = stream_read(s, zlookup->sock, 2);
if (nbytes < 2) {
zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d",
__FILE__, __PRETTY_FUNCTION__, nbytes);
zclient_lookup_failed(zlookup);
return -1;
}
length = stream_getw(s);
len = length - 2;
if (len < MIN_LEN) {
zlog_err("%s %s: failure reading zclient lookup socket: len=%d < MIN_LEN=%d",
__FILE__, __PRETTY_FUNCTION__, len, MIN_LEN);
zclient_lookup_failed(zlookup);
return -2;
}
nbytes = stream_read(s, zlookup->sock, len);
if (nbytes < (length - 2)) {
zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d < len=%d",
__FILE__, __PRETTY_FUNCTION__, nbytes, len);
zclient_lookup_failed(zlookup);
return -3;
}
marker = stream_getc(s);
version = stream_getc(s);
if (version != ZSERV_VERSION || marker != ZEBRA_HEADER_MARKER) {
zlog_err("%s: socket %d version mismatch, marker %d, version %d",
__func__, zlookup->sock, marker, version);
return -4;
}
command = stream_getw(s);
if (command != ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB) {
zlog_err("%s: socket %d command mismatch: %d",
__func__, zlookup->sock, command);
return -5;
}
raddr.s_addr = stream_get_ipv4(s);
if (raddr.s_addr != addr.s_addr) {
char addr_str[100];
char raddr_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
pim_inet4_dump("<raddr?>", raddr, raddr_str, sizeof(raddr_str));
zlog_warn("%s: address mismatch: addr=%s raddr=%s",
__PRETTY_FUNCTION__,
addr_str, raddr_str);
/* warning only */
}
distance = stream_getc(s);
metric = stream_getl(s);
nexthop_num = stream_getc(s);
if (nexthop_num < 1) {
zlog_err("%s: socket %d bad nexthop_num=%d",
__func__, zlookup->sock, nexthop_num);
return -6;
}
len -= MIN_LEN;
for (i = 0; i < nexthop_num; ++i) {
enum nexthop_types_t nexthop_type;
if (len < 1) {
zlog_err("%s: socket %d empty input expecting nexthop_type: len=%d",
__func__, zlookup->sock, len);
return -7;
}
nexthop_type = stream_getc(s);
--len;
switch (nexthop_type) {
case ZEBRA_NEXTHOP_IFINDEX:
case ZEBRA_NEXTHOP_IFNAME:
case ZEBRA_NEXTHOP_IPV4_IFINDEX:
if (num_ifindex >= tab_size) {
char addr_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
__FILE__, __PRETTY_FUNCTION__,
(num_ifindex + 1), tab_size, addr_str);
return num_ifindex;
}
if (nexthop_type == ZEBRA_NEXTHOP_IPV4_IFINDEX) {
if (len < 4) {
zlog_err("%s: socket %d short input expecting nexthop IPv4-addr: len=%d",
__func__, zlookup->sock, len);
return -8;
}
nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
len -= 4;
}
else {
nexthop_tab[num_ifindex].nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
}
nexthop_tab[num_ifindex].ifindex = stream_getl(s);
nexthop_tab[num_ifindex].protocol_distance = distance;
nexthop_tab[num_ifindex].route_metric = metric;
++num_ifindex;
break;
case ZEBRA_NEXTHOP_IPV4:
if (num_ifindex >= tab_size) {
char addr_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
__FILE__, __PRETTY_FUNCTION__,
(num_ifindex + 1), tab_size, addr_str);
return num_ifindex;
}
nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
len -= 4;
nexthop_tab[num_ifindex].ifindex = 0;
nexthop_tab[num_ifindex].protocol_distance = distance;
nexthop_tab[num_ifindex].route_metric = metric;
{
char addr_str[100];
char nexthop_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
pim_inet4_dump("<nexthop?>", nexthop_tab[num_ifindex].nexthop_addr, nexthop_str, sizeof(nexthop_str));
zlog_warn("%s %s: zebra returned recursive nexthop %s for address %s",
__FILE__, __PRETTY_FUNCTION__,
nexthop_str, addr_str);
}
++num_ifindex;
break;
default:
/* do nothing */
{
char addr_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_warn("%s %s: found non-ifindex nexthop type=%d for address %s",
__FILE__, __PRETTY_FUNCTION__,
nexthop_type, addr_str);
}
break;
}
}
return num_ifindex;
}
static int zclient_lookup_nexthop_once(struct zclient *zlookup,
struct pim_zlookup_nexthop nexthop_tab[],
const int tab_size,
struct in_addr addr)
{
struct stream *s;
int ret;
if (PIM_DEBUG_ZEBRA) {
char addr_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_debug("%s: addr=%s",
__PRETTY_FUNCTION__,
addr_str);
}
/* Check socket. */
if (zlookup->sock < 0) {
zlog_err("%s %s: zclient lookup socket is not connected",
__FILE__, __PRETTY_FUNCTION__);
zclient_lookup_failed(zlookup);
return -1;
}
s = zlookup->obuf;
stream_reset(s);
zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB);
stream_put_in_addr(s, &addr);
stream_putw_at(s, 0, stream_get_endp(s));
ret = writen(zlookup->sock, s->data, stream_get_endp(s));
if (ret < 0) {
zlog_err("%s %s: writen() failure writing to zclient lookup socket",
__FILE__, __PRETTY_FUNCTION__);
zclient_lookup_failed(zlookup);
return -2;
}
if (ret == 0) {
zlog_err("%s %s: connection closed on zclient lookup socket",
__FILE__, __PRETTY_FUNCTION__);
zclient_lookup_failed(zlookup);
return -3;
}
return zclient_read_nexthop(zlookup, nexthop_tab,
tab_size, addr);
}
int zclient_lookup_nexthop(struct zclient *zlookup,
struct pim_zlookup_nexthop nexthop_tab[],
const int tab_size,
struct in_addr addr,
int max_lookup)
{
int lookup;
uint32_t route_metric = 0xFFFFFFFF;
uint8_t protocol_distance = 0xFF;
for (lookup = 0; lookup < max_lookup; ++lookup) {
int num_ifindex;
int first_ifindex;
struct in_addr nexthop_addr;
num_ifindex = zclient_lookup_nexthop_once(qpim_zclient_lookup, nexthop_tab,
PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr);
if (num_ifindex < 1) {
char addr_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_warn("%s %s: lookup=%d/%d: could not find nexthop ifindex for address %s",
__FILE__, __PRETTY_FUNCTION__,
lookup, max_lookup, addr_str);
return -1;
}
if (lookup < 1) {
/* this is the non-recursive lookup - save original metric/distance */
route_metric = nexthop_tab[0].route_metric;
protocol_distance = nexthop_tab[0].protocol_distance;
}
/*
FIXME: Non-recursive nexthop ensured only for first ifindex.
However, recursive route lookup should really be fixed in zebra daemon.
See also TODO T24.
*/
first_ifindex = nexthop_tab[0].ifindex;
nexthop_addr = nexthop_tab[0].nexthop_addr;
if (first_ifindex > 0) {
/* found: first ifindex is non-recursive nexthop */
if (lookup > 0) {
/* Report non-recursive success after first lookup */
char addr_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_info("%s %s: lookup=%d/%d: found non-recursive ifindex=%d for address %s dist=%d met=%d",
__FILE__, __PRETTY_FUNCTION__,
lookup, max_lookup, first_ifindex, addr_str,
nexthop_tab[0].protocol_distance,
nexthop_tab[0].route_metric);
/* use last address as nexthop address */
nexthop_tab[0].nexthop_addr = addr;
/* report original route metric/distance */
nexthop_tab[0].route_metric = route_metric;
nexthop_tab[0].protocol_distance = protocol_distance;
}
return num_ifindex;
}
{
char addr_str[100];
char nexthop_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
pim_inet4_dump("<nexthop?>", nexthop_addr, nexthop_str, sizeof(nexthop_str));
zlog_warn("%s %s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s dist=%d met=%d",
__FILE__, __PRETTY_FUNCTION__,
lookup, max_lookup, nexthop_str, addr_str,
nexthop_tab[0].protocol_distance,
nexthop_tab[0].route_metric);
}
addr = nexthop_addr; /* use nexthop addr for recursive lookup */
} /* for (max_lookup) */
char addr_str[100];
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
zlog_warn("%s %s: lookup=%d/%d: failure searching recursive nexthop ifindex for address %s",
__FILE__, __PRETTY_FUNCTION__,
lookup, max_lookup, addr_str);
return -2;
}

47
pimd/pim_zlookup.h Normal file
View file

@ -0,0 +1,47 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIM_ZLOOKUP_H
#define PIM_ZLOOKUP_H
#include <zebra.h>
#include "zclient.h"
#define PIM_NEXTHOP_LOOKUP_MAX (3) /* max. recursive route lookup */
struct pim_zlookup_nexthop {
struct in_addr nexthop_addr;
int ifindex;
uint32_t route_metric;
uint8_t protocol_distance;
};
struct zclient *zclient_lookup_new(void);
int zclient_lookup_nexthop(struct zclient *zlookup,
struct pim_zlookup_nexthop nexthop_tab[],
const int tab_size,
struct in_addr addr,
int max_lookup);
#endif /* PIM_ZLOOKUP_H */

141
pimd/pimd.c Normal file
View file

@ -0,0 +1,141 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "log.h"
#include "memory.h"
#include "pimd.h"
#include "pim_cmd.h"
#include "pim_iface.h"
#include "pim_zebra.h"
#include "pim_str.h"
#include "pim_oil.h"
#include "pim_pim.h"
#include "pim_upstream.h"
#include "pim_rand.h"
#include "pim_rpf.h"
#include "pim_ssmpingd.h"
const char *const PIM_ALL_SYSTEMS = MCAST_ALL_SYSTEMS;
const char *const PIM_ALL_ROUTERS = MCAST_ALL_ROUTERS;
const char *const PIM_ALL_PIM_ROUTERS = MCAST_ALL_PIM_ROUTERS;
const char *const PIM_ALL_IGMP_ROUTERS = MCAST_ALL_IGMP_ROUTERS;
struct thread_master *master = 0;
uint32_t qpim_debugs = 0;
int qpim_mroute_socket_fd = -1;
int64_t qpim_mroute_socket_creation = 0; /* timestamp of creation */
struct thread *qpim_mroute_socket_reader = 0;
int qpim_mroute_oif_highest_vif_index = -1;
struct list *qpim_channel_oil_list = 0;
int qpim_t_periodic = PIM_DEFAULT_T_PERIODIC; /* Period between Join/Prune Messages */
struct list *qpim_upstream_list = 0;
struct zclient *qpim_zclient_update = 0;
struct zclient *qpim_zclient_lookup = 0;
struct pim_assert_metric qpim_infinite_assert_metric;
long qpim_rpf_cache_refresh_delay_msec = 10000;
struct thread *qpim_rpf_cache_refresher = 0;
int64_t qpim_rpf_cache_refresh_requests = 0;
int64_t qpim_rpf_cache_refresh_events = 0;
int64_t qpim_rpf_cache_refresh_last = 0;
struct in_addr qpim_inaddr_any;
struct list *qpim_ssmpingd_list = 0;
struct in_addr qpim_ssmpingd_group_addr;
int64_t qpim_scan_oil_events = 0;
int64_t qpim_scan_oil_last = 0;
int64_t qpim_mroute_add_events = 0;
int64_t qpim_mroute_add_last = 0;
int64_t qpim_mroute_del_events = 0;
int64_t qpim_mroute_del_last = 0;
static void pim_free()
{
pim_ssmpingd_destroy();
if (qpim_channel_oil_list)
list_free(qpim_channel_oil_list);
if (qpim_upstream_list)
list_free(qpim_upstream_list);
}
void pim_init()
{
pim_rand_init();
if (!inet_aton(PIM_ALL_PIM_ROUTERS, &qpim_all_pim_routers_addr)) {
zlog_err("%s %s: could not solve %s to group address: errno=%d: %s",
__FILE__, __PRETTY_FUNCTION__,
PIM_ALL_PIM_ROUTERS, errno, safe_strerror(errno));
zassert(0);
return;
}
qpim_channel_oil_list = list_new();
if (!qpim_channel_oil_list) {
zlog_err("%s %s: failure: channel_oil_list=list_new()",
__FILE__, __PRETTY_FUNCTION__);
return;
}
qpim_channel_oil_list->del = (void (*)(void *)) pim_channel_oil_free;
qpim_upstream_list = list_new();
if (!qpim_upstream_list) {
zlog_err("%s %s: failure: upstream_list=list_new()",
__FILE__, __PRETTY_FUNCTION__);
pim_free();
return;
}
qpim_upstream_list->del = (void (*)(void *)) pim_upstream_free;
qpim_mroute_socket_fd = -1; /* mark mroute as disabled */
qpim_mroute_oif_highest_vif_index = -1;
zassert(!qpim_debugs);
zassert(!PIM_MROUTE_IS_ENABLED);
qpim_inaddr_any.s_addr = PIM_NET_INADDR_ANY;
/*
RFC 4601: 4.6.3. Assert Metrics
assert_metric
infinite_assert_metric() {
return {1,infinity,infinity,0}
}
*/
qpim_infinite_assert_metric.rpt_bit_flag = 1;
qpim_infinite_assert_metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX;
qpim_infinite_assert_metric.route_metric = PIM_ASSERT_ROUTE_METRIC_MAX;
qpim_infinite_assert_metric.ip_address = qpim_inaddr_any;
pim_if_init();
pim_cmd_init();
pim_ssmpingd_init();
}
void pim_terminate()
{
pim_free();
}

41
pimd/pimd.conf.sample Normal file
View file

@ -0,0 +1,41 @@
!
! pimd sample configuration file
! $QuaggaId: $Format:%an, %ai, %h$ $
!
hostname quagga-pimd-router
password zebra
!enable password zebra
!
!log file pimd.log
log stdout
!
line vty
exec-timeout 60
!
!debug igmp
!debug pim
!debug pim zebra
!
ip multicast-routing
!
! ! You may want to enable ssmpingd for troubleshooting
! ! See http://www.venaas.no/multicast/ssmping/
! !
! ip ssmpingd 1.1.1.1
! ip ssmpingd 2.2.2.2
!
! ! HINTS:
! ! - Enable "ip pim ssm" on the interface directly attached to the
! ! multicast source host (if this is the first-hop router)
! ! - Enable "ip pim ssm" on pim-routers-facing interfaces
! ! - Enable "ip igmp" on IGMPv3-hosts-facing interfaces
! ! - In order to inject IGMPv3 local membership information in the
! ! PIM protocol state, enable both "ip pim ssm" and "ip igmp" on
! ! the same interface; otherwise PIM won't advertise
! ! IGMPv3-learned membership to other PIM routers
!
interface eth0
ip pim ssm
ip igmp
! -x-

157
pimd/pimd.h Normal file
View file

@ -0,0 +1,157 @@
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
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; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#ifndef PIMD_H
#define PIMD_H
#include <stdint.h>
#include "pim_mroute.h"
#include "pim_assert.h"
#define PIMD_PROGNAME "pimd"
#define PIMD_DEFAULT_CONFIG "pimd.conf"
#define PIMD_VTY_PORT 2611
#define PIMD_BUG_ADDRESS "https://github.com/udhos/qpimd"
#define PIM_IP_HEADER_MIN_LEN (20)
#define PIM_IP_HEADER_MAX_LEN (60)
#define PIM_IP_PROTO_IGMP (2)
#define PIM_IP_PROTO_PIM (103)
#define PIM_IGMP_MIN_LEN (8)
#define PIM_MSG_HEADER_LEN (4)
#define PIM_PIM_MIN_LEN PIM_MSG_HEADER_LEN
#define PIM_PROTO_VERSION (2)
#define MCAST_ALL_SYSTEMS "224.0.0.1"
#define MCAST_ALL_ROUTERS "224.0.0.2"
#define MCAST_ALL_PIM_ROUTERS "224.0.0.13"
#define MCAST_ALL_IGMP_ROUTERS "224.0.0.22"
#define PIM_FORCE_BOOLEAN(expr) ((expr) != 0)
#define PIM_NET_INADDR_ANY (htonl(INADDR_ANY))
#define PIM_INADDR_IS_ANY(addr) ((addr).s_addr == PIM_NET_INADDR_ANY) /* struct in_addr addr */
#define PIM_INADDR_ISNOT_ANY(addr) ((addr).s_addr != PIM_NET_INADDR_ANY) /* struct in_addr addr */
#define PIM_MASK_PIM_EVENTS (1 << 0)
#define PIM_MASK_PIM_PACKETS (1 << 1)
#define PIM_MASK_PIM_PACKETDUMP_SEND (1 << 2)
#define PIM_MASK_PIM_PACKETDUMP_RECV (1 << 3)
#define PIM_MASK_PIM_TRACE (1 << 4)
#define PIM_MASK_IGMP_EVENTS (1 << 5)
#define PIM_MASK_IGMP_PACKETS (1 << 6)
#define PIM_MASK_IGMP_TRACE (1 << 7)
#define PIM_MASK_ZEBRA (1 << 8)
#define PIM_MASK_SSMPINGD (1 << 9)
#define PIM_MASK_MROUTE (1 << 10)
#define PIM_MASK_PIM_HELLO (1 << 11)
#define PIM_MASK_PIM_J_P (1 << 12)
const char *const PIM_ALL_SYSTEMS;
const char *const PIM_ALL_ROUTERS;
const char *const PIM_ALL_PIM_ROUTERS;
const char *const PIM_ALL_IGMP_ROUTERS;
struct thread_master *master;
uint32_t qpim_debugs;
int qpim_mroute_socket_fd;
int64_t qpim_mroute_socket_creation; /* timestamp of creation */
struct thread *qpim_mroute_socket_reader;
int qpim_mroute_oif_highest_vif_index;
struct list *qpim_channel_oil_list; /* list of struct channel_oil */
struct in_addr qpim_all_pim_routers_addr;
int qpim_t_periodic; /* Period between Join/Prune Messages */
struct list *qpim_upstream_list; /* list of struct pim_upstream */
struct zclient *qpim_zclient_update;
struct zclient *qpim_zclient_lookup;
struct pim_assert_metric qpim_infinite_assert_metric;
long qpim_rpf_cache_refresh_delay_msec;
struct thread *qpim_rpf_cache_refresher;
int64_t qpim_rpf_cache_refresh_requests;
int64_t qpim_rpf_cache_refresh_events;
int64_t qpim_rpf_cache_refresh_last;
struct in_addr qpim_inaddr_any;
struct list *qpim_ssmpingd_list; /* list of struct ssmpingd_sock */
struct in_addr qpim_ssmpingd_group_addr;
int64_t qpim_scan_oil_events;
int64_t qpim_scan_oil_last;
int64_t qpim_mroute_add_events;
int64_t qpim_mroute_add_last;
int64_t qpim_mroute_del_events;
int64_t qpim_mroute_del_last;
#define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2)
#define PIM_MROUTE_IS_ENABLED (qpim_mroute_socket_fd >= 0)
#define PIM_MROUTE_IS_DISABLED (qpim_mroute_socket_fd < 0)
#define PIM_DEBUG_PIM_EVENTS (qpim_debugs & PIM_MASK_PIM_EVENTS)
#define PIM_DEBUG_PIM_PACKETS (qpim_debugs & PIM_MASK_PIM_PACKETS)
#define PIM_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs & PIM_MASK_PIM_PACKETDUMP_SEND)
#define PIM_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs & PIM_MASK_PIM_PACKETDUMP_RECV)
#define PIM_DEBUG_PIM_TRACE (qpim_debugs & PIM_MASK_PIM_TRACE)
#define PIM_DEBUG_IGMP_EVENTS (qpim_debugs & PIM_MASK_IGMP_EVENTS)
#define PIM_DEBUG_IGMP_PACKETS (qpim_debugs & PIM_MASK_IGMP_PACKETS)
#define PIM_DEBUG_IGMP_TRACE (qpim_debugs & PIM_MASK_IGMP_TRACE)
#define PIM_DEBUG_ZEBRA (qpim_debugs & PIM_MASK_ZEBRA)
#define PIM_DEBUG_SSMPINGD (qpim_debugs & PIM_MASK_SSMPINGD)
#define PIM_DEBUG_MROUTE (qpim_debugs & PIM_MASK_MROUTE)
#define PIM_DEBUG_PIM_HELLO (qpim_debugs & PIM_MASK_PIM_HELLO)
#define PIM_DEBUG_PIM_J_P (qpim_debugs & PIM_MASK_PIM_J_P)
#define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS))
#define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS))
#define PIM_DEBUG_TRACE (qpim_debugs & (PIM_MASK_PIM_TRACE | PIM_MASK_IGMP_TRACE))
#define PIM_DO_DEBUG_PIM_EVENTS (qpim_debugs |= PIM_MASK_PIM_EVENTS)
#define PIM_DO_DEBUG_PIM_PACKETS (qpim_debugs |= PIM_MASK_PIM_PACKETS)
#define PIM_DO_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs |= PIM_MASK_PIM_PACKETDUMP_SEND)
#define PIM_DO_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs |= PIM_MASK_PIM_PACKETDUMP_RECV)
#define PIM_DO_DEBUG_PIM_TRACE (qpim_debugs |= PIM_MASK_PIM_TRACE)
#define PIM_DO_DEBUG_IGMP_EVENTS (qpim_debugs |= PIM_MASK_IGMP_EVENTS)
#define PIM_DO_DEBUG_IGMP_PACKETS (qpim_debugs |= PIM_MASK_IGMP_PACKETS)
#define PIM_DO_DEBUG_IGMP_TRACE (qpim_debugs |= PIM_MASK_IGMP_TRACE)
#define PIM_DO_DEBUG_ZEBRA (qpim_debugs |= PIM_MASK_ZEBRA)
#define PIM_DO_DEBUG_SSMPINGD (qpim_debugs |= PIM_MASK_SSMPINGD)
#define PIM_DO_DEBUG_MROUTE (qpim_debugs |= PIM_MASK_MROUTE)
#define PIM_DO_DEBUG_PIM_HELLO (qpim_debugs |= PIM_MASK_PIM_HELLO)
#define PIM_DO_DEBUG_PIM_J_P (qpim_debugs |= PIM_MASK_PIM_J_P)
#define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS)
#define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS)
#define PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs &= ~PIM_MASK_PIM_PACKETDUMP_SEND)
#define PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs &= ~PIM_MASK_PIM_PACKETDUMP_RECV)
#define PIM_DONT_DEBUG_PIM_TRACE (qpim_debugs &= ~PIM_MASK_PIM_TRACE)
#define PIM_DONT_DEBUG_IGMP_EVENTS (qpim_debugs &= ~PIM_MASK_IGMP_EVENTS)
#define PIM_DONT_DEBUG_IGMP_PACKETS (qpim_debugs &= ~PIM_MASK_IGMP_PACKETS)
#define PIM_DONT_DEBUG_IGMP_TRACE (qpim_debugs &= ~PIM_MASK_IGMP_TRACE)
#define PIM_DONT_DEBUG_ZEBRA (qpim_debugs &= ~PIM_MASK_ZEBRA)
#define PIM_DONT_DEBUG_SSMPINGD (qpim_debugs &= ~PIM_MASK_SSMPINGD)
#define PIM_DONT_DEBUG_MROUTE (qpim_debugs &= ~PIM_MASK_MROUTE)
#define PIM_DONT_DEBUG_PIM_HELLO (qpim_debugs &= ~PIM_MASK_PIM_HELLO)
#define PIM_DONT_DEBUG_PIM_J_P (qpim_debugs &= ~PIM_MASK_PIM_J_P)
void pim_init(void);
void pim_terminate(void);
#endif /* PIMD_H */

23
pimd/quagga-bootstrap.sh Executable file
View file

@ -0,0 +1,23 @@
#! /bin/bash
#
# Bootstrap Quagga autotools for pimd.
#
# Run from quagga's top dir as:
# ./pimd/quagga-bootstrap.sh
#
# $QuaggaId: $Format:%an, %ai, %h$ $
me=`basename $0`
msg () {
echo >&2 $me: $*
}
if [ -f ./bootstrap.sh ]; then
msg found ./bootstrap.sh from quagga
./bootstrap.sh
else
msg missing ./bootstrap.sh from quagga
#autoreconf -i --force
#bootstrap from tarball prefers autoreconf -i
autoreconf -i
fi

10
pimd/quagga-build-no-vtysh.sh Executable file
View file

@ -0,0 +1,10 @@
#! /bin/bash
#
# Build minimum Quagga needed for pimd.
#
# Run from quagga's top dir as:
# ./pimd/quagga-build-no-vtysh.sh
#
# $QuaggaId: $Format:%an, %ai, %h$ $
./pimd/quagga-memtypes.sh && ./pimd/quagga-bootstrap.sh && ./pimd/quagga-configure-no-vtysh.sh && make

10
pimd/quagga-build.sh Executable file
View file

@ -0,0 +1,10 @@
#! /bin/bash
#
# Build minimum Quagga needed for pimd.
#
# Run from quagga's top dir as:
# ./pimd/quagga-build.sh
#
# $QuaggaId: $Format:%an, %ai, %h$ $
./pimd/quagga-memtypes.sh && ./pimd/quagga-bootstrap.sh && ./pimd/quagga-configure.sh && make

View file

@ -0,0 +1,10 @@
#! /bin/bash
#
# Configure for minimum Quagga build needed for pimd.
#
# Run from quagga's top dir as:
# ./pimd/quagga-configure-no-vtysh.sh
#
# $QuaggaId: $Format:%an, %ai, %h$ $
./configure --disable-babeld --disable-bgpd --disable-ripd --disable-ripngd --disable-ospfd --disable-ospf6d --disable-watchquagga --disable-bgp-announce --disable-ospfapi --disable-ospfclient --disable-rtadv --disable-irdp --enable-pimd --enable-tcp-zebra --enable-ipv6

10
pimd/quagga-configure.sh Executable file
View file

@ -0,0 +1,10 @@
#! /bin/bash
#
# Configure for minimum Quagga build needed for pimd.
#
# Run from quagga's top dir as:
# . pimd/quagga-configure.sh
#
# $QuaggaId: $Format:%an, %ai, %h$ $
tail -1 ./pimd/quagga-configure-no-vtysh.sh --enable-vtysh

Some files were not shown because too many files have changed in this diff Show more