mirror of
https://github.com/FRRouting/frr.git
synced 2025-04-30 13:37:17 +02:00
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:
parent
5b282f59b1
commit
12e41d03bd
|
@ -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 \
|
||||
|
|
1
SERVICES
1
SERVICES
|
@ -17,3 +17,4 @@ bgpd 2605/tcp
|
|||
ospf6d 2606/tcp
|
||||
ospfapi 2607/tcp
|
||||
isisd 2608/tcp
|
||||
pimd 2611/tcp
|
||||
|
|
55
configure.ac
55
configure.ac
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
125
doc/pimd.8
Normal 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.
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -50,6 +50,7 @@ const char *zlog_proto_names[] =
|
|||
"RIPNG",
|
||||
"OSPF6",
|
||||
"ISIS",
|
||||
"PIM",
|
||||
"MASC",
|
||||
NULL,
|
||||
};
|
||||
|
|
|
@ -52,6 +52,7 @@ typedef enum
|
|||
ZLOG_RIPNG,
|
||||
ZLOG_OSPF6,
|
||||
ZLOG_ISIS,
|
||||
ZLOG_PIM,
|
||||
ZLOG_MASC
|
||||
} zlog_proto_t;
|
||||
|
||||
|
|
11
lib/memory.c
11
lib/memory.c
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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},
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
16
pimd/.gitignore
vendored
Normal 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
9
pimd/AUTHORS
Normal 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
178
pimd/CAVEATS
Normal 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
81
pimd/COMMANDS
Normal 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
340
pimd/COPYING
Normal 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
86
pimd/DEBUG
Normal 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-
|
26
pimd/LINUX_KERNEL_MROUTE_MFC
Normal file
26
pimd/LINUX_KERNEL_MROUTE_MFC
Normal 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
76
pimd/Makefile.am
Normal 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
164
pimd/README
Normal 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
426
pimd/TODO
Normal 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
33
pimd/TROUBLESHOOTING
Normal 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
32
pimd/WHY_SSM
Normal 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
27
pimd/git-clone-github.sh
Executable 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
27
pimd/git-clone-savannah.sh
Executable 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
808
pimd/pim_assert.c
Normal 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
75
pimd/pim_assert.h
Normal 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
4500
pimd/pim_cmd.c
Normal file
File diff suppressed because it is too large
Load diff
66
pimd/pim_cmd.h
Normal file
66
pimd/pim_cmd.h
Normal 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
529
pimd/pim_hello.c
Normal 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
46
pimd/pim_hello.h
Normal 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
1229
pimd/pim_iface.c
Normal file
File diff suppressed because it is too large
Load diff
161
pimd/pim_iface.h
Normal file
161
pimd/pim_iface.h
Normal 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
893
pimd/pim_ifchannel.c
Normal 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
145
pimd/pim_ifchannel.h
Normal 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
1433
pimd/pim_igmp.c
Normal file
File diff suppressed because it is too large
Load diff
176
pimd/pim_igmp.h
Normal file
176
pimd/pim_igmp.h
Normal 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
67
pimd/pim_igmp_join.c
Normal 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
32
pimd/pim_igmp_join.h
Normal 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
1729
pimd/pim_igmpv3.c
Normal file
File diff suppressed because it is too large
Load diff
100
pimd/pim_igmpv3.h
Normal file
100
pimd/pim_igmpv3.h
Normal 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
44
pimd/pim_int.c
Normal 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
31
pimd/pim_int.h
Normal 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
445
pimd/pim_join.c
Normal 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
43
pimd/pim_join.h
Normal 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
437
pimd/pim_macro.c
Normal 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
44
pimd/pim_macro.h
Normal 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
294
pimd/pim_main.c
Normal 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
451
pimd/pim_mroute.c
Normal 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
173
pimd/pim_mroute.h
Normal 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
106
pimd/pim_msg.c
Normal 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
52
pimd/pim_msg.h
Normal 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
719
pimd/pim_neighbor.c
Normal 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
74
pimd/pim_neighbor.h
Normal 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
140
pimd/pim_oil.c
Normal 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
53
pimd/pim_oil.h
Normal 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
742
pimd/pim_pim.c
Normal 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
71
pimd/pim_pim.h
Normal 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
60
pimd/pim_rand.c
Normal 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
30
pimd/pim_rand.h
Normal 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
260
pimd/pim_rpf.c
Normal 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
36
pimd/pim_rpf.h
Normal 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
86
pimd/pim_signals.c
Normal 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
28
pimd/pim_signals.h
Normal 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
389
pimd/pim_sock.c
Normal 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
57
pimd/pim_sock.h
Normal 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
448
pimd/pim_ssmpingd.c
Normal 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
45
pimd/pim_ssmpingd.h
Normal 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
46
pimd/pim_str.c
Normal 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
32
pimd/pim_str.h
Normal 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
165
pimd/pim_time.c
Normal 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
40
pimd/pim_time.h
Normal 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
721
pimd/pim_tlv.c
Normal 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
133
pimd/pim_tlv.h
Normal 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
683
pimd/pim_upstream.c
Normal 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
122
pimd/pim_upstream.h
Normal 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
122
pimd/pim_util.c
Normal 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
37
pimd/pim_util.h
Normal 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
25
pimd/pim_version.c
Normal 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
30
pimd/pim_version.h
Normal 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
178
pimd/pim_vty.c
Normal 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
32
pimd/pim_vty.h
Normal 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
1312
pimd/pim_zebra.c
Normal file
File diff suppressed because it is too large
Load diff
42
pimd/pim_zebra.h
Normal file
42
pimd/pim_zebra.h
Normal 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
457
pimd/pim_zlookup.c
Normal 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
47
pimd/pim_zlookup.h
Normal 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
141
pimd/pimd.c
Normal 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
41
pimd/pimd.conf.sample
Normal 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
157
pimd/pimd.h
Normal 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
23
pimd/quagga-bootstrap.sh
Executable 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
10
pimd/quagga-build-no-vtysh.sh
Executable 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
10
pimd/quagga-build.sh
Executable 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
|
10
pimd/quagga-configure-no-vtysh.sh
Executable file
10
pimd/quagga-configure-no-vtysh.sh
Executable 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
10
pimd/quagga-configure.sh
Executable 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
Loading…
Reference in a new issue