Browse Source

[pim] Initial pim 0.155

Everton Marques 10 years ago
parent
commit
871dbcfede
90 changed files with 21722 additions and 17 deletions
  1. 2 2
      Makefile.am
  2. 1 0
      SERVICES
  3. 53 1
      configure.ac
  4. 3 1
      doc/Makefile.am
  5. 1 0
      doc/install.texi
  6. 108 0
      doc/pimd.8
  7. 2 0
      lib/command.h
  8. 2 0
      lib/log.c
  9. 2 0
      lib/log.h
  10. 14 1
      lib/memory.c
  11. 17 0
      lib/memtypes.c
  12. 15 2
      lib/zebra.h
  13. 9 0
      pimd/AUTHORS
  14. 137 0
      pimd/CAVEATS
  15. 62 0
      pimd/COMMANDS
  16. 340 0
      pimd/COPYING
  17. 49 0
      pimd/DEBUG
  18. 22 0
      pimd/LINUX_KERNEL_MROUTE_MFC
  19. 70 0
      pimd/Makefile.am
  20. 157 0
      pimd/README
  21. 356 0
      pimd/TODO
  22. 803 0
      pimd/pim_assert.c
  23. 75 0
      pimd/pim_assert.h
  24. 3923 0
      pimd/pim_cmd.c
  25. 56 0
      pimd/pim_cmd.h
  26. 529 0
      pimd/pim_hello.c
  27. 46 0
      pimd/pim_hello.h
  28. 1132 0
      pimd/pim_iface.c
  29. 160 0
      pimd/pim_iface.h
  30. 893 0
      pimd/pim_ifchannel.c
  31. 145 0
      pimd/pim_ifchannel.h
  32. 1411 0
      pimd/pim_igmp.c
  33. 172 0
      pimd/pim_igmp.h
  34. 1717 0
      pimd/pim_igmpv3.c
  35. 100 0
      pimd/pim_igmpv3.h
  36. 428 0
      pimd/pim_join.c
  37. 43 0
      pimd/pim_join.h
  38. 437 0
      pimd/pim_macro.c
  39. 44 0
      pimd/pim_macro.h
  40. 276 0
      pimd/pim_main.c
  41. 432 0
      pimd/pim_mroute.c
  42. 173 0
      pimd/pim_mroute.h
  43. 106 0
      pimd/pim_msg.c
  44. 52 0
      pimd/pim_msg.h
  45. 696 0
      pimd/pim_neighbor.c
  46. 74 0
      pimd/pim_neighbor.h
  47. 140 0
      pimd/pim_oil.c
  48. 53 0
      pimd/pim_oil.h
  49. 716 0
      pimd/pim_pim.c
  50. 71 0
      pimd/pim_pim.h
  51. 60 0
      pimd/pim_rand.c
  52. 30 0
      pimd/pim_rand.h
  53. 254 0
      pimd/pim_rpf.c
  54. 36 0
      pimd/pim_rpf.h
  55. 85 0
      pimd/pim_signals.c
  56. 28 0
      pimd/pim_signals.h
  57. 348 0
      pimd/pim_sock.c
  58. 52 0
      pimd/pim_sock.h
  59. 46 0
      pimd/pim_str.c
  60. 32 0
      pimd/pim_str.h
  61. 151 0
      pimd/pim_time.c
  62. 39 0
      pimd/pim_time.h
  63. 707 0
      pimd/pim_tlv.c
  64. 133 0
      pimd/pim_tlv.h
  65. 686 0
      pimd/pim_upstream.c
  66. 122 0
      pimd/pim_upstream.h
  67. 132 0
      pimd/pim_util.c
  68. 41 0
      pimd/pim_util.h
  69. 25 0
      pimd/pim_version.c
  70. 30 0
      pimd/pim_version.h
  71. 145 0
      pimd/pim_vty.c
  72. 32 0
      pimd/pim_vty.h
  73. 1172 0
      pimd/pim_zebra.c
  74. 40 0
      pimd/pim_zebra.h
  75. 455 0
      pimd/pim_zlookup.c
  76. 47 0
      pimd/pim_zlookup.h
  77. 125 0
      pimd/pimd.c
  78. 35 0
      pimd/pimd.conf.sample
  79. 121 0
      pimd/pimd.h
  80. 21 0
      pimd/quagga-bootstrap.sh
  81. 10 0
      pimd/quagga-build.sh
  82. 10 0
      pimd/quagga-configure.sh
  83. 12 0
      pimd/quagga-git-add.sh
  84. 22 0
      pimd/quagga-memtypes.sh
  85. 16 0
      pimd/savannah-git-clone.sh
  86. 1 0
      ports/Makefile
  87. 1 0
      ports/pkg/DESCR
  88. 7 4
      redhat/quagga.spec.in
  89. 87 6
      zebra/zebra_rib.c
  90. 1 0
      zebra/zserv.c

+ 2 - 2
Makefile.am

@@ -1,12 +1,12 @@
 ## Process this file with automake to produce Makefile.in.
 
 SUBDIRS = lib @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @BABELD@ \
-         @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 babeld \
 	  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 - 0
SERVICES

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

+ 53 - 1
configure.ac

@@ -220,6 +220,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,
@@ -1362,6 +1364,12 @@ case "${enable_isisd}" in
 esac
 AM_CONDITIONAL(ISISD, test "x$ISISD" = "xisisd")
 
+case "${enable_pimd}" in
+  "yes") PIMD="pimd";;
+  "no" ) PIMD="";;
+  *    ) ;;
+esac
+
 # XXX Perhaps auto-enable on Solaris, but that's messy for cross builds.
 case "${enable_solaris}" in
   "yes") SOLARIS="solaris";;
@@ -1385,6 +1393,7 @@ AC_SUBST(OSPF6D)
 AC_SUBST(BABELD)
 AC_SUBST(WATCHQUAGGA)
 AC_SUBST(ISISD)
+AC_SUBST(PIMD)
 AC_SUBST(SOLARIS)
 AC_SUBST(VTYSH)
 AC_SUBST(INCLUDES)
@@ -1467,7 +1476,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],
@@ -1496,6 +1506,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 --------------------------------------
@@ -1685,6 +1734,7 @@ 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_BABELD_PID, "$quagga_statedir/babeld.pid",babeld 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)
@@ -1695,6 +1745,7 @@ AC_DEFINE_UNQUOTED(OSPF_VTYSH_PATH, "$quagga_statedir/ospfd.vty",ospfd vty socke
 AC_DEFINE_UNQUOTED(OSPF6_VTYSH_PATH, "$quagga_statedir/ospf6d.vty",ospf6d vty socket)
 AC_DEFINE_UNQUOTED(BABEL_VTYSH_PATH, "$quagga_statedir/babeld.vty",babeld 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 -------------------------------
@@ -1720,6 +1771,7 @@ AC_CONFIG_FILES([Makefile lib/Makefile zebra/Makefile ripd/Makefile
 	  ripngd/Makefile bgpd/Makefile ospfd/Makefile watchquagga/Makefile
 	  ospf6d/Makefile isisd/Makefile babeld/Makefile vtysh/Makefile
 	  doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile
+	  pimd/Makefile
 	  tests/bgpd.tests/Makefile
 	  tests/libzebra.tests/Makefile
 	  redhat/Makefile

+ 3 - 1
doc/Makefile.am

@@ -61,7 +61,9 @@ quagga_TEXINFOS = appendix.texi babeld.texi basic.texi bgpd.texi filter.texi \
 .dia.png:
 	$(DIATOPNG) "$@" $<
 
-man_MANS = 
+if PIMD
+man_MANS += pimd.8
+endif
 
 if BGPD
 man_MANS += bgpd.8

+ 1 - 0
doc/install.texi

@@ -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

+ 108 - 0
doc/pimd.8

@@ -0,0 +1,108 @@
+.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 \-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\-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 $(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 http://savannah.nongnu.org/projects/qpimd
+.SH AUTHORS
+See
+.BI http://savannah.nongnu.org/projects/qpimd
+for an accurate list of authors.
+

+ 2 - 0
lib/command.h

@@ -1,6 +1,7 @@
 /*
  * Zebra configuration command interface routine
  * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ * Portions Copyright (c) 2008 Everton da Silva Marques <everton.marques@gmail.com>
  *
  * This file is part of GNU Zebra.
  *
@@ -88,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. */

+ 2 - 0
lib/log.c

@@ -1,6 +1,7 @@
 /*
  * Logging of zebra
  * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro
+ * Portions Copyright (c) 2008 Everton da Silva Marques <everton.marques@gmail.com>
  *
  * This file is part of GNU Zebra.
  *
@@ -51,6 +52,7 @@ const char *zlog_proto_names[] =
   "BABEL",
   "OSPF6",
   "ISIS",
+  "PIM",
   "MASC",
   NULL,
 };

+ 2 - 0
lib/log.h

@@ -1,6 +1,7 @@
 /*
  * Zebra logging funcions.
  * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro
+ * Portions Copyright (c) 2008 Everton da Silva Marques <everton.marques@gmail.com>
  *
  * This file is part of GNU Zebra.
  *
@@ -53,6 +54,7 @@ typedef enum
   ZLOG_BABEL,
   ZLOG_OSPF6,
   ZLOG_ISIS,
+  ZLOG_PIM,
   ZLOG_MASC
 } zlog_proto_t;
 

+ 14 - 1
lib/memory.c

@@ -1,6 +1,7 @@
 /*
  * Memory management routine
  * Copyright (C) 1998 Kunihiro Ishiguro
+ * Portions Copyright (c) 2008 Everton da Silva Marques <everton.marques@gmail.com>
  *
  * This file is part of GNU Zebra.
  *
@@ -521,6 +522,17 @@ DEFUN (show_memory_isis,
   return CMD_SUCCESS;
 }
 
+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)
 {
@@ -545,6 +557,7 @@ memory_init (void)
   install_element (VIEW_NODE, &show_memory_ospf_cmd);
   install_element (VIEW_NODE, &show_memory_ospf6_cmd);
   install_element (VIEW_NODE, &show_memory_isis_cmd);
+  install_element (VIEW_NODE, &show_memory_pim_cmd);
 
   install_element (ENABLE_NODE, &show_memory_cmd);
   install_element (ENABLE_NODE, &show_memory_all_cmd);
@@ -556,7 +569,7 @@ memory_init (void)
   install_element (ENABLE_NODE, &show_memory_bgp_cmd);
   install_element (ENABLE_NODE, &show_memory_ospf_cmd);
   install_element (ENABLE_NODE, &show_memory_ospf6_cmd);
-  install_element (ENABLE_NODE, &show_memory_isis_cmd);
+  install_element (ENABLE_NODE, &show_memory_pim_cmd);
 }
 
 /* Stats querying from users */

+ 17 - 0
lib/memtypes.c

@@ -1,4 +1,6 @@
 /*
+ * Portions Copyright (c) 2008 Everton da Silva Marques <everton.marques@gmail.com>
+ *
  * Memory type definitions. This file is parsed by memtypes.awk to extract
  * MTYPE_ and memory_list_.. information in order to autogenerate 
  * memtypes.h.
@@ -255,6 +257,20 @@ 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"       },
+  { -1, NULL },
+};
+
 struct memory_list memory_list_vtysh[] =
 {
   { MTYPE_VTYSH_CONFIG,		"Vtysh configuration",		},
@@ -271,5 +287,6 @@ struct mlist mlists[] __attribute__ ((unused)) = {
   { memory_list_ospf6,	"OSPF6"	},
   { memory_list_isis,	"ISIS"	},
   { memory_list_bgp,	"BGP"	},
+  { memory_list_pim,	"PIM"	},
   { NULL, NULL},
 };

+ 15 - 2
lib/zebra.h

@@ -1,5 +1,6 @@
 /* Zebra common header.
    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Kunihiro Ishiguro
+   Portions Copyright (c) 2008 Everton da Silva Marques <everton.marques@gmail.com>
 
 This file is part of GNU Zebra.
 
@@ -434,8 +435,20 @@ struct in_pktinfo
  */
 #define ZEBRA_HEADER_MARKER              255
 
-/* Zebra route's types are defined in route_types.h */
-#include "route_types.h"
+/* Zebra route's types. */
+#define ZEBRA_ROUTE_SYSTEM               0
+#define ZEBRA_ROUTE_KERNEL               1
+#define ZEBRA_ROUTE_CONNECT              2
+#define ZEBRA_ROUTE_STATIC               3
+#define ZEBRA_ROUTE_RIP                  4
+#define ZEBRA_ROUTE_RIPNG                5
+#define ZEBRA_ROUTE_OSPF                 6
+#define ZEBRA_ROUTE_OSPF6                7
+#define ZEBRA_ROUTE_ISIS                 8
+#define ZEBRA_ROUTE_BGP                  9
+#define ZEBRA_ROUTE_HSLS		 10
+#define ZEBRA_ROUTE_PIM                  11
+#define ZEBRA_ROUTE_MAX                  12
 
 /* Note: whenever a new route-type or zserv-command is added the
  * corresponding {command,route}_types[] table in lib/log.c MUST be

+ 9 - 0
pimd/AUTHORS

@@ -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-

+ 137 - 0
pimd/CAVEATS

@@ -0,0 +1,137 @@
+# $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 (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 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 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 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.
+
+-x-

+ 62 - 0
pimd/COMMANDS

@@ -0,0 +1,62 @@
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+global configuration commands:
+       ip multicast-routing	Enable IP multicast forwarding
+
+interface configuration commands:
+       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:
+       show ip igmp interface			IGMP interface 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 route				IP routing table
+
+debug commands:
+       clear ip interfaces		Reset interfaces
+       clear ip igmp interfaces		Reset IGMP interfaces
+       clear ip pim interfaces		Reset PIM interfaces
+       debug igmp			IGMP protocol activity
+       debug pim			PIM protocol activity
+       debug pim zebra			ZEBRA protocol 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 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:
+       show memory pim		PIM memory statistics
+
+-x-

+ 340 - 0
pimd/COPYING

@@ -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.

+ 49 - 0
pimd/DEBUG

@@ -0,0 +1,49 @@
+# $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.
+
+  - 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 -b 1.1.1.1 -c 239.1.1.1 -T 10 -W 1000 -r 100k -a 1d
+
+  - Remotely you can receive that stream by running:
+
+    nepim -j 1.1.1.1+239.1.1.1@eth0
+    (Remember of enabling both "ip pim ssm" and "ip igmp" under eth0.)
+
+
+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-

+ 22 - 0
pimd/LINUX_KERNEL_MROUTE_MFC

@@ -0,0 +1,22 @@
+# $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
+
+# -- end-of-file --

+ 70 - 0
pimd/Makefile.am

@@ -0,0 +1,70 @@
+## 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_MOTD_VERSION: Includes pimd version in default MOTD
+# PIM_USE_QUAGGA_INET_CHECKSUM: Prefer Quagga inet checksum
+# 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_MOTD_VERSION
+PIM_DEFS += -DPIM_USE_QUAGGA_INET_CHECKSUM
+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@ 
+noinst_LIBRARIES = libpim.a
+sbin_PROGRAMS = pimd 
+
+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
+
+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
+
+pimd_SOURCES = \
+	pim_main.c $(libpim_a_SOURCES)
+
+pimd_LDADD = ../lib/libzebra.la @LIBCAP@
+
+examplesdir = $(exampledir)
+dist_examples_DATA = pimd.conf.sample

+ 157 - 0
pimd/README

@@ -0,0 +1,157 @@
+#
+# $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:
+
+        http://savannah.nongnu.org/projects/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:
+
+        http://savannah.nongnu.org/projects/qpimd
+
+RELATED WORK
+
+	igmprt:	An IGMPv3-router implementation
+	- http://www.loria.fr/~lahmadi/igmpv3-router.html
+
+	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)
+
+	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 --

+ 356 - 0
pimd/TODO

@@ -0,0 +1,356 @@
+# $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 CAVEAT C15.
+    See also RFC 4601: 4.3.1.  Sending Hello Messages
+
+T32 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://www.ietf.org/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt
+    http://www.ietf.org/html.charters/mboned-charter.html
+
+-x-

+ 803 - 0
pimd/pim_assert.c

@@ -0,0 +1,803 @@
+/*
+  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_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 (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_info("%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);
+  }
+
+  {
+    char src_str[100];
+    char grp_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("<winner?>", winner, winner_str, sizeof(winner_str));
+    zlog_info("%s: (S,G)=(%s,%s) assert winner now is %s on interface %s",
+	      __PRETTY_FUNCTION__,
+	      src_str, grp_str,
+	      winner_str, ch->interface->name);
+  }
+
+  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,
+		    char *buf, int buf_size)
+{
+  struct prefix            msg_group_addr;
+  struct prefix            msg_source_addr;
+  struct pim_assert_metric msg_metric;
+  int offset;
+  char *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 = ntohl(*(const uint32_t *) 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 = ntohl(*(const uint32_t *) 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(char *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)
+{
+  char *buf_pastend = pim_msg + buf_size;
+  char *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 */
+  *((uint32_t *) pim_msg_curr) = htonl(rpt_bit_flag ?
+				       metric_preference | 0x80000000 :
+				       metric_preference);
+  pim_msg_curr += 4;
+
+  /* Route metric */
+  *((uint32_t *) pim_msg_curr) = htonl(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;
+  char 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 no 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 - 0
pimd/pim_assert.h

@@ -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,
+		    char *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(char *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 */

File diff suppressed because it is too large
+ 3923 - 0
pimd/pim_cmd.c


+ 56 - 0
pimd/pim_cmd.h

@@ -0,0 +1,56 @@
+/*
+  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 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_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_TRACE_STR                         "PIM internal daemon activity\n"
+#define DEBUG_PIM_ZEBRA_STR                         "ZEBRA protocol 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 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 - 0
pimd/pim_hello.c

@@ -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=%x",
+	       label, 
+	       src_str, ifname,
+	       tlv_name,
+	       addr_list ? ((int) listcount(addr_list)) : -1,
+	       (unsigned) 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,
+		   char *tlv_buf, int tlv_buf_size)
+{
+  struct pim_interface *pim_ifp;
+  struct pim_neighbor *neigh;
+  char *tlv_curr;
+  char *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 > max=%d 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) {