summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--HACKING4
-rw-r--r--Makefile.am4
-rw-r--r--SERVICES1
-rw-r--r--bgpd/bgp_advertise.c4
-rw-r--r--bgpd/bgp_advertise.h32
-rw-r--r--bgpd/bgp_fsm.c2
-rw-r--r--bgpd/bgp_network.c64
-rw-r--r--bgpd/bgp_packet.c80
-rw-r--r--bgpd/bgp_route.c901
-rw-r--r--bgpd/bgp_vty.c151
-rw-r--r--bgpd/bgpd.c3
-rwxr-xr-xconfigure.ac70
-rw-r--r--doc/Makefile.am2
-rw-r--r--doc/install.texi1
-rw-r--r--doc/ipv6.texi10
-rw-r--r--doc/pimd.8108
-rw-r--r--isisd/Makefile.am11
-rw-r--r--isisd/isis_adjacency.c2
-rw-r--r--isisd/isis_bpf.c3
-rw-r--r--isisd/isis_circuit.c70
-rw-r--r--isisd/isis_circuit.h1
-rw-r--r--isisd/isis_common.h1
-rw-r--r--isisd/isis_csm.c9
-rw-r--r--isisd/isis_dlpi.c3
-rw-r--r--isisd/isis_lsp.c23
-rw-r--r--isisd/isis_lsp.h1
-rw-r--r--isisd/isis_pdu.c278
-rw-r--r--isisd/isis_pdu.h5
-rw-r--r--isisd/isis_pfpacket.c7
-rw-r--r--isisd/isis_spf.c65
-rw-r--r--isisd/isis_tlv.c8
-rw-r--r--isisd/isisd.c13
-rw-r--r--isisd/isisd.h2
-rw-r--r--lib/checksum.c53
-rw-r--r--lib/command.c340
-rw-r--r--lib/command.h4
-rw-r--r--lib/if.c2
-rw-r--r--lib/if.h1
-rw-r--r--lib/log.c20
-rw-r--r--lib/log.h2
-rw-r--r--lib/md5.c73
-rw-r--r--lib/md5.h3
-rw-r--r--lib/memory.c15
-rw-r--r--lib/memtypes.c18
-rw-r--r--lib/prefix.c54
-rw-r--r--lib/prefix.h2
-rw-r--r--lib/route_types.txt11
-rw-r--r--lib/sockunion.c10
-rw-r--r--lib/sockunion.h1
-rw-r--r--lib/stream.c26
-rw-r--r--lib/stream.h4
-rw-r--r--lib/thread.c274
-rw-r--r--lib/thread.h13
-rw-r--r--lib/vty.c30
-rw-r--r--lib/workqueue.c17
-rw-r--r--lib/zebra.h43
-rw-r--r--ospf6d/ospf6_interface.c28
-rw-r--r--ospfd/ospf_abr.c45
-rw-r--r--ospfd/ospf_apiserver.c2
-rw-r--r--ospfd/ospf_ase.c104
-rw-r--r--ospfd/ospf_ase.h2
-rw-r--r--ospfd/ospf_dump.c4
-rw-r--r--ospfd/ospf_flood.c36
-rw-r--r--ospfd/ospf_interface.c54
-rw-r--r--ospfd/ospf_interface.h13
-rw-r--r--ospfd/ospf_ism.c48
-rw-r--r--ospfd/ospf_lsa.c86
-rw-r--r--ospfd/ospf_neighbor.c244
-rw-r--r--ospfd/ospf_neighbor.h13
-rw-r--r--ospfd/ospf_nsm.c30
-rw-r--r--ospfd/ospf_nsm.h2
-rw-r--r--ospfd/ospf_opaque.c21
-rw-r--r--ospfd/ospf_packet.c272
-rw-r--r--ospfd/ospf_packet.h1
-rw-r--r--ospfd/ospf_route.c67
-rw-r--r--ospfd/ospf_route.h6
-rw-r--r--ospfd/ospf_snmp.c21
-rw-r--r--ospfd/ospf_spf.c163
-rw-r--r--ospfd/ospf_vty.c329
-rw-r--r--ospfd/ospf_zebra.c18
-rw-r--r--ospfd/ospf_zebra.h2
-rw-r--r--ospfd/ospfd.c301
-rw-r--r--ospfd/ospfd.h9
-rw-r--r--pimd/.gitignore19
-rw-r--r--pimd/AUTHORS9
-rw-r--r--pimd/CAVEATS137
-rw-r--r--pimd/COMMANDS66
-rw-r--r--pimd/COPYING340
-rw-r--r--pimd/DEBUG61
-rw-r--r--pimd/LINUX_KERNEL_MROUTE_MFC26
-rw-r--r--pimd/Makefile.am76
-rw-r--r--pimd/README157
-rw-r--r--pimd/TODO373
-rw-r--r--pimd/pim_assert.c804
-rw-r--r--pimd/pim_assert.h75
-rw-r--r--pimd/pim_cmd.c4284
-rw-r--r--pimd/pim_cmd.h62
-rw-r--r--pimd/pim_hello.c529
-rw-r--r--pimd/pim_hello.h46
-rw-r--r--pimd/pim_iface.c1134
-rw-r--r--pimd/pim_iface.h160
-rw-r--r--pimd/pim_ifchannel.c893
-rw-r--r--pimd/pim_ifchannel.h145
-rw-r--r--pimd/pim_igmp.c1413
-rw-r--r--pimd/pim_igmp.h175
-rw-r--r--pimd/pim_igmp_join.c65
-rw-r--r--pimd/pim_igmp_join.h32
-rw-r--r--pimd/pim_igmpv3.c1728
-rw-r--r--pimd/pim_igmpv3.h100
-rw-r--r--pimd/pim_int.c44
-rw-r--r--pimd/pim_int.h31
-rw-r--r--pimd/pim_join.c430
-rw-r--r--pimd/pim_join.h43
-rw-r--r--pimd/pim_macro.c437
-rw-r--r--pimd/pim_macro.h44
-rw-r--r--pimd/pim_main.c298
-rw-r--r--pimd/pim_mroute.c448
-rw-r--r--pimd/pim_mroute.h178
-rw-r--r--pimd/pim_msg.c106
-rw-r--r--pimd/pim_msg.h52
-rw-r--r--pimd/pim_neighbor.c715
-rw-r--r--pimd/pim_neighbor.h74
-rw-r--r--pimd/pim_oil.c140
-rw-r--r--pimd/pim_oil.h53
-rw-r--r--pimd/pim_pim.c734
-rw-r--r--pimd/pim_pim.h71
-rw-r--r--pimd/pim_rand.c60
-rw-r--r--pimd/pim_rand.h30
-rw-r--r--pimd/pim_rpf.c254
-rw-r--r--pimd/pim_rpf.h36
-rw-r--r--pimd/pim_signals.c85
-rw-r--r--pimd/pim_signals.h28
-rw-r--r--pimd/pim_sock.c389
-rw-r--r--pimd/pim_sock.h57
-rw-r--r--pimd/pim_ssmpingd.c448
-rw-r--r--pimd/pim_ssmpingd.h45
-rw-r--r--pimd/pim_str.c46
-rw-r--r--pimd/pim_str.h32
-rw-r--r--pimd/pim_time.c151
-rw-r--r--pimd/pim_time.h39
-rw-r--r--pimd/pim_tlv.c726
-rw-r--r--pimd/pim_tlv.h133
-rw-r--r--pimd/pim_upstream.c686
-rw-r--r--pimd/pim_upstream.h122
-rw-r--r--pimd/pim_util.c159
-rw-r--r--pimd/pim_util.h43
-rw-r--r--pimd/pim_version.c25
-rw-r--r--pimd/pim_version.h30
-rw-r--r--pimd/pim_vty.c173
-rw-r--r--pimd/pim_vty.h32
-rw-r--r--pimd/pim_zebra.c1188
-rw-r--r--pimd/pim_zebra.h40
-rw-r--r--pimd/pim_zlookup.c456
-rw-r--r--pimd/pim_zlookup.h47
-rw-r--r--pimd/pimd.c134
-rw-r--r--pimd/pimd.conf.sample41
-rw-r--r--pimd/pimd.h138
-rwxr-xr-xpimd/quagga-bootstrap.sh21
-rwxr-xr-xpimd/quagga-build.sh10
-rwxr-xr-xpimd/quagga-configure.sh10
-rwxr-xr-xpimd/quagga-git-add.sh12
-rwxr-xr-xpimd/quagga-memtypes.sh22
-rwxr-xr-xpimd/savannah-git-clone.sh27
-rw-r--r--pimd/test_igmpv3_join.c149
-rw-r--r--ports/Makefile1
-rw-r--r--ports/pkg/DESCR1
-rw-r--r--redhat/quagga.spec.in11
-rw-r--r--vtysh/Makefile.am2
-rw-r--r--vtysh/vtysh.c1
-rw-r--r--vtysh/vtysh.h5
-rw-r--r--zebra/interface.c59
-rw-r--r--zebra/interface.h14
-rw-r--r--zebra/kernel_socket.c12
-rw-r--r--zebra/rib.h1
-rw-r--r--zebra/rtadv.c137
-rw-r--r--zebra/rtadv.h14
-rw-r--r--zebra/zebra_rib.c145
-rw-r--r--zebra/zserv.c93
-rw-r--r--zebra/zserv.h3
179 files changed, 26757 insertions, 1549 deletions
diff --git a/HACKING b/HACKING
index 5a207274..d9cd19d8 100644
--- a/HACKING
+++ b/HACKING
@@ -316,15 +316,17 @@ Daemons which are in a testing phase are
ospf6d
isisd
+ pimd
watchquagga
IMPORT OR UPDATE VENDOR SPECIFIC ROUTING PROTOCOLS
-The source code of Quagga is based on two vendors:
+The source code of Quagga is based on these vendors:
zebra_org (http://www.zebra.org/)
isisd_sf (http://isisd.sf.net/)
+ qpimd (http://http://savannah.nongnu.org/projects/qpimd/)
To import code from further sources, e.g. for archival purposes without
necessarily having to review and/or fix some changeset, create a branch from
diff --git a/Makefile.am b/Makefile.am
index 007758f2..6e9172c4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,11 +2,11 @@
SUBDIRS = lib @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ \
@ISISD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \
- redhat @SOLARIS@
+ redhat @SOLARIS@ @PIMD@
DIST_SUBDIRS = lib zebra bgpd ripd ripngd ospfd ospf6d \
isisd watchquagga vtysh ospfclient doc m4 pkgsrc redhat tests \
- solaris
+ solaris pimd
EXTRA_DIST = aclocal.m4 SERVICES TODO REPORTING-BUGS INSTALL.quagga.txt \
update-autotools \
diff --git a/SERVICES b/SERVICES
index 9c3546bc..c69d0c1a 100644
--- a/SERVICES
+++ b/SERVICES
@@ -17,3 +17,4 @@ bgpd 2605/tcp
ospf6d 2606/tcp
ospfapi 2607/tcp
isisd 2608/tcp
+pimd 2611/tcp
diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c
index 87eb7ac7..4163ab96 100644
--- a/bgpd/bgp_advertise.c
+++ b/bgpd/bgp_advertise.c
@@ -263,7 +263,7 @@ bgp_adj_out_set (struct bgp_node *rn, struct peer *peer, struct prefix *p,
/* Add new advertisement to advertisement attribute list. */
bgp_advertise_add (adv->baa, adv);
- FIFO_ADD (&peer->sync[afi][safi]->update, &adv->fifo);
+ FIFO_ADD (&peer->sync[afi][safi]->update, adv);
}
void
@@ -297,7 +297,7 @@ bgp_adj_out_unset (struct bgp_node *rn, struct peer *peer, struct prefix *p,
adv->adj = adj;
/* Add to synchronization entry for withdraw announcement. */
- FIFO_ADD (&peer->sync[afi][safi]->withdraw, &adv->fifo);
+ FIFO_ADD (&peer->sync[afi][safi]->withdraw, adv);
/* Schedule packet write. */
BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h
index 4ebde907..53427eb3 100644
--- a/bgpd/bgp_advertise.h
+++ b/bgpd/bgp_advertise.h
@@ -102,6 +102,38 @@ struct bgp_synchronize
struct bgp_advertise_fifo withdraw_low;
};
+static inline void FIFO_INIT(struct bgp_advertise_fifo *fifo)
+{
+ fifo->next = fifo->prev = (struct bgp_advertise *) fifo;
+}
+
+static inline void FIFO_ADD(struct bgp_advertise_fifo *fifo,
+ struct bgp_advertise *node)
+{
+ node->fifo.next = (struct bgp_advertise *) fifo;
+ node->fifo.prev = fifo->prev;
+ fifo->prev = fifo->prev->next = node;
+}
+
+static inline void FIFO_DEL(struct bgp_advertise *node)
+{
+ struct bgp_advertise_fifo *fifo = &node->fifo;
+
+ fifo->prev->next = fifo->next;
+ fifo->next->prev = fifo->prev;
+}
+
+static inline int FIFO_EMPTY(const struct bgp_advertise_fifo *fifo)
+{
+ return (fifo->next == (const struct bgp_advertise *) fifo);
+}
+
+static inline struct bgp_advertise *FIFO_HEAD(struct bgp_advertise_fifo *fifo)
+{
+ return FIFO_EMPTY(fifo) ? NULL : fifo->next;
+}
+
+
/* BGP adjacency linked list. */
#define BGP_INFO_ADD(N,A,TYPE) \
do { \
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 487ebddb..e18fc8cd 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -704,7 +704,7 @@ bgp_start (struct peer *peer)
peer->fd);
return -1;
}
- BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
+
BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
break;
}
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index 1a8587e9..83b1142e 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -150,6 +150,7 @@ bgp_accept (struct thread *thread)
zlog_err ("[Error] BGP socket accept failed (%s)", safe_strerror (errno));
return -1;
}
+ set_nonblocking (bgp_sock);
if (BGP_DEBUG (events, EVENTS))
zlog_debug ("[Event] BGP connection from host %s", inet_sutop (&su, buf));
@@ -233,46 +234,36 @@ bgp_bind (struct peer *peer)
}
static int
-bgp_bind_address (int sock, struct in_addr *addr)
+bgp_update_address (struct interface *ifp, const union sockunion *dst,
+ union sockunion *addr)
{
- int ret;
- struct sockaddr_in local;
-
- memset (&local, 0, sizeof (struct sockaddr_in));
- local.sin_family = AF_INET;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- local.sin_len = sizeof(struct sockaddr_in);
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
- memcpy (&local.sin_addr, addr, sizeof (struct in_addr));
-
- if ( bgpd_privs.change (ZPRIVS_RAISE) )
- zlog_err ("bgp_bind_address: could not raise privs");
-
- ret = bind (sock, (struct sockaddr *)&local, sizeof (struct sockaddr_in));
- if (ret < 0)
- ;
-
- if (bgpd_privs.change (ZPRIVS_LOWER) )
- zlog_err ("bgp_bind_address: could not lower privs");
-
- return 0;
-}
-
-static struct in_addr *
-bgp_update_address (struct interface *ifp)
-{
- struct prefix_ipv4 *p;
+ struct prefix *p, *sel, *d;
struct connected *connected;
struct listnode *node;
+ int common;
+
+ d = sockunion2hostprefix (dst);
+ sel = NULL;
+ common = -1;
for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected))
{
- p = (struct prefix_ipv4 *) connected->address;
-
- if (p->family == AF_INET)
- return &p->prefix;
+ p = connected->address;
+ if (p->family != d->family)
+ continue;
+ if (prefix_common_bits (p, d) > common)
+ {
+ sel = p;
+ common = prefix_common_bits (sel, d);
+ }
}
- return NULL;
+
+ prefix_free (d);
+ if (!sel)
+ return 1;
+
+ prefix2sockunion (sel, addr);
+ return 0;
}
/* Update source selection. */
@@ -280,7 +271,7 @@ static void
bgp_update_source (struct peer *peer)
{
struct interface *ifp;
- struct in_addr *addr;
+ union sockunion addr;
/* Source is specified with interface name. */
if (peer->update_if)
@@ -289,11 +280,10 @@ bgp_update_source (struct peer *peer)
if (! ifp)
return;
- addr = bgp_update_address (ifp);
- if (! addr)
+ if (bgp_update_address (ifp, &peer->su, &addr))
return;
- bgp_bind_address (peer->fd, addr);
+ sockunion_bind (peer->fd, &addr, 0, &addr);
}
/* Source is specified with IP address. */
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index b29bc1f0..1e2f95e3 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -107,8 +107,7 @@ bgp_connect_check (struct peer *peer)
socklen_t slen;
int ret;
- /* Anyway I have to reset read and write thread. */
- BGP_READ_OFF (peer->t_read);
+ /* Anyway I have to reset write thread. */
BGP_WRITE_OFF (peer->t_write);
/* Check file descriptor. */
@@ -597,7 +596,6 @@ bgp_write (struct thread *thread)
struct stream *s;
int num;
unsigned int count = 0;
- int write_errno;
/* Yes first of all get peer pointer. */
peer = THREAD_ARG (thread);
@@ -610,46 +608,37 @@ bgp_write (struct thread *thread)
return 0;
}
- /* Nonblocking write until TCP output buffer is full. */
- while (1)
+ s = bgp_write_packet (peer);
+ if (!s)
+ return 0; /* nothing to send */
+
+ sockopt_cork (peer->fd, 1);
+
+ /* Nonblocking write until TCP output buffer is full. */
+ do
{
int writenum;
- int val;
-
- s = bgp_write_packet (peer);
- if (! s)
- return 0;
-
- /* XXX: FIXME, the socket should be NONBLOCK from the start
- * status shouldnt need to be toggled on each write
- */
- val = fcntl (peer->fd, F_GETFL, 0);
- fcntl (peer->fd, F_SETFL, val|O_NONBLOCK);
/* Number of bytes to be sent. */
writenum = stream_get_endp (s) - stream_get_getp (s);
/* Call write() system call. */
num = write (peer->fd, STREAM_PNT (s), writenum);
- write_errno = errno;
- fcntl (peer->fd, F_SETFL, val);
- if (num <= 0)
+ if (num < 0)
{
- /* Partial write. */
- if (write_errno == EWOULDBLOCK || write_errno == EAGAIN)
- break;
+ /* write failed either retry needed or error */
+ if (ERRNO_IO_RETRY(errno))
+ break;
BGP_EVENT_ADD (peer, TCP_fatal_error);
return 0;
}
+
if (num != writenum)
{
+ /* Partial write */
stream_forward_getp (s, num);
-
- if (write_errno == EAGAIN)
- break;
-
- continue;
+ break;
}
/* Retrieve BGP packet type. */
@@ -690,13 +679,14 @@ bgp_write (struct thread *thread)
/* OK we send packet so delete it. */
bgp_packet_delete (peer);
-
- if (++count >= BGP_WRITE_PACKET_MAX)
- break;
}
+ while (++count < BGP_WRITE_PACKET_MAX &&
+ (s = bgp_write_packet (peer)) != NULL);
if (bgp_write_proceed (peer))
BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+ else
+ sockopt_cork (peer->fd, 0);
return 0;
}
@@ -705,7 +695,7 @@ bgp_write (struct thread *thread)
static int
bgp_write_notify (struct peer *peer)
{
- int ret;
+ int ret, val;
u_char type;
struct stream *s;
@@ -715,7 +705,10 @@ bgp_write_notify (struct peer *peer)
return 0;
assert (stream_get_endp (s) >= BGP_HEADER_SIZE);
- /* I'm not sure fd is writable. */
+ /* Put socket in blocking mode. */
+ val = fcntl (peer->fd, F_GETFL, 0);
+ fcntl (peer->fd, F_SETFL, val & ~O_NONBLOCK);
+
ret = writen (peer->fd, STREAM_DATA (s), stream_get_endp (s));
if (ret <= 0)
{
@@ -2237,12 +2230,13 @@ bgp_read_packet (struct peer *peer)
return 0;
/* Read packet from fd. */
- nbytes = stream_read_unblock (peer->ibuf, peer->fd, readsize);
+ nbytes = stream_read_try (peer->ibuf, peer->fd, readsize);
/* If read byte is smaller than zero then error occured. */
if (nbytes < 0)
{
- if (errno == EAGAIN)
+ /* Transient error should retry */
+ if (nbytes == -2)
return -1;
plog_err (peer->log, "%s [Error] bgp_read_packet error: %s",
@@ -2319,22 +2313,14 @@ bgp_read (struct thread *thread)
peer = THREAD_ARG (thread);
peer->t_read = NULL;
- /* For non-blocking IO check. */
- if (peer->status == Connect)
- {
- bgp_connect_check (peer);
- goto done;
- }
- else
+ if (peer->fd < 0)
{
- if (peer->fd < 0)
- {
- zlog_err ("bgp_read peer's fd is negative value %d", peer->fd);
- return -1;
- }
- BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
+ zlog_err ("bgp_read peer's fd is negative value %d", peer->fd);
+ return -1;
}
+ BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
+
/* Read packet header to determine type of the packet */
if (peer->packet_size == 0)
peer->packet_size = BGP_HEADER_SIZE;
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index a92ca4e2..3d0417b1 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -1614,14 +1614,13 @@ bgp_process_queue_init (void)
}
bm->process_main_queue->spec.workfunc = &bgp_process_main;
- bm->process_rsclient_queue->spec.workfunc = &bgp_process_rsclient;
bm->process_main_queue->spec.del_item_data = &bgp_processq_del;
- bm->process_rsclient_queue->spec.del_item_data
- = bm->process_main_queue->spec.del_item_data;
- bm->process_main_queue->spec.max_retries
- = bm->process_main_queue->spec.max_retries = 0;
- bm->process_rsclient_queue->spec.hold
- = bm->process_main_queue->spec.hold = 50;
+ bm->process_main_queue->spec.max_retries = 0;
+ bm->process_main_queue->spec.hold = 50;
+
+ memcpy (bm->process_rsclient_queue, bm->process_main_queue,
+ sizeof (struct work_queue *));
+ bm->process_rsclient_queue->spec.workfunc = &bgp_process_rsclient;
}
void
@@ -5613,23 +5612,7 @@ route_vty_out_route (struct prefix *p, struct vty *vty)
u_int32_t destination;
char buf[BUFSIZ];
- if (p->family == AF_INET)
- {
- len = vty_out (vty, "%s", inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ));
- destination = ntohl (p->u.prefix4.s_addr);
-
- if ((IN_CLASSC (destination) && p->prefixlen == 24)
- || (IN_CLASSB (destination) && p->prefixlen == 16)
- || (IN_CLASSA (destination) && p->prefixlen == 8)
- || p->u.prefix4.s_addr == 0)
- {
- /* When mask is natural, mask is not displayed. */
- }
- else
- len += vty_out (vty, "/%d", p->prefixlen);
- }
- else
- len = vty_out (vty, "%s/%d", inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
+ len = vty_out (vty, "%s/%d", inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
p->prefixlen);
len = 17 - len;
@@ -6663,6 +6646,15 @@ DEFUN (show_ip_bgp_ipv4,
return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_normal, NULL);
}
+ALIAS (show_ip_bgp_ipv4,
+ show_bgp_ipv4_safi_cmd,
+ "show bgp ipv4 (unicast|multicast)",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n")
+
DEFUN (show_ip_bgp_route,
show_ip_bgp_route_cmd,
"show ip bgp A.B.C.D",
@@ -6691,6 +6683,16 @@ DEFUN (show_ip_bgp_ipv4_route,
return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 0);
}
+ALIAS (show_ip_bgp_ipv4_route,
+ show_bgp_ipv4_safi_route_cmd,
+ "show bgp ipv4 (unicast|multicast) A.B.C.D",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Network in the BGP routing table to display\n")
+
DEFUN (show_ip_bgp_vpnv4_all_route,
show_ip_bgp_vpnv4_all_route_cmd,
"show ip bgp vpnv4 all A.B.C.D",
@@ -6755,6 +6757,16 @@ DEFUN (show_ip_bgp_ipv4_prefix,
return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 1);
}
+ALIAS (show_ip_bgp_ipv4_prefix,
+ show_bgp_ipv4_safi_prefix_cmd,
+ "show bgp ipv4 (unicast|multicast) A.B.C.D/M",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+
DEFUN (show_ip_bgp_vpnv4_all_prefix,
show_ip_bgp_vpnv4_all_prefix_cmd,
"show ip bgp vpnv4 all A.B.C.D/M",
@@ -6857,6 +6869,22 @@ ALIAS (show_bgp,
BGP_STR
"Address family\n")
+DEFUN (show_bgp_ipv6_safi,
+ show_bgp_ipv6_safi_cmd,
+ "show bgp ipv6 (unicast|multicast)",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n")
+{
+ if (strncmp (argv[0], "m", 1) == 0)
+ return bgp_show (vty, NULL, AFI_IP6, SAFI_MULTICAST, bgp_show_type_normal,
+ NULL);
+
+ return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, bgp_show_type_normal, NULL);
+}
+
/* old command */
DEFUN (show_ipv6_bgp,
show_ipv6_bgp_cmd,
@@ -6887,6 +6915,22 @@ ALIAS (show_bgp_route,
"Address family\n"
"Network in the BGP routing table to display\n")
+DEFUN (show_bgp_ipv6_safi_route,
+ show_bgp_ipv6_safi_route_cmd,
+ "show bgp ipv6 (unicast|multicast) X:X::X:X",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Network in the BGP routing table to display\n")
+{
+ if (strncmp (argv[0], "m", 1) == 0)
+ return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 0);
+
+ return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 0);
+}
+
/* old command */
DEFUN (show_ipv6_bgp_route,
show_ipv6_bgp_route_cmd,
@@ -6917,6 +6961,22 @@ ALIAS (show_bgp_prefix,
"Address family\n"
"IPv6 prefix <network>/<length>\n")
+DEFUN (show_bgp_ipv6_safi_prefix,
+ show_bgp_ipv6_safi_prefix_cmd,
+ "show bgp ipv6 (unicast|multicast) X:X::X:X/M",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
+{
+ if (strncmp (argv[0], "m", 1) == 0)
+ return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 1);
+
+ return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1);
+}
+
/* old command */
DEFUN (show_ipv6_bgp_prefix,
show_ipv6_bgp_prefix_cmd,
@@ -7621,15 +7681,36 @@ DEFUN (show_ipv6_mbgp_community_all,
#endif /* HAVE_IPV6 */
static int
-bgp_show_community (struct vty *vty, int argc, const char **argv, int exact,
- u_int16_t afi, u_char safi)
+bgp_show_community (struct vty *vty, const char *view_name, int argc,
+ const char **argv, int exact, afi_t afi, safi_t safi)
{
struct community *com;
struct buffer *b;
+ struct bgp *bgp;
int i;
char *str;
int first = 0;
+ /* BGP structure lookup */
+ if (view_name)
+ {
+ bgp = bgp_lookup_by_name (view_name);
+ if (bgp == NULL)
+ {
+ vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+ else
+ {
+ bgp = bgp_get_default ();
+ if (bgp == NULL)
+ {
+ vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
b = buffer_new (1024);
for (i = 0; i < argc; i++)
{
@@ -7657,7 +7738,7 @@ bgp_show_community (struct vty *vty, int argc, const char **argv, int exact,
return CMD_WARNING;
}
- return bgp_show (vty, NULL, afi, safi,
+ return bgp_show (vty, bgp, afi, safi,
(exact ? bgp_show_type_community_exact :
bgp_show_type_community), com);
}
@@ -7674,7 +7755,7 @@ DEFUN (show_ip_bgp_community,
"Do not advertise to any peer (well-known community)\n"
"Do not export to next AS (well-known community)\n")
{
- return bgp_show_community (vty, argc, argv, 0, AFI_IP, SAFI_UNICAST);
+ return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_UNICAST);
}
ALIAS (show_ip_bgp_community,
@@ -7753,9 +7834,9 @@ DEFUN (show_ip_bgp_ipv4_community,
"Do not export to next AS (well-known community)\n")
{
if (strncmp (argv[0], "m", 1) == 0)
- return bgp_show_community (vty, argc, argv, 0, AFI_IP, SAFI_MULTICAST);
+ return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_MULTICAST);
- return bgp_show_community (vty, argc, argv, 0, AFI_IP, SAFI_UNICAST);
+ return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_UNICAST);
}
ALIAS (show_ip_bgp_ipv4_community,
@@ -7827,6 +7908,177 @@ ALIAS (show_ip_bgp_ipv4_community,
"Do not advertise to any peer (well-known community)\n"
"Do not export to next AS (well-known community)\n")
+DEFUN (show_bgp_view_afi_safi_community_all,
+ show_bgp_view_afi_safi_community_all_cmd,
+#ifdef HAVE_IPV6
+ "show bgp view WORD (ipv4|ipv6) (unicast|multicast) community",
+#else
+ "show bgp view WORD ipv4 (unicast|multicast) community",
+#endif
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "BGP view name\n"
+ "Address family\n"
+#ifdef HAVE_IPV6
+ "Address family\n"
+#endif
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Display routes containing communities\n")
+{
+ int afi;
+ int safi;
+ struct bgp *bgp;
+
+ /* BGP structure lookup. */
+ bgp = bgp_lookup_by_name (argv[0]);
+ if (bgp == NULL)
+ {
+ vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+#ifdef HAVE_IPV6
+ afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP;
+ safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+#else
+ afi = AFI_IP;
+ safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+#endif
+ return bgp_show (vty, bgp, afi, safi, bgp_show_type_community_all, NULL);
+}
+
+DEFUN (show_bgp_view_afi_safi_community,
+ show_bgp_view_afi_safi_community_cmd,
+#ifdef HAVE_IPV6
+ "show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export)",
+#else
+ "show bgp view WORD ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export)",
+#endif
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "BGP view name\n"
+ "Address family\n"
+#ifdef HAVE_IPV6
+ "Address family\n"
+#endif
+ "Address family modifier\n"
+ "Address family modifier\n"
+ "Display routes matching the communities\n"
+ "community number\n"
+ "Do not send outside local AS (well-known community)\n"
+ "Do not advertise to any peer (well-known community)\n"
+ "Do not export to next AS (well-known community)\n")
+{
+ int afi;
+ int safi;
+
+#ifdef HAVE_IPV6
+ afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP;
+ safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ return bgp_show_community (vty, argv[0], argc-3, &argv[3], 0, afi, safi);
+#else
+ afi = AFI_IP;
+ safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ return bgp_show_community (vty, argv[0], argc-2, &argv[2], 0, afi, safi);
+#endif
+}
+
+ALIAS (show_bgp_view_afi_safi_community,
+ show_bgp_view_afi_safi_community2_cmd,
+#ifdef HAVE_IPV6
+ "show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+#else
+ "show bgp view WORD ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+#endif
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "BGP view name\n"
+ "Address family\n"
+#ifdef HAVE_IPV6
+ "Address family\n"
+#endif
+ "Address family modifier\n"
+ "Address family modifier\n"
+ "Display routes matching the communities\n"
+ "community number\n"
+ "Do not send outside local AS (well-known community)\n"
+ "Do not advertise to any peer (well-known community)\n"
+ "Do not export to next AS (well-known community)\n"
+ "community number\n"
+ "Do not send outside local AS (well-known community)\n"
+ "Do not advertise to any peer (well-known community)\n"
+ "Do not export to next AS (well-known community)\n")
+
+ALIAS (show_bgp_view_afi_safi_community,
+ show_bgp_view_afi_safi_community3_cmd,
+#ifdef HAVE_IPV6
+ "show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+#else
+ "show bgp view WORD ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+#endif
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "BGP view name\n"
+ "Address family\n"
+#ifdef HAVE_IPV6
+ "Address family\n"
+#endif
+ "Address family modifier\n"
+ "Address family modifier\n"
+ "Display routes matching the communities\n"
+ "community number\n"
+ "Do not send outside local AS (well-known community)\n"
+ "Do not advertise to any peer (well-known community)\n"
+ "Do not export to next AS (well-known community)\n"
+ "community number\n"
+ "Do not send outside local AS (well-known community)\n"
+ "Do not advertise to any peer (well-known community)\n"
+ "Do not export to next AS (well-known community)\n"
+ "community number\n"
+ "Do not send outside local AS (well-known community)\n"
+ "Do not advertise to any peer (well-known community)\n"
+ "Do not export to next AS (well-known community)\n")
+
+ALIAS (show_bgp_view_afi_safi_community,
+ show_bgp_view_afi_safi_community4_cmd,
+#ifdef HAVE_IPV6
+ "show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+#else
+ "show bgp view WORD ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+#endif
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "BGP view name\n"
+ "Address family\n"
+#ifdef HAVE_IPV6
+ "Address family\n"
+#endif
+ "Address family modifier\n"
+ "Address family modifier\n"
+ "Display routes matching the communities\n"
+ "community number\n"
+ "Do not send outside local AS (well-known community)\n"
+ "Do not advertise to any peer (well-known community)\n"
+ "Do not export to next AS (well-known community)\n"
+ "community number\n"
+ "Do not send outside local AS (well-known community)\n"
+ "Do not advertise to any peer (well-known community)\n"
+ "Do not export to next AS (well-known community)\n"
+ "community number\n"
+ "Do not send outside local AS (well-known community)\n"
+ "Do not advertise to any peer (well-known community)\n"
+ "Do not export to next AS (well-known community)\n"
+ "community number\n"
+ "Do not send outside local AS (well-known community)\n"
+ "Do not advertise to any peer (well-known community)\n"
+ "Do not export to next AS (well-known community)\n")
+
DEFUN (show_ip_bgp_community_exact,
show_ip_bgp_community_exact_cmd,
"show ip bgp community (AA:NN|local-AS|no-advertise|no-export) exact-match",
@@ -7840,7 +8092,7 @@ DEFUN (show_ip_bgp_community_exact,
"Do not export to next AS (well-known community)\n"
"Exact match of the communities")
{
- return bgp_show_community (vty, argc, argv, 1, AFI_IP, SAFI_UNICAST);
+ return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_UNICAST);
}
ALIAS (show_ip_bgp_community_exact,
@@ -7923,9 +8175,9 @@ DEFUN (show_ip_bgp_ipv4_community_exact,
"Exact match of the communities")
{
if (strncmp (argv[0], "m", 1) == 0)
- return bgp_show_community (vty, argc, argv, 1, AFI_IP, SAFI_MULTICAST);
+ return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_MULTICAST);
- return bgp_show_community (vty, argc, argv, 1, AFI_IP, SAFI_UNICAST);
+ return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_UNICAST);
}
ALIAS (show_ip_bgp_ipv4_community_exact,
@@ -8012,7 +8264,7 @@ DEFUN (show_bgp_community,
"Do not advertise to any peer (well-known community)\n"
"Do not export to next AS (well-known community)\n")
{
- return bgp_show_community (vty, argc, argv, 0, AFI_IP6, SAFI_UNICAST);
+ return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP6, SAFI_UNICAST);
}
ALIAS (show_bgp_community,
@@ -8157,7 +8409,7 @@ DEFUN (show_ipv6_bgp_community,
"Do not advertise to any peer (well-known community)\n"
"Do not export to next AS (well-known community)\n")
{
- return bgp_show_community (vty, argc, argv, 0, AFI_IP6, SAFI_UNICAST);
+ return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP6, SAFI_UNICAST);
}
/* old command */
@@ -8235,7 +8487,7 @@ DEFUN (show_bgp_community_exact,
"Do not export to next AS (well-known community)\n"
"Exact match of the communities")
{
- return bgp_show_community (vty, argc, argv, 1, AFI_IP6, SAFI_UNICAST);
+ return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP6, SAFI_UNICAST);
}
ALIAS (show_bgp_community_exact,
@@ -8388,7 +8640,7 @@ DEFUN (show_ipv6_bgp_community_exact,
"Do not export to next AS (well-known community)\n"
"Exact match of the communities")
{
- return bgp_show_community (vty, argc, argv, 1, AFI_IP6, SAFI_UNICAST);
+ return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP6, SAFI_UNICAST);
}
/* old command */
@@ -8470,7 +8722,7 @@ DEFUN (show_ipv6_mbgp_community,
"Do not advertise to any peer (well-known community)\n"
"Do not export to next AS (well-known community)\n")
{
- return bgp_show_community (vty, argc, argv, 0, AFI_IP6, SAFI_MULTICAST);
+ return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP6, SAFI_MULTICAST);
}
/* old command */
@@ -8550,7 +8802,7 @@ DEFUN (show_ipv6_mbgp_community_exact,
"Do not export to next AS (well-known community)\n"
"Exact match of the communities")
{
- return bgp_show_community (vty, argc, argv, 1, AFI_IP6, SAFI_MULTICAST);
+ return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP6, SAFI_MULTICAST);
}
/* old command */
@@ -9959,6 +10211,56 @@ DEFUN (show_ip_bgp_ipv4_neighbor_received_routes,
return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 1);
}
+DEFUN (show_bgp_view_afi_safi_neighbor_adv_recd_routes,
+ show_bgp_view_afi_safi_neighbor_adv_recd_routes_cmd,
+#ifdef HAVE_IPV6
+ "show bgp view WORD (ipv4|ipv6) (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) (advertised-routes|received-routes)",
+#else
+ "show bgp view WORD ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) (advertised-routes|received-routes)",
+#endif
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "BGP view name\n"
+ "Address family\n"
+#ifdef HAVE_IPV6
+ "Address family\n"
+#endif
+ "Address family modifier\n"
+ "Address family modifier\n"
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "Neighbor to display information about\n"
+ "Neighbor to display information about\n"
+ "Display the advertised routes to neighbor\n"
+ "Display the received routes from neighbor\n")
+{
+ int afi;
+ int safi;
+ int in;
+ struct peer *peer;
+
+#ifdef HAVE_IPV6
+ peer = peer_lookup_in_view (vty, argv[0], argv[3]);
+#else
+ peer = peer_lookup_in_view (vty, argv[0], argv[2]);
+#endif
+
+ if (! peer)
+ return CMD_WARNING;
+
+#ifdef HAVE_IPV6
+ afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP;
+ safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ in = (strncmp (argv[4], "r", 1) == 0) ? 1 : 0;
+#else
+ afi = AFI_IP;
+ safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ in = (strncmp (argv[3], "r", 1) == 0) ? 1 : 0;
+#endif
+
+ return peer_adj_routes (vty, peer, afi, safi, in);
+}
+
DEFUN (show_ip_bgp_neighbor_received_prefix_filter,
show_ip_bgp_neighbor_received_prefix_filter_cmd,
"show ip bgp neighbors (A.B.C.D|X:X::X:X) received prefix-filter",
@@ -10366,6 +10668,65 @@ ALIAS (show_ip_bgp_view_rsclient,
"Information about Route Server Client\n"
NEIGHBOR_ADDR_STR)
+DEFUN (show_bgp_view_ipv4_safi_rsclient,
+ show_bgp_view_ipv4_safi_rsclient_cmd,
+ "show bgp view WORD ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X)",
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "BGP view name\n"
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Client\n"
+ NEIGHBOR_ADDR_STR)
+{
+ struct bgp_table *table;
+ struct peer *peer;
+ safi_t safi;
+
+ if (argc == 3) {
+ peer = peer_lookup_in_view (vty, argv[0], argv[2]);
+ safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ } else {
+ peer = peer_lookup_in_view (vty, NULL, argv[1]);
+ safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ }
+
+ if (! peer)
+ return CMD_WARNING;
+
+ if (! peer->afc[AFI_IP][safi])
+ {
+ vty_out (vty, "%% Activate the neighbor for the address family first%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][safi],
+ PEER_FLAG_RSERVER_CLIENT))
+ {
+ vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ table = peer->rib[AFI_IP][safi];
+
+ return bgp_show_table (vty, table, &peer->remote_id, bgp_show_type_normal, NULL);
+}
+
+ALIAS (show_bgp_view_ipv4_safi_rsclient,
+ show_bgp_ipv4_safi_rsclient_cmd,
+ "show bgp ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X)",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Client\n"
+ NEIGHBOR_ADDR_STR)
+
DEFUN (show_ip_bgp_view_rsclient_route,
show_ip_bgp_view_rsclient_route_cmd,
"show ip bgp view WORD rsclient (A.B.C.D|X:X::X:X) A.B.C.D",
@@ -10439,6 +10800,87 @@ ALIAS (show_ip_bgp_view_rsclient_route,
NEIGHBOR_ADDR_STR
"Network in the BGP routing table to display\n")
+DEFUN (show_bgp_view_ipv4_safi_rsclient_route,
+ show_bgp_view_ipv4_safi_rsclient_route_cmd,
+ "show bgp view WORD ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D",
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "BGP view name\n"
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Client\n"
+ NEIGHBOR_ADDR_STR
+ "Network in the BGP routing table to display\n")
+{
+ struct bgp *bgp;
+ struct peer *peer;
+ safi_t safi;
+
+ /* BGP structure lookup. */
+ if (argc == 4)
+ {
+ bgp = bgp_lookup_by_name (argv[0]);
+ if (bgp == NULL)
+ {
+ vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+ else
+ {
+ bgp = bgp_get_default ();
+ if (bgp == NULL)
+ {
+ vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ if (argc == 4) {
+ peer = peer_lookup_in_view (vty, argv[0], argv[2]);
+ safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ } else {
+ peer = peer_lookup_in_view (vty, NULL, argv[1]);
+ safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ }
+
+ if (! peer)
+ return CMD_WARNING;
+
+ if (! peer->afc[AFI_IP][safi])
+ {
+ vty_out (vty, "%% Activate the neighbor for the address family first%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+}
+
+ if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][safi],
+ PEER_FLAG_RSERVER_CLIENT))
+ {
+ vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][safi],
+ (argc == 4) ? argv[3] : argv[2],
+ AFI_IP, safi, NULL, 0);
+}
+
+ALIAS (show_bgp_view_ipv4_safi_rsclient_route,
+ show_bgp_ipv4_safi_rsclient_route_cmd,
+ "show bgp ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Client\n"
+ NEIGHBOR_ADDR_STR
+ "Network in the BGP routing table to display\n")
+
DEFUN (show_ip_bgp_view_rsclient_prefix,
show_ip_bgp_view_rsclient_prefix_cmd,
"show ip bgp view WORD rsclient (A.B.C.D|X:X::X:X) A.B.C.D/M",
@@ -10512,6 +10954,86 @@ ALIAS (show_ip_bgp_view_rsclient_prefix,
NEIGHBOR_ADDR_STR
"IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+DEFUN (show_bgp_view_ipv4_safi_rsclient_prefix,
+ show_bgp_view_ipv4_safi_rsclient_prefix_cmd,
+ "show bgp view WORD ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D/M",
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "BGP view name\n"
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Client\n"
+ NEIGHBOR_ADDR_STR
+ "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+ struct bgp *bgp;
+ struct peer *peer;
+ safi_t safi;
+
+ /* BGP structure lookup. */
+ if (argc == 4)
+ {
+ bgp = bgp_lookup_by_name (argv[0]);
+ if (bgp == NULL)
+ {
+ vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+ else
+ {
+ bgp = bgp_get_default ();
+ if (bgp == NULL)
+ {
+ vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ if (argc == 4) {
+ peer = peer_lookup_in_view (vty, argv[0], argv[2]);
+ safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ } else {
+ peer = peer_lookup_in_view (vty, NULL, argv[1]);
+ safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ }
+
+ if (! peer)
+ return CMD_WARNING;
+
+ if (! peer->afc[AFI_IP][safi])
+ {
+ vty_out (vty, "%% Activate the neighbor for the address family first%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+}
+
+ if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][safi],
+ PEER_FLAG_RSERVER_CLIENT))
+{
+ vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][safi],
+ (argc == 4) ? argv[3] : argv[2],
+ AFI_IP, safi, NULL, 1);
+}
+
+ALIAS (show_bgp_view_ipv4_safi_rsclient_prefix,
+ show_bgp_ipv4_safi_rsclient_prefix_cmd,
+ "show bgp ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D/M",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Client\n"
+ NEIGHBOR_ADDR_STR
+ "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
#ifdef HAVE_IPV6
DEFUN (show_bgp_view_neighbor_routes,
@@ -10778,6 +11300,65 @@ ALIAS (show_bgp_view_rsclient,
"Information about Route Server Client\n"
NEIGHBOR_ADDR_STR)
+DEFUN (show_bgp_view_ipv6_safi_rsclient,
+ show_bgp_view_ipv6_safi_rsclient_cmd,
+ "show bgp view WORD ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X)",
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "BGP view name\n"
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Client\n"
+ NEIGHBOR_ADDR_STR)
+{
+ struct bgp_table *table;
+ struct peer *peer;
+ safi_t safi;
+
+ if (argc == 3) {
+ peer = peer_lookup_in_view (vty, argv[0], argv[2]);
+ safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ } else {
+ peer = peer_lookup_in_view (vty, NULL, argv[1]);
+ safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ }
+
+ if (! peer)
+ return CMD_WARNING;
+
+ if (! peer->afc[AFI_IP6][safi])
+ {
+ vty_out (vty, "%% Activate the neighbor for the address family first%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][safi],
+ PEER_FLAG_RSERVER_CLIENT))
+ {
+ vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ table = peer->rib[AFI_IP6][safi];
+
+ return bgp_show_table (vty, table, &peer->remote_id, bgp_show_type_normal, NULL);
+}
+
+ALIAS (show_bgp_view_ipv6_safi_rsclient,
+ show_bgp_ipv6_safi_rsclient_cmd,
+ "show bgp ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X)",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Client\n"
+ NEIGHBOR_ADDR_STR)
+
DEFUN (show_bgp_view_rsclient_route,
show_bgp_view_rsclient_route_cmd,
"show bgp view WORD rsclient (A.B.C.D|X:X::X:X) X:X::X:X",
@@ -10849,6 +11430,87 @@ ALIAS (show_bgp_view_rsclient_route,
NEIGHBOR_ADDR_STR
"Network in the BGP routing table to display\n")
+DEFUN (show_bgp_view_ipv6_safi_rsclient_route,
+ show_bgp_view_ipv6_safi_rsclient_route_cmd,
+ "show bgp view WORD ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) X:X::X:X",
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "BGP view name\n"
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Client\n"
+ NEIGHBOR_ADDR_STR
+ "Network in the BGP routing table to display\n")
+{
+ struct bgp *bgp;
+ struct peer *peer;
+ safi_t safi;
+
+ /* BGP structure lookup. */
+ if (argc == 4)
+ {
+ bgp = bgp_lookup_by_name (argv[0]);
+ if (bgp == NULL)
+ {
+ vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+ else
+ {
+ bgp = bgp_get_default ();
+ if (bgp == NULL)
+ {
+ vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ if (argc == 4) {
+ peer = peer_lookup_in_view (vty, argv[0], argv[2]);
+ safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ } else {
+ peer = peer_lookup_in_view (vty, NULL, argv[1]);
+ safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ }
+
+ if (! peer)
+ return CMD_WARNING;
+
+ if (! peer->afc[AFI_IP6][safi])
+ {
+ vty_out (vty, "%% Activate the neighbor for the address family first%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+}
+
+ if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][safi],
+ PEER_FLAG_RSERVER_CLIENT))
+ {
+ vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][safi],
+ (argc == 4) ? argv[3] : argv[2],
+ AFI_IP6, safi, NULL, 0);
+}
+
+ALIAS (show_bgp_view_ipv6_safi_rsclient_route,
+ show_bgp_ipv6_safi_rsclient_route_cmd,
+ "show bgp ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) X:X::X:X",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Client\n"
+ NEIGHBOR_ADDR_STR
+ "Network in the BGP routing table to display\n")
+
DEFUN (show_bgp_view_rsclient_prefix,
show_bgp_view_rsclient_prefix_cmd,
"show bgp view WORD rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M",
@@ -10920,6 +11582,87 @@ ALIAS (show_bgp_view_rsclient_prefix,
NEIGHBOR_ADDR_STR
"IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
+DEFUN (show_bgp_view_ipv6_safi_rsclient_prefix,
+ show_bgp_view_ipv6_safi_rsclient_prefix_cmd,
+ "show bgp view WORD ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M",
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "BGP view name\n"
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Client\n"
+ NEIGHBOR_ADDR_STR
+ "IP prefix <network>/<length>, e.g., 3ffe::/16\n")
+{
+ struct bgp *bgp;
+ struct peer *peer;
+ safi_t safi;
+
+ /* BGP structure lookup. */
+ if (argc == 4)
+ {
+ bgp = bgp_lookup_by_name (argv[0]);
+ if (bgp == NULL)
+ {
+ vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+ else
+ {
+ bgp = bgp_get_default ();
+ if (bgp == NULL)
+ {
+ vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ if (argc == 4) {
+ peer = peer_lookup_in_view (vty, argv[0], argv[2]);
+ safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ } else {
+ peer = peer_lookup_in_view (vty, NULL, argv[1]);
+ safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ }
+
+ if (! peer)
+ return CMD_WARNING;
+
+ if (! peer->afc[AFI_IP6][safi])
+ {
+ vty_out (vty, "%% Activate the neighbor for the address family first%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+}
+
+ if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][safi],
+ PEER_FLAG_RSERVER_CLIENT))
+{
+ vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][safi],
+ (argc == 4) ? argv[3] : argv[2],
+ AFI_IP6, safi, NULL, 1);
+}
+
+ALIAS (show_bgp_view_ipv6_safi_rsclient_prefix,
+ show_bgp_ipv6_safi_rsclient_prefix_cmd,
+ "show bgp ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Client\n"
+ NEIGHBOR_ADDR_STR
+ "IP prefix <network>/<length>, e.g., 3ffe::/16\n")
+
#endif /* HAVE_IPV6 */
struct bgp_table *bgp_distance_table;
@@ -11521,18 +12264,9 @@ bgp_config_write_network (struct vty *vty, struct bgp *bgp,
destination = ntohl (p->u.prefix4.s_addr);
masklen2ip (p->prefixlen, &netmask);
- vty_out (vty, " network %s",
- inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN));
-
- if ((IN_CLASSC (destination) && p->prefixlen == 24)
- || (IN_CLASSB (destination) && p->prefixlen == 16)
- || (IN_CLASSA (destination) && p->prefixlen == 8)
- || p->u.prefix4.s_addr == 0)
- {
- /* Natural mask is not display. */
- }
- else
- vty_out (vty, " mask %s", inet_ntoa (netmask));
+ vty_out (vty, " network %s mask %s",
+ inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+ inet_ntoa (netmask));
}
else
{
@@ -11773,12 +12507,15 @@ bgp_route_init (void)
install_element (VIEW_NODE, &show_ip_bgp_cmd);
install_element (VIEW_NODE, &show_ip_bgp_ipv4_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv4_safi_cmd);
install_element (VIEW_NODE, &show_ip_bgp_route_cmd);
install_element (VIEW_NODE, &show_ip_bgp_ipv4_route_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv4_safi_route_cmd);
install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_route_cmd);
install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_route_cmd);
install_element (VIEW_NODE, &show_ip_bgp_prefix_cmd);
install_element (VIEW_NODE, &show_ip_bgp_ipv4_prefix_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv4_safi_prefix_cmd);
install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_prefix_cmd);
install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_prefix_cmd);
install_element (VIEW_NODE, &show_ip_bgp_view_cmd);
@@ -11804,6 +12541,11 @@ bgp_route_init (void)
install_element (VIEW_NODE, &show_ip_bgp_ipv4_community2_cmd);
install_element (VIEW_NODE, &show_ip_bgp_ipv4_community3_cmd);
install_element (VIEW_NODE, &show_ip_bgp_ipv4_community4_cmd);
+ install_element (VIEW_NODE, &show_bgp_view_afi_safi_community_all_cmd);
+ install_element (VIEW_NODE, &show_bgp_view_afi_safi_community_cmd);
+ install_element (VIEW_NODE, &show_bgp_view_afi_safi_community2_cmd);
+ install_element (VIEW_NODE, &show_bgp_view_afi_safi_community3_cmd);
+ install_element (VIEW_NODE, &show_bgp_view_afi_safi_community4_cmd);
install_element (VIEW_NODE, &show_ip_bgp_community_exact_cmd);
install_element (VIEW_NODE, &show_ip_bgp_community2_exact_cmd);
install_element (VIEW_NODE, &show_ip_bgp_community3_exact_cmd);
@@ -11822,6 +12564,7 @@ bgp_route_init (void)
install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbor_advertised_route_cmd);
install_element (VIEW_NODE, &show_ip_bgp_neighbor_received_routes_cmd);
install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbor_received_routes_cmd);
+ install_element (VIEW_NODE, &show_bgp_view_afi_safi_neighbor_adv_recd_routes_cmd);
install_element (VIEW_NODE, &show_ip_bgp_neighbor_routes_cmd);
install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbor_routes_cmd);
install_element (VIEW_NODE, &show_ip_bgp_neighbor_received_prefix_filter_cmd);
@@ -11839,20 +12582,28 @@ bgp_route_init (void)
install_element (VIEW_NODE, &show_ip_bgp_neighbor_flap_cmd);
install_element (VIEW_NODE, &show_ip_bgp_neighbor_damp_cmd);
install_element (VIEW_NODE, &show_ip_bgp_rsclient_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_cmd);
install_element (VIEW_NODE, &show_ip_bgp_rsclient_route_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_route_cmd);
install_element (VIEW_NODE, &show_ip_bgp_rsclient_prefix_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_prefix_cmd);
install_element (VIEW_NODE, &show_ip_bgp_view_neighbor_advertised_route_cmd);
install_element (VIEW_NODE, &show_ip_bgp_view_neighbor_received_routes_cmd);
install_element (VIEW_NODE, &show_ip_bgp_view_rsclient_cmd);
+ install_element (VIEW_NODE, &show_bgp_view_ipv4_safi_rsclient_cmd);
install_element (VIEW_NODE, &show_ip_bgp_view_rsclient_route_cmd);
+ install_element (VIEW_NODE, &show_bgp_view_ipv4_safi_rsclient_route_cmd);
install_element (VIEW_NODE, &show_ip_bgp_view_rsclient_prefix_cmd);
+ install_element (VIEW_NODE, &show_bgp_view_ipv4_safi_rsclient_prefix_cmd);
/* Restricted node: VIEW_NODE - (set of dangerous commands) */
install_element (RESTRICTED_NODE, &show_ip_bgp_route_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_route_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_route_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_route_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_prefix_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_prefix_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_prefix_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_all_prefix_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_prefix_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_view_route_cmd);
@@ -11865,6 +12616,11 @@ bgp_route_init (void)
install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community2_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community3_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community4_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community_all_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community2_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community3_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community4_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_community_exact_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_community2_exact_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_community3_exact_cmd);
@@ -11874,18 +12630,25 @@ bgp_route_init (void)
install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community3_exact_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community4_exact_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_rsclient_route_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rsclient_route_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_rsclient_prefix_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rsclient_prefix_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_view_rsclient_route_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_view_ipv4_safi_rsclient_route_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_view_rsclient_prefix_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_view_ipv4_safi_rsclient_prefix_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_ipv4_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv4_safi_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_route_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_ipv4_route_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv4_safi_route_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_route_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_route_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_prefix_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_ipv4_prefix_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv4_safi_prefix_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_prefix_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_prefix_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_view_cmd);
@@ -11911,6 +12674,11 @@ bgp_route_init (void)
install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community2_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community3_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community4_cmd);
+ install_element (ENABLE_NODE, &show_bgp_view_afi_safi_community_all_cmd);
+ install_element (ENABLE_NODE, &show_bgp_view_afi_safi_community_cmd);
+ install_element (ENABLE_NODE, &show_bgp_view_afi_safi_community2_cmd);
+ install_element (ENABLE_NODE, &show_bgp_view_afi_safi_community3_cmd);
+ install_element (ENABLE_NODE, &show_bgp_view_afi_safi_community4_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_community_exact_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_community2_exact_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_community3_exact_cmd);
@@ -11929,6 +12697,7 @@ bgp_route_init (void)
install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_advertised_route_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_neighbor_received_routes_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_received_routes_cmd);
+ install_element (ENABLE_NODE, &show_bgp_view_afi_safi_neighbor_adv_recd_routes_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_neighbor_routes_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_routes_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_neighbor_received_prefix_filter_cmd);
@@ -11946,13 +12715,19 @@ bgp_route_init (void)
install_element (ENABLE_NODE, &show_ip_bgp_neighbor_flap_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_neighbor_damp_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_rsclient_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv4_safi_rsclient_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_rsclient_route_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv4_safi_rsclient_route_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_rsclient_prefix_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv4_safi_rsclient_prefix_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_view_neighbor_advertised_route_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_view_neighbor_received_routes_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_view_rsclient_cmd);
+ install_element (ENABLE_NODE, &show_bgp_view_ipv4_safi_rsclient_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_view_rsclient_route_cmd);
+ install_element (ENABLE_NODE, &show_bgp_view_ipv4_safi_rsclient_route_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_view_rsclient_prefix_cmd);
+ install_element (ENABLE_NODE, &show_bgp_view_ipv4_safi_rsclient_prefix_cmd);
/* BGP dampening clear commands */
install_element (ENABLE_NODE, &clear_ip_bgp_dampening_cmd);
@@ -11991,10 +12766,13 @@ bgp_route_init (void)
install_element (VIEW_NODE, &show_bgp_cmd);
install_element (VIEW_NODE, &show_bgp_ipv6_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv6_safi_cmd);
install_element (VIEW_NODE, &show_bgp_route_cmd);
install_element (VIEW_NODE, &show_bgp_ipv6_route_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv6_safi_route_cmd);
install_element (VIEW_NODE, &show_bgp_prefix_cmd);
install_element (VIEW_NODE, &show_bgp_ipv6_prefix_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv6_safi_prefix_cmd);
install_element (VIEW_NODE, &show_bgp_regexp_cmd);
install_element (VIEW_NODE, &show_bgp_ipv6_regexp_cmd);
install_element (VIEW_NODE, &show_bgp_prefix_list_cmd);
@@ -12040,8 +12818,11 @@ bgp_route_init (void)
install_element (VIEW_NODE, &show_bgp_neighbor_damp_cmd);
install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_damp_cmd);
install_element (VIEW_NODE, &show_bgp_rsclient_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_cmd);
install_element (VIEW_NODE, &show_bgp_rsclient_route_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_route_cmd);
install_element (VIEW_NODE, &show_bgp_rsclient_prefix_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_prefix_cmd);
install_element (VIEW_NODE, &show_bgp_view_cmd);
install_element (VIEW_NODE, &show_bgp_view_ipv6_cmd);
install_element (VIEW_NODE, &show_bgp_view_route_cmd);
@@ -12061,16 +12842,21 @@ bgp_route_init (void)
install_element (VIEW_NODE, &show_bgp_view_neighbor_damp_cmd);
install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_damp_cmd);
install_element (VIEW_NODE, &show_bgp_view_rsclient_cmd);
+ install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_cmd);
install_element (VIEW_NODE, &show_bgp_view_rsclient_route_cmd);
+ install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_route_cmd);
install_element (VIEW_NODE, &show_bgp_view_rsclient_prefix_cmd);
+ install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_prefix_cmd);
/* Restricted:
* VIEW_NODE - (set of dangerous commands) - (commands dependent on prev)
*/
install_element (RESTRICTED_NODE, &show_bgp_route_cmd);
install_element (RESTRICTED_NODE, &show_bgp_ipv6_route_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_route_cmd);
install_element (RESTRICTED_NODE, &show_bgp_prefix_cmd);
install_element (RESTRICTED_NODE, &show_bgp_ipv6_prefix_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_prefix_cmd);
install_element (RESTRICTED_NODE, &show_bgp_community_cmd);
install_element (RESTRICTED_NODE, &show_bgp_ipv6_community_cmd);
install_element (RESTRICTED_NODE, &show_bgp_community2_cmd);
@@ -12088,7 +12874,9 @@ bgp_route_init (void)
install_element (RESTRICTED_NODE, &show_bgp_community4_exact_cmd);
install_element (RESTRICTED_NODE, &show_bgp_ipv6_community4_exact_cmd);
install_element (RESTRICTED_NODE, &show_bgp_rsclient_route_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rsclient_route_cmd);
install_element (RESTRICTED_NODE, &show_bgp_rsclient_prefix_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rsclient_prefix_cmd);
install_element (RESTRICTED_NODE, &show_bgp_view_route_cmd);
install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_route_cmd);
install_element (RESTRICTED_NODE, &show_bgp_view_prefix_cmd);
@@ -12096,14 +12884,19 @@ bgp_route_init (void)
install_element (RESTRICTED_NODE, &show_bgp_view_neighbor_received_prefix_filter_cmd);
install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_neighbor_received_prefix_filter_cmd);
install_element (RESTRICTED_NODE, &show_bgp_view_rsclient_route_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_safi_rsclient_route_cmd);
install_element (RESTRICTED_NODE, &show_bgp_view_rsclient_prefix_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_safi_rsclient_prefix_cmd);
install_element (ENABLE_NODE, &show_bgp_cmd);
install_element (ENABLE_NODE, &show_bgp_ipv6_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv6_safi_cmd);
install_element (ENABLE_NODE, &show_bgp_route_cmd);
install_element (ENABLE_NODE, &show_bgp_ipv6_route_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv6_safi_route_cmd);
install_element (ENABLE_NODE, &show_bgp_prefix_cmd);
install_element (ENABLE_NODE, &show_bgp_ipv6_prefix_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv6_safi_prefix_cmd);
install_element (ENABLE_NODE, &show_bgp_regexp_cmd);
install_element (ENABLE_NODE, &show_bgp_ipv6_regexp_cmd);
install_element (ENABLE_NODE, &show_bgp_prefix_list_cmd);
@@ -12149,8 +12942,11 @@ bgp_route_init (void)
install_element (ENABLE_NODE, &show_bgp_neighbor_damp_cmd);
install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_damp_cmd);
install_element (ENABLE_NODE, &show_bgp_rsclient_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv6_safi_rsclient_cmd);
install_element (ENABLE_NODE, &show_bgp_rsclient_route_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv6_safi_rsclient_route_cmd);
install_element (ENABLE_NODE, &show_bgp_rsclient_prefix_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv6_safi_rsclient_prefix_cmd);
install_element (ENABLE_NODE, &show_bgp_view_cmd);
install_element (ENABLE_NODE, &show_bgp_view_ipv6_cmd);
install_element (ENABLE_NODE, &show_bgp_view_route_cmd);
@@ -12170,8 +12966,11 @@ bgp_route_init (void)
install_element (ENABLE_NODE, &show_bgp_view_neighbor_damp_cmd);
install_element (ENABLE_NODE, &show_bgp_view_ipv6_neighbor_damp_cmd);
install_element (ENABLE_NODE, &show_bgp_view_rsclient_cmd);
+ install_element (ENABLE_NODE, &show_bgp_view_ipv6_safi_rsclient_cmd);
install_element (ENABLE_NODE, &show_bgp_view_rsclient_route_cmd);
+ install_element (ENABLE_NODE, &show_bgp_view_ipv6_safi_rsclient_route_cmd);
install_element (ENABLE_NODE, &show_bgp_view_rsclient_prefix_cmd);
+ install_element (ENABLE_NODE, &show_bgp_view_ipv6_safi_rsclient_prefix_cmd);
/* Statistics */
install_element (ENABLE_NODE, &show_bgp_statistics_cmd);
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index c442baab..959f45c6 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -6864,6 +6864,16 @@ DEFUN (show_ip_bgp_ipv4_summary,
return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST);
}
+ALIAS (show_ip_bgp_ipv4_summary,
+ show_bgp_ipv4_safi_summary_cmd,
+ "show bgp ipv4 (unicast|multicast) summary",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Summary of BGP neighbor status\n")
+
DEFUN (show_ip_bgp_instance_ipv4_summary,
show_ip_bgp_instance_ipv4_summary_cmd,
"show ip bgp view WORD ipv4 (unicast|multicast) summary",
@@ -6883,6 +6893,18 @@ DEFUN (show_ip_bgp_instance_ipv4_summary,
return bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST);
}
+ALIAS (show_ip_bgp_instance_ipv4_summary,
+ show_bgp_instance_ipv4_safi_summary_cmd,
+ "show bgp view WORD ipv4 (unicast|multicast) summary",
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "View name\n"
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Summary of BGP neighbor status\n")
+
DEFUN (show_ip_bgp_vpnv4_all_summary,
show_ip_bgp_vpnv4_all_summary_cmd,
"show ip bgp vpnv4 all summary",
@@ -6961,6 +6983,40 @@ ALIAS (show_bgp_instance_summary,
"Address family\n"
"Summary of BGP neighbor status\n")
+DEFUN (show_bgp_ipv6_safi_summary,
+ show_bgp_ipv6_safi_summary_cmd,
+ "show bgp ipv6 (unicast|multicast) summary",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Summary of BGP neighbor status\n")
+{
+ if (strncmp (argv[0], "m", 1) == 0)
+ return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST);
+
+ return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_instance_ipv6_safi_summary,
+ show_bgp_instance_ipv6_safi_summary_cmd,
+ "show bgp view WORD ipv6 (unicast|multicast) summary",
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "View name\n"
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Summary of BGP neighbor status\n")
+{
+ if (strncmp (argv[1], "m", 1) == 0)
+ return bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_MULTICAST);
+
+ return bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST);
+}
+
/* old command */
DEFUN (show_ipv6_bgp_summary,
show_ipv6_bgp_summary_cmd,
@@ -8162,6 +8218,41 @@ DEFUN (show_ip_bgp_instance_ipv4_rsclient_summary,
return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST);
}
+DEFUN (show_bgp_instance_ipv4_safi_rsclient_summary,
+ show_bgp_instance_ipv4_safi_rsclient_summary_cmd,
+ "show bgp view WORD ipv4 (unicast|multicast) rsclient summary",
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "View name\n"
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Clients\n"
+ "Summary of all Route Server Clients\n")
+{
+ safi_t safi;
+
+ if (argc == 2) {
+ safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, safi);
+ } else {
+ safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ return bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, safi);
+ }
+}
+
+ALIAS (show_bgp_instance_ipv4_safi_rsclient_summary,
+ show_bgp_ipv4_safi_rsclient_summary_cmd,
+ "show bgp ipv4 (unicast|multicast) rsclient summary",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Clients\n"
+ "Summary of all Route Server Clients\n")
+
#ifdef HAVE_IPV6
DEFUN (show_bgp_rsclient_summary,
show_bgp_rsclient_summary_cmd,
@@ -8206,6 +8297,42 @@ ALIAS (show_bgp_instance_rsclient_summary,
"Address family\n"
"Information about Route Server Clients\n"
"Summary of all Route Server Clients\n")
+
+DEFUN (show_bgp_instance_ipv6_safi_rsclient_summary,
+ show_bgp_instance_ipv6_safi_rsclient_summary_cmd,
+ "show bgp view WORD ipv6 (unicast|multicast) rsclient summary",
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "View name\n"
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Clients\n"
+ "Summary of all Route Server Clients\n")
+{
+ safi_t safi;
+
+ if (argc == 2) {
+ safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, safi);
+ } else {
+ safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+ return bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, safi);
+ }
+}
+
+ALIAS (show_bgp_instance_ipv6_safi_rsclient_summary,
+ show_bgp_ipv6_safi_rsclient_summary_cmd,
+ "show bgp ipv6 (unicast|multicast) rsclient summary",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "Information about Route Server Clients\n"
+ "Summary of all Route Server Clients\n")
+
#endif /* HAVE IPV6 */
/* Redistribute VTY commands. */
@@ -9635,38 +9762,50 @@ bgp_vty_init (void)
install_element (VIEW_NODE, &show_ip_bgp_summary_cmd);
install_element (VIEW_NODE, &show_ip_bgp_instance_summary_cmd);
install_element (VIEW_NODE, &show_ip_bgp_ipv4_summary_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv4_safi_summary_cmd);
install_element (VIEW_NODE, &show_ip_bgp_instance_ipv4_summary_cmd);
+ install_element (VIEW_NODE, &show_bgp_instance_ipv4_safi_summary_cmd);
install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_summary_cmd);
install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_summary_cmd);
#ifdef HAVE_IPV6
install_element (VIEW_NODE, &show_bgp_summary_cmd);
install_element (VIEW_NODE, &show_bgp_instance_summary_cmd);
install_element (VIEW_NODE, &show_bgp_ipv6_summary_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv6_safi_summary_cmd);
install_element (VIEW_NODE, &show_bgp_instance_ipv6_summary_cmd);
+ install_element (VIEW_NODE, &show_bgp_instance_ipv6_safi_summary_cmd);
#endif /* HAVE_IPV6 */
install_element (RESTRICTED_NODE, &show_ip_bgp_summary_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_instance_summary_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_summary_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_summary_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_instance_ipv4_summary_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_instance_ipv4_safi_summary_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_all_summary_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_summary_cmd);
#ifdef HAVE_IPV6
install_element (RESTRICTED_NODE, &show_bgp_summary_cmd);
install_element (RESTRICTED_NODE, &show_bgp_instance_summary_cmd);
install_element (RESTRICTED_NODE, &show_bgp_ipv6_summary_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_summary_cmd);
install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_summary_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_safi_summary_cmd);
#endif /* HAVE_IPV6 */
install_element (ENABLE_NODE, &show_ip_bgp_summary_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_instance_summary_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_ipv4_summary_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv4_safi_summary_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_instance_ipv4_summary_cmd);
+ install_element (ENABLE_NODE, &show_bgp_instance_ipv4_safi_summary_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_summary_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_summary_cmd);
#ifdef HAVE_IPV6
install_element (ENABLE_NODE, &show_bgp_summary_cmd);
install_element (ENABLE_NODE, &show_bgp_instance_summary_cmd);
install_element (ENABLE_NODE, &show_bgp_ipv6_summary_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv6_safi_summary_cmd);
install_element (ENABLE_NODE, &show_bgp_instance_ipv6_summary_cmd);
+ install_element (ENABLE_NODE, &show_bgp_instance_ipv6_safi_summary_cmd);
#endif /* HAVE_IPV6 */
/* "show ip bgp neighbors" commands. */
@@ -9730,28 +9869,40 @@ bgp_vty_init (void)
install_element (VIEW_NODE, &show_ip_bgp_instance_rsclient_summary_cmd);
install_element (VIEW_NODE, &show_ip_bgp_ipv4_rsclient_summary_cmd);
install_element (VIEW_NODE, &show_ip_bgp_instance_ipv4_rsclient_summary_cmd);
+ install_element (VIEW_NODE, &show_bgp_instance_ipv4_safi_rsclient_summary_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_summary_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_rsclient_summary_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_instance_rsclient_summary_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_rsclient_summary_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_instance_ipv4_rsclient_summary_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_instance_ipv4_safi_rsclient_summary_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rsclient_summary_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_rsclient_summary_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_instance_rsclient_summary_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_ipv4_rsclient_summary_cmd);
install_element (ENABLE_NODE, &show_ip_bgp_instance_ipv4_rsclient_summary_cmd);
+ install_element (ENABLE_NODE, &show_bgp_instance_ipv4_safi_rsclient_summary_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv4_safi_rsclient_summary_cmd);
#ifdef HAVE_IPV6
install_element (VIEW_NODE, &show_bgp_rsclient_summary_cmd);
install_element (VIEW_NODE, &show_bgp_ipv6_rsclient_summary_cmd);
install_element (VIEW_NODE, &show_bgp_instance_rsclient_summary_cmd);
install_element (VIEW_NODE, &show_bgp_instance_ipv6_rsclient_summary_cmd);
+ install_element (VIEW_NODE, &show_bgp_instance_ipv6_safi_rsclient_summary_cmd);
+ install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_summary_cmd);
install_element (RESTRICTED_NODE, &show_bgp_rsclient_summary_cmd);
install_element (RESTRICTED_NODE, &show_bgp_ipv6_rsclient_summary_cmd);
install_element (RESTRICTED_NODE, &show_bgp_instance_rsclient_summary_cmd);
install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_rsclient_summary_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_safi_rsclient_summary_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rsclient_summary_cmd);
install_element (ENABLE_NODE, &show_bgp_rsclient_summary_cmd);
install_element (ENABLE_NODE, &show_bgp_ipv6_rsclient_summary_cmd);
install_element (ENABLE_NODE, &show_bgp_instance_rsclient_summary_cmd);
install_element (ENABLE_NODE, &show_bgp_instance_ipv6_rsclient_summary_cmd);
+ install_element (ENABLE_NODE, &show_bgp_instance_ipv6_safi_rsclient_summary_cmd);
+ install_element (ENABLE_NODE, &show_bgp_ipv6_safi_rsclient_summary_cmd);
#endif /* HAVE_IPV6 */
/* "show ip bgp paths" commands. */
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 882fe37c..a03025f6 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -2257,6 +2257,9 @@ peer_change_action (struct peer *peer, afi_t afi, safi_t safi,
if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
return;
+ if (peer->status != Established)
+ return;
+
if (type == peer_change_reset)
bgp_notify_send (peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
diff --git a/configure.ac b/configure.ac
index 0d2ad980..d53c4c56 100755
--- a/configure.ac
+++ b/configure.ac
@@ -8,7 +8,7 @@
## $Id$
AC_PREREQ(2.53)
-AC_INIT(Quagga, 0.99.15, [http://bugzilla.quagga.net])
+AC_INIT(Quagga, [1.1.0-dn42.11], [http://bugzilla.quagga.net])
AC_CONFIG_SRCDIR(lib/zebra.h)
AC_CONFIG_MACRO_DIR([m4])
@@ -202,6 +202,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,
@@ -780,15 +782,18 @@ AC_SUBST(OTHER_METHOD)
dnl --------------------------
dnl Determine IS-IS I/O method
dnl --------------------------
+AC_DEFINE(ISIS_METHOD_PFPACKET, 1, [ constant value for isis method pfpacket ])
+AC_DEFINE(ISIS_METHOD_DLPI, 2, [ constant value for isis method dlpi ])
+AC_DEFINE(ISIS_METHOD_BPF, 3, [ constant value for isis method bpf ])
AC_CHECK_HEADER(net/bpf.h)
AC_CHECK_HEADER(sys/dlpi.h)
AC_MSG_CHECKING(zebra IS-IS I/O method)
if test x"$opsys" = x"gnu-linux"; then
AC_MSG_RESULT(pfpacket)
- ISIS_METHOD=isis_pfpacket.o
+ ISIS_METHOD_MACRO="ISIS_METHOD_PFPACKET"
elif test x"$opsys" = x"sol2-6" -o x"$opsys" = x"sol8"; then
AC_MSG_RESULT(DLPI)
- ISIS_METHOD="isis_dlpi.o"
+ ISIS_METHOD_MACRO="ISIS_METHOD_DLPI"
else
if test $ac_cv_header_net_bpf_h = no; then
if test $ac_cv_header_sys_dlpi_h = no; then
@@ -798,13 +803,13 @@ else
else
AC_MSG_RESULT(DLPI)
fi
- ISIS_METHOD="isis_dlpi.o"
+ ISIS_METHOD_MACRO="ISIS_METHOD_DLPI"
else
AC_MSG_RESULT(BPF)
- ISIS_METHOD="isis_bpf.o"
+ ISIS_METHOD_MACRO="ISIS_METHOD_BPF"
fi
fi
-AC_SUBST(ISIS_METHOD)
+AC_DEFINE_UNQUOTED(ISIS_METHOD, $ISIS_METHOD_MACRO, [ selected method for isis, == one of the constants ])
dnl ------------------------------------
dnl check for broken CMSG_FIRSTHDR macro
@@ -1233,6 +1238,12 @@ case "${enable_isisd}" in
* ) ;;
esac
+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";;
@@ -1255,6 +1266,7 @@ AC_SUBST(OSPFD)
AC_SUBST(OSPF6D)
AC_SUBST(WATCHQUAGGA)
AC_SUBST(ISISD)
+AC_SUBST(PIMD)
AC_SUBST(SOLARIS)
AC_SUBST(VTYSH)
AC_SUBST(INCLUDES)
@@ -1319,10 +1331,12 @@ 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],
+ struct nd_opt_homeagent_info, struct nd_opt_adv_interval,
+ struct nd_opt_rdnss],
[], [], QUAGGA_INCLUDES)
AC_CHECK_MEMBERS([struct sockaddr.sa_len,
@@ -1347,6 +1361,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 --------------------------------------
@@ -1519,6 +1572,7 @@ AC_CONFIG_FILES([Makefile lib/Makefile zebra/Makefile ripd/Makefile
ripngd/Makefile bgpd/Makefile ospfd/Makefile watchquagga/Makefile
ospf6d/Makefile isisd/Makefile vtysh/Makefile doc/Makefile
ospfclient/Makefile tests/Makefile m4/Makefile redhat/Makefile
+ pimd/Makefile
pkgsrc/Makefile
redhat/quagga.spec
lib/version.h
diff --git a/doc/Makefile.am b/doc/Makefile.am
index c3016b2b..5c041572 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -61,7 +61,7 @@ quagga_TEXINFOS = appendix.texi basic.texi bgpd.texi filter.texi install.texi \
.dia.png:
$(DIATOPNG) "$@" $<
-man_MANS = vtysh.1 bgpd.8 ospf6d.8 ospfd.8 ripd.8 ripngd.8 zebra.8 isisd.8
+man_MANS = vtysh.1 bgpd.8 ospf6d.8 ospfd.8 ripd.8 ripngd.8 zebra.8 isisd.8 pimd.8
EXTRA_DIST = BGP-TypeCode draft-zebra-00.ms draft-zebra-00.txt $(man_MANS) \
mpls/ChangeLog.opaque.txt mpls/cli_summary.txt \
diff --git a/doc/install.texi b/doc/install.texi
index 16e29c99..fbc2401d 100644
--- a/doc/install.texi
+++ b/doc/install.texi
@@ -267,6 +267,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
diff --git a/doc/ipv6.texi b/doc/ipv6.texi
index a78a92fe..519b7798 100644
--- a/doc/ipv6.texi
+++ b/doc/ipv6.texi
@@ -157,15 +157,25 @@ Set default router preference in IPv6 router advertisements per RFC4191.
Default: medium
@end deffn
+@deffn {Interface Command} {ipv6 nd rdnss ipv6prefix [lifetime]} {} Include
+RDNSS options to advertise recursive DNS server addresses. Additionally a
+maximum lifetime (in seconds) can be specified to limit the lifetime of this
+option. The valid range is between @code{<0-4294967295>} where 0 means that the
+RDNSS address must no longer be used. The default lifetime - without explicitly
+specify a lifetime - is unlimited.
+@end deffn
+
@example
@group
interface eth0
no ipv6 nd suppress-ra
ipv6 nd prefix 2001:0DB8:5009::/64
+ ipv6 nd rdnss 2001:0DB8:5009::1
@end group
@end example
For more information see @cite{RFC2462 (IPv6 Stateless Address Autoconfiguration)}
, @cite{RFC2461 (Neighbor Discovery for IP Version 6 (IPv6))}
, @cite{RFC3775 (Mobility Support in IPv6 (Mobile IPv6))}
+, @cite{RFC5006 (IPv6 Router Advertisement Option for DNS Configuration)}
and @cite{RFC4191 (Default Router Preferences and More-Specific Routes)}.
diff --git a/doc/pimd.8 b/doc/pimd.8
new file mode 100644
index 00000000..b26b1128
--- /dev/null
+++ b/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.
+
diff --git a/isisd/Makefile.am b/isisd/Makefile.am
index a7117fd6..8a682a49 100644
--- a/isisd/Makefile.am
+++ b/isisd/Makefile.am
@@ -12,8 +12,6 @@ noinst_LIBRARIES = libisis.a
sbin_PROGRAMS = isisd
SUBDIRS = topology
-isis_method = @ISIS_METHOD@
-
libisis_a_SOURCES = \
isis_adjacency.c isis_lsp.c dict.c isis_circuit.c isis_pdu.c \
isis_tlv.c isisd.c isis_misc.c isis_zebra.c isis_dr.c \
@@ -29,13 +27,10 @@ noinst_HEADERS = \
include-netbsd/clnp.h include-netbsd/esis.h include-netbsd/iso.h
isisd_SOURCES = \
- isis_main.c $(libisis_a_SOURCES)
-
-isisd_LDADD = $(isis_method) @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@
-
-isisd_DEPENDENCIES = $(isis_method)
+ isis_main.c $(libisis_a_SOURCES) \
+ isis_bpf.c isis_dlpi.c isis_pfpacket.c
-EXTRA_DIST = isis_bpf.c isis_dlpi.c isis_pfpacket.c
+isisd_LDADD = @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@
examplesdir = $(exampledir)
dist_examples_DATA = isisd.conf.sample
diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c
index aab8d1a3..de34bea9 100644
--- a/isisd/isis_adjacency.c
+++ b/isisd/isis_adjacency.c
@@ -172,7 +172,7 @@ isis_adj_state_change (struct isis_adjacency *adj, enum isis_adj_state state,
circuit->upadjcount[level - 1]++;
if (state == ISIS_ADJ_DOWN)
{
- isis_delete_adj (adj, adj->circuit->u.bc.adjdb[level - 1]);
+ listnode_delete (adj->circuit->u.bc.adjdb[level - 1], adj);
circuit->upadjcount[level - 1]--;
}
diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c
index 8c3602db..05f11386 100644
--- a/isisd/isis_bpf.c
+++ b/isisd/isis_bpf.c
@@ -21,6 +21,7 @@
*/
#include <zebra.h>
+#if ISIS_METHOD == ISIS_METHOD_BPF
#include <net/if.h>
#include <netinet/if_ether.h>
#include <sys/time.h>
@@ -339,3 +340,5 @@ isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
{
return ISIS_OK;
}
+
+#endif /* ISIS_METHOD == ISIS_METHOD_BPF */
diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c
index d2923b57..99e2bf6f 100644
--- a/isisd/isis_circuit.c
+++ b/isisd/isis_circuit.c
@@ -142,6 +142,11 @@ isis_circuit_deconfigure (struct isis_circuit *circuit,
struct isis_area *area)
{
+ /* destroy adjacencies */
+ if (circuit->u.bc.adjdb[0])
+ isis_adjdb_iterate (circuit->u.bc.adjdb[0], (void(*) (struct isis_adjacency *, void *)) isis_delete_adj, circuit->u.bc.adjdb[0]);
+ if (circuit->u.bc.adjdb[1])
+ isis_adjdb_iterate (circuit->u.bc.adjdb[1], (void(*) (struct isis_adjacency *, void *)) isis_delete_adj, circuit->u.bc.adjdb[1]);
/* Remove circuit from area */
listnode_delete (area->circuit_list, circuit);
/* Free the index of SRM and SSN flags */
@@ -602,6 +607,13 @@ isis_circuit_down (struct isis_circuit *circuit)
{
THREAD_TIMER_OFF (circuit->u.p2p.t_send_p2p_hello);
}
+
+ if (circuit->t_send_psnp[0]) {
+ THREAD_TIMER_OFF (circuit->t_send_psnp[0]);
+ }
+ if (circuit->t_send_psnp[1]) {
+ THREAD_TIMER_OFF (circuit->t_send_psnp[1]);
+ }
/* close the socket */
close (circuit->fd);
@@ -818,6 +830,21 @@ isis_interface_config_write (struct vty *vty)
}
}
}
+ if (c->passwd.type==ISIS_PASSWD_TYPE_HMAC_MD5)
+ {
+ vty_out (vty, " isis password md5 %s%s", c->passwd.passwd,
+ VTY_NEWLINE);
+ write++;
+ }
+ else
+ {
+ if (c->passwd.type==ISIS_PASSWD_TYPE_CLEARTXT)
+ {
+ vty_out (vty, " isis password clear %s%s", c->passwd.passwd,
+ VTY_NEWLINE);
+ write++;
+ }
+ }
}
}
@@ -1010,11 +1037,44 @@ DEFUN (no_isis_circuit_type,
return CMD_SUCCESS;
}
-DEFUN (isis_passwd,
- isis_passwd_cmd,
- "isis password WORD",
+DEFUN (isis_passwd_md5,
+ isis_passwd_md5_cmd,
+ "isis password md5 WORD",
+ "IS-IS commands\n"
+ "Configure the authentication password for interface\n"
+ "Authentication Type\n"
+ "Password\n")
+{
+ struct isis_circuit *circuit;
+ struct interface *ifp;
+ int len;
+
+ ifp = vty->index;
+ circuit = ifp->info;
+ if (circuit == NULL)
+ {
+ return CMD_WARNING;
+ }
+
+ len = strlen (argv[0]);
+ if (len > 254)
+ {
+ vty_out (vty, "Too long circuit password (>254)%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ circuit->passwd.len = len;
+ circuit->passwd.type = ISIS_PASSWD_TYPE_HMAC_MD5;
+ strncpy ((char *)circuit->passwd.passwd, argv[0], 255);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (isis_passwd_clear,
+ isis_passwd_clear_cmd,
+ "isis password clear WORD",
"IS-IS commands\n"
"Configure the authentication password for interface\n"
+ "Authentication Type\n"
"Password\n")
{
struct isis_circuit *circuit;
@@ -1063,7 +1123,6 @@ DEFUN (no_isis_passwd,
return CMD_SUCCESS;
}
-
DEFUN (isis_priority,
isis_priority_cmd,
"isis priority <0-127>",
@@ -2074,7 +2133,8 @@ isis_circuit_init ()
install_element (INTERFACE_NODE, &isis_circuit_type_cmd);
install_element (INTERFACE_NODE, &no_isis_circuit_type_cmd);
- install_element (INTERFACE_NODE, &isis_passwd_cmd);
+ install_element (INTERFACE_NODE, &isis_passwd_clear_cmd);
+ install_element (INTERFACE_NODE, &isis_passwd_md5_cmd);
install_element (INTERFACE_NODE, &no_isis_passwd_cmd);
install_element (INTERFACE_NODE, &isis_priority_cmd);
diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h
index a7e719f6..f32d1dda 100644
--- a/isisd/isis_circuit.h
+++ b/isisd/isis_circuit.h
@@ -65,6 +65,7 @@ struct isis_p2p_info
struct isis_circuit
{
int state;
+ int connected;
u_char circuit_id; /* l1/l2 p2p/bcast CircuitID */
struct isis_area *area; /* back pointer to the area */
struct interface *interface; /* interface info from z */
diff --git a/isisd/isis_common.h b/isisd/isis_common.h
index 26338556..334d3394 100644
--- a/isisd/isis_common.h
+++ b/isisd/isis_common.h
@@ -35,6 +35,7 @@ struct isis_passwd
u_char len;
#define ISIS_PASSWD_TYPE_UNUSED 0
#define ISIS_PASSWD_TYPE_CLEARTXT 1
+#define ISIS_PASSWD_TYPE_HMAC_MD5 54
#define ISIS_PASSWD_TYPE_PRIVATE 255
u_char type;
/* Authenticate SNPs? */
diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c
index 80d0c906..6cdde46a 100644
--- a/isisd/isis_csm.c
+++ b/isisd/isis_csm.c
@@ -112,6 +112,7 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg)
isis_circuit_configure (circuit, (struct isis_area *) arg);
isis_circuit_up (circuit);
circuit->state = C_STATE_UP;
+ circuit->connected = 1;
isis_event_circuit_state_change (circuit, 1);
listnode_delete (isis->init_circ_list, circuit);
break;
@@ -136,9 +137,12 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg)
zlog_warn ("circuit already enabled");
break;
case IF_UP_FROM_Z:
- isis_circuit_if_add (circuit, (struct interface *) arg);
- isis_circuit_up (circuit);
+ if (!circuit->connected) {
+ isis_circuit_if_add (circuit, (struct interface *) arg);
+ isis_circuit_up (circuit);
+ }
circuit->state = C_STATE_UP;
+ circuit->connected = 1;
isis_event_circuit_state_change (circuit, 1);
break;
case ISIS_DISABLE:
@@ -167,7 +171,6 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg)
isis_event_circuit_state_change (circuit, 0);
break;
case IF_DOWN_FROM_Z:
- isis_circuit_if_del (circuit);
circuit->state = C_STATE_CONF;
isis_event_circuit_state_change (circuit, 0);
break;
diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c
index 3cbe0b4f..fe872a95 100644
--- a/isisd/isis_dlpi.c
+++ b/isisd/isis_dlpi.c
@@ -21,6 +21,7 @@
*/
#include <zebra.h>
+#if ISIS_METHOD == ISIS_METHOD_DLPI
#include <net/if.h>
#include <netinet/if_ether.h>
#include <sys/types.h>
@@ -622,3 +623,5 @@ isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
sock_buff, stream_get_endp (circuit->snd_stream) + LLC_LEN, 0);
return ISIS_OK;
}
+
+#endif /* ISIS_METHOD == ISIS_METHOD_DLPI */
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index 50289db3..fd40bb37 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -353,10 +353,25 @@ isis_lsp_authinfo_check (struct stream *stream, struct isis_area *area,
ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN,
pdulen - ISIS_FIXED_HDR_LEN
- ISIS_LSP_HDR_LEN, &expected, &found, &tlvs);
+
if (retval || !(found & TLVFLAG_AUTH_INFO))
return 1; /* Auth fail (parsing failed or no auth-tlv) */
- return authentication_check (passwd, &tlvs.auth_info);
+ switch (tlvs.auth_info.type)
+ {
+ case ISIS_PASSWD_TYPE_HMAC_MD5:
+ zlog_debug("Got LSP with ISIS_PASSWD_TYPE_HMAC_MD5");
+ break;
+ case ISIS_PASSWD_TYPE_CLEARTXT:
+ zlog_debug("Got LSP with ISIS_PASSWD_TYPE_CLEARTXT");
+ break;
+ default:
+ zlog_debug("Unknown authentication type in LSP");
+ break;
+ }
+
+ return 0;
+ /* return authentication_check (passwd, &tlvs.auth_info);*/
}
static void
@@ -1640,7 +1655,7 @@ lsp_regenerate_schedule (struct isis_area *area)
if (diff < MIN_LSP_GEN_INTERVAL)
{
area->lsp_regenerate_pending[0] = 1;
- thread_add_timer (master, lsp_l1_regenerate, area,
+ area->t_lsp_l1_regenerate=thread_add_timer (master, lsp_l1_regenerate, area,
MIN_LSP_GEN_INTERVAL - diff);
goto L2;
}
@@ -1663,7 +1678,7 @@ L2:
if (diff < MIN_LSP_GEN_INTERVAL)
{
area->lsp_regenerate_pending[1] = 1;
- thread_add_timer (master, lsp_l2_regenerate, area,
+ area->t_lsp_l2_regenerate=thread_add_timer (master, lsp_l2_regenerate, area,
MIN_LSP_GEN_INTERVAL - diff);
return ISIS_OK;
}
@@ -2037,6 +2052,8 @@ lsp_tick (struct thread *thread)
{
for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit))
{
+ if (circuit->state != C_STATE_UP)
+ continue;
for (ALL_LIST_ELEMENTS_RO (lsp_list, lspnode, lsp))
{
if (ISIS_CHECK_FLAG (lsp->SRMflags, circuit))
diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h
index adbde78e..8d648d05 100644
--- a/isisd/isis_lsp.h
+++ b/isisd/isis_lsp.h
@@ -58,7 +58,6 @@ struct isis_lsp
#endif
/* used for 60 second counting when rem_lifetime is zero */
int age_out;
- struct isis_adjacency *adj;
/* FIXME: For now only topology LSP's use this. Is it helpful for others? */
struct isis_area *area;
struct tlvs tlv_data; /* Simplifies TLV access */
diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c
index a2ab0649..d67df31b 100644
--- a/isisd/isis_pdu.c
+++ b/isisd/isis_pdu.c
@@ -29,10 +29,11 @@
#include "log.h"
#include "stream.h"
#include "vty.h"
-#include "hash.c"
+#include "hash.h"
#include "prefix.h"
#include "if.h"
#include "checksum.h"
+#include "md5.h"
#include "isisd/dict.h"
#include "isisd/include-netbsd/iso.h"
@@ -168,26 +169,38 @@ accept_level (int level, int circuit_t)
return retval;
}
+
+/*
+ * Verify authentication information
+ * Support cleartext and HMAC MD5 authentication
+ */
int
-authentication_check (struct isis_passwd *one, struct isis_passwd *theother)
+authentication_check (struct isis_passwd *remote, struct isis_passwd *local, struct isis_circuit* c)
{
- if (one->type != theother->type)
+ unsigned char digest[ISIS_AUTH_MD5_SIZE];
+
+ if (c->passwd.type)
{
- zlog_warn ("Unsupported authentication type %d", theother->type);
- return 1; /* Auth fail (different authentication types) */
- }
- switch (one->type)
+ switch (c->passwd.type)
{
+ case ISIS_PASSWD_TYPE_HMAC_MD5:
+ /* HMAC MD5 (RFC 3567) */
+ /* MD5 computation according to RFC 2104 */
+ hmac_md5(c->rcv_stream->data, stream_get_endp(c->rcv_stream), (unsigned char *) &(local->passwd), c->passwd.len, (unsigned char *) &digest);
+ return memcmp (digest, remote->passwd, ISIS_AUTH_MD5_SIZE);
+ break;
case ISIS_PASSWD_TYPE_CLEARTXT:
- if (one->len != theother->len)
+ /* Cleartext (ISO 10589) */
+ if (local->len != remote->len)
return 1; /* Auth fail () - passwd len mismatch */
- return memcmp (one->passwd, theother->passwd, one->len);
+ return memcmp (local->passwd, remote->passwd, local->len);
break;
default:
zlog_warn ("Unsupported authentication type");
break;
}
- return 0; /* Auth pass */
+ }
+ return 0; /* Authentication pass when no authentication is configured */
}
/*
@@ -372,7 +385,7 @@ process_p2p_hello (struct isis_circuit *circuit)
if (circuit->passwd.type)
{
if (!(found & TLVFLAG_AUTH_INFO) ||
- authentication_check (&circuit->passwd, &tlvs.auth_info))
+ authentication_check (&tlvs.auth_info, &circuit->passwd, circuit))
{
isis_event_auth_failure (circuit->area->area_tag,
"P2P hello authentication failure",
@@ -744,10 +757,11 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa)
goto out;
}
+ /* Verify authentication, either cleartext of HMAC MD5 */
if (circuit->passwd.type)
{
if (!(found & TLVFLAG_AUTH_INFO) ||
- authentication_check (&circuit->passwd, &tlvs.auth_info))
+ authentication_check (&tlvs.auth_info, &circuit->passwd, circuit))
{
isis_event_auth_failure (circuit->area->area_tag,
"LAN hello authentication failure",
@@ -1187,6 +1201,7 @@ dontcheckadj:
/* 7.3.15.1 e) 1) LSP newer than the one in db or no LSP in db */
if ((!lsp || comp == LSP_NEWER))
{
+ int regenerate = (lsp == NULL);
/* i */
if (lsp)
{
@@ -1221,7 +1236,6 @@ dontcheckadj:
ntohs (hdr->pdu_len), lsp0,
circuit->area);
lsp->level = level;
- lsp->adj = adj;
lsp_insert (lsp, circuit->area->lspdb[level - 1]);
/* ii */
ISIS_FLAGS_SET_ALL (lsp->SRMflags);
@@ -1232,6 +1246,9 @@ dontcheckadj:
if (circuit->circ_type != CIRCUIT_T_BROADCAST)
ISIS_SET_FLAG (lsp->SSNflags, circuit);
/* FIXME: v) */
+ if (regenerate && circuit->u.bc.is_dr[level - 1]) {
+ lsp_l1_pseudo_generate (circuit);
+ }
}
/* 7.3.15.1 e) 2) LSP equal to the one in db */
else if (comp == LSP_EQUAL)
@@ -1250,8 +1267,7 @@ dontcheckadj:
ISIS_CLEAR_FLAG (lsp->SSNflags, circuit);
}
}
- if (lsp)
- lsp->adj = adj;
+
return retval;
}
@@ -1414,7 +1430,7 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit,
if (passwd->type)
{
if (!(found & TLVFLAG_AUTH_INFO) ||
- authentication_check (passwd, &tlvs.auth_info))
+ authentication_check (&tlvs.auth_info, passwd, circuit))
{
isis_event_auth_failure (circuit->area->area_tag,
"SNP authentication" " failure",
@@ -1781,6 +1797,9 @@ isis_receive (struct thread *thread)
circuit = THREAD_ARG (thread);
assert (circuit);
+ if (!circuit->area)
+ return ISIS_OK;
+
if (circuit->rcv_stream == NULL)
circuit->rcv_stream = stream_new (ISO_MTU (circuit));
else
@@ -1908,11 +1927,15 @@ send_hello (struct isis_circuit *circuit, int level)
struct isis_fixed_hdr fixed_hdr;
struct isis_lan_hello_hdr hello_hdr;
struct isis_p2p_hello_hdr p2p_hello_hdr;
+ char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
u_int32_t interval;
- unsigned long len_pointer, length;
+ unsigned long len_pointer, length, auth_tlv;
int retval;
+ if (circuit->state != C_STATE_UP || circuit->interface == NULL)
+ return ISIS_WARNING;
+
if (circuit->interface->mtu == 0)
{
zlog_warn ("circuit has zero MTU");
@@ -1940,6 +1963,9 @@ send_hello (struct isis_circuit *circuit, int level)
memset (&hello_hdr, 0, sizeof (struct isis_lan_hello_hdr));
interval = circuit->hello_multiplier[level - 1] *
circuit->hello_interval[level - 1];
+ /* If we are the DIS then hello interval is divided by three, as is the hold-timer */
+ if (circuit->u.bc.is_dr[level - 1])
+ interval=interval/3;
if (interval > USHRT_MAX)
interval = USHRT_MAX;
hello_hdr.circuit_t = circuit->circuit_is_type;
@@ -1976,34 +2002,36 @@ send_hello (struct isis_circuit *circuit, int level)
/*
* Then the variable length part
*/
+
/* add circuit password */
- if (circuit->passwd.type)
- if (tlv_add_authinfo (circuit->passwd.type, circuit->passwd.len,
+ /* Cleartext */
+ if (circuit->passwd.type == ISIS_PASSWD_TYPE_CLEARTXT)
+ if (tlv_add_authinfo (ISIS_PASSWD_TYPE_CLEARTXT, circuit->passwd.len,
circuit->passwd.passwd, circuit->snd_stream))
return ISIS_WARNING;
- /* Area Addresses TLV */
- assert (circuit->area);
- if (circuit->area->area_addrs && circuit->area->area_addrs->count > 0)
- if (tlv_add_area_addrs (circuit->area->area_addrs, circuit->snd_stream))
- return ISIS_WARNING;
- /* LAN Neighbors TLV */
- if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+ /* or HMAC MD5 */
+ if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5)
{
- if (level == 1 && circuit->u.bc.lan_neighs[0]->count > 0)
- if (tlv_add_lan_neighs (circuit->u.bc.lan_neighs[0],
- circuit->snd_stream))
- return ISIS_WARNING;
- if (level == 2 && circuit->u.bc.lan_neighs[1]->count > 0)
- if (tlv_add_lan_neighs (circuit->u.bc.lan_neighs[1],
- circuit->snd_stream))
- return ISIS_WARNING;
+ /* Remember where TLV is written so we can later overwrite the MD5 hash */
+ auth_tlv = stream_get_endp (circuit->snd_stream);
+ memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
+ if (tlv_add_authinfo (ISIS_PASSWD_TYPE_HMAC_MD5, ISIS_AUTH_MD5_SIZE,
+ hmac_md5_hash, circuit->snd_stream))
+ return ISIS_WARNING;
}
/* Protocols Supported TLV */
if (circuit->nlpids.count > 0)
if (tlv_add_nlpid (&circuit->nlpids, circuit->snd_stream))
return ISIS_WARNING;
+
+ /* Area Addresses TLV */
+ assert (circuit->area);
+ if (circuit->area->area_addrs && circuit->area->area_addrs->count > 0)
+ if (tlv_add_area_addrs (circuit->area->area_addrs, circuit->snd_stream))
+ return ISIS_WARNING;
+
/* IP interface Address TLV */
if (circuit->ip_router && circuit->ip_addrs && circuit->ip_addrs->count > 0)
if (tlv_add_ip_addrs (circuit->ip_addrs, circuit->snd_stream))
@@ -2017,6 +2045,22 @@ send_hello (struct isis_circuit *circuit, int level)
return ISIS_WARNING;
#endif /* HAVE_IPV6 */
+ /* Restart signaling, vendor C sends it too */
+ retval = add_tlv (211, 3, 0, circuit->snd_stream);
+
+ /* LAN Neighbors TLV */
+ if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+ {
+ if (level == 1 && circuit->u.bc.lan_neighs[0]->count > 0)
+ if (tlv_add_lan_neighs (circuit->u.bc.lan_neighs[0],
+ circuit->snd_stream))
+ return ISIS_WARNING;
+ if (level == 2 && circuit->u.bc.lan_neighs[1]->count > 0)
+ if (tlv_add_lan_neighs (circuit->u.bc.lan_neighs[1],
+ circuit->snd_stream))
+ return ISIS_WARNING;
+ }
+
if (circuit->u.bc.pad_hellos)
if (tlv_add_padding (circuit->snd_stream))
return ISIS_WARNING;
@@ -2025,6 +2069,14 @@ send_hello (struct isis_circuit *circuit, int level)
/* Update PDU length */
stream_putw_at (circuit->snd_stream, len_pointer, (u_int16_t) length);
+ /* For HMAC MD5 we need to compute the md5 hash and store it */
+ if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5)
+ {
+ hmac_md5(circuit->snd_stream->data, stream_get_endp(circuit->snd_stream), (unsigned char *) &circuit->passwd.passwd, circuit->passwd.len, (unsigned char *) &hmac_md5_hash);
+ /* Copy the hash into the stream */
+ memcpy(circuit->snd_stream->data+auth_tlv+3,hmac_md5_hash,ISIS_AUTH_MD5_SIZE);
+ }
+
retval = circuit->tx (circuit, level);
if (retval)
zlog_warn ("sending of LAN Level %d Hello failed", level);
@@ -2062,9 +2114,21 @@ send_lan_l1_hello (struct thread *thread)
{
struct isis_circuit *circuit;
int retval;
+ unsigned long next_hello;
circuit = THREAD_ARG (thread);
assert (circuit);
+
+ if (!circuit->area) {
+ return ISIS_OK;
+ }
+
+ /* Pseudonode sends hellos three times more than the other nodes */
+ if (circuit->u.bc.is_dr[0])
+ next_hello=circuit->hello_interval[0]/3+1;
+ else
+ next_hello=circuit->hello_interval[0];
+
circuit->u.bc.t_send_lan_hello[0] = NULL;
if (circuit->u.bc.run_dr_elect[0])
@@ -2075,7 +2139,7 @@ send_lan_l1_hello (struct thread *thread)
/* set next timer thread */
THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[0],
send_lan_l1_hello, circuit,
- isis_jitter (circuit->hello_interval[0], IIH_JITTER));
+ isis_jitter (next_hello, IIH_JITTER));
return retval;
}
@@ -2085,9 +2149,21 @@ send_lan_l2_hello (struct thread *thread)
{
struct isis_circuit *circuit;
int retval;
+ unsigned long next_hello;
circuit = THREAD_ARG (thread);
assert (circuit);
+
+ if (!circuit->area) {
+ return ISIS_OK;
+ }
+
+ /* Pseudonode sends hellos three times more than the other nodes */
+ if (circuit->u.bc.is_dr[1])
+ next_hello=circuit->hello_interval[1]/3+1;
+ else
+ next_hello=circuit->hello_interval[1];
+
circuit->u.bc.t_send_lan_hello[1] = NULL;
if (circuit->u.bc.run_dr_elect[1])
@@ -2098,7 +2174,7 @@ send_lan_l2_hello (struct thread *thread)
/* set next timer thread */
THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[1],
send_lan_l2_hello, circuit,
- isis_jitter (circuit->hello_interval[1], IIH_JITTER));
+ isis_jitter (next_hello, IIH_JITTER));
return retval;
}
@@ -2192,6 +2268,9 @@ send_csnp (struct isis_circuit *circuit, int level)
struct listnode *node;
struct isis_lsp *lsp;
+ if (circuit->state != C_STATE_UP || circuit->interface == NULL)
+ return ISIS_WARNING;
+
memset (start, 0x00, ISIS_SYS_ID_LEN + 2);
memset (stop, 0xff, ISIS_SYS_ID_LEN + 2);
@@ -2246,9 +2325,7 @@ send_l1_csnp (struct thread *thread)
circuit->t_send_csnp[0] = NULL;
if (circuit->circ_type == CIRCUIT_T_BROADCAST && circuit->u.bc.is_dr[0])
- {
send_csnp (circuit, 1);
- }
/* set next timer thread */
THREAD_TIMER_ON (master, circuit->t_send_csnp[0], send_l1_csnp, circuit,
isis_jitter (circuit->csnp_interval[0], CSNP_JITTER));
@@ -2268,9 +2345,7 @@ send_l2_csnp (struct thread *thread)
circuit->t_send_csnp[1] = NULL;
if (circuit->circ_type == CIRCUIT_T_BROADCAST && circuit->u.bc.is_dr[1])
- {
send_csnp (circuit, 2);
- }
/* set next timer thread */
THREAD_TIMER_ON (master, circuit->t_send_csnp[1], send_l2_csnp, circuit,
isis_jitter (circuit->csnp_interval[1], CSNP_JITTER));
@@ -2357,6 +2432,9 @@ send_psnp (int level, struct isis_circuit *circuit)
struct list *list = NULL;
struct listnode *node;
+ if (circuit->state != C_STATE_UP || circuit->interface == NULL)
+ return ISIS_WARNING;
+
if ((circuit->circ_type == CIRCUIT_T_BROADCAST &&
!circuit->u.bc.is_dr[level - 1]) ||
circuit->circ_type != CIRCUIT_T_BROADCAST)
@@ -2463,85 +2541,85 @@ send_lsp (struct thread *thread)
circuit = THREAD_ARG (thread);
assert (circuit);
- if (circuit->state == C_STATE_UP)
+ if (circuit->state != C_STATE_UP || circuit->interface == NULL)
+ return ISIS_WARNING;
+
+ lsp = listgetdata ((node = listhead (circuit->lsp_queue)));
+
+ /*
+ * Do not send if levels do not match
+ */
+ if (!(lsp->level & circuit->circuit_is_type))
+ goto dontsend;
+
+ /*
+ * Do not send if we do not have adjacencies in state up on the circuit
+ */
+ if (circuit->upadjcount[lsp->level - 1] == 0)
+ goto dontsend;
+ /* only send if it needs sending */
+ if ((time (NULL) - lsp->last_sent) >=
+ circuit->area->lsp_gen_interval[lsp->level - 1])
{
- lsp = listgetdata ((node = listhead (circuit->lsp_queue)));
- /*
- * Do not send if levels do not match
- */
- if (!(lsp->level & circuit->circuit_is_type))
- goto dontsend;
+ if (isis->debugs & DEBUG_UPDATE_PACKETS)
+ {
+ zlog_debug
+ ("ISIS-Upd (%s): Sent L%d LSP %s, seq 0x%08x, cksum 0x%04x,"
+ " lifetime %us on %s", circuit->area->area_tag, lsp->level,
+ rawlspid_print (lsp->lsp_header->lsp_id),
+ ntohl (lsp->lsp_header->seq_num),
+ ntohs (lsp->lsp_header->checksum),
+ ntohs (lsp->lsp_header->rem_lifetime),
+ circuit->interface->name);
+ }
+ /* copy our lsp to the send buffer */
+ stream_copy (circuit->snd_stream, lsp->pdu);
+
+ retval = circuit->tx (circuit, lsp->level);
/*
- * Do not send if we do not have adjacencies in state up on the circuit
+ * If the sending succeeded, we can del the lsp from circuits
+ * lsp_queue
*/
- if (circuit->upadjcount[lsp->level - 1] == 0)
- goto dontsend;
- /* only send if it needs sending */
- if ((time (NULL) - lsp->last_sent) >=
- circuit->area->lsp_gen_interval[lsp->level - 1])
+ if (retval == ISIS_OK)
{
-
- if (isis->debugs & DEBUG_UPDATE_PACKETS)
- {
- zlog_debug
- ("ISIS-Upd (%s): Sent L%d LSP %s, seq 0x%08x, cksum 0x%04x,"
- " lifetime %us on %s", circuit->area->area_tag, lsp->level,
- rawlspid_print (lsp->lsp_header->lsp_id),
- ntohl (lsp->lsp_header->seq_num),
- ntohs (lsp->lsp_header->checksum),
- ntohs (lsp->lsp_header->rem_lifetime),
- circuit->interface->name);
- }
- /* copy our lsp to the send buffer */
- stream_copy (circuit->snd_stream, lsp->pdu);
-
- retval = circuit->tx (circuit, lsp->level);
+ list_delete_node (circuit->lsp_queue, node);
/*
- * If the sending succeeded, we can del the lsp from circuits
- * lsp_queue
+ * On broadcast circuits also the SRMflag can be cleared
*/
- if (retval == ISIS_OK)
- {
- list_delete_node (circuit->lsp_queue, node);
+ if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+ ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
+ if (flags_any_set (lsp->SRMflags) == 0)
+ {
/*
- * On broadcast circuits also the SRMflag can be cleared
+ * need to remember when we were last sent
*/
- if (circuit->circ_type == CIRCUIT_T_BROADCAST)
- ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
-
- if (flags_any_set (lsp->SRMflags) == 0)
- {
- /*
- * need to remember when we were last sent
- */
- lsp->last_sent = time (NULL);
- }
- }
- else
- {
- zlog_debug ("sending of level %d link state failed", lsp->level);
+ lsp->last_sent = time (NULL);
}
}
else
{
- /* my belief is that if it wasn't his time, the lsp can be removed
- * from the queue
- */
- dontsend:
- list_delete_node (circuit->lsp_queue, node);
+ zlog_debug ("sending of level %d link state failed", lsp->level);
}
-#if 0
- /*
- * If there are still LSPs send next one after lsp-interval (33 msecs)
+ }
+ else
+ {
+ /* my belief is that if it wasn't his time, the lsp can be removed
+ * from the queue
*/
- if (listcount (circuit->lsp_queue) > 0)
- thread_add_timer (master, send_lsp, circuit, 1);
-#endif
+ dontsend:
+ list_delete_node (circuit->lsp_queue, node);
}
+#if 0
+ /*
+ * If there are still LSPs send next one after lsp-interval (33 msecs)
+ */
+ if (listcount (circuit->lsp_queue) > 0)
+ thread_add_timer (master, send_lsp, circuit, 1);
+#endif
return retval;
}
diff --git a/isisd/isis_pdu.h b/isisd/isis_pdu.h
index 95c1ee4f..c4c38e22 100644
--- a/isisd/isis_pdu.h
+++ b/isisd/isis_pdu.h
@@ -258,8 +258,7 @@ int ack_lsp (struct isis_link_state_hdr *hdr,
void fill_fixed_hdr (struct isis_fixed_hdr *hdr, u_char pdu_type);
int send_hello (struct isis_circuit *circuit, int level);
-
-int authentication_check (struct isis_passwd *one,
- struct isis_passwd *theother);
+#define ISIS_AUTH_MD5_SIZE 16U
+int authentication_check (struct isis_passwd *remote, struct isis_passwd *local, struct isis_circuit *c);
#endif /* _ZEBRA_ISIS_PDU_H */
diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c
index 8752dba5..8a5c3ed0 100644
--- a/isisd/isis_pfpacket.c
+++ b/isisd/isis_pfpacket.c
@@ -21,6 +21,7 @@
*/
#include <zebra.h>
+#if ISIS_METHOD == ISIS_METHOD_PFPACKET
#include <net/ethernet.h> /* the L2 protocols */
#include <netpacket/packet.h>
@@ -231,6 +232,10 @@ isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
LLC_LEN, MSG_PEEK,
(struct sockaddr *) &s_addr, (socklen_t *) &addr_len);
+ if (!circuit->area) {
+ return ISIS_OK;
+ }
+
if (bytesread < 0)
{
zlog_warn ("isis_recv_packet_bcast(): fd %d, recvfrom (): %s",
@@ -371,3 +376,5 @@ isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
return ISIS_OK;
}
+
+#endif /* ISIS_METHOD == ISIS_METHOD_PFPACKET */
diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c
index 5d7e9da4..5d0b161f 100644
--- a/isisd/isis_spf.c
+++ b/isisd/isis_spf.c
@@ -405,12 +405,13 @@ isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype,
if (adj)
listnode_add (vertex->Adj_N, adj);
+
#ifdef EXTREME_DEBUG
zlog_debug ("ISIS-Spf: add to TENT %s %s depth %d dist %d",
vtype2string (vertex->type), vid2string (vertex, buff),
vertex->depth, vertex->d_N);
#endif /* EXTREME_DEBUG */
- listnode_add (spftree->tents, vertex);
+
if (list_isempty (spftree->tents))
{
listnode_add (spftree->tents, vertex);
@@ -549,7 +550,8 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id,
*/
static int
isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp,
- uint32_t cost, uint16_t depth, int family)
+ uint32_t cost, uint16_t depth, int family,
+ struct isis_adjacency *adj)
{
struct listnode *node, *fragnode = NULL;
u_int16_t dist;
@@ -563,9 +565,6 @@ isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp,
struct ipv6_reachability *ip6reach;
#endif /* HAVE_IPV6 */
-
- if (!lsp->adj)
- return ISIS_WARNING;
if (lsp->tlv_data.nlpids == NULL || !speaks (lsp->tlv_data.nlpids, family))
return ISIS_OK;
@@ -591,7 +590,7 @@ lspfragloop:
vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS
: VTYPE_NONPSEUDO_IS;
process_N (spftree, vtype, (void *) is_neigh->neigh_id, dist,
- depth + 1, lsp->adj, family);
+ depth + 1, adj, family);
}
}
if (lsp->tlv_data.te_is_neighs)
@@ -607,7 +606,7 @@ lspfragloop:
vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS
: VTYPE_NONPSEUDO_TE_IS;
process_N (spftree, vtype, (void *) te_is_neigh->neigh_id, dist,
- depth + 1, lsp->adj, family);
+ depth + 1, adj, family);
}
}
if (family == AF_INET && lsp->tlv_data.ipv4_int_reachs)
@@ -621,7 +620,7 @@ lspfragloop:
prefix.u.prefix4 = ipreach->prefix;
prefix.prefixlen = ip_masklen (ipreach->mask);
process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
- lsp->adj, family);
+ adj, family);
}
}
@@ -636,7 +635,7 @@ lspfragloop:
prefix.u.prefix4 = ipreach->prefix;
prefix.prefixlen = ip_masklen (ipreach->mask);
process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
- lsp->adj, family);
+ adj, family);
}
}
if (family == AF_INET && lsp->tlv_data.te_ipv4_reachs)
@@ -651,7 +650,7 @@ lspfragloop:
te_ipv4_reach->control);
prefix.prefixlen = (te_ipv4_reach->control & 0x3F);
process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
- lsp->adj, family);
+ adj, family);
}
}
#ifdef HAVE_IPV6
@@ -668,7 +667,7 @@ lspfragloop:
memcpy (&prefix.u.prefix6.s6_addr, ip6reach->prefix,
PSIZE (ip6reach->prefix_len));
process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
- lsp->adj, family);
+ adj, family);
}
}
#endif /* HAVE_IPV6 */
@@ -691,7 +690,8 @@ lspfragloop:
static int
isis_spf_process_pseudo_lsp (struct isis_spftree *spftree,
struct isis_lsp *lsp, uint16_t cost,
- uint16_t depth, int family)
+ uint16_t depth, int family,
+ struct isis_adjacency *adj)
{
struct listnode *node, *fragnode = NULL;
struct is_neigh *is_neigh;
@@ -715,13 +715,13 @@ pseudofragloop:
/* Two way connectivity */
if (!memcmp (is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
continue;
- if (isis_find_vertex
- (spftree->tents, (void *) is_neigh->neigh_id, vtype) == NULL
+ if ((depth > 0 || isis_find_vertex
+ (spftree->tents, (void *) is_neigh->neigh_id, vtype) == NULL)
&& isis_find_vertex (spftree->paths, (void *) is_neigh->neigh_id,
vtype) == NULL)
{
/* C.2.5 i) */
- isis_spf_add2tent (spftree, vtype, is_neigh->neigh_id, lsp->adj,
+ isis_spf_add2tent (spftree, vtype, is_neigh->neigh_id, adj,
cost, depth, family);
}
}
@@ -733,13 +733,13 @@ pseudofragloop:
/* Two way connectivity */
if (!memcmp (te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
continue;
- if (isis_find_vertex
- (spftree->tents, (void *) te_is_neigh->neigh_id, vtype) == NULL
+ if ((depth > 0 || isis_find_vertex
+ (spftree->tents, (void *) te_is_neigh->neigh_id, vtype) == NULL)
&& isis_find_vertex (spftree->paths, (void *) te_is_neigh->neigh_id,
vtype) == NULL)
{
/* C.2.5 i) */
- isis_spf_add2tent (spftree, vtype, te_is_neigh->neigh_id, lsp->adj,
+ isis_spf_add2tent (spftree, vtype, te_is_neigh->neigh_id, adj,
cost, depth, family);
}
}
@@ -860,9 +860,6 @@ isis_spf_preload_tent (struct isis_spftree *spftree,
lsp = lsp_search (lsp_id, area->lspdb[level - 1]);
if (!lsp)
zlog_warn ("No lsp found for IS adjacency");
- /* else {
- isis_spf_process_lsp (spftree, lsp, vertex->d_N, 1, family);
- } */
break;
case ISIS_SYSTYPE_UNKNOWN:
default:
@@ -885,14 +882,14 @@ isis_spf_preload_tent (struct isis_spftree *spftree,
{
zlog_warn ("ISIS-Spf: No adjacency found for DR");
}
- if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0)
+ else if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0)
{
zlog_warn ("ISIS-Spf: No lsp found for DR");
}
else
{
isis_spf_process_pseudo_lsp (spftree, lsp,
- circuit->te_metric[level - 1], 0, family);
+ circuit->te_metric[level - 1], 0, family, adj);
}
}
@@ -982,6 +979,7 @@ isis_run_spf (struct isis_area *area, int level, int family)
struct isis_spftree *spftree = NULL;
u_char lsp_id[ISIS_SYS_ID_LEN + 2];
struct isis_lsp *lsp;
+ struct isis_adjacency *adj = NULL;
struct route_table *table = NULL;
struct route_node *rode;
struct isis_route_info *rinfo;
@@ -1032,16 +1030,28 @@ isis_run_spf (struct isis_area *area, int level, int family)
while (listcount (spftree->tents) > 0)
{
+ /* C.2.7 a) 1) */
node = listhead (spftree->tents);
vertex = listgetdata (node);
- /* Remove from tent list */
+
+ /* C.2.7 a) 2) */
list_delete_node (spftree->tents, node);
+
+ /* C.2.7 a) 3) */
if (isis_find_vertex (spftree->paths, vertex->N.id, vertex->type))
continue;
add_to_paths (spftree, vertex, area, level);
+
if (vertex->type == VTYPE_PSEUDO_IS ||
- vertex->type == VTYPE_NONPSEUDO_IS)
+ vertex->type == VTYPE_NONPSEUDO_IS ||
+ vertex->type == VTYPE_PSEUDO_TE_IS ||
+ vertex->type == VTYPE_NONPSEUDO_TE_IS )
{
+ if (listcount(vertex->Adj_N) == 0) {
+ continue;
+ }
+ adj = listgetdata(vertex->Adj_N->head);
+
memcpy (lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1);
LSP_FRAGMENT (lsp_id) = 0;
lsp = lsp_search (lsp_id, area->lspdb[level - 1]);
@@ -1050,13 +1060,12 @@ isis_run_spf (struct isis_area *area, int level, int family)
if (LSP_PSEUDO_ID (lsp_id))
{
isis_spf_process_pseudo_lsp (spftree, lsp, vertex->d_N,
- vertex->depth, family);
-
+ vertex->depth, family, adj);
}
else
{
isis_spf_process_lsp (spftree, lsp, vertex->d_N,
- vertex->depth, family);
+ vertex->depth, family, adj);
}
}
else
diff --git a/isisd/isis_tlv.c b/isisd/isis_tlv.c
index 94fa65ed..3fc717e3 100644
--- a/isisd/isis_tlv.c
+++ b/isisd/isis_tlv.c
@@ -446,6 +446,10 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
tlvs->auth_info.len = length-1;
pnt++;
memcpy (tlvs->auth_info.passwd, pnt, length - 1);
+ /* Fill authentication with 0 for later computation
+ * of MD5 (RFC 5304, 2)
+ */
+ memset (pnt, 0, length - 1);
pnt += length - 1;
}
else
@@ -741,7 +745,7 @@ add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream)
stream_putc (stream, len); /* LENGTH */
stream_put (stream, value, (int) len); /* VALUE */
-#ifdef EXTREME_DEBUG
+#ifdef EXTREME_TLV_DEBUG
zlog_debug ("Added TLV %d len %d", tag, len);
#endif /* EXTREME DEBUG */
return ISIS_OK;
@@ -878,7 +882,7 @@ tlv_add_authinfo (char auth_type, char auth_len, u_char *auth_value,
{
u_char value[255];
u_char *pos = value;
- *pos++ = ISIS_PASSWD_TYPE_CLEARTXT;
+ *pos++ = auth_type;
memcpy (pos, auth_value, auth_len);
return add_tlv (AUTH_INFO, auth_len + 1, value, stream);
diff --git a/isisd/isisd.c b/isisd/isisd.c
index 1e84a1ce..20a32809 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -52,6 +52,7 @@
#include "isisd/isis_route.h"
#include "isisd/isis_zebra.h"
#include "isisd/isis_events.h"
+#include "isisd/isis_csm.h"
#ifdef TOPOLOGY_GENERATE
#include "spgrid.h"
@@ -217,21 +218,31 @@ isis_area_destroy (struct vty *vty, const char *area_tag)
for (ALL_LIST_ELEMENTS (area->circuit_list, node, nnode, circuit))
{
/* The fact that it's in circuit_list means that it was configured */
+ isis_csm_state_change (ISIS_DISABLE, circuit, area);
+ isis_circuit_down (circuit);
isis_circuit_deconfigure (circuit, area);
- isis_circuit_del (circuit);
}
list_delete (area->circuit_list);
}
listnode_delete (isis->area_list, area);
+
THREAD_TIMER_OFF (area->t_tick);
if (area->t_remove_aged)
thread_cancel (area->t_remove_aged);
THREAD_TIMER_OFF (area->t_lsp_refresh[0]);
THREAD_TIMER_OFF (area->t_lsp_refresh[1]);
+ THREAD_TIMER_OFF (area->spftree[0]->t_spf);
+ THREAD_TIMER_OFF (area->spftree[1]->t_spf);
+
+ THREAD_TIMER_OFF (area->t_lsp_l1_regenerate);
+ THREAD_TIMER_OFF (area->t_lsp_l2_regenerate);
+
XFREE (MTYPE_ISIS_AREA, area);
+ isis->sysid_set=0;
+
return CMD_SUCCESS;
}
diff --git a/isisd/isisd.h b/isisd/isisd.h
index 2277f27c..b17982e2 100644
--- a/isisd/isisd.h
+++ b/isisd/isisd.h
@@ -93,6 +93,8 @@ struct isis_area
struct flags flags;
struct thread *t_tick; /* LSP walker */
struct thread *t_remove_aged;
+ struct thread *t_lsp_l1_regenerate;
+ struct thread *t_lsp_l2_regenerate;
int lsp_regenerate_pending[ISIS_LEVELS];
struct thread *t_lsp_refresh[ISIS_LEVELS];
diff --git a/lib/checksum.c b/lib/checksum.c
index 3ddde815..af4f2550 100644
--- a/lib/checksum.c
+++ b/lib/checksum.c
@@ -14,27 +14,20 @@ in_cksum(void *parg, int nbytes)
{
u_short *ptr = parg;
register long sum; /* assumes long == 32 bits */
- u_short oddbyte;
register u_short answer; /* assumes u_short == 16 bits */
-
+ register int count;
/*
* Our algorithm is simple, using a 32-bit accumulator (sum),
* we add sequential 16-bit words to it, and at the end, fold back
* all the carry bits from the top 16 bits into the lower 16 bits.
*/
-
sum = 0;
- while (nbytes > 1) {
- sum += *ptr++;
- nbytes -= 2;
- }
-
- /* mop up an odd byte, if necessary */
- if (nbytes == 1) {
- oddbyte = 0; /* make sure top half is zero */
- *((u_char *) &oddbyte) = *(u_char *)ptr; /* one byte only */
- sum += oddbyte;
- }
+ count = nbytes >> 1; /* div by 2 */
+ for(ptr--; count; --count)
+ sum += *++ptr;
+
+ if (nbytes & 1) /* Odd */
+ sum += *(u_char *)(++ptr); /* one byte only */
/*
* Add back carry outs from top 16 bits to low 16 bits.
@@ -56,11 +49,8 @@ fletcher_checksum(u_char * buffer, const size_t len, const uint16_t offset)
{
u_int8_t *p;
int x, y, c0, c1;
- u_int16_t checksum;
u_int16_t *csum;
size_t partial_len, i, left = len;
-
- checksum = 0;
assert (offset < len);
@@ -68,47 +58,42 @@ fletcher_checksum(u_char * buffer, const size_t len, const uint16_t offset)
* Zero the csum in the packet.
*/
csum = (u_int16_t *) (buffer + offset);
- *(csum) = 0;
+ *csum = 0;
- p = buffer;
+ p = buffer - 1;
c0 = 0;
c1 = 0;
while (left != 0)
{
partial_len = MIN(left, MODX);
+ left -= partial_len;
- for (i = 0; i < partial_len; i++)
+ do
{
- c0 = c0 + *(p++);
+ c0 = c0 + *(++p);
c1 += c0;
- }
+ } while (--partial_len);
c0 = c0 % 255;
c1 = c1 % 255;
-
- left -= partial_len;
}
-
+
/* The cast is important, to ensure the mod is taken as a signed value. */
x = (int)((len - offset - 1) * c0 - c1) % 255;
if (x <= 0)
x += 255;
y = 510 - c0 - x;
- if (y > 255)
+ if (y > 255)
y -= 255;
-
+
/*
* Now we write this to the packet.
* We could skip this step too, since the checksum returned would
* be stored into the checksum field by the caller.
+ * Checksum is always big endian.
*/
- buffer[offset] = x;
- buffer[offset + 1] = y;
-
- /* Take care of the endian issue */
- checksum = htons((x << 8) | (y & 0xFF));
-
- return checksum;
+ *csum = htons((x << 8) | (y & 0xFF));
+ return *csum;
}
diff --git a/lib/command.c b/lib/command.c
index 478125f2..19fc192a 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -32,6 +32,7 @@ Boston, MA 02111-1307, USA. */
#include "vty.h"
#include "command.h"
#include "workqueue.h"
+#include <stdbool.h>
/* Command vector which includes some level of command lists. Normally
each daemon maintains each own cmdvec. */
@@ -84,9 +85,9 @@ static struct cmd_node config_node =
/* Default motd string. */
static const char *default_motd =
"\r\n\
-Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\
-" QUAGGA_COPYRIGHT "\r\n\
-\r\n";
+This is " QUAGGA_PROGNAME " " QUAGGA_VERSION "\r\n\r\n\
+You're using the dn42 branch. Send bug reports to equinox@diac24.net\r\n\
+The 1.1.0 version number is for package management purposes only.\r\n";
static const struct facility_map {
@@ -686,7 +687,8 @@ cmd_filter_by_symbol (char *command, char *symbol)
/* Completion match types. */
enum match_type
{
- no_match,
+ no_match = 0,
+ any_match,
extend_match,
ipv4_prefix_match,
ipv4_match,
@@ -695,7 +697,7 @@ enum match_type
range_match,
vararg_match,
partly_match,
- exact_match
+ exact_match,
};
static enum match_type
@@ -1132,12 +1134,100 @@ cmd_range_match (const char *range, const char *str)
return 1;
}
-/* Make completion match and return match type flag. */
+/* helper to retrieve the 'real' argument string from an optional argument */
+static char *
+cmd_deopt (const char *str)
+{
+ /* we've got "[blah]". We want to strip off the []s and redo the
+ * match check for "blah"
+ */
+ size_t len = strlen (str);
+ char *tmp;
+
+ if (len < 3)
+ return NULL;
+
+ /* tmp will hold a string of len-2 chars, so 'len' size is fine */
+ tmp = XMALLOC(MTYPE_TMP, len);
+
+ memcpy (tmp, (str + 1), len - 2);
+ tmp[len - 2] = '\0';
+
+ return tmp;
+}
+
+static enum match_type
+cmd_match (const char *str, const char *command,
+ enum match_type min, bool recur)
+{
+
+ if (recur && CMD_OPTION(str))
+ {
+ enum match_type ret;
+ char *tmp = cmd_deopt (str);
+
+ /* this would be a bug in a command, however handle it gracefully
+ * as it we only discover it if a user tries to run it
+ */
+ if (tmp == NULL)
+ return no_match;
+
+ ret = cmd_match (tmp, command, min, false);
+
+ XFREE (MTYPE_TMP, tmp);
+
+ return ret;
+ }
+ else if (CMD_VARARG (str))
+ return vararg_match;
+ else if (CMD_RANGE (str))
+ {
+ if (cmd_range_match (str, command))
+ return range_match;
+ }
+#ifdef HAVE_IPV6
+ else if (CMD_IPV6 (str))
+ {
+ if (cmd_ipv6_match (command) >= min)
+ return ipv6_match;
+ }
+ else if (CMD_IPV6_PREFIX (str))
+ {
+ if (cmd_ipv6_prefix_match (command) >= min)
+ return ipv6_prefix_match;
+ }
+#endif /* HAVE_IPV6 */
+ else if (CMD_IPV4 (str))
+ {
+ if (cmd_ipv4_match (command) >= min)
+ return ipv4_match;
+ }
+ else if (CMD_IPV4_PREFIX (str))
+ {
+ if (cmd_ipv4_prefix_match (command) >= min)
+ return ipv4_prefix_match;
+ }
+ else if (CMD_VARIABLE (str))
+ return extend_match;
+ else if (strncmp (command, str, strlen (command)) == 0)
+ {
+ if (strcmp (command, str) == 0)
+ return exact_match;
+ else if (partly_match >= min)
+ return partly_match;
+ }
+
+ return no_match;
+}
+
+/* Filter vector at the specified index and by the given command string, to
+ * the desired matching level (thus allowing part matches), and return match
+ * type flag.
+ */
static enum match_type
-cmd_filter_by_completion (char *command, vector v, unsigned int index)
+cmd_filter (char *command, vector v, unsigned int index, enum match_type level)
{
unsigned int i;
- const char *str;
struct cmd_element *cmd_element;
enum match_type match_type;
vector descvec;
@@ -1160,112 +1250,42 @@ cmd_filter_by_completion (char *command, vector v, unsigned int index)
for (j = 0; j < vector_active (descvec); j++)
if ((desc = vector_slot (descvec, j)))
- {
- str = desc->cmd;
-
- if (CMD_VARARG (str))
- {
- if (match_type < vararg_match)
- match_type = vararg_match;
- matched++;
- }
- else if (CMD_RANGE (str))
- {
- if (cmd_range_match (str, command))
- {
- if (match_type < range_match)
- match_type = range_match;
+ {
+ enum match_type ret;
- matched++;
- }
- }
-#ifdef HAVE_IPV6
- else if (CMD_IPV6 (str))
- {
- if (cmd_ipv6_match (command))
- {
- if (match_type < ipv6_match)
- match_type = ipv6_match;
-
- matched++;
- }
- }
- else if (CMD_IPV6_PREFIX (str))
- {
- if (cmd_ipv6_prefix_match (command))
- {
- if (match_type < ipv6_prefix_match)
- match_type = ipv6_prefix_match;
-
- matched++;
- }
- }
-#endif /* HAVE_IPV6 */
- else if (CMD_IPV4 (str))
- {
- if (cmd_ipv4_match (command))
- {
- if (match_type < ipv4_match)
- match_type = ipv4_match;
+ ret = cmd_match (desc->cmd, command, level, true);
+
+ if (ret != no_match)
+ matched++;
- matched++;
- }
- }
- else if (CMD_IPV4_PREFIX (str))
- {
- if (cmd_ipv4_prefix_match (command))
- {
- if (match_type < ipv4_prefix_match)
- match_type = ipv4_prefix_match;
- matched++;
- }
- }
- else
- /* Check is this point's argument optional ? */
- if (CMD_OPTION (str) || CMD_VARIABLE (str))
- {
- if (match_type < extend_match)
- match_type = extend_match;
- matched++;
- }
- else if (strncmp (command, str, strlen (command)) == 0)
- {
- if (strcmp (command, str) == 0)
- match_type = exact_match;
- else
- {
- if (match_type < partly_match)
- match_type = partly_match;
- }
- matched++;
- }
+ if (match_type < ret)
+ match_type = ret;
}
if (!matched)
vector_slot (v, i) = NULL;
}
}
- return match_type;
-}
-/* Filter vector by command character with index. */
-static enum match_type
-cmd_filter_by_string (char *command, vector v, unsigned int index)
-{
- unsigned int i;
- const char *str;
- struct cmd_element *cmd_element;
- enum match_type match_type;
- vector descvec;
- struct desc *desc;
-
- match_type = no_match;
+ if (match_type == no_match)
+ return no_match;
- /* If command and cmd_element string does not match set NULL to vector */
+ /* 2nd pass: We now know the 'strongest' match type for the index, so we
+ * go again and filter out commands whose argument (at this index) is
+ * 'weaker'. E.g., if we have 2 commands:
+ *
+ * foo bar <1-255>
+ * foo bar BLAH
+ *
+ * and the command string is 'foo bar 10', then we will get here with with
+ * 'range_match' being the strongest match. However, if 'BLAH' came
+ * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
+ *
+ * If we don't do a 2nd pass and filter it out, the higher-layers will
+ * consider this to be ambiguous.
+ */
for (i = 0; i < vector_active (v); i++)
if ((cmd_element = vector_slot (v, i)) != NULL)
{
- /* If given index is bigger than max string vector of command,
- set NULL */
if (index >= vector_active (cmd_element->strvec))
vector_slot (v, i) = NULL;
else
@@ -1277,81 +1297,19 @@ cmd_filter_by_string (char *command, vector v, unsigned int index)
for (j = 0; j < vector_active (descvec); j++)
if ((desc = vector_slot (descvec, j)))
- {
- str = desc->cmd;
+ {
+ enum match_type ret;
- if (CMD_VARARG (str))
- {
- if (match_type < vararg_match)
- match_type = vararg_match;
- matched++;
- }
- else if (CMD_RANGE (str))
- {
- if (cmd_range_match (str, command))
- {
- if (match_type < range_match)
- match_type = range_match;
- matched++;
- }
- }
-#ifdef HAVE_IPV6
- else if (CMD_IPV6 (str))
- {
- if (cmd_ipv6_match (command) == exact_match)
- {
- if (match_type < ipv6_match)
- match_type = ipv6_match;
- matched++;
- }
- }
- else if (CMD_IPV6_PREFIX (str))
- {
- if (cmd_ipv6_prefix_match (command) == exact_match)
- {
- if (match_type < ipv6_prefix_match)
- match_type = ipv6_prefix_match;
- matched++;
- }
- }
-#endif /* HAVE_IPV6 */
- else if (CMD_IPV4 (str))
- {
- if (cmd_ipv4_match (command) == exact_match)
- {
- if (match_type < ipv4_match)
- match_type = ipv4_match;
- matched++;
- }
- }
- else if (CMD_IPV4_PREFIX (str))
- {
- if (cmd_ipv4_prefix_match (command) == exact_match)
- {
- if (match_type < ipv4_prefix_match)
- match_type = ipv4_prefix_match;
- matched++;
- }
- }
- else if (CMD_OPTION (str) || CMD_VARIABLE (str))
- {
- if (match_type < extend_match)
- match_type = extend_match;
- matched++;
- }
- else
- {
- if (strcmp (command, str) == 0)
- {
- match_type = exact_match;
- matched++;
- }
- }
+ ret = cmd_match (desc->cmd, command, any_match, true);
+
+ if (ret >= match_type)
+ matched++;
}
if (!matched)
vector_slot (v, i) = NULL;
}
}
+
return match_type;
}
@@ -1361,7 +1319,6 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type)
{
unsigned int i;
unsigned int j;
- const char *str = NULL;
struct cmd_element *cmd_element;
const char *matched = NULL;
vector descvec;
@@ -1378,18 +1335,21 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type)
if ((desc = vector_slot (descvec, j)))
{
enum match_type ret;
-
- str = desc->cmd;
+ char *str = desc->cmd;
+ if (CMD_OPTION(str))
+ if ((str = cmd_deopt (str)) == NULL)
+ continue;
+
switch (type)
{
case exact_match:
- if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
+ if (!(CMD_VARIABLE (str))
&& strcmp (command, str) == 0)
match++;
break;
case partly_match:
- if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
+ if (!(CMD_VARIABLE (str))
&& strncmp (command, str, strlen (command)) == 0)
{
if (matched && strcmp (matched, str) != 0)
@@ -1438,13 +1398,16 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type)
}
break;
case extend_match:
- if (CMD_OPTION (str) || CMD_VARIABLE (str))
+ if (CMD_VARIABLE (str))
match++;
break;
case no_match:
default:
break;
}
+
+ if (CMD_OPTION(desc->cmd))
+ XFREE (MTYPE_TMP, str);
}
if (!match)
vector_slot (v, i) = NULL;
@@ -1614,7 +1577,7 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status)
for (i = 0; i < index; i++)
if ((command = vector_slot (vline, i)))
{
- match = cmd_filter_by_completion (command, cmd_vector, i);
+ match = cmd_filter (command, cmd_vector, i, any_match);
if (match == vararg_match)
{
@@ -1663,7 +1626,7 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status)
/* Make sure that cmd_vector is filtered based on current word */
command = vector_slot (vline, index);
if (command)
- match = cmd_filter_by_completion (command, cmd_vector, index);
+ match = cmd_filter (command, cmd_vector, index, any_match);
/* Make description vector. */
for (i = 0; i < vector_active (cmd_vector); i++)
@@ -1817,7 +1780,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
int ret;
/* First try completion match, if there is exactly match return 1 */
- match = cmd_filter_by_completion (command, cmd_vector, i);
+ match = cmd_filter (command, cmd_vector, i, any_match);
/* If there is exact match then filter ambiguous match else check
ambiguousness. */
@@ -2026,7 +1989,7 @@ cmd_execute_command_real (vector vline, struct vty *vty,
{
int ret;
- match = cmd_filter_by_completion (command, cmd_vector, index);
+ match = cmd_filter (command, cmd_vector, index, any_match);
if (match == vararg_match)
break;
@@ -2205,8 +2168,8 @@ cmd_execute_command_strict (vector vline, struct vty *vty,
{
int ret;
- match = cmd_filter_by_string (vector_slot (vline, index),
- cmd_vector, index);
+ match = cmd_filter (vector_slot (vline, index), cmd_vector,
+ index, exact_match);
/* If command meets '.VARARG' then finish matching. */
if (match == vararg_match)
@@ -2301,13 +2264,15 @@ cmd_execute_command_strict (vector vline, struct vty *vty,
/* Configration make from file. */
int
-config_from_file (struct vty *vty, FILE *fp)
+config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num)
{
int ret;
+ *line_num = 0;
vector vline;
while (fgets (vty->buf, VTY_BUFSIZ, fp))
{
+ ++(*line_num);
vline = cmd_make_strvec (vty->buf);
/* In case of comment line */
@@ -2408,6 +2373,7 @@ DEFUN (config_exit,
case KEYCHAIN_NODE:
case MASC_NODE:
case RMAP_NODE:
+ case PIM_NODE:
case VTY_NODE:
vty->node = CONFIG_NODE;
break;
@@ -2464,6 +2430,7 @@ DEFUN (config_end,
case KEYCHAIN_NODE:
case KEYCHAIN_KEY_NODE:
case MASC_NODE:
+ case PIM_NODE:
case VTY_NODE:
vty_config_unlock (vty);
vty->node = ENABLE_NODE;
@@ -3056,7 +3023,8 @@ DEFUN (config_logmsg,
if ((level = level_match(argv[0])) == ZLOG_DISABLED)
return CMD_ERR_NO_MATCH;
- zlog(NULL, level, ((message = argv_concat(argv, argc, 1)) ? message : ""));
+ message = argv_concat(argv, argc, 1);
+ zlog(NULL, level, "%s", message ? message : "");
if (message)
XFREE(MTYPE_TMP, message);
return CMD_SUCCESS;
@@ -3650,6 +3618,8 @@ cmd_init (int terminal)
install_element (VIEW_NODE, &show_thread_cpu_cmd);
install_element (ENABLE_NODE, &show_thread_cpu_cmd);
install_element (RESTRICTED_NODE, &show_thread_cpu_cmd);
+
+ install_element (ENABLE_NODE, &clear_thread_cpu_cmd);
install_element (VIEW_NODE, &show_work_queues_cmd);
install_element (ENABLE_NODE, &show_work_queues_cmd);
}
diff --git a/lib/command.h b/lib/command.h
index 1275efee..a2f5eab8 100644
--- a/lib/command.h
+++ b/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.
*
@@ -87,6 +88,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. */
@@ -341,7 +343,7 @@ extern void cmd_free_strvec (vector);
extern vector cmd_describe_command (vector, struct vty *, int *status);
extern char **cmd_complete_command (vector, struct vty *, int *status);
extern const char *cmd_prompt (enum node_type);
-extern int config_from_file (struct vty *, FILE *);
+extern int config_from_file (struct vty *, FILE *, unsigned int *line_num);
extern enum node_type node_parent (enum node_type);
extern int cmd_execute_command (vector, struct vty *, struct cmd_element **, int);
extern int cmd_execute_command_strict (vector, struct vty *, struct cmd_element **);
diff --git a/lib/if.c b/lib/if.c
index e3107116..d14cfb93 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -664,7 +664,7 @@ connected_log (struct connected *connected, char *str)
strncat (logbuf, inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
BUFSIZ - strlen(logbuf));
}
- zlog (NULL, LOG_INFO, logbuf);
+ zlog (NULL, LOG_INFO, "%s", logbuf);
}
/* If two connected address has same prefix return 1. */
diff --git a/lib/if.h b/lib/if.h
index 1b29f470..b28f32b5 100644
--- a/lib/if.h
+++ b/lib/if.h
@@ -90,6 +90,7 @@ struct interface
#define ZEBRA_INTERFACE_ACTIVE (1 << 0)
#define ZEBRA_INTERFACE_SUB (1 << 1)
#define ZEBRA_INTERFACE_LINKDETECTION (1 << 2)
+#define ZEBRA_INTERFACE_UNNUMBERED (1 << 3)
/* Interface flags. */
uint64_t flags;
diff --git a/lib/log.c b/lib/log.c
index 58b25a09..cfac61b1 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -3,6 +3,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.
*
@@ -52,6 +53,7 @@ const char *zlog_proto_names[] =
"RIPNG",
"OSPF6",
"ISIS",
+ "PIM",
"MASC",
NULL,
};
@@ -925,12 +927,18 @@ proto_redistnum(int afi, const char *s)
return ZEBRA_ROUTE_STATIC;
else if (strncmp (s, "r", 1) == 0)
return ZEBRA_ROUTE_RIP;
- else if (strncmp (s, "o", 1) == 0)
+ else if (strncmp (s, "os", 2) == 0)
return ZEBRA_ROUTE_OSPF;
else if (strncmp (s, "i", 1) == 0)
return ZEBRA_ROUTE_ISIS;
- else if (strncmp (s, "b", 1) == 0)
+ else if (strncmp (s, "bg", 2) == 0)
return ZEBRA_ROUTE_BGP;
+ else if (strncmp (s, "ol", 2) == 0)
+ return ZEBRA_ROUTE_OLSR;
+ else if (strncmp (s, "ba", 2) == 0)
+ return ZEBRA_ROUTE_BATMAN;
+ else if (strncmp (s, "d", 1) == 0)
+ return ZEBRA_ROUTE_DHCP;
}
if (afi == AFI_IP6)
{
@@ -942,12 +950,16 @@ proto_redistnum(int afi, const char *s)
return ZEBRA_ROUTE_STATIC;
else if (strncmp (s, "r", 1) == 0)
return ZEBRA_ROUTE_RIPNG;
- else if (strncmp (s, "o", 1) == 0)
+ else if (strncmp (s, "os", 2) == 0)
return ZEBRA_ROUTE_OSPF6;
else if (strncmp (s, "i", 1) == 0)
return ZEBRA_ROUTE_ISIS;
- else if (strncmp (s, "b", 1) == 0)
+ else if (strncmp (s, "bg", 2) == 0)
return ZEBRA_ROUTE_BGP;
+ else if (strncmp (s, "ol", 2) == 0)
+ return ZEBRA_ROUTE_OLSR;
+ else if (strncmp (s, "ba", 2) == 0)
+ return ZEBRA_ROUTE_BATMAN;
}
return -1;
}
diff --git a/lib/log.h b/lib/log.h
index 2dd1d313..185bcee8 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -3,6 +3,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.
*
@@ -54,6 +55,7 @@ typedef enum
ZLOG_RIPNG,
ZLOG_OSPF6,
ZLOG_ISIS,
+ ZLOG_PIM,
ZLOG_MASC
} zlog_proto_t;
diff --git a/lib/md5.c b/lib/md5.c
index 894de648..2fc36e17 100644
--- a/lib/md5.c
+++ b/lib/md5.c
@@ -298,3 +298,76 @@ static void md5_calc(const uint8_t *b64, md5_ctxt * ctxt)
ctxt->md5_stc += C;
ctxt->md5_std += D;
}
+
+/* From RFC 2104 */
+void
+hmac_md5(text, text_len, key, key_len, digest)
+unsigned char* text; /* pointer to data stream */
+int text_len; /* length of data stream */
+unsigned char* key; /* pointer to authentication key */
+int key_len; /* length of authentication key */
+caddr_t digest; /* caller digest to be filled in */
+
+{
+ MD5_CTX context;
+ unsigned char k_ipad[65]; /* inner padding -
+ * key XORd with ipad
+ */
+ unsigned char k_opad[65]; /* outer padding -
+ * key XORd with opad
+ */
+ unsigned char tk[16];
+ int i;
+ /* if key is longer than 64 bytes reset it to key=MD5(key) */
+ if (key_len > 64) {
+
+ MD5_CTX tctx;
+
+ MD5Init(&tctx);
+ MD5Update(&tctx, key, key_len);
+ MD5Final(tk, &tctx);
+
+ key = tk;
+ key_len = 16;
+ }
+
+ /*
+ * the HMAC_MD5 transform looks like:
+ *
+ * MD5(K XOR opad, MD5(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and text is the data being protected
+ */
+
+ /* start out by storing key in pads */
+ bzero( k_ipad, sizeof k_ipad);
+ bzero( k_opad, sizeof k_opad);
+ bcopy( key, k_ipad, key_len);
+ bcopy( key, k_opad, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i=0; i<64; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+ /*
+ * perform inner MD5
+ */
+ MD5Init(&context); /* init context for 1st
+ * pass */
+ MD5Update(&context, k_ipad, 64); /* start with inner pad */
+ MD5Update(&context, text, text_len); /* then text of datagram */
+ MD5Final(digest, &context); /* finish up 1st pass */
+ /*
+ * perform outer MD5
+ */
+ MD5Init(&context); /* init context for 2nd
+ * pass */
+ MD5Update(&context, k_opad, 64); /* start with outer pad */
+ MD5Update(&context, digest, 16); /* then results of 1st
+ * hash */
+ MD5Final(digest, &context); /* finish up 2nd pass */
+}
diff --git a/lib/md5.h b/lib/md5.h
index 89b9a320..3ce83a63 100644
--- a/lib/md5.h
+++ b/lib/md5.h
@@ -82,4 +82,7 @@ do { \
md5_result((x), (y)); \
} while (0)
+/* From RFC 2104 */
+void hmac_md5(unsigned char* text, int text_len, unsigned char* key, int key_len, caddr_t digest);
+
#endif /* ! _LIBZEBRA_MD5_H_*/
diff --git a/lib/memory.c b/lib/memory.c
index dc09d8a6..6291bf0d 100644
--- a/lib/memory.c
+++ b/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.
*
@@ -482,6 +483,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)
{
@@ -504,6 +516,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);
@@ -514,7 +527,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 */
diff --git a/lib/memtypes.c b/lib/memtypes.c
index 05d93225..fd99136c 100644
--- a/lib/memtypes.c
+++ b/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.
@@ -244,6 +246,21 @@ struct memory_list memory_list_isis[] =
{ -1, NULL },
};
+struct memory_list memory_list_pim[] =
+{
+ { MTYPE_PIM_CHANNEL_OIL, "PIM SSM (S,G) channel OIL" },
+ { MTYPE_PIM_INTERFACE, "PIM interface" },
+ { MTYPE_PIM_IGMP_JOIN, "PIM interface IGMP static join" },
+ { MTYPE_PIM_IGMP_SOCKET, "PIM interface IGMP socket" },
+ { MTYPE_PIM_IGMP_GROUP, "PIM interface IGMP group" },
+ { MTYPE_PIM_IGMP_GROUP_SOURCE, "PIM interface IGMP source" },
+ { MTYPE_PIM_NEIGHBOR, "PIM interface neighbor" },
+ { MTYPE_PIM_IFCHANNEL, "PIM interface (S,G) state" },
+ { MTYPE_PIM_UPSTREAM, "PIM upstream (S,G) state" },
+ { MTYPE_PIM_SSMPINGD, "PIM sspimgd socket" },
+ { -1, NULL },
+};
+
struct memory_list memory_list_vtysh[] =
{
{ MTYPE_VTYSH_CONFIG, "Vtysh configuration", },
@@ -260,5 +277,6 @@ struct mlist mlists[] __attribute__ ((unused)) = {
{ memory_list_ospf6, "OSPF6" },
{ memory_list_isis, "ISIS" },
{ memory_list_bgp, "BGP" },
+ { memory_list_pim, "PIM" },
{ NULL, NULL},
};
diff --git a/lib/prefix.c b/lib/prefix.c
index c85e6594..f5de4bde 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -180,6 +180,46 @@ prefix_cmp (const struct prefix *p1, const struct prefix *p2)
return 0;
}
+/*
+ * Count the number of common bits in 2 prefixes. The prefix length is
+ * ignored for this function; the whole prefix is compared. If the prefix
+ * address families don't match, return -1; otherwise the return value is
+ * in range 0 ... maximum prefix length for the address family.
+ */
+int
+prefix_common_bits (const struct prefix *p1, const struct prefix *p2)
+{
+ int pos, bit;
+ int length = 0;
+ u_char xor;
+
+ /* Set both prefix's head pointer. */
+ const u_char *pp1 = (const u_char *)&p1->u.prefix;
+ const u_char *pp2 = (const u_char *)&p2->u.prefix;
+
+ if (p1->family == AF_INET)
+ length = IPV4_MAX_BYTELEN;
+#ifdef HAVE_IPV6
+ if (p1->family == AF_INET6)
+ length = IPV6_MAX_BYTELEN;
+#endif
+ if (p1->family != p2->family || !length)
+ return -1;
+
+ for (pos = 0; pos < length; pos++)
+ if (pp1[pos] != pp2[pos])
+ break;
+ if (pos == length)
+ return pos * 8;
+
+ xor = pp1[pos] ^ pp2[pos];
+ for (bit = 0; bit < 8; bit++)
+ if (xor & (1 << (7 - bit)))
+ break;
+
+ return pos * 8 + bit;
+}
+
/* Return prefix family type string. */
const char *
prefix_family_str (const struct prefix *p)
@@ -569,6 +609,20 @@ sockunion2hostprefix (const union sockunion *su)
return NULL;
}
+void
+prefix2sockunion (const struct prefix *p, union sockunion *su)
+{
+ memset (su, 0, sizeof (*su));
+
+ su->sa.sa_family = p->family;
+ if (p->family == AF_INET)
+ su->sin.sin_addr = p->u.prefix4;
+#ifdef HAVE_IPV6
+ if (p->family == AF_INET6)
+ memcpy (&su->sin6.sin6_addr, &p->u.prefix6, sizeof (struct in6_addr));
+#endif /* HAVE_IPV6 */
+}
+
int
prefix_blen (const struct prefix *p)
{
diff --git a/lib/prefix.h b/lib/prefix.h
index a7598b7e..f6259dee 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -156,12 +156,14 @@ extern int prefix2str (const struct prefix *, char *, int);
extern int prefix_match (const struct prefix *, const struct prefix *);
extern int prefix_same (const struct prefix *, const struct prefix *);
extern int prefix_cmp (const struct prefix *, const struct prefix *);
+extern int prefix_common_bits (const struct prefix *, const struct prefix *);
extern void prefix_copy (struct prefix *dest, const struct prefix *src);
extern void apply_mask (struct prefix *);
extern struct prefix *sockunion2prefix (const union sockunion *dest,
const union sockunion *mask);
extern struct prefix *sockunion2hostprefix (const union sockunion *);
+extern void prefix2sockunion (const struct prefix *, union sockunion *);
extern struct prefix_ipv4 *prefix_ipv4_new (void);
extern void prefix_ipv4_free (struct prefix_ipv4 *);
diff --git a/lib/route_types.txt b/lib/route_types.txt
index fde0bc8d..dfa34d74 100644
--- a/lib/route_types.txt
+++ b/lib/route_types.txt
@@ -56,8 +56,11 @@ ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, "BGP"
# This at least makes it trivial for users of these protocols
# to 'switch on' redist support (direct numeric entry remaining
# possible).
-ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, "HSLS"
-ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, "OLSR"
+ZEBRA_ROUTE_HSLS, hsls, hslsd, 'h', 1, 1, "HSLS"
+ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 1, 1, "OLSR"
+ZEBRA_ROUTE_BATMAN, batman, batman, 'b', 1, 1, "BATMAN"
+ZEBRA_ROUTE_DHCP, dhcp, dhcpc, 'D', 1, 0, "DHCP"
+ZEBRA_ROUTE_PIM, pim, pimd, 'P', 0, 0, "PIM-SSM"
## help strings
ZEBRA_ROUTE_SYSTEM, "Reserved route type, for internal use only"
@@ -72,3 +75,7 @@ ZEBRA_ROUTE_ISIS, "Intermediate System to Intermediate System (IS-IS)"
ZEBRA_ROUTE_BGP, "Border Gateway Protocol (BGP)"
ZEBRA_ROUTE_HSLS, "Hazy-Sighted Link State Protocol (HSLS)"
ZEBRA_ROUTE_OLSR, "Optimised Link State Routing (OLSR)"
+ZEBRA_ROUTE_BATMAN, "Better Approach To Mobile Ad-hoc Networking (BATMAN)"
+ZEBRA_ROUTE_DHCP, "Dynamic Host Configuration Protocol (DHCP)"
+ZEBRA_ROUTE_PIM, "Protocol Independent Multicast, Source Specific Mode (PIM-SSM)"
+
diff --git a/lib/sockunion.c b/lib/sockunion.c
index d1f6d80b..5de3bcfc 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -551,6 +551,16 @@ sockopt_v6only (int family, int sock)
return 0;
}
+int
+sockopt_cork (int sock, int onoff)
+{
+#ifdef TCP_CORK
+ return setsockopt (sock, IPPROTO_TCP, TCP_CORK, &onoff, sizeof(onoff));
+#else
+ return 0;
+#endif
+}
+
/* If same family and same prefix return 1. */
int
sockunion_same (union sockunion *su1, union sockunion *su2)
diff --git a/lib/sockunion.h b/lib/sockunion.h
index db145cf2..2e8ce2d0 100644
--- a/lib/sockunion.h
+++ b/lib/sockunion.h
@@ -103,6 +103,7 @@ extern int sockopt_v6only (int family, int sock);
extern int sockunion_bind (int sock, union sockunion *,
unsigned short, union sockunion *);
extern int sockopt_ttl (int family, int sock, int ttl);
+extern int sockopt_cork (int sock, int onoff);
extern int sockunion_socket (union sockunion *su);
extern const char *inet_sutop (union sockunion *su, char *str);
extern enum connect_result sockunion_connect (int fd, union sockunion *su,
diff --git a/lib/stream.c b/lib/stream.c
index 983330ff..bff74d7b 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -711,32 +711,6 @@ stream_read (struct stream *s, int fd, size_t size)
return nbytes;
}
-/* Read size from fd. */
-int
-stream_read_unblock (struct stream *s, int fd, size_t size)
-{
- int nbytes;
- int val;
-
- STREAM_VERIFY_SANE(s);
-
- if (STREAM_WRITEABLE (s) < size)
- {
- STREAM_BOUND_WARN (s, "put");
- return 0;
- }
-
- val = fcntl (fd, F_GETFL, 0);
- fcntl (fd, F_SETFL, val|O_NONBLOCK);
- nbytes = read (fd, s->data + s->endp, size);
- fcntl (fd, F_SETFL, val);
-
- if (nbytes > 0)
- s->endp += nbytes;
-
- return nbytes;
-}
-
ssize_t
stream_read_try(struct stream *s, int fd, size_t size)
{
diff --git a/lib/stream.h b/lib/stream.h
index 3e4ba7b4..486a3f93 100644
--- a/lib/stream.h
+++ b/lib/stream.h
@@ -181,10 +181,6 @@ extern u_int32_t stream_get_ipv4 (struct stream *);
Use stream_read_try instead. */
extern int stream_read (struct stream *, int, size_t);
-/* Deprecated: all file descriptors should already be non-blocking.
- Will be removed. Use stream_read_try instead. */
-extern int stream_read_unblock (struct stream *, int, size_t);
-
/* Read up to size bytes into the stream.
Return code:
>0: number of bytes read
diff --git a/lib/thread.c b/lib/thread.c
index e89af541..e083e8c7 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -20,8 +20,9 @@
*/
/* #define DEBUG */
-
+#include <errno.h>
#include <zebra.h>
+#include <sys/times.h>
#include "thread.h"
#include "memory.h"
@@ -31,8 +32,7 @@
#include "sigevent.h"
/* Recent absolute time of day */
-struct timeval recent_time;
-static struct timeval last_recent_time;
+static struct timeval recent_time;
/* Relative time, since startup */
static struct timeval relative_time;
static struct timeval relative_time_base;
@@ -94,54 +94,98 @@ timeval_elapsed (struct timeval a, struct timeval b)
}
#ifndef HAVE_CLOCK_MONOTONIC
+static unsigned long recent_clock_mark; /* Holds value of last call to times(2) */
+static unsigned long last_clock_mark;
+static unsigned long clocks_per_sec, msec_scale, usec_scale;
+
+static unsigned long quagga_times(void)
+{
+#if defined(GNU_LINUX)
+ unsigned long ret;
+
+ errno = 0;
+ ret = times(NULL); /* Linux can handle NULL */
+ /* Workaround broken syscall impl.
+ * A bugfix exists for the kernel, hopefully
+ * it will make it into 2.6.28
+ */
+ if (errno)
+ ret = (unsigned long) (-errno);
+ return ret;
+#else
+ struct tms dummy; /* Only return value is used */
+
+ return times(&dummy);
+#endif
+}
+
static void
quagga_gettimeofday_relative_adjust (void)
{
- struct timeval diff;
- if (timeval_cmp (recent_time, last_recent_time) < 0)
- {
- relative_time.tv_sec++;
- relative_time.tv_usec = 0;
- }
- else
- {
- diff = timeval_subtract (recent_time, last_recent_time);
- relative_time.tv_sec += diff.tv_sec;
- relative_time.tv_usec += diff.tv_usec;
- relative_time = timeval_adjust (relative_time);
- }
- last_recent_time = recent_time;
+ unsigned long diff;
+
+ diff = recent_clock_mark - last_clock_mark;
+ if (!diff)
+ return;
+ /* save mark for next calculation */
+ last_clock_mark = recent_clock_mark;
+
+ relative_time.tv_sec += diff / clocks_per_sec; /* convert to seconds */
+ relative_time.tv_usec += (diff % clocks_per_sec) * usec_scale; /* convert to useconds */
+ relative_time = timeval_adjust (relative_time);
}
#endif /* !HAVE_CLOCK_MONOTONIC */
+static int init_quagga_timers(void)
+{
+ int ret;
+
+ ret = gettimeofday (&recent_time, NULL);
+ relative_time_base = recent_time;
+#if !defined(HAVE_CLOCK_MONOTONIC)
+ clocks_per_sec = sysconf(_SC_CLK_TCK);
+ assert(clocks_per_sec != 0);
+
+ /* Precondition: 1000 % clocks_per_sec == 0 */
+ assert(1000 % clocks_per_sec == 0);
+ msec_scale = 1000 / clocks_per_sec;
+
+ /* Precondition: TIMER_SECOND_MICRO % clocks_per_sec == 0 */
+ assert(TIMER_SECOND_MICRO % clocks_per_sec == 0);
+ usec_scale = TIMER_SECOND_MICRO / clocks_per_sec;
+ recent_clock_mark = quagga_times();
+ last_clock_mark = recent_clock_mark;
+#endif
+ if (ret)
+ return ret;
+ timers_inited = 1;
+ return 0;
+}
+
/* gettimeofday wrapper, to keep recent_time updated */
static int
quagga_gettimeofday (struct timeval *tv)
{
int ret;
-
- assert (tv);
-
- if (!(ret = gettimeofday (&recent_time, NULL)))
- {
- /* init... */
- if (!timers_inited)
- {
- relative_time_base = last_recent_time = recent_time;
- timers_inited = 1;
- }
- /* avoid copy if user passed recent_time pointer.. */
- if (tv != &recent_time)
- *tv = recent_time;
- return 0;
- }
- return ret;
+
+ /* init... */
+ if (!timers_inited)
+ ret = init_quagga_timers();
+ else
+ ret = gettimeofday (&recent_time, NULL);
+ if (ret)
+ return ret;
+
+ /* avoid copy if user passed recent_time pointer.. */
+ if (tv != &recent_time)
+ *tv = recent_time;
+ return 0;
}
static int
quagga_get_relative (struct timeval *tv)
{
- int ret;
+ int ret = 0;
#ifdef HAVE_CLOCK_MONOTONIC
{
@@ -153,8 +197,12 @@ quagga_get_relative (struct timeval *tv)
}
}
#else /* !HAVE_CLOCK_MONOTONIC */
- if (!(ret = quagga_gettimeofday (&recent_time)))
- quagga_gettimeofday_relative_adjust();
+ /* init... */
+ if (!timers_inited)
+ ret = init_quagga_timers();
+
+ recent_clock_mark = quagga_times();
+ quagga_gettimeofday_relative_adjust();
#endif /* HAVE_CLOCK_MONOTONIC */
if (tv)
@@ -382,6 +430,89 @@ DEFUN(show_thread_cpu,
cpu_record_print(vty, filter);
return CMD_SUCCESS;
}
+
+static void
+cpu_record_hash_clear (struct hash_backet *bucket,
+ void *args)
+{
+ thread_type *filter = args;
+ struct cpu_thread_history *a = bucket->data;
+
+ a = bucket->data;
+ if ( !(a->types & *filter) )
+ return;
+
+ hash_release (cpu_record, bucket->data);
+}
+
+static void
+cpu_record_clear (thread_type filter)
+{
+ thread_type *tmp = &filter;
+ hash_iterate (cpu_record,
+ (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear,
+ tmp);
+}
+
+DEFUN(clear_thread_cpu,
+ clear_thread_cpu_cmd,
+ "clear thread cpu [FILTER]",
+ "Clear stored data\n"
+ "Thread information\n"
+ "Thread CPU usage\n"
+ "Display filter (rwtexb)\n")
+{
+ int i = 0;
+ thread_type filter = (thread_type) -1U;
+
+ if (argc > 0)
+ {
+ filter = 0;
+ while (argv[0][i] != '\0')
+ {
+ switch ( argv[0][i] )
+ {
+ case 'r':
+ case 'R':
+ filter |= (1 << THREAD_READ);
+ break;
+ case 'w':
+ case 'W':
+ filter |= (1 << THREAD_WRITE);
+ break;
+ case 't':
+ case 'T':
+ filter |= (1 << THREAD_TIMER);
+ break;
+ case 'e':
+ case 'E':
+ filter |= (1 << THREAD_EVENT);
+ break;
+ case 'x':
+ case 'X':
+ filter |= (1 << THREAD_EXECUTE);
+ break;
+ case 'b':
+ case 'B':
+ filter |= (1 << THREAD_BACKGROUND);
+ break;
+ default:
+ break;
+ }
+ ++i;
+ }
+ if (filter == 0)
+ {
+ vty_out(vty, "Invalid filter \"%s\" specified,"
+ " must contain at least one of 'RWTEXB'%s",
+ argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ cpu_record_clear (filter);
+ return CMD_SUCCESS;
+}
/* List allocation and head/tail print out. */
static void
@@ -903,6 +1034,24 @@ thread_timer_process (struct thread_list *list, struct timeval *timenow)
return ready;
}
+/* process a list en masse, e.g. for event thread lists */
+static unsigned int
+thread_process (struct thread_list *list)
+{
+ struct thread *thread;
+ unsigned int ready = 0;
+
+ for (thread = list->head; thread; thread = thread->next)
+ {
+ thread_list_delete (list, thread);
+ thread->type = THREAD_READY;
+ thread_list_add (&thread->master->ready, thread);
+ ready++;
+ }
+ return ready;
+}
+
+
/* Fetch next ready thread. */
struct thread *
thread_fetch (struct thread_master *m, struct thread *fetch)
@@ -910,44 +1059,49 @@ thread_fetch (struct thread_master *m, struct thread *fetch)
struct thread *thread;
fd_set readfd;
fd_set writefd;
- fd_set exceptfd;
- struct timeval timer_val;
+ struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 };
struct timeval timer_val_bg;
- struct timeval *timer_wait;
+ struct timeval *timer_wait = &timer_val;
struct timeval *timer_wait_bg;
while (1)
{
int num = 0;
- /* Signals are highest priority */
+ /* Signals pre-empt everything */
quagga_sigevent_process ();
- /* Normal event are the next highest priority. */
- if ((thread = thread_trim_head (&m->event)) != NULL)
- return thread_run (m, thread, fetch);
-
- /* If there are any ready threads from previous scheduler runs,
- * process top of them.
+ /* Drain the ready queue of already scheduled jobs, before scheduling
+ * more.
*/
if ((thread = thread_trim_head (&m->ready)) != NULL)
return thread_run (m, thread, fetch);
+ /* To be fair to all kinds of threads, and avoid starvation, we
+ * need to be careful to consider all thread types for scheduling
+ * in each quanta. I.e. we should not return early from here on.
+ */
+
+ /* Normal event are the next highest priority. */
+ thread_process (&m->event);
+
/* Structure copy. */
readfd = m->readfd;
writefd = m->writefd;
- exceptfd = m->exceptfd;
/* Calculate select wait timer if nothing else to do */
- quagga_get_relative (NULL);
- timer_wait = thread_timer_wait (&m->timer, &timer_val);
- timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg);
-
- if (timer_wait_bg &&
- (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0)))
- timer_wait = timer_wait_bg;
+ if (m->ready.count == 0)
+ {
+ quagga_get_relative (NULL);
+ timer_wait = thread_timer_wait (&m->timer, &timer_val);
+ timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg);
+
+ if (timer_wait_bg &&
+ (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0)))
+ timer_wait = timer_wait_bg;
+ }
- num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
+ num = select (FD_SETSIZE, &readfd, &writefd, NULL, timer_wait);
/* Signals should get quick treatment */
if (num < 0)
@@ -1028,14 +1182,6 @@ thread_getrusage (RUSAGE_T *r)
getrusage(RUSAGE_SELF, &(r->cpu));
#endif
r->real = relative_time;
-
-#ifdef HAVE_CLOCK_MONOTONIC
- /* quagga_get_relative() only updates recent_time if gettimeofday
- * based, not when using CLOCK_MONOTONIC. As we export recent_time
- * and guarantee to update it before threads are run...
- */
- quagga_gettimeofday(&recent_time);
-#endif /* HAVE_CLOCK_MONOTONIC */
}
/* We check thread consumed time. If the system has getrusage, we'll
diff --git a/lib/thread.h b/lib/thread.h
index b52bc541..7de679c8 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -1,5 +1,6 @@
/* Thread management routine header.
* Copyright (C) 1998 Kunihiro Ishiguro
+ * Portions Copyright (c) 2008 Everton da Silva Marques <everton.marques@gmail.com>
*
* This file is part of GNU Zebra.
*
@@ -53,7 +54,6 @@ struct thread_master
struct thread_list background;
fd_set readfd;
fd_set writefd;
- fd_set exceptfd;
unsigned long alloc;
};
@@ -137,6 +137,12 @@ enum quagga_clkid {
thread = thread_add_timer (master, func, arg, time); \
} while (0)
+#define THREAD_TIMER_MSEC_ON(master,thread,func,arg,time) \
+ do { \
+ if (! thread) \
+ thread = thread_add_timer_msec (master, func, arg, time); \
+ } while (0)
+
#define THREAD_OFF(thread) \
do { \
if (thread) \
@@ -197,6 +203,7 @@ extern int thread_should_yield (struct thread *);
/* Internal libzebra exports */
extern void thread_getrusage (RUSAGE_T *);
extern struct cmd_element show_thread_cpu_cmd;
+extern struct cmd_element clear_thread_cpu_cmd;
/* replacements for the system gettimeofday(), clock_gettime() and
* time() functions, providing support for non-decrementing clock on
@@ -209,10 +216,6 @@ extern time_t quagga_time (time_t *);
extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before,
unsigned long *cpu_time_elapsed);
-/* Global variable containing a recent result from gettimeofday. This can
- be used instead of calling gettimeofday if a recent value is sufficient.
- This is guaranteed to be refreshed before a thread is called. */
-extern struct timeval recent_time;
/* Similar to recent_time, but a monotonically increasing time value */
extern struct timeval recent_relative_time (void);
#endif /* _ZEBRA_THREAD_H */
diff --git a/lib/vty.c b/lib/vty.c
index fa3f7718..c1bcb328 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -275,7 +275,7 @@ vty_hello (struct vty *vty)
vty_out (vty, "MOTD file not found%s", VTY_NEWLINE);
}
else if (host.motd)
- vty_out (vty, host.motd);
+ vty_out (vty, "%s", host.motd);
}
/* Put out prompt and wait input from user. */
@@ -737,6 +737,7 @@ vty_end_config (struct vty *vty)
case KEYCHAIN_NODE:
case KEYCHAIN_KEY_NODE:
case MASC_NODE:
+ case PIM_NODE:
case VTY_NODE:
vty_config_unlock (vty);
vty->node = ENABLE_NODE;
@@ -1140,6 +1141,7 @@ vty_stop_input (struct vty *vty)
case KEYCHAIN_NODE:
case KEYCHAIN_KEY_NODE:
case MASC_NODE:
+ case PIM_NODE:
case VTY_NODE:
vty_config_unlock (vty);
vty->node = ENABLE_NODE;
@@ -1865,7 +1867,7 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
freeaddrinfo (ainfo_save);
}
-#endif /* HAVE_IPV6 && ! NRL */
+#else /* HAVE_IPV6 && ! NRL */
/* Make vty server socket. */
static void
@@ -1931,6 +1933,7 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)
/* Add vty server event. */
vty_event (VTY_SERV, accept_sock, NULL);
}
+#endif /* HAVE_IPV6 && ! NRL */
#ifdef VTYSH
/* For sockaddr_un. */
@@ -2255,28 +2258,37 @@ vty_read_file (FILE *confp)
{
int ret;
struct vty *vty;
+ unsigned int line_num = 15;
vty = vty_new ();
- vty->fd = 0; /* stdout */
- vty->type = VTY_TERM;
+ vty->fd = dup(STDERR_FILENO); /* vty_close() will close this */
+ if (vty->fd < 0)
+ {
+ /* Fine, we couldn't make a new fd. vty_close doesn't close stdout. */
+ vty->fd = STDOUT_FILENO;
+ }
+ vty->type = VTY_FILE;
vty->node = CONFIG_NODE;
/* Execute configuration file */
- ret = config_from_file (vty, confp);
+ ret = config_from_file (vty, confp, &line_num);
+
+ /* Flush any previous errors before printing messages below */
+ buffer_flush_all (vty->obuf, vty->fd);
if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
{
switch (ret)
{
case CMD_ERR_AMBIGUOUS:
- fprintf (stderr, "Ambiguous command.\n");
+ fprintf (stderr, "*** Error reading config: Ambiguous command.\n");
break;
case CMD_ERR_NO_MATCH:
- fprintf (stderr, "There is no such command.\n");
+ fprintf (stderr, "*** Error reading config: There is no such command.\n");
break;
}
- fprintf (stderr, "Error occured during reading below line.\n%s\n",
- vty->buf);
+ fprintf (stderr, "*** Error occured processing line %u, below:\n%s\n",
+ line_num, vty->buf);
vty_close (vty);
exit (1);
}
diff --git a/lib/workqueue.c b/lib/workqueue.c
index 7c811edd..52b5f41c 100644
--- a/lib/workqueue.c
+++ b/lib/workqueue.c
@@ -341,7 +341,7 @@ work_queue_run (struct thread *thread)
stats:
-#define WQ_HYSTERIS_FACTOR 2
+#define WQ_HYSTERESIS_FACTOR 4
/* we yielded, check whether granularity should be reduced */
if (yielded && (cycles < wq->cycles.granularity))
@@ -349,17 +349,18 @@ stats:
wq->cycles.granularity = ((cycles > 0) ? cycles
: WORK_QUEUE_MIN_GRANULARITY);
}
-
- if (cycles >= (wq->cycles.granularity))
+ /* otherwise, should granularity increase? */
+ else if (cycles >= (wq->cycles.granularity))
{
if (cycles > wq->cycles.best)
wq->cycles.best = cycles;
- /* along with yielded check, provides hysteris for granularity */
- if (cycles > (wq->cycles.granularity * WQ_HYSTERIS_FACTOR * 2))
- wq->cycles.granularity *= WQ_HYSTERIS_FACTOR; /* quick ramp-up */
- else if (cycles > (wq->cycles.granularity * WQ_HYSTERIS_FACTOR))
- wq->cycles.granularity += WQ_HYSTERIS_FACTOR;
+ /* along with yielded check, provides hysteresis for granularity */
+ if (cycles > (wq->cycles.granularity * WQ_HYSTERESIS_FACTOR
+ * WQ_HYSTERESIS_FACTOR))
+ wq->cycles.granularity *= WQ_HYSTERESIS_FACTOR; /* quick ramp-up */
+ else if (cycles > (wq->cycles.granularity * WQ_HYSTERESIS_FACTOR))
+ wq->cycles.granularity += WQ_HYSTERESIS_FACTOR;
}
#undef WQ_HYSTERIS_FACTOR
diff --git a/lib/zebra.h b/lib/zebra.h
index fa37b48d..63eb373a 100644
--- a/lib/zebra.h
+++ b/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.
@@ -421,7 +422,8 @@ struct in_pktinfo
#define ZEBRA_ROUTER_ID_ADD 20
#define ZEBRA_ROUTER_ID_DELETE 21
#define ZEBRA_ROUTER_ID_UPDATE 22
-#define ZEBRA_MESSAGE_MAX 23
+#define ZEBRA_IPV4_NEXTHOP_LOOKUP_V2 23
+#define ZEBRA_MESSAGE_MAX 24
/* Marker value used in new Zserv, in the byte location corresponding
* the command value in the old zserv header. To allow old and new
@@ -531,43 +533,4 @@ typedef u_int8_t safi_t;
typedef u_int16_t zebra_size_t;
typedef u_int16_t zebra_command_t;
-/* FIFO -- first in first out structure and macros. */
-struct fifo
-{
- struct fifo *next;
- struct fifo *prev;
-};
-
-#define FIFO_INIT(F) \
- do { \
- struct fifo *Xfifo = (struct fifo *)(F); \
- Xfifo->next = Xfifo->prev = Xfifo; \
- } while (0)
-
-#define FIFO_ADD(F,N) \
- do { \
- struct fifo *Xfifo = (struct fifo *)(F); \
- struct fifo *Xnode = (struct fifo *)(N); \
- Xnode->next = Xfifo; \
- Xnode->prev = Xfifo->prev; \
- Xfifo->prev = Xfifo->prev->next = Xnode; \
- } while (0)
-
-#define FIFO_DEL(N) \
- do { \
- struct fifo *Xnode = (struct fifo *)(N); \
- Xnode->prev->next = Xnode->next; \
- Xnode->next->prev = Xnode->prev; \
- } while (0)
-
-#define FIFO_HEAD(F) \
- ((((struct fifo *)(F))->next == (struct fifo *)(F)) \
- ? NULL : (F)->next)
-
-#define FIFO_EMPTY(F) \
- (((struct fifo *)(F))->next == (struct fifo *)(F))
-
-#define FIFO_TOP(F) \
- (FIFO_EMPTY(F) ? NULL : ((struct fifo *)(F))->next)
-
#endif /* _ZEBRA_H */
diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c
index 777bc7c9..cb347451 100644
--- a/ospf6d/ospf6_interface.c
+++ b/ospf6d/ospf6_interface.c
@@ -1394,13 +1394,17 @@ DEFUN (ipv6_ospf6_advertise_prefix_list,
oi->plist_name = XSTRDUP (MTYPE_PREFIX_LIST_STR, argv[0]);
ospf6_interface_connected_route_update (oi->interface);
- OSPF6_LINK_LSA_SCHEDULE (oi);
- if (oi->state == OSPF6_INTERFACE_DR)
+
+ if (oi->area)
{
- OSPF6_NETWORK_LSA_SCHEDULE (oi);
- OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi);
+ OSPF6_LINK_LSA_SCHEDULE (oi);
+ if (oi->state == OSPF6_INTERFACE_DR)
+ {
+ OSPF6_NETWORK_LSA_SCHEDULE (oi);
+ OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi);
+ }
+ OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area);
}
- OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area);
return CMD_SUCCESS;
}
@@ -1433,13 +1437,17 @@ DEFUN (no_ipv6_ospf6_advertise_prefix_list,
}
ospf6_interface_connected_route_update (oi->interface);
- OSPF6_LINK_LSA_SCHEDULE (oi);
- if (oi->state == OSPF6_INTERFACE_DR)
+
+ if (oi->area)
{
- OSPF6_NETWORK_LSA_SCHEDULE (oi);
- OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi);
+ OSPF6_LINK_LSA_SCHEDULE (oi);
+ if (oi->state == OSPF6_INTERFACE_DR)
+ {
+ OSPF6_NETWORK_LSA_SCHEDULE (oi);
+ OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi);
+ }
+ OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area);
}
- OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area);
return CMD_SUCCESS;
}
diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c
index 7e32195b..def55f10 100644
--- a/ospfd/ospf_abr.c
+++ b/ospfd/ospf_abr.c
@@ -614,15 +614,6 @@ set_metric (struct ospf_lsa *lsa, u_int32_t metric)
memcpy(header->metric, mp, 3);
}
-static int
-ospf_abr_check_nssa_range (struct prefix_ipv4 *p, u_int32_t cost,
- struct ospf_area *area)
-{
- /* The Type-7 is tested against the aggregated prefix and forwarded
- for lsa installation and flooding */
- return 0;
-}
-
/* ospf_abr_translate_nssa */
static int
ospf_abr_translate_nssa (struct ospf_area *area, struct ospf_lsa *lsa)
@@ -1578,42 +1569,6 @@ ospf_abr_send_nssa_aggregates (struct ospf *ospf) /* temporarily turned off */
}
static void
-ospf_abr_announce_nssa_defaults (struct ospf *ospf) /* By ABR-Translator */
-{
- struct listnode *node;
- struct ospf_area *area;
-
- if (! IS_OSPF_ABR (ospf))
- return;
-
- if (IS_DEBUG_OSPF_NSSA)
- zlog_debug ("ospf_abr_announce_stub_defaults(): Start");
-
- for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
- {
- if (IS_DEBUG_OSPF_NSSA)
- zlog_debug ("ospf_abr_announce_nssa_defaults(): looking at area %s",
- inet_ntoa (area->area_id));
-
- if (area->external_routing != OSPF_AREA_NSSA)
- continue;
-
- if (OSPF_IS_AREA_BACKBONE (area))
- continue; /* Sanity Check */
-
- /* if (!TranslatorRole continue V 1.0 look for "always" conf */
- if (area->NSSATranslatorState)
- {
- if (IS_DEBUG_OSPF_NSSA)
- zlog_debug ("ospf_abr_announce_nssa_defaults(): "
- "announcing 0.0.0.0/0 to this nssa");
- /* ospf_abr_announce_nssa_asbr_to_as (&p, area->default_cost, area); */
- /*ospf_abr_announce_network_to_area (&p, area->default_cost, area);*/
- }
- }
-}
-
-static void
ospf_abr_announce_stub_defaults (struct ospf *ospf)
{
struct listnode *node;
diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c
index 15fd2e5f..bd5c026c 100644
--- a/ospfd/ospf_apiserver.c
+++ b/ospfd/ospf_apiserver.c
@@ -2470,7 +2470,7 @@ ospf_apiserver_clients_notify_nsm_change (struct ospf_neighbor *nbr)
ifaddr = nbr->oi->address->u.prefix4;
}
- nbraddr = nbr->address.u.prefix4;
+ nbraddr = nbr->src;
msg = new_msg_nsm_change (0, ifaddr, nbraddr, nbr->router_id, nbr->state);
if (!msg)
diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c
index 5d0cae42..c06f0178 100644
--- a/ospfd/ospf_ase.c
+++ b/ospfd/ospf_ase.c
@@ -31,6 +31,7 @@
#include "table.h"
#include "vty.h"
#include "log.h"
+#include "workqueue.h"
#include "ospfd/ospfd.h"
#include "ospfd/ospf_interface.h"
@@ -130,18 +131,6 @@ ospf_find_asbr_route_through_area (struct route_table *rtrs,
return NULL;
}
-static void
-ospf_ase_complete_direct_routes (struct ospf_route *ro, struct in_addr nexthop)
-{
- struct listnode *node;
- struct ospf_path *op;
- struct interface *ifp;
-
- for (ALL_LIST_ELEMENTS_RO (ro->paths, node, op))
- if (op->nexthop.s_addr == 0)
- op->nexthop.s_addr = nexthop.s_addr;
-}
-
static int
ospf_ase_forward_address_check (struct ospf *ospf, struct in_addr fwd_addr)
{
@@ -284,8 +273,8 @@ ospf_ase_calculate_new_route (struct ospf_lsa *lsa,
#define OSPF_ASE_CALC_INTERVAL 1
-int
-ospf_ase_calculate_route (struct ospf *ospf, struct ospf_lsa * lsa)
+static int
+ospf_ase_calculate_route (struct ospf *ospf, struct ospf_lsa *lsa)
{
u_int32_t metric;
struct as_external_lsa *al;
@@ -451,8 +440,8 @@ ospf_ase_calculate_route (struct ospf *ospf, struct ospf_lsa * lsa)
/* if there is a Intra/Inter area route to the N
do not install external route */
- if (rn = route_node_lookup (ospf->new_table,
- (struct prefix *) &p))
+ if ((rn = route_node_lookup (ospf->new_table,
+ (struct prefix *) &p)))
{
route_unlock_node(rn);
if (rn->info == NULL)
@@ -463,8 +452,8 @@ ospf_ase_calculate_route (struct ospf *ospf, struct ospf_lsa * lsa)
}
/* Find a route to the same dest */
/* If there is no route, create new one. */
- if (rn = route_node_lookup (ospf->new_external_route,
- (struct prefix *) &p))
+ if ((rn = route_node_lookup (ospf->new_external_route,
+ (struct prefix *) &p)))
route_unlock_node(rn);
if (!rn || (or = rn->info) == NULL)
@@ -475,8 +464,6 @@ ospf_ase_calculate_route (struct ospf *ospf, struct ospf_lsa * lsa)
ospf_route_add (ospf->new_external_route, &p, new, asbr_route);
- if (al->e[0].fwd_addr.s_addr)
- ospf_ase_complete_direct_routes (new, al->e[0].fwd_addr);
return 0;
}
else
@@ -513,8 +500,7 @@ ospf_ase_calculate_route (struct ospf *ospf, struct ospf_lsa * lsa)
if (IS_DEBUG_OSPF (lsa, LSA))
zlog_debug ("Route[External]: New route is better");
ospf_route_subst (rn, new, asbr_route);
- if (al->e[0].fwd_addr.s_addr)
- ospf_ase_complete_direct_routes (new, al->e[0].fwd_addr);
+
or = new;
new = NULL;
}
@@ -531,8 +517,6 @@ ospf_ase_calculate_route (struct ospf *ospf, struct ospf_lsa * lsa)
if (IS_DEBUG_OSPF (lsa, LSA))
zlog_debug ("Route[External]: Routes are equal");
ospf_route_copy_nexthops (or, asbr_route->paths);
- if (al->e[0].fwd_addr.s_addr)
- ospf_ase_complete_direct_routes (or, al->e[0].fwd_addr);
}
}
/* Make sure setting newly calculated ASBR route.*/
@@ -629,6 +613,50 @@ ospf_ase_compare_tables (struct route_table *new_external_route,
return 0;
}
+static int ospf_ase_calculate_timer (struct thread *);
+
+static void ospf_ase_calc_completion (struct work_queue *wq)
+{
+ struct ospf *ospf = wq->spec.data;
+
+ /* Compare old and new external routing table and install the
+ difference info zebra/kernel */
+ ospf_ase_compare_tables (ospf->new_external_route,
+ ospf->old_external_route);
+
+ /* Delete old external routing table */
+ ospf_route_table_free (ospf->old_external_route);
+ ospf->old_external_route = ospf->new_external_route;
+ ospf->new_external_route = route_table_init ();
+
+ /* another timer fired since, so another run might be needed */
+ if (ospf->ase_calc && !ospf->t_ase_calc)
+ ospf->t_ase_calc = thread_add_timer (master, ospf_ase_calculate_timer,
+ ospf, 0);
+}
+
+static wq_item_status
+ospf_ase_calc_process (struct work_queue *wq, void *data)
+{
+ struct ospf_lsa *lsa = data;
+ ospf_ase_calculate_route (wq->spec.data, lsa);
+ ospf_lsa_unlock (&lsa);
+ return WQ_SUCCESS;
+}
+
+static void
+ospf_ase_calc_queue_init (struct ospf *ospf)
+{
+ ospf->ase_calc_queue = work_queue_new (master, "ase_calc_queue");
+
+ ospf->ase_calc_queue->spec.workfunc = &ospf_ase_calc_process;
+ ospf->ase_calc_queue->spec.completion_func = &ospf_ase_calc_completion;
+
+ ospf->ase_calc_queue->spec.data = ospf;
+ ospf->ase_calc_queue->spec.max_retries = 0;
+ ospf->ase_calc_queue->spec.hold = 0;
+}
+
static int
ospf_ase_calculate_timer (struct thread *t)
{
@@ -645,9 +673,12 @@ ospf_ase_calculate_timer (struct thread *t)
{
ospf->ase_calc = 0;
+ if (!ospf->ase_calc_queue)
+ ospf_ase_calc_queue_init (ospf);
+
/* Calculate external route for each AS-external-LSA */
LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa)
- ospf_ase_calculate_route (ospf, lsa);
+ work_queue_add (ospf->ase_calc_queue, ospf_lsa_lock (lsa));
/* This version simple adds to the table all NSSA areas */
if (ospf->anyNSSA)
@@ -659,39 +690,22 @@ ospf_ase_calculate_timer (struct thread *t)
if (area->external_routing == OSPF_AREA_NSSA)
LSDB_LOOP (NSSA_LSDB (area), rn, lsa)
- ospf_ase_calculate_route (ospf, lsa);
+ work_queue_add (ospf->ase_calc_queue, ospf_lsa_lock (lsa));
}
/* kevinm: And add the NSSA routes in ospf_top */
LSDB_LOOP (NSSA_LSDB (ospf),rn,lsa)
- ospf_ase_calculate_route(ospf,lsa);
-
- /* Compare old and new external routing table and install the
- difference info zebra/kernel */
- ospf_ase_compare_tables (ospf->new_external_route,
- ospf->old_external_route);
-
- /* Delete old external routing table */
- ospf_route_table_free (ospf->old_external_route);
- ospf->old_external_route = ospf->new_external_route;
- ospf->new_external_route = route_table_init ();
+ work_queue_add (ospf->ase_calc_queue, ospf_lsa_lock (lsa));
}
return 0;
}
void
-ospf_ase_calculate_schedule (struct ospf *ospf)
+ospf_ase_calculate_timer_add (struct ospf *ospf)
{
if (ospf == NULL)
return;
ospf->ase_calc = 1;
-}
-
-void
-ospf_ase_calculate_timer_add (struct ospf *ospf)
-{
- if (ospf == NULL)
- return;
if (! ospf->t_ase_calc)
ospf->t_ase_calc = thread_add_timer (master, ospf_ase_calculate_timer,
diff --git a/ospfd/ospf_ase.h b/ospfd/ospf_ase.h
index e6a1b2fb..646a38c0 100644
--- a/ospfd/ospf_ase.h
+++ b/ospfd/ospf_ase.h
@@ -34,8 +34,6 @@ extern struct ospf_route *ospf_find_asbr_route_through_area (struct
struct ospf_area
*);
-extern int ospf_ase_calculate_route (struct ospf *, struct ospf_lsa *);
-extern void ospf_ase_calculate_schedule (struct ospf *);
extern void ospf_ase_calculate_timer_add (struct ospf *);
extern void ospf_ase_external_lsas_finish (struct route_table *);
diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c
index e65b2e33..4786a87c 100644
--- a/ospfd/ospf_dump.c
+++ b/ospfd/ospf_dump.c
@@ -215,9 +215,9 @@ ospf_nbr_state_message (struct ospf_neighbor *nbr, char *buf, size_t size)
int state;
struct ospf_interface *oi = nbr->oi;
- if (IPV4_ADDR_SAME (&DR (oi), &nbr->address.u.prefix4))
+ if (IPV4_ADDR_SAME (&DR (oi), &nbr->src))
state = ISM_DR;
- else if (IPV4_ADDR_SAME (&BDR (oi), &nbr->address.u.prefix4))
+ else if (IPV4_ADDR_SAME (&BDR (oi), &nbr->src))
state = ISM_Backup;
else
state = ISM_DROther;
diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c
index 41661da2..5ebf4f2f 100644
--- a/ospfd/ospf_flood.c
+++ b/ospfd/ospf_flood.c
@@ -346,7 +346,7 @@ ospf_flood_through_interface (struct ospf_interface *oi,
struct ospf_lsa *lsa)
{
struct ospf_neighbor *onbr;
- struct route_node *rn;
+ struct listnode *node;
int retx_flag;
if (IS_DEBUG_OSPF_EVENT)
@@ -364,14 +364,10 @@ ospf_flood_through_interface (struct ospf_interface *oi,
/* Each of the neighbors attached to this interface are examined,
to determine whether they must receive the new LSA. The following
steps are executed for each neighbor: */
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, node, onbr))
{
struct ospf_lsa *ls_req;
-
- if (rn->info == NULL)
- continue;
- onbr = rn->info;
if (IS_DEBUG_OSPF_EVENT)
zlog_debug ("ospf_flood_through_interface(): considering nbr %s (%s)",
inet_ntoa (onbr->router_id),
@@ -543,13 +539,12 @@ ospf_flood_through_interface (struct ospf_interface *oi,
addresses. */
if (oi->type == OSPF_IFTYPE_NBMA)
{
- struct route_node *rn;
+ struct listnode *node;
struct ospf_neighbor *nbr;
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info) != NULL)
- if (nbr != oi->nbr_self && nbr->state >= NSM_Exchange)
- ospf_ls_upd_send_lsa (nbr, lsa, OSPF_SEND_PACKET_DIRECT);
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, node, nbr))
+ if (nbr != oi->nbr_self && nbr->state >= NSM_Exchange)
+ ospf_ls_upd_send_lsa (nbr, lsa, OSPF_SEND_PACKET_DIRECT);
}
else
ospf_ls_upd_send_lsa (oi->nbr_self, lsa, OSPF_SEND_PACKET_INDIRECT);
@@ -934,21 +929,20 @@ static void
ospf_ls_retransmit_delete_nbr_if (struct ospf_interface *oi,
struct ospf_lsa *lsa)
{
- struct route_node *rn;
+ struct listnode *node;
struct ospf_neighbor *nbr;
struct ospf_lsa *lsr;
if (ospf_if_is_enable (oi))
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, node, nbr))
/* If LSA find in LS-retransmit list, then remove it. */
- if ((nbr = rn->info) != NULL)
- {
- lsr = ospf_ls_retransmit_lookup (nbr, lsa);
-
- /* If LSA find in ls-retransmit list, remove it. */
- if (lsr != NULL && lsr->data->ls_seqnum == lsa->data->ls_seqnum)
- ospf_ls_retransmit_delete (nbr, lsr);
- }
+ {
+ lsr = ospf_ls_retransmit_lookup (nbr, lsa);
+
+ /* If LSA find in ls-retransmit list, remove it. */
+ if (lsr != NULL && lsr->data->ls_seqnum == lsa->data->ls_seqnum)
+ ospf_ls_retransmit_delete (nbr, lsr);
+ }
}
void
diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c
index afe3acf1..33a83b4d 100644
--- a/ospfd/ospf_interface.c
+++ b/ospfd/ospf_interface.c
@@ -211,7 +211,9 @@ ospf_if_new (struct ospf *ospf, struct interface *ifp, struct prefix *p)
}
else
return oi;
-
+
+ ospf_lsa_pos_set(-1,-1, oi); /* delete position in router LSA */
+
/* Set zebra interface pointer. */
oi->ifp = ifp;
oi->address = p;
@@ -223,7 +225,7 @@ ospf_if_new (struct ospf *ospf, struct interface *ifp, struct prefix *p)
oi->network_lsa_self = NULL;
/* Initialize neighbor list. */
- oi->nbrs = route_table_init ();
+ oi->nbrs = list_new ();
/* Initialize static neighbor list. */
oi->nbr_nbma = list_new ();
@@ -258,7 +260,6 @@ ospf_if_new (struct ospf *ospf, struct interface *ifp, struct prefix *p)
void
ospf_if_cleanup (struct ospf_interface *oi)
{
- struct route_node *rn;
struct listnode *node, *nnode;
struct ospf_neighbor *nbr;
struct ospf_nbr_nbma *nbr_nbma;
@@ -282,10 +283,9 @@ ospf_if_cleanup (struct ospf_interface *oi)
}
/* send Neighbor event KillNbr to all associated neighbors. */
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info) != NULL)
- if (nbr != oi->nbr_self)
- OSPF_NSM_EVENT_EXECUTE (nbr, NSM_KillNbr);
+ for (ALL_LIST_ELEMENTS (oi->nbrs, node, nnode, nbr))
+ if (nbr != oi->nbr_self)
+ OSPF_NSM_EVENT_EXECUTE (nbr, NSM_KillNbr);
/* Cleanup Link State Acknowlegdment list. */
for (ALL_LIST_ELEMENTS (oi->ls_ack, node, nnode, lsa))
@@ -321,7 +321,7 @@ ospf_if_free (struct ospf_interface *oi)
/* Free Pseudo Neighbour */
ospf_nbr_delete (oi->nbr_self);
- route_table_finish (oi->nbrs);
+ list_free (oi->nbrs);
route_table_finish (oi->ls_upd_queue);
/* Free any lists that should be freed */
@@ -397,6 +397,29 @@ ospf_if_exists (struct ospf_interface *oic)
return NULL;
}
+/* Lookup OSPF interface by router LSA posistion */
+struct ospf_interface *
+ospf_if_lookup_by_lsa_pos (struct ospf_area *area, int lsa_pos)
+{
+ struct listnode *node;
+ struct ospf_interface *oi;
+
+ for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi))
+ {
+ if (lsa_pos >= oi->lsa_pos_beg && lsa_pos < oi->lsa_pos_end)
+ return oi;
+ }
+ return NULL;
+}
+
+/* Set OSPF interface position in router LSA */
+void
+ospf_lsa_pos_set(int pos_beg, int pos_end, struct ospf_interface *oi)
+{
+ oi->lsa_pos_beg = pos_beg;
+ oi->lsa_pos_end = pos_end;
+}
+
struct ospf_interface *
ospf_if_lookup_by_local_addr (struct ospf *ospf,
struct interface *ifp, struct in_addr address)
@@ -479,6 +502,20 @@ ospf_if_lookup_recv_if (struct ospf *ospf, struct in_addr src,
return match;
}
+
+struct ospf_interface *
+ospf_if_lookup_by_ifindex(struct ospf_area *area, unsigned int ifindex)
+{
+ struct listnode *node;
+ struct ospf_interface *oi;
+
+ for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi))
+ {
+ if (oi->ifp->ifindex == ifindex)
+ return oi;
+ }
+ return NULL;
+}
void
ospf_if_stream_set (struct ospf_interface *oi)
@@ -803,6 +840,7 @@ ospf_if_down (struct ospf_interface *oi)
return 0;
OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceDown);
+ ospf_lsa_pos_set(-1, -1, oi); /* delete position in router LSA */
/* Shutdown packet reception and sending */
ospf_if_stream_unset (oi);
diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h
index ab0b7580..82bf31a5 100644
--- a/ospfd/ospf_interface.h
+++ b/ospfd/ospf_interface.h
@@ -47,6 +47,7 @@ struct ospf_if_params
DECLARE_IF_PARAM (u_int32_t, retransmit_interval); /* Retransmission Interval */
DECLARE_IF_PARAM (u_char, passive_interface); /* OSPF Interface is passive: no sending or receiving (no need to join multicast groups) */
DECLARE_IF_PARAM (u_char, priority); /* OSPF Interface priority */
+ DECLARE_IF_PARAM (struct in_addr, if_area); /* Enable OSPF on this interface with area if_area */
DECLARE_IF_PARAM (u_char, type); /* type of interface */
#define OSPF_IF_ACTIVE 0
#define OSPF_IF_PASSIVE 1
@@ -124,6 +125,10 @@ struct ospf_interface
/* OSPF Area. */
struct ospf_area *area;
+/* Position range in Router LSA */
+ int lsa_pos_beg; /* inclusive, >= */
+ int lsa_pos_end; /* exclusive, < */
+
/* Interface data from zebra. */
struct interface *ifp;
struct ospf_vl_data *vl_data; /* Data for Virtual Link */
@@ -171,7 +176,7 @@ struct ospf_interface
u_int32_t output_cost; /* Acutual Interface Output Cost */
/* Neighbor information. */
- struct route_table *nbrs; /* OSPF Neighbor List */
+ struct list *nbrs; /* OSPF Neighbor List */
struct ospf_neighbor *nbr_self; /* Neighbor Self */
#define DR(I) ((I)->nbr_self->d_router)
#define BDR(I) ((I)->nbr_self->bd_router)
@@ -242,6 +247,9 @@ extern int ospf_if_down (struct ospf_interface *);
extern int ospf_if_is_up (struct ospf_interface *);
extern struct ospf_interface *ospf_if_exists (struct ospf_interface *);
+extern struct ospf_interface *ospf_if_lookup_by_lsa_pos (struct ospf_area *,
+ int);
+extern void ospf_lsa_pos_set(int, int, struct ospf_interface *);
extern struct ospf_interface *ospf_if_lookup_by_local_addr (struct ospf *,
struct interface
*,
@@ -256,7 +264,8 @@ extern struct ospf_interface *ospf_if_lookup_recv_if (struct ospf *,
struct interface *);
extern struct ospf_interface *ospf_if_is_configured (struct ospf *,
struct in_addr *);
-
+extern struct ospf_interface *ospf_if_lookup_by_ifindex(struct ospf_area *,
+ unsigned int);
extern struct ospf_if_params *ospf_lookup_if_params (struct interface *,
struct in_addr);
extern struct ospf_if_params *ospf_get_if_params (struct interface *,
diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c
index 18402836..2186e894 100644
--- a/ospfd/ospf_ism.c
+++ b/ospfd/ospf_ism.c
@@ -88,7 +88,7 @@ ospf_elect_dr (struct ospf_interface *oi, struct list *el_list)
listnode_add (dr_list, nbr);
/* Preserve neighbor BDR. */
- if (IPV4_ADDR_SAME (&BDR (oi), &nbr->address.u.prefix4))
+ if (IPV4_ADDR_SAME (&BDR (oi), &nbr->src))
bdr = nbr;
}
@@ -100,7 +100,7 @@ ospf_elect_dr (struct ospf_interface *oi, struct list *el_list)
/* Set DR to interface. */
if (dr)
- DR (oi) = dr->address.u.prefix4;
+ DR (oi) = dr->src;
else
DR (oi).s_addr = 0;
@@ -141,7 +141,7 @@ ospf_elect_bdr (struct ospf_interface *oi, struct list *el_list)
/* Set BDR to interface. */
if (bdr)
- BDR (oi) = bdr->address.u.prefix4;
+ BDR (oi) = bdr->src;
else
BDR (oi).s_addr = 0;
@@ -163,38 +163,36 @@ ospf_ism_state (struct ospf_interface *oi)
}
static void
-ospf_dr_eligible_routers (struct route_table *nbrs, struct list *el_list)
+ospf_dr_eligible_routers (struct list *nbrs, struct list *el_list)
{
- struct route_node *rn;
+ struct listnode *node;
struct ospf_neighbor *nbr;
- for (rn = route_top (nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info) != NULL)
- /* Ignore 0.0.0.0 node*/
- if (nbr->router_id.s_addr != 0)
- /* Is neighbor eligible? */
- if (nbr->priority > 0)
- /* Is neighbor upper 2-Way? */
- if (nbr->state >= NSM_TwoWay)
- listnode_add (el_list, nbr);
+ for (ALL_LIST_ELEMENTS_RO (nbrs, node, nbr))
+ /* Ignore 0.0.0.0 node*/
+ if (nbr->router_id.s_addr != 0)
+ /* Is neighbor eligible? */
+ if (nbr->priority > 0)
+ /* Is neighbor upper 2-Way? */
+ if (nbr->state >= NSM_TwoWay)
+ listnode_add (el_list, nbr);
}
/* Generate AdjOK? NSM event. */
static void
-ospf_dr_change (struct ospf *ospf, struct route_table *nbrs)
+ospf_dr_change (struct ospf *ospf, struct list *nbrs)
{
- struct route_node *rn;
+ struct listnode *node;
struct ospf_neighbor *nbr;
- for (rn = route_top (nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info) != NULL)
- /* Ignore 0.0.0.0 node*/
- if (nbr->router_id.s_addr != 0)
- /* Is neighbor upper 2-Way? */
- if (nbr->state >= NSM_TwoWay)
- /* Ignore myself. */
- if (!IPV4_ADDR_SAME (&nbr->router_id, &ospf->router_id))
- OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_AdjOK);
+ for (ALL_LIST_ELEMENTS_RO (nbrs, node, nbr))
+ /* Ignore 0.0.0.0 node*/
+ if (nbr->router_id.s_addr != 0)
+ /* Is neighbor upper 2-Way? */
+ if (nbr->state >= NSM_TwoWay)
+ /* Ignore myself. */
+ if (!IPV4_ADDR_SAME (&nbr->router_id, &ospf->router_id))
+ OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_AdjOK);
}
static int
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
index e708d5e2..25bd754d 100644
--- a/ospfd/ospf_lsa.c
+++ b/ospfd/ospf_lsa.c
@@ -436,17 +436,13 @@ struct ospf_neighbor *
ospf_nbr_lookup_ptop (struct ospf_interface *oi)
{
struct ospf_neighbor *nbr = NULL;
- struct route_node *rn;
+ struct listnode *node;
/* Search neighbor, there must be one of two nbrs. */
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info))
- if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id))
- if (nbr->state == NSM_Full)
- {
- route_unlock_node (rn);
- break;
- }
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, node, nbr))
+ if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id))
+ if (nbr->state == NSM_Full)
+ break;
/* PtoP link must have only 1 neighbor. */
if (ospf_nbr_count (oi, 0) > 1)
@@ -531,9 +527,16 @@ lsa_link_ptop_set (struct stream *s, struct ospf_interface *oi)
{
/* For unnumbered point-to-point networks, the Link Data field
should specify the interface's MIB-II ifIndex value. */
- links += link_info_set (s, nbr->router_id, oi->address->u.prefix4,
+ if (CHECK_FLAG(oi->ifp->status, ZEBRA_INTERFACE_UNNUMBERED))
+ id.s_addr = htonl(oi->ifp->ifindex);
+ else
+ id = oi->address->u.prefix4;
+
+ links += link_info_set (s, nbr->router_id, id,
LSA_LINK_TYPE_POINTOPOINT, 0, cost);
}
+ if (CHECK_FLAG(oi->ifp->status, ZEBRA_INTERFACE_UNNUMBERED))
+ return links;
/* Regardless of the state of the neighboring router, we must
add a Type 3 link (stub network).
@@ -623,7 +626,7 @@ static int
lsa_link_ptomp_set (struct stream *s, struct ospf_interface *oi)
{
int links = 0;
- struct route_node *rn;
+ struct listnode *node;
struct ospf_neighbor *nbr = NULL;
struct in_addr id, mask;
u_int16_t cost = ospf_link_cost (oi);
@@ -636,20 +639,18 @@ lsa_link_ptomp_set (struct stream *s, struct ospf_interface *oi)
zlog_debug ("PointToMultipoint: running ptomultip_set");
/* Search neighbor, */
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info) != NULL)
- /* Ignore myself. */
- if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id))
- if (nbr->state == NSM_Full)
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, node, nbr))
+ /* Ignore myself. */
+ if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id))
+ if (nbr->state == NSM_Full)
+ {
+ links += link_info_set (s, nbr->router_id, oi->address->u.prefix4,
+ LSA_LINK_TYPE_POINTOPOINT, 0, cost);
+ if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+ zlog_debug ("PointToMultipoint: set link to %s",
+ inet_ntoa(oi->address->u.prefix4));
+ }
- {
- links += link_info_set (s, nbr->router_id, oi->address->u.prefix4,
- LSA_LINK_TYPE_POINTOPOINT, 0, cost);
- if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
- zlog_debug ("PointToMultipoint: set link to %s",
- inet_ntoa(oi->address->u.prefix4));
- }
-
return links;
}
@@ -657,9 +658,11 @@ lsa_link_ptomp_set (struct stream *s, struct ospf_interface *oi)
static int
router_lsa_link_set (struct stream *s, struct ospf_area *area)
{
+ struct ospf_host_route *host;
+ struct in_addr host_mask = {~0}; /* All ones */
struct listnode *node;
struct ospf_interface *oi;
- int links = 0;
+ int links = 0, old_links;
for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi))
{
@@ -670,6 +673,7 @@ router_lsa_link_set (struct stream *s, struct ospf_area *area)
{
if (oi->state != ISM_Down)
{
+ old_links = links;
/* Describe each link. */
switch (oi->type)
{
@@ -691,10 +695,14 @@ router_lsa_link_set (struct stream *s, struct ospf_area *area)
case OSPF_IFTYPE_LOOPBACK:
links += lsa_link_loopback_set (s, oi);
}
+ ospf_lsa_pos_set(old_links, links, oi);
}
}
}
-
+ for (ALL_LIST_ELEMENTS_RO (area->ospf->hostlist, node, host))
+ if (host->area == NULL || host->area == area)
+ links += link_info_set (s, host->host_addr, host_mask,
+ LSA_LINK_TYPE_STUB, 0, host->cost);
return links;
}
@@ -1025,7 +1033,7 @@ static void
ospf_network_lsa_body_set (struct stream *s, struct ospf_interface *oi)
{
struct in_addr mask;
- struct route_node *rn;
+ struct listnode *node;
struct ospf_neighbor *nbr;
masklen2ip (oi->address->prefixlen, &mask);
@@ -1036,10 +1044,9 @@ ospf_network_lsa_body_set (struct stream *s, struct ospf_interface *oi)
its OSPF Router ID. The Designated Router includes itself in this
list. RFC2328, Section 12.4.2 */
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info) != NULL)
- if (nbr->state == NSM_Full || nbr == oi->nbr_self)
- stream_put_ipv4 (s, nbr->router_id.s_addr);
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, node, nbr))
+ if (nbr->state == NSM_Full || nbr == oi->nbr_self)
+ stream_put_ipv4 (s, nbr->router_id.s_addr);
}
static struct ospf_lsa *
@@ -2839,19 +2846,14 @@ ospf_check_nbr_status (struct ospf *ospf)
struct listnode *node, *nnode;
struct ospf_interface *oi;
- for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi))
+ for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
{
- struct route_node *rn;
struct ospf_neighbor *nbr;
if (ospf_if_is_enable (oi))
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info) != NULL)
- if (nbr->state == NSM_Exchange || nbr->state == NSM_Loading)
- {
- route_unlock_node (rn);
- return 0;
- }
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, nnode, nbr))
+ if (nbr->state == NSM_Exchange || nbr->state == NSM_Loading)
+ return 0;
}
return 1;
@@ -2912,6 +2914,10 @@ ospf_maxage_lsa_remover (struct thread *thread)
continue;
}
+ /* TODO: maybe convert this function to a work-queue */
+ if (thread_should_yield (thread))
+ OSPF_TIMER_ON (ospf->t_maxage, ospf_maxage_lsa_remover, 0);
+
/* Remove LSA from the LSDB */
if (CHECK_FLAG (lsa->flags, OSPF_LSA_SELF))
if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c
index 967ca15d..2b7ca08d 100644
--- a/ospfd/ospf_neighbor.c
+++ b/ospfd/ospf_neighbor.c
@@ -43,26 +43,6 @@
#include "ospfd/ospf_flood.h"
#include "ospfd/ospf_dump.h"
-/* Fill in the the 'key' as appropriate to retrieve the entry for nbr
- * from the ospf_interface's nbrs table. Indexed by interface address
- * for all cases except Virtual-link interfaces, where neighbours are
- * indexed by router-ID instead.
- */
-static void
-ospf_nbr_key (struct ospf_interface *oi, struct ospf_neighbor *nbr,
- struct prefix *key)
-{
- key->family = AF_INET;
- key->prefixlen = IPV4_MAX_BITLEN;
-
- /* vlinks are indexed by router-id */
- if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
- key->u.prefix4 = nbr->router_id;
- else
- key->u.prefix4 = nbr->src;
- return;
-}
-
struct ospf_neighbor *
ospf_nbr_new (struct ospf_interface *oi)
{
@@ -143,44 +123,43 @@ ospf_nbr_free (struct ospf_neighbor *nbr)
XFREE (MTYPE_OSPF_NEIGHBOR, nbr);
}
+/* lookup nbr by address - use this only if you know you must
+ * otherwise use the ospf_nbr_lookup() wrapper, which deals
+ * with virtual link and PointToPoint neighbours
+ */
+struct ospf_neighbor *
+ospf_nbr_lookup_by_addr (struct list *nbrs,
+ struct in_addr *addr)
+{
+ struct listnode *node;
+ struct ospf_neighbor *nbr;
-/* Delete specified OSPF neighbor from interface. */
-void
-ospf_nbr_delete (struct ospf_neighbor *nbr)
+ for (ALL_LIST_ELEMENTS_RO (nbrs, node, nbr))
+ if (IPV4_ADDR_SAME (&nbr->src, addr))
+ return nbr;
+
+ return NULL;
+}
+
+struct ospf_neighbor *
+ospf_nbr_lookup_by_routerid (struct list *nbrs,
+ struct in_addr *id)
{
- struct ospf_interface *oi;
- struct route_node *rn;
- struct prefix p;
+ struct listnode *node;
+ struct ospf_neighbor *nbr;
- oi = nbr->oi;
-
- /* get appropriate prefix 'key' */
- ospf_nbr_key (oi, nbr, &p);
+ for (ALL_LIST_ELEMENTS_RO (nbrs, node, nbr))
+ if (IPV4_ADDR_SAME (&nbr->router_id, id))
+ return nbr;
- rn = route_node_lookup (oi->nbrs, &p);
- if (rn)
- {
- /* If lookup for a NBR succeeds, the leaf route_node could
- * only exist because there is (or was) a nbr there.
- * If the nbr was deleted, the leaf route_node should have
- * lost its last refcount too, and be deleted.
- * Therefore a looked-up leaf route_node in nbrs table
- * should never have NULL info.
- */
- assert (rn->info);
-
- if (rn->info)
- {
- rn->info = NULL;
- route_unlock_node (rn);
- }
- else
- zlog_info ("Can't find neighbor %s in the interface %s",
- inet_ntoa (nbr->src), IF_NAME (oi));
-
- route_unlock_node (rn);
- }
+ return NULL;
+}
+/* Delete specified OSPF neighbor from interface. */
+void
+ospf_nbr_delete (struct ospf_neighbor *nbr)
+{
+ listnode_delete(nbr->oi->nbrs, nbr);
/* Free ospf_neighbor structure. */
ospf_nbr_free (nbr);
}
@@ -206,11 +185,9 @@ ospf_nbr_bidirectional (struct in_addr *router_id,
void
ospf_nbr_add_self (struct ospf_interface *oi)
{
- struct prefix p;
- struct route_node *rn;
+ struct ospf_neighbor *nbr;
/* Initial state */
- oi->nbr_self->address = *oi->address;
oi->nbr_self->priority = OSPF_IF_PARAM (oi, priority);
oi->nbr_self->router_id = oi->ospf->router_id;
oi->nbr_self->src = oi->address->u.prefix4;
@@ -229,19 +206,23 @@ ospf_nbr_add_self (struct ospf_interface *oi)
SET_FLAG (oi->nbr_self->options, OSPF_OPTION_NP);
break;
}
-
- /* Add nbr_self to nbrs table */
- ospf_nbr_key (oi, oi->nbr_self, &p);
-
- rn = route_node_get (oi->nbrs, &p);
- if (rn->info)
+
+ /* Sanity check, should not be needed */
+ if (oi->type == OSPF_IFTYPE_VIRTUALLINK ||
+ oi->type == OSPF_IFTYPE_POINTOPOINT)
+ nbr = ospf_nbr_lookup_by_routerid (oi->nbrs, &oi->nbr_self->router_id);
+ else
+ nbr = ospf_nbr_lookup_by_addr (oi->nbrs, &oi->nbr_self->src);
+ if (nbr)
{
- /* There is already pseudo neighbor. */
- assert (oi->nbr_self == rn->info);
- route_unlock_node (rn);
+ assert (oi->nbr_self == nbr);
+ zlog_info("Self neighbor already added for ospf I/F:%s",
+ oi->ifp->name);
+ return;
}
- else
- rn->info = oi->nbr_self;
+
+ /* Add nbr_self to nbrs table */
+ listnode_add(oi->nbrs, oi->nbr_self);
}
/* Get neighbor count by status.
@@ -250,14 +231,13 @@ int
ospf_nbr_count (struct ospf_interface *oi, int state)
{
struct ospf_neighbor *nbr;
- struct route_node *rn;
+ struct listnode *node;
int count = 0;
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info))
- if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id))
- if (state == 0 || nbr->state == state)
- count++;
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, node, nbr))
+ if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id))
+ if (state == 0 || nbr->state == state)
+ count++;
return count;
}
@@ -267,80 +247,24 @@ int
ospf_nbr_count_opaque_capable (struct ospf_interface *oi)
{
struct ospf_neighbor *nbr;
- struct route_node *rn;
+ struct listnode *node;
int count = 0;
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info))
- if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id))
- if (nbr->state == NSM_Full)
- if (CHECK_FLAG (nbr->options, OSPF_OPTION_O))
- count++;
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, node, nbr))
+ if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id))
+ if (nbr->state == NSM_Full)
+ if (CHECK_FLAG (nbr->options, OSPF_OPTION_O))
+ count++;
return count;
}
#endif /* HAVE_OPAQUE_LSA */
-/* lookup nbr by address - use this only if you know you must
- * otherwise use the ospf_nbr_lookup() wrapper, which deals
- * with virtual link neighbours
- */
-struct ospf_neighbor *
-ospf_nbr_lookup_by_addr (struct route_table *nbrs,
- struct in_addr *addr)
-{
- struct prefix p;
- struct route_node *rn;
- struct ospf_neighbor *nbr;
-
- p.family = AF_INET;
- p.prefixlen = IPV4_MAX_BITLEN;
- p.u.prefix4 = *addr;
-
- rn = route_node_lookup (nbrs, &p);
- if (! rn)
- return NULL;
-
- /* See comment in ospf_nbr_delete */
- assert (rn->info);
-
- if (rn->info == NULL)
- {
- route_unlock_node (rn);
- return NULL;
- }
-
- nbr = (struct ospf_neighbor *) rn->info;
- route_unlock_node (rn);
-
- return nbr;
-}
-
-struct ospf_neighbor *
-ospf_nbr_lookup_by_routerid (struct route_table *nbrs,
- struct in_addr *id)
-{
- struct route_node *rn;
- struct ospf_neighbor *nbr;
-
- for (rn = route_top (nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info) != NULL)
- if (IPV4_ADDR_SAME (&nbr->router_id, id))
- {
- route_unlock_node(rn);
- return nbr;
- }
-
- return NULL;
-}
-
void
ospf_renegotiate_optional_capabilities (struct ospf *top)
{
- struct listnode *node;
+ struct listnode *node, *nnode;
struct ospf_interface *oi;
- struct route_table *nbrs;
- struct route_node *rn;
struct ospf_neighbor *nbr;
/* At first, flush self-originated LSAs from routing domain. */
@@ -349,12 +273,9 @@ ospf_renegotiate_optional_capabilities (struct ospf *top)
/* Revert all neighbor status to ExStart. */
for (ALL_LIST_ELEMENTS_RO (top->oiflist, node, oi))
{
- if ((nbrs = oi->nbrs) == NULL)
- continue;
-
- for (rn = route_top (nbrs); rn; rn = route_next (rn))
- {
- if ((nbr = rn->info) == NULL || nbr == oi->nbr_self)
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, nnode, nbr))
+ {
+ if (nbr == oi->nbr_self)
continue;
if (nbr->state < NSM_ExStart)
@@ -375,22 +296,22 @@ struct ospf_neighbor *
ospf_nbr_lookup (struct ospf_interface *oi, struct ip *iph,
struct ospf_header *ospfh)
{
- if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
- return (ospf_nbr_lookup_by_routerid (oi->nbrs, &ospfh->router_id));
+ if (oi->type == OSPF_IFTYPE_VIRTUALLINK ||
+ oi->type == OSPF_IFTYPE_POINTOPOINT)
+ return ospf_nbr_lookup_by_routerid (oi->nbrs, &ospfh->router_id);
else
- return (ospf_nbr_lookup_by_addr (oi->nbrs, &iph->ip_src));
+ return ospf_nbr_lookup_by_addr (oi->nbrs, &iph->ip_src);
}
static struct ospf_neighbor *
ospf_nbr_add (struct ospf_interface *oi, struct ospf_header *ospfh,
- struct prefix *p)
+ struct ip *iph)
{
struct ospf_neighbor *nbr;
nbr = ospf_nbr_new (oi);
nbr->state = NSM_Down;
- nbr->src = p->u.prefix4;
- memcpy (&nbr->address, p, sizeof (struct prefix));
+ nbr->src = iph->ip_src;
nbr->nbr_nbma = NULL;
if (oi->type == OSPF_IFTYPE_NBMA)
@@ -420,43 +341,30 @@ ospf_nbr_add (struct ospf_interface *oi, struct ospf_header *ospfh,
if (IS_DEBUG_OSPF_EVENT)
zlog_debug ("NSM[%s:%s]: start", IF_NAME (nbr->oi),
inet_ntoa (nbr->router_id));
-
+
+ listnode_add(oi->nbrs, nbr);
return nbr;
}
struct ospf_neighbor *
ospf_nbr_get (struct ospf_interface *oi, struct ospf_header *ospfh,
- struct ip *iph, struct prefix *p)
+ struct ip *iph)
{
- struct route_node *rn;
- struct prefix key;
struct ospf_neighbor *nbr;
-
- key.family = AF_INET;
- key.prefixlen = IPV4_MAX_BITLEN;
- if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
- key.u.prefix4 = ospfh->router_id; /* index vlink nbrs by router-id */
- else
- key.u.prefix4 = iph->ip_src;
-
- rn = route_node_get (oi->nbrs, &key);
- if (rn->info)
+ nbr = ospf_nbr_lookup (oi, iph, ospfh);
+ if (nbr)
{
- route_unlock_node (rn);
- nbr = rn->info;
-
if (oi->type == OSPF_IFTYPE_NBMA && nbr->state == NSM_Attempt)
{
nbr->src = iph->ip_src;
- memcpy (&nbr->address, p, sizeof (struct prefix));
}
}
else
{
- rn->info = nbr = ospf_nbr_add (oi, ospfh, p);
+ nbr = ospf_nbr_add (oi, ospfh, iph);
}
-
+
nbr->router_id = ospfh->router_id;
return nbr;
diff --git a/ospfd/ospf_neighbor.h b/ospfd/ospf_neighbor.h
index 25f13524..70cc7855 100644
--- a/ospfd/ospf_neighbor.h
+++ b/ospfd/ospf_neighbor.h
@@ -37,8 +37,6 @@ struct ospf_neighbor
u_int32_t dd_seqnum; /* DD Sequence Number. */
/* Neighbor Information from Hello. */
- struct prefix address; /* Neighbor Interface Address. */
-
struct in_addr src; /* Src address. */
struct in_addr router_id; /* Router ID. */
u_char options; /* Options. */
@@ -91,8 +89,8 @@ struct ospf_neighbor
};
/* Macros. */
-#define NBR_IS_DR(n) IPV4_ADDR_SAME (&n->address.u.prefix4, &n->d_router)
-#define NBR_IS_BDR(n) IPV4_ADDR_SAME (&n->address.u.prefix4, &n->bd_router)
+#define NBR_IS_DR(n) IPV4_ADDR_SAME (&n->src, &n->d_router)
+#define NBR_IS_BDR(n) IPV4_ADDR_SAME (&n->src, &n->bd_router)
/* Prototypes. */
extern struct ospf_neighbor *ospf_nbr_new (struct ospf_interface *);
@@ -106,14 +104,13 @@ extern int ospf_nbr_count_opaque_capable (struct ospf_interface *);
#endif /* HAVE_OPAQUE_LSA */
extern struct ospf_neighbor *ospf_nbr_get (struct ospf_interface *,
struct ospf_header *,
- struct ip *, struct prefix *);
+ struct ip *);
extern struct ospf_neighbor *ospf_nbr_lookup (struct ospf_interface *,
struct ip *,
struct ospf_header *);
-extern struct ospf_neighbor *ospf_nbr_lookup_by_addr (struct route_table *,
+extern struct ospf_neighbor *ospf_nbr_lookup_by_addr (struct list *,
struct in_addr *);
-extern struct ospf_neighbor *ospf_nbr_lookup_by_routerid (struct route_table
- *,
+extern struct ospf_neighbor *ospf_nbr_lookup_by_routerid (struct list *,
struct in_addr *);
extern void ospf_renegotiate_optional_capabilities (struct ospf *top);
diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c
index 15fff349..26e32d9b 100644
--- a/ospfd/ospf_nsm.c
+++ b/ospfd/ospf_nsm.c
@@ -153,8 +153,8 @@ nsm_should_adj (struct ospf_neighbor *nbr)
|| IPV4_ADDR_SAME (&oi->address->u.prefix4, &DR (oi))
|| IPV4_ADDR_SAME (&oi->address->u.prefix4, &BDR (oi))
/* Neighboring Router is the DRouter or the BDRouter. */
- || IPV4_ADDR_SAME (&nbr->address.u.prefix4, &DR (oi))
- || IPV4_ADDR_SAME (&nbr->address.u.prefix4, &BDR (oi)))
+ || IPV4_ADDR_SAME (&nbr->src, &DR (oi))
+ || IPV4_ADDR_SAME (&nbr->src, &BDR (oi)))
return 1;
return 0;
@@ -162,7 +162,7 @@ nsm_should_adj (struct ospf_neighbor *nbr)
/* OSPF NSM functions. */
static int
-nsm_hello_received (struct ospf_neighbor *nbr)
+nsm_packet_received (struct ospf_neighbor *nbr)
{
/* Start or Restart Inactivity Timer. */
OSPF_NSM_TIMER_OFF (nbr->t_inactivity);
@@ -393,7 +393,7 @@ nsm_kill_nbr (struct ospf_neighbor *nbr)
if (IS_DEBUG_OSPF (nsm, NSM_EVENTS))
zlog_debug ("NSM[%s:%s]: Down (PollIntervalTimer scheduled)",
- IF_NAME (nbr->oi), inet_ntoa (nbr->address.u.prefix4));
+ IF_NAME (nbr->oi), inet_ntoa (nbr->src));
}
return 0;
@@ -408,7 +408,7 @@ struct {
{
/* DependUpon: dummy state. */
{ NULL, NSM_DependUpon }, /* NoEvent */
- { NULL, NSM_DependUpon }, /* HelloReceived */
+ { NULL, NSM_DependUpon }, /* PacketReceived */
{ NULL, NSM_DependUpon }, /* Start */
{ NULL, NSM_DependUpon }, /* 2-WayReceived */
{ NULL, NSM_DependUpon }, /* NegotiationDone */
@@ -425,7 +425,7 @@ struct {
{
/* Deleted: dummy state. */
{ NULL, NSM_Deleted }, /* NoEvent */
- { NULL, NSM_Deleted }, /* HelloReceived */
+ { NULL, NSM_Deleted }, /* PacketReceived */
{ NULL, NSM_Deleted }, /* Start */
{ NULL, NSM_Deleted }, /* 2-WayReceived */
{ NULL, NSM_Deleted }, /* NegotiationDone */
@@ -442,7 +442,7 @@ struct {
{
/* Down: */
{ NULL, NSM_DependUpon }, /* NoEvent */
- { nsm_hello_received, NSM_Init }, /* HelloReceived */
+ { nsm_packet_received, NSM_Init }, /* PacketReceived */
{ nsm_start, NSM_Attempt }, /* Start */
{ NULL, NSM_Down }, /* 2-WayReceived */
{ NULL, NSM_Down }, /* NegotiationDone */
@@ -459,7 +459,7 @@ struct {
{
/* Attempt: */
{ NULL, NSM_DependUpon }, /* NoEvent */
- { nsm_hello_received, NSM_Init }, /* HelloReceived */
+ { nsm_packet_received, NSM_Init }, /* PacketReceived */
{ NULL, NSM_Attempt }, /* Start */
{ NULL, NSM_Attempt }, /* 2-WayReceived */
{ NULL, NSM_Attempt }, /* NegotiationDone */
@@ -476,7 +476,7 @@ struct {
{
/* Init: */
{ NULL, NSM_DependUpon }, /* NoEvent */
- { nsm_hello_received, NSM_Init }, /* HelloReceived */
+ { nsm_packet_received, NSM_Init }, /* PacketReceived */
{ NULL, NSM_Init }, /* Start */
{ nsm_twoway_received, NSM_DependUpon }, /* 2-WayReceived */
{ NULL, NSM_Init }, /* NegotiationDone */
@@ -493,7 +493,7 @@ struct {
{
/* 2-Way: */
{ NULL, NSM_DependUpon }, /* NoEvent */
- { nsm_hello_received, NSM_TwoWay }, /* HelloReceived */
+ { nsm_packet_received, NSM_TwoWay }, /* HelloReceived */
{ NULL, NSM_TwoWay }, /* Start */
{ NULL, NSM_TwoWay }, /* 2-WayReceived */
{ NULL, NSM_TwoWay }, /* NegotiationDone */
@@ -510,7 +510,7 @@ struct {
{
/* ExStart: */
{ NULL, NSM_DependUpon }, /* NoEvent */
- { nsm_hello_received, NSM_ExStart }, /* HelloReceived */
+ { nsm_packet_received, NSM_ExStart }, /* PacaketReceived */
{ NULL, NSM_ExStart }, /* Start */
{ NULL, NSM_ExStart }, /* 2-WayReceived */
{ nsm_negotiation_done, NSM_Exchange }, /* NegotiationDone */
@@ -527,7 +527,7 @@ struct {
{
/* Exchange: */
{ NULL, NSM_DependUpon }, /* NoEvent */
- { nsm_hello_received, NSM_Exchange }, /* HelloReceived */
+ { nsm_packet_received, NSM_Exchange }, /* PacketReceived */
{ NULL, NSM_Exchange }, /* Start */
{ NULL, NSM_Exchange }, /* 2-WayReceived */
{ NULL, NSM_Exchange }, /* NegotiationDone */
@@ -544,7 +544,7 @@ struct {
{
/* Loading: */
{ NULL, NSM_DependUpon }, /* NoEvent */
- { nsm_hello_received, NSM_Loading }, /* HelloReceived */
+ { nsm_packet_received, NSM_Loading }, /* PacketReceived */
{ NULL, NSM_Loading }, /* Start */
{ NULL, NSM_Loading }, /* 2-WayReceived */
{ NULL, NSM_Loading }, /* NegotiationDone */
@@ -560,7 +560,7 @@ struct {
},
{ /* Full: */
{ NULL, NSM_DependUpon }, /* NoEvent */
- { nsm_hello_received, NSM_Full }, /* HelloReceived */
+ { nsm_packet_received, NSM_Full }, /* PacketReceived */
{ NULL, NSM_Full }, /* Start */
{ NULL, NSM_Full }, /* 2-WayReceived */
{ NULL, NSM_Full }, /* NegotiationDone */
@@ -579,7 +579,7 @@ struct {
static const char *ospf_nsm_event_str[] =
{
"NoEvent",
- "HelloReceived",
+ "PacketReceived",
"Start",
"2-WayReceived",
"NegotiationDone",
diff --git a/ospfd/ospf_nsm.h b/ospfd/ospf_nsm.h
index 1121dae6..4f2ae808 100644
--- a/ospfd/ospf_nsm.h
+++ b/ospfd/ospf_nsm.h
@@ -39,7 +39,7 @@
/* OSPF Neighbor State Machine Event. */
#define NSM_NoEvent 0
-#define NSM_HelloReceived 1
+#define NSM_PacketReceived 1 /* HelloReceived in the protocol */
#define NSM_Start 2
#define NSM_TwoWayReceived 3
#define NSM_NegotiationDone 4
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c
index 0b6ac4cb..826d149f 100644
--- a/ospfd/ospf_opaque.c
+++ b/ospfd/ospf_opaque.c
@@ -2118,11 +2118,12 @@ out:
* Followings are control functions to block origination after restart.
*------------------------------------------------------------------------*/
-static void ospf_opaque_exclude_lsa_from_lsreq (struct route_table *nbrs, struct ospf_neighbor *inbr, struct ospf_lsa *lsa);
+static void ospf_opaque_exclude_lsa_from_lsreq (struct list *nbrs,
+ struct ospf_neighbor *inbr, struct ospf_lsa *lsa);
static void ospf_opaque_type9_lsa_rxmt_nbr_check (struct ospf_interface *oi);
static void ospf_opaque_type10_lsa_rxmt_nbr_check (struct ospf_area *area);
static void ospf_opaque_type11_lsa_rxmt_nbr_check (struct ospf *top);
-static unsigned long ospf_opaque_nrxmt_self (struct route_table *nbrs, int lsa_type);
+static unsigned long ospf_opaque_nrxmt_self (struct list *nbrs, int lsa_type);
void
ospf_opaque_adjust_lsreq (struct ospf_neighbor *nbr, struct list *lsas)
@@ -2199,18 +2200,16 @@ out:
}
static void
-ospf_opaque_exclude_lsa_from_lsreq (struct route_table *nbrs,
+ospf_opaque_exclude_lsa_from_lsreq (struct list *nbrs,
struct ospf_neighbor *inbr,
struct ospf_lsa *lsa)
{
- struct route_node *rn;
+ struct listnode *node;
struct ospf_neighbor *onbr;
struct ospf_lsa *ls_req;
- for (rn = route_top (nbrs); rn; rn = route_next (rn))
+ for (ALL_LIST_ELEMENTS_RO (nbrs, node, onbr))
{
- if ((onbr = rn->info) == NULL)
- continue;
if (onbr == inbr)
continue;
if ((ls_req = ospf_ls_request_lookup (onbr, lsa)) == NULL)
@@ -2412,17 +2411,15 @@ out:
}
static unsigned long
-ospf_opaque_nrxmt_self (struct route_table *nbrs, int lsa_type)
+ospf_opaque_nrxmt_self (struct list *nbrs, int lsa_type)
{
- struct route_node *rn;
+ struct listnode *node;
struct ospf_neighbor *nbr;
struct ospf *top;
unsigned long n = 0;
- for (rn = route_top (nbrs); rn; rn = route_next (rn))
+ for (ALL_LIST_ELEMENTS_RO (nbrs, node, nbr))
{
- if ((nbr = rn->info) == NULL)
- continue;
if ((top = oi_to_top (nbr->oi)) == NULL)
continue;
if (IPV4_ADDR_SAME (&nbr->router_id, &top->router_id))
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index 1066e64f..c4a6bf92 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -125,6 +125,20 @@ ospf_fifo_push (struct ospf_fifo *fifo, struct ospf_packet *op)
fifo->count++;
}
+/* Add new packet to head of fifo. */
+static void
+ospf_fifo_push_head (struct ospf_fifo *fifo, struct ospf_packet *op)
+{
+ op->next = fifo->head;
+
+ if (fifo->tail == NULL)
+ fifo->tail = op;
+
+ fifo->head = op;
+
+ fifo->count++;
+}
+
/* Delete first packet from fifo. */
struct ospf_packet *
ospf_fifo_pop (struct ospf_fifo *fifo)
@@ -199,6 +213,27 @@ ospf_packet_add (struct ospf_interface *oi, struct ospf_packet *op)
/* ospf_fifo_debug (oi->obuf); */
}
+static void
+ospf_packet_add_top (struct ospf_interface *oi, struct ospf_packet *op)
+{
+ if (!oi->obuf)
+ {
+ zlog_err("ospf_packet_add(interface %s in state %d [%s], packet type %s, "
+ "destination %s) called with NULL obuf, ignoring "
+ "(please report this bug)!\n",
+ IF_NAME(oi), oi->state, LOOKUP (ospf_ism_state_msg, oi->state),
+ ospf_packet_type_str[stream_getc_from(op->s, 1)],
+ inet_ntoa (op->dst));
+ return;
+ }
+
+ /* Add packet to head of queue. */
+ ospf_fifo_push_head (oi->obuf, op);
+
+ /* Debug of packet fifo*/
+ /* ospf_fifo_debug (oi->obuf); */
+}
+
void
ospf_packet_delete (struct ospf_interface *oi)
{
@@ -324,7 +359,7 @@ static int
ospf_make_md5_digest (struct ospf_interface *oi, struct ospf_packet *op)
{
struct ospf_header *ospfh;
- unsigned char digest[OSPF_AUTH_MD5_SIZE];
+ unsigned char digest[OSPF_AUTH_MD5_SIZE] = {0};
MD5_CTX ctx;
void *ibuf;
u_int32_t t;
@@ -351,7 +386,7 @@ ospf_make_md5_digest (struct ospf_interface *oi, struct ospf_packet *op)
/* Get MD5 Authentication key from auth_key list. */
if (list_isempty (OSPF_IF_PARAM (oi, auth_crypt)))
- auth_key = (const u_int8_t *) "";
+ auth_key = (const u_int8_t *) digest;
else
{
ck = listgetdata (listtail(OSPF_IF_PARAM (oi, auth_crypt)));
@@ -745,7 +780,6 @@ ospf_hello (struct ip *iph, struct ospf_header *ospfh,
struct ospf_hello *hello;
struct ospf_neighbor *nbr;
int old_state;
- struct prefix p;
/* increment statistics. */
oi->hello_in++;
@@ -765,20 +799,15 @@ ospf_hello (struct ip *iph, struct ospf_header *ospfh,
return;
}
- /* get neighbor prefix. */
- p.family = AF_INET;
- p.prefixlen = ip_masklen (hello->network_mask);
- p.u.prefix4 = iph->ip_src;
-
/* Compare network mask. */
/* Checking is ignored for Point-to-Point and Virtual link. */
if (oi->type != OSPF_IFTYPE_POINTOPOINT
&& oi->type != OSPF_IFTYPE_VIRTUALLINK)
- if (oi->address->prefixlen != p.prefixlen)
+ if (oi->address->prefixlen != ip_masklen (hello->network_mask))
{
zlog_warn ("Packet %s [Hello:RECV]: NetworkMask mismatch on %s (configured prefix length is %d, but hello packet indicates %d).",
inet_ntoa(ospfh->router_id), IF_NAME(oi),
- (int)oi->address->prefixlen, (int)p.prefixlen);
+ (int)oi->address->prefixlen, (int)ip_masklen (hello->network_mask));
return;
}
@@ -873,7 +902,7 @@ ospf_hello (struct ip *iph, struct ospf_header *ospfh,
}
/* get neighbour struct */
- nbr = ospf_nbr_get (oi, ospfh, iph, &p);
+ nbr = ospf_nbr_get (oi, ospfh, iph);
/* neighbour must be valid, ospf_nbr_get creates if none existed */
assert (nbr);
@@ -881,7 +910,7 @@ ospf_hello (struct ip *iph, struct ospf_header *ospfh,
old_state = nbr->state;
/* Add event to thread. */
- OSPF_NSM_EVENT_EXECUTE (nbr, NSM_HelloReceived);
+ OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived);
/* RFC2328 Section 9.5.1
If the router is not eligible to become Designated Router,
@@ -901,58 +930,63 @@ ospf_hello (struct ip *iph, struct ospf_header *ospfh,
if (oi->type == OSPF_IFTYPE_NBMA &&
(old_state == NSM_Down || old_state == NSM_Attempt))
{
- OSPF_NSM_EVENT_EXECUTE (nbr, NSM_OneWayReceived);
- nbr->priority = hello->priority;
- nbr->d_router = hello->d_router;
- nbr->bd_router = hello->bd_router;
- return;
+ OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_OneWayReceived);
+ goto done;
}
if (ospf_nbr_bidirectional (&oi->ospf->router_id, hello->neighbors,
size - OSPF_HELLO_MIN_SIZE))
{
- OSPF_NSM_EVENT_EXECUTE (nbr, NSM_TwoWayReceived);
+ OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_TwoWayReceived);
nbr->options |= hello->options;
}
else
{
- OSPF_NSM_EVENT_EXECUTE (nbr, NSM_OneWayReceived);
- /* Set neighbor information. */
- nbr->priority = hello->priority;
- nbr->d_router = hello->d_router;
- nbr->bd_router = hello->bd_router;
- return;
+ OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_OneWayReceived);
+ goto done;
}
+ /* Neighbor priority check. */
+ if (nbr->priority >= 0 && nbr->priority != hello->priority)
+ OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange);
+
/* If neighbor itself declares DR and no BDR exists,
cause event BackupSeen */
- if (IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->d_router))
- if (hello->bd_router.s_addr == 0 && oi->state == ISM_Waiting)
+ if (oi->state == ISM_Waiting &&
+ IPV4_ADDR_SAME (&nbr->src, &hello->d_router) &&
+ hello->bd_router.s_addr == 0)
+ {
OSPF_ISM_EVENT_SCHEDULE (oi, ISM_BackupSeen);
+ goto done;
+ }
+ /* had not previously. */
+ if ((IPV4_ADDR_SAME (&nbr->src, &hello->d_router) &&
+ IPV4_ADDR_CMP (&nbr->src, &nbr->d_router)) ||
+ (IPV4_ADDR_CMP (&nbr->src, &hello->d_router) &&
+ IPV4_ADDR_SAME (&nbr->src, &nbr->d_router)))
+ {
+ OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange);
+ goto done;
+ }
/* neighbor itself declares BDR. */
if (oi->state == ISM_Waiting &&
- IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->bd_router))
- OSPF_ISM_EVENT_SCHEDULE (oi, ISM_BackupSeen);
-
- /* had not previously. */
- if ((IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->d_router) &&
- IPV4_ADDR_CMP (&nbr->address.u.prefix4, &nbr->d_router)) ||
- (IPV4_ADDR_CMP (&nbr->address.u.prefix4, &hello->d_router) &&
- IPV4_ADDR_SAME (&nbr->address.u.prefix4, &nbr->d_router)))
- OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange);
-
+ IPV4_ADDR_SAME (&nbr->src, &hello->bd_router))
+ {
+ OSPF_ISM_EVENT_SCHEDULE (oi, ISM_BackupSeen);
+ goto done;
+ }
/* had not previously. */
- if ((IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->bd_router) &&
- IPV4_ADDR_CMP (&nbr->address.u.prefix4, &nbr->bd_router)) ||
- (IPV4_ADDR_CMP (&nbr->address.u.prefix4, &hello->bd_router) &&
- IPV4_ADDR_SAME (&nbr->address.u.prefix4, &nbr->bd_router)))
- OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange);
-
- /* Neighbor priority check. */
- if (nbr->priority >= 0 && nbr->priority != hello->priority)
- OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange);
+ if ((IPV4_ADDR_SAME (&nbr->src, &hello->bd_router) &&
+ IPV4_ADDR_CMP (&nbr->src, &nbr->bd_router)) ||
+ (IPV4_ADDR_CMP (&nbr->src, &hello->bd_router) &&
+ IPV4_ADDR_SAME (&nbr->src, &nbr->bd_router)))
+ {
+ OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange);
+ goto done;
+ }
+ done:
/* Set neighbor information. */
nbr->priority = hello->priority;
nbr->d_router = hello->d_router;
@@ -1191,6 +1225,9 @@ ospf_db_desc (struct ip *iph, struct ospf_header *ospfh,
}
#endif /* HAVE_OPAQUE_LSA */
+ /* Add event to thread. */
+ OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived);
+
/* Process DD packet by neighbor status. */
switch (nbr->state)
{
@@ -1260,7 +1297,7 @@ ospf_db_desc (struct ip *iph, struct ospf_header *ospfh,
CHECK_FLAG (nbr->options, OSPF_OPTION_O) ? "" : "NOT ");
if (! CHECK_FLAG (nbr->options, OSPF_OPTION_O)
- && IPV4_ADDR_SAME (&DR (oi), &nbr->address.u.prefix4))
+ && IPV4_ADDR_SAME (&DR (oi), &nbr->src))
{
zlog_warn ("DR-neighbor[%s] is NOT opaque-capable; "
"Opaque-LSAs cannot be reliably advertised "
@@ -1412,6 +1449,9 @@ ospf_ls_req (struct ip *iph, struct ospf_header *ospfh,
return;
}
+ /* Add event to thread. */
+ OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived);
+
/* Neighbor State should be Exchange or later. */
if (nbr->state != NSM_Exchange &&
nbr->state != NSM_Loading &&
@@ -1644,6 +1684,9 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh,
return;
}
+ /* Add event to thread. */
+ OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived);
+
/* Check neighbor state. */
if (nbr->state < NSM_Exchange)
{
@@ -1977,6 +2020,9 @@ ospf_ls_ack (struct ip *iph, struct ospf_header *ospfh,
return;
}
+ /* Add event to thread. */
+ OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived);
+
if (nbr->state < NSM_Exchange)
{
zlog_warn ("Link State Acknowledgment: "
@@ -2620,7 +2666,7 @@ static int
ospf_make_hello (struct ospf_interface *oi, struct stream *s)
{
struct ospf_neighbor *nbr;
- struct route_node *rn;
+ struct listnode *node;
u_int16_t length = OSPF_HELLO_MIN_SIZE;
struct in_addr mask;
unsigned long p;
@@ -2662,10 +2708,9 @@ ospf_make_hello (struct ospf_interface *oi, struct stream *s)
stream_put_ipv4 (s, BDR (oi).s_addr);
/* Add neighbor seen. */
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info))
- if (nbr->router_id.s_addr != 0) /* Ignore 0.0.0.0 node. */
- if (nbr->state != NSM_Attempt) /* Ignore Down neighbor. */
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, node, nbr))
+ if (nbr->router_id.s_addr != 0) /* Ignore 0.0.0.0 node. */
+ if (nbr->state != NSM_Attempt) /* Ignore Down neighbor. */
if (nbr->state != NSM_Down) /* This is myself for DR election. */
if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id))
{
@@ -2955,8 +3000,8 @@ ospf_make_ls_ack (struct ospf_interface *oi, struct list *ack, struct stream *s)
return length;
}
-void
-ospf_hello_send_sub (struct ospf_interface *oi, struct in_addr *addr)
+static void
+ospf_hello_send_sub (struct ospf_interface *oi, in_addr_t addr)
{
struct ospf_packet *op;
u_int16_t length = OSPF_HEADER_SIZE;
@@ -2975,10 +3020,12 @@ ospf_hello_send_sub (struct ospf_interface *oi, struct in_addr *addr)
/* Set packet length. */
op->length = length;
- op->dst.s_addr = addr->s_addr;
+ op->dst.s_addr = addr;
- /* Add packet to the interface output queue. */
- ospf_packet_add (oi, op);
+ /* Add packet to the top of the interface output queue, so that they
+ * can't get delayed by things like long queues of LS Update packets
+ */
+ ospf_packet_add_top (oi, op);
/* Hook thread to write packet. */
OSPF_ISM_WRITE_ON (oi->ospf);
@@ -3009,7 +3056,7 @@ ospf_poll_send (struct ospf_nbr_nbma *nbr_nbma)
&& oi->state != ISM_DR && oi->state != ISM_Backup)
return;
- ospf_hello_send_sub (oi, &nbr_nbma->addr);
+ ospf_hello_send_sub (oi, nbr_nbma->addr.s_addr);
}
int
@@ -3048,7 +3095,7 @@ ospf_hello_reply_timer (struct thread *thread)
zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (hello-reply timer expire)",
IF_NAME (nbr->oi), inet_ntoa (nbr->router_id));
- ospf_hello_send_sub (nbr->oi, &nbr->address.u.prefix4);
+ ospf_hello_send_sub (nbr->oi, nbr->src.s_addr);
return 0;
}
@@ -3057,84 +3104,48 @@ ospf_hello_reply_timer (struct thread *thread)
void
ospf_hello_send (struct ospf_interface *oi)
{
- struct ospf_packet *op;
- u_int16_t length = OSPF_HEADER_SIZE;
-
/* If this is passive interface, do not send OSPF Hello. */
if (OSPF_IF_PASSIVE_STATUS (oi) == OSPF_IF_PASSIVE)
return;
- op = ospf_packet_new (oi->ifp->mtu);
-
- /* Prepare OSPF common header. */
- ospf_make_header (OSPF_MSG_HELLO, oi, op->s);
-
- /* Prepare OSPF Hello body. */
- length += ospf_make_hello (oi, op->s);
-
- /* Fill OSPF header. */
- ospf_fill_header (oi, op->s, length);
-
- /* Set packet length. */
- op->length = length;
-
if (oi->type == OSPF_IFTYPE_NBMA)
{
struct ospf_neighbor *nbr;
- struct route_node *rn;
-
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info))
- if (nbr != oi->nbr_self)
- if (nbr->state != NSM_Down)
- {
- /* RFC 2328 Section 9.5.1
- If the router is not eligible to become Designated Router,
- it must periodically send Hello Packets to both the
- Designated Router and the Backup Designated Router (if they
- exist). */
- if (PRIORITY(oi) == 0 &&
- IPV4_ADDR_CMP(&DR(oi), &nbr->address.u.prefix4) &&
- IPV4_ADDR_CMP(&BDR(oi), &nbr->address.u.prefix4))
- continue;
-
- /* If the router is eligible to become Designated Router, it
- must periodically send Hello Packets to all neighbors that
- are also eligible. In addition, if the router is itself the
- Designated Router or Backup Designated Router, it must also
- send periodic Hello Packets to all other neighbors. */
-
- if (nbr->priority == 0 && oi->state == ISM_DROther)
- continue;
- /* if oi->state == Waiting, send hello to all neighbors */
- {
- struct ospf_packet *op_dup;
-
- op_dup = ospf_packet_dup(op);
- op_dup->dst = nbr->address.u.prefix4;
+ struct listnode *node;
- /* Add packet to the interface output queue. */
- ospf_packet_add (oi, op_dup);
-
- OSPF_ISM_WRITE_ON (oi->ospf);
- }
-
- }
- ospf_packet_free (op);
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, node, nbr))
+ if (nbr != oi->nbr_self)
+ if (nbr->state != NSM_Down)
+ {
+ /* RFC 2328 Section 9.5.1
+ If the router is not eligible to become Designated Router,
+ it must periodically send Hello Packets to both the
+ Designated Router and the Backup Designated Router (if they
+ exist). */
+ if (PRIORITY(oi) == 0 &&
+ IPV4_ADDR_CMP(&DR(oi), &nbr->src) &&
+ IPV4_ADDR_CMP(&BDR(oi), &nbr->src))
+ continue;
+
+ /* If the router is eligible to become Designated Router, it
+ must periodically send Hello Packets to all neighbors that
+ are also eligible. In addition, if the router is itself the
+ Designated Router or Backup Designated Router, it must also
+ send periodic Hello Packets to all other neighbors. */
+
+ if (nbr->priority == 0 && oi->state == ISM_DROther)
+ continue;
+ /* if oi->state == Waiting, send hello to all neighbors */
+ ospf_hello_send_sub (oi, nbr->src.s_addr);
+ }
}
else
{
/* Decide destination address. */
if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
- op->dst.s_addr = oi->vl_data->peer_addr.s_addr;
- else
- op->dst.s_addr = htonl (OSPF_ALLSPFROUTERS);
-
- /* Add packet to the interface output queue. */
- ospf_packet_add (oi, op);
-
- /* Hook thread to write packet. */
- OSPF_ISM_WRITE_ON (oi->ospf);
+ ospf_hello_send_sub (oi, oi->vl_data->peer_addr.s_addr);
+ else
+ ospf_hello_send_sub (oi, htonl (OSPF_ALLSPFROUTERS));
}
}
@@ -3165,7 +3176,7 @@ ospf_db_desc_send (struct ospf_neighbor *nbr)
if (oi->type == OSPF_IFTYPE_POINTOPOINT)
op->dst.s_addr = htonl (OSPF_ALLSPFROUTERS);
else
- op->dst = nbr->address.u.prefix4;
+ op->dst = nbr->src;
/* Add packet to the interface output queue. */
ospf_packet_add (oi, op);
@@ -3227,7 +3238,7 @@ ospf_ls_req_send (struct ospf_neighbor *nbr)
if (oi->type == OSPF_IFTYPE_POINTOPOINT)
op->dst.s_addr = htonl (OSPF_ALLSPFROUTERS);
else
- op->dst = nbr->address.u.prefix4;
+ op->dst = nbr->src;
/* Add packet to the interface output queue. */
ospf_packet_add (oi, op);
@@ -3434,7 +3445,7 @@ ospf_ls_upd_send (struct ospf_neighbor *nbr, struct list *update, int flag)
else if (oi->type == OSPF_IFTYPE_POINTOPOINT)
p.prefix.s_addr = htonl (OSPF_ALLSPFROUTERS);
else if (flag == OSPF_SEND_PACKET_DIRECT)
- p.prefix = nbr->address.u.prefix4;
+ p.prefix = nbr->src;
else if (oi->state == ISM_DR || oi->state == ISM_Backup)
p.prefix.s_addr = htonl (OSPF_ALLSPFROUTERS);
else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT)
@@ -3514,7 +3525,7 @@ ospf_ls_ack_send (struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
struct ospf_interface *oi = nbr->oi;
if (listcount (oi->ls_ack_direct.ls_ack) == 0)
- oi->ls_ack_direct.dst = nbr->address.u.prefix4;
+ oi->ls_ack_direct.dst = nbr->src;
listnode_add (oi->ls_ack_direct.ls_ack, ospf_lsa_lock (lsa));
@@ -3537,13 +3548,12 @@ ospf_ls_ack_send_delayed (struct ospf_interface *oi)
if (oi->type == OSPF_IFTYPE_NBMA)
{
struct ospf_neighbor *nbr;
- struct route_node *rn;
+ struct listnode *node;
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info) != NULL)
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, node, nbr))
if (nbr != oi->nbr_self && nbr->state >= NSM_Exchange)
while (listcount (oi->ls_ack))
- ospf_ls_ack_send_list (oi, oi->ls_ack, nbr->address.u.prefix4);
+ ospf_ls_ack_send_list (oi, oi->ls_ack, nbr->src);
return;
}
if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
diff --git a/ospfd/ospf_packet.h b/ospfd/ospf_packet.h
index 7b3d6866..9a472081 100644
--- a/ospfd/ospf_packet.h
+++ b/ospfd/ospf_packet.h
@@ -162,6 +162,5 @@ extern int ospf_ls_upd_timer (struct thread *);
extern int ospf_ls_ack_timer (struct thread *);
extern int ospf_poll_timer (struct thread *);
extern int ospf_hello_reply_timer (struct thread *);
-extern void ospf_hello_send_sub (struct ospf_interface *, struct in_addr *);
#endif /* _ZEBRA_OSPF_PACKET_H */
diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c
index 267237b8..10608f50 100644
--- a/ospfd/ospf_route.c
+++ b/ospfd/ospf_route.c
@@ -272,61 +272,6 @@ ospf_route_install (struct ospf *ospf, struct route_table *rt)
}
}
-static void
-ospf_intra_route_add (struct route_table *rt, struct vertex *v,
- struct ospf_area *area)
-{
- struct route_node *rn;
- struct ospf_route *or;
- struct prefix_ipv4 p;
- struct ospf_path *path;
- struct vertex_parent *parent;
- struct listnode *node, *nnode;
-
- p.family = AF_INET;
- p.prefix = v->id;
- if (v->type == OSPF_VERTEX_ROUTER)
- p.prefixlen = IPV4_MAX_BITLEN;
- else
- {
- struct network_lsa *lsa = (struct network_lsa *) v->lsa;
- p.prefixlen = ip_masklen (lsa->mask);
- }
- apply_mask_ipv4 (&p);
-
- rn = route_node_get (rt, (struct prefix *) &p);
- if (rn->info)
- {
- zlog_warn ("Same routing information exists for %s", inet_ntoa (v->id));
- route_unlock_node (rn);
- return;
- }
-
- or = ospf_route_new ();
-
- if (v->type == OSPF_VERTEX_NETWORK)
- {
- or->type = OSPF_DESTINATION_NETWORK;
-
- for (ALL_LIST_ELEMENTS (v->parents, node, nnode, parent))
- {
- path = ospf_path_new ();
- path->nexthop = parent->nexthop->router;
- listnode_add (or->paths, path);
- }
- }
- else
- or->type = OSPF_DESTINATION_ROUTER;
-
- or->id = v->id;
- or->u.std.area_id = area->area_id;
- or->u.std.external_routing= area->external_routing;
- or->path_type = OSPF_PATH_INTRA_AREA;
- or->cost = v->distance;
-
- rn->info = or;
-}
-
/* RFC2328 16.1. (4). For "router". */
void
ospf_intra_add_router (struct route_table *rt, struct vertex *v,
@@ -810,12 +755,18 @@ ospf_route_copy_nexthops_from_vertex (struct ospf_route *to,
{
nexthop = vp->nexthop;
- if (nexthop->oi != NULL)
+ if (nexthop->oi != NULL)
{
- if (! ospf_path_exist (to->paths, nexthop->router, nexthop->oi))
+ if (!ospf_path_exist (to->paths, nexthop->router, nexthop->oi))
{
path = ospf_path_new ();
- path->nexthop = nexthop->router;
+
+ /* PtoP I/F's are always directly connected */
+ if (if_is_pointopoint (nexthop->oi->ifp))
+ path->nexthop.s_addr = INADDR_ANY;
+ else
+ path->nexthop = nexthop->router;
+
path->ifindex = nexthop->oi->ifp->ifindex;
listnode_add (to->paths, path);
}
diff --git a/ospfd/ospf_route.h b/ospfd/ospf_route.h
index 17ab68e5..5483aaf5 100644
--- a/ospfd/ospf_route.h
+++ b/ospfd/ospf_route.h
@@ -121,6 +121,12 @@ struct ospf_route
} u;
};
+struct ospf_host_route {
+ struct in_addr host_addr;
+ u_int32_t cost; /* i.e. metric. */
+ struct ospf_area *area; /* NULL == all areas */
+};
+
extern struct ospf_path *ospf_path_new (void);
extern void ospf_path_free (struct ospf_path *);
extern struct ospf_path *ospf_path_lookup (struct list *, struct ospf_path *);
diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c
index cc4974ce..84b169f4 100644
--- a/ospfd/ospf_snmp.c
+++ b/ospfd/ospf_snmp.c
@@ -2105,16 +2105,15 @@ static struct ospf_neighbor *
ospf_snmp_nbr_lookup (struct ospf *ospf, struct in_addr *nbr_addr,
unsigned int *ifindex)
{
- struct listnode *node, *nnode;
+ struct listnode *node, *nnode, *nbrnode;
struct ospf_interface *oi;
struct ospf_neighbor *nbr;
struct route_node *rn;
for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi))
{
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info) != NULL
- && nbr != oi->nbr_self
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, nbrnode, nbr))
+ if (nbr != oi->nbr_self
/* If EXACT match is needed, provide ALL entry found
&& nbr->state != NSM_Down
*/
@@ -2134,10 +2133,9 @@ static struct ospf_neighbor *
ospf_snmp_nbr_lookup_next (struct in_addr *nbr_addr, unsigned int *ifindex,
int first)
{
- struct listnode *nn;
+ struct listnode *nn, *nbrnode;
struct ospf_interface *oi;
struct ospf_neighbor *nbr;
- struct route_node *rn;
struct ospf_neighbor *min = NULL;
struct ospf *ospf = ospf;
@@ -2145,9 +2143,8 @@ ospf_snmp_nbr_lookup_next (struct in_addr *nbr_addr, unsigned int *ifindex,
for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, nn, oi))
{
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info) != NULL
- && nbr != oi->nbr_self
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, nbrnode, nbr))
+ if (nbr != oi->nbr_self
&& nbr->state != NSM_Down
&& nbr->src.s_addr != 0)
{
@@ -2613,9 +2610,9 @@ ospfTrapNbrStateChange (struct ospf_neighbor *on)
ospf_nbr_state_message(on, msgbuf, sizeof(msgbuf));
zlog (NULL, LOG_INFO, "ospfTrapNbrStateChange trap sent: %s now %s",
- inet_ntoa(on->address.u.prefix4), msgbuf);
+ inet_ntoa(on->router_id), msgbuf);
- oid_copy_addr (index, &(on->address.u.prefix4), IN_ADDR_SIZE);
+ oid_copy_addr (index, &(on->router_id), IN_ADDR_SIZE);
index[IN_ADDR_SIZE] = 0;
smux_trap (ospf_oid, sizeof ospf_oid / sizeof (oid),
@@ -2632,7 +2629,7 @@ ospfTrapVirtNbrStateChange (struct ospf_neighbor *on)
zlog (NULL, LOG_INFO, "ospfTrapVirtNbrStateChange trap sent");
- oid_copy_addr (index, &(on->address.u.prefix4), IN_ADDR_SIZE);
+ oid_copy_addr (index, &(on->router_id), IN_ADDR_SIZE);
index[IN_ADDR_SIZE] = 0;
smux_trap (ospf_oid, sizeof ospf_oid / sizeof (oid),
diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c
index ca200222..b06c7b9e 100644
--- a/ospfd/ospf_spf.c
+++ b/ospfd/ospf_spf.c
@@ -480,13 +480,15 @@ ospf_spf_add_parent (struct vertex *v, struct vertex *w,
static unsigned int
ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v,
struct vertex *w, struct router_lsa_link *l,
- unsigned int distance)
+ unsigned int distance, int lsa_pos)
{
struct listnode *node, *nnode;
struct vertex_nexthop *nh;
struct vertex_parent *vp;
struct ospf_interface *oi = NULL;
unsigned int added = 0;
+ char buf1[BUFSIZ];
+ char buf2[BUFSIZ];
if (IS_DEBUG_OSPF_EVENT)
{
@@ -505,30 +507,38 @@ ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v,
the OSPF interface connecting to the destination network/router.
*/
+ /* we *must* be supplied with the link data */
+ assert (l != NULL);
+ oi = ospf_if_lookup_by_lsa_pos (area, lsa_pos);
+ if (!oi)
+ {
+ zlog_debug("%s: OI not found in LSA: lsa_pos:%d link_id:%s link_data:%s",
+ __func__, lsa_pos,
+ inet_ntop (AF_INET, &l->link_id, buf1, BUFSIZ),
+ inet_ntop (AF_INET, &l->link_data, buf2, BUFSIZ));
+ return 0;
+ }
+
+ if (IS_DEBUG_OSPF_EVENT)
+ {
+ zlog_debug("%s: considering link:%s "
+ "type:%d link_id:%s link_data:%s",
+ __func__, oi->ifp->name, l->m[0].type,
+ inet_ntop (AF_INET, &l->link_id, buf1, BUFSIZ),
+ inet_ntop (AF_INET, &l->link_data, buf2, BUFSIZ));
+ }
+
if (w->type == OSPF_VERTEX_ROUTER)
{
/* l is a link from v to w
* l2 will be link from w to v
*/
struct router_lsa_link *l2 = NULL;
-
- /* we *must* be supplied with the link data */
- assert (l != NULL);
-
- if (IS_DEBUG_OSPF_EVENT)
- {
- char buf1[BUFSIZ];
- char buf2[BUFSIZ];
-
- zlog_debug("ospf_nexthop_calculation(): considering link "
- "type %d link_id %s link_data %s",
- l->m[0].type,
- inet_ntop (AF_INET, &l->link_id, buf1, BUFSIZ),
- inet_ntop (AF_INET, &l->link_data, buf2, BUFSIZ));
- }
if (l->m[0].type == LSA_LINK_TYPE_POINTOPOINT)
{
+ struct in_addr nexthop;
+
/* If the destination is a router which connects to
the calculating router via a Point-to-MultiPoint
network, the destination's next hop IP address(es)
@@ -539,68 +549,53 @@ ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v,
provides an IP address of the next hop router.
At this point l is a link from V to W, and V is the
- root ("us"). Find the local interface associated
- with l (its address is in l->link_data). If it
- is a point-to-multipoint interface, then look through
- the links in the opposite direction (W to V). If
- any of them have an address that lands within the
+ root ("us"). If it is a point-to-multipoint interface,
+ then look through the links in the opposite direction (W to V).
+ If any of them have an address that lands within the
subnet declared by the PtMP link, then that link
- is a constituent of the PtMP link, and its address is
+ is a constituent of the PtMP link, and its address is
a nexthop address for V.
*/
- oi = ospf_if_is_configured (area->ospf, &l->link_data);
- if (oi && oi->type == OSPF_IFTYPE_POINTOMULTIPOINT)
- {
- struct prefix_ipv4 la;
-
- la.family = AF_INET;
- la.prefixlen = oi->address->prefixlen;
-
- /* V links to W on PtMP interface
- - find the interface address on W */
- while ((l2 = ospf_get_next_link (w, v, l2)))
- {
- la.prefix = l2->link_data;
-
- if (prefix_cmp ((struct prefix *) &la,
- oi->address) == 0)
- /* link_data is on our PtMP network */
- break;
- }
- } /* end l is on point-to-multipoint link */
- else
- {
- /* l is a regular point-to-point link.
- Look for a link from W to V.
- */
- while ((l2 = ospf_get_next_link (w, v, l2)))
- {
- oi = ospf_if_is_configured (area->ospf,
- &(l2->link_data));
-
- if (oi == NULL)
- continue;
-
- if (!IPV4_ADDR_SAME (&oi->address->u.prefix4,
- &l->link_data))
- continue;
-
- break;
- }
- }
-
- if (oi && l2)
+ if (oi->type == OSPF_IFTYPE_POINTOPOINT)
+ {
+ added = 1;
+ nexthop.s_addr = 0; /* Nexthop not required */
+ }
+ else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT)
+ {
+ struct prefix_ipv4 la;
+
+ la.family = AF_INET;
+ la.prefixlen = oi->address->prefixlen;
+
+ /* V links to W on PtMP interface
+ - find the interface address on W */
+ while ((l2 = ospf_get_next_link (w, v, l2)))
+ {
+ la.prefix = l2->link_data;
+
+ if (prefix_cmp ((struct prefix *) &la,
+ oi->address) != 0)
+ continue;
+ /* link_data is on our PtMP network */
+ added = 1;
+ nexthop = l2->link_data;
+ break;
+ }
+ }
+
+ if (added)
{
/* found all necessary info to build nexthop */
nh = vertex_nexthop_new ();
nh->oi = oi;
- nh->router = l2->link_data;
+ nh->router = nexthop;
ospf_spf_add_parent (v, w, nh, distance);
return 1;
}
else
- zlog_info("ospf_nexthop_calculation(): "
- "could not determine nexthop for link");
+ zlog_info("%s: could not determine nexthop for link %s",
+ __func__, oi->ifp->name);
} /* end point-to-point link from V to W */
else if (l->m[0].type == LSA_LINK_TYPE_VIRTUALLINK)
{
@@ -633,19 +628,13 @@ ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v,
else
{
assert(w->type == OSPF_VERTEX_NETWORK);
- oi = ospf_if_is_configured (area->ospf, &(l->link_data));
- if (oi)
- {
- nh = vertex_nexthop_new ();
- nh->oi = oi;
- nh->router.s_addr = 0;
- ospf_spf_add_parent (v, w, nh, distance);
- return 1;
- }
+
+ nh = vertex_nexthop_new ();
+ nh->oi = oi;
+ nh->router.s_addr = 0; /* Nexthop not required */
+ ospf_spf_add_parent (v, w, nh, distance);
+ return 1;
}
- zlog_info("ospf_nexthop_calculation(): "
- "Unknown attached link");
- return 0;
} /* end V is the root */
/* Check if W's parent is a network connected to root. */
else if (v->type == OSPF_VERTEX_NETWORK)
@@ -726,7 +715,7 @@ ospf_spf_next (struct vertex *v, struct ospf_area *area,
u_char *lim;
struct router_lsa_link *l = NULL;
struct in_addr *r;
- int type = 0;
+ int type = 0, lsa_pos=-1, lsa_pos_next=0;
/* If this is a router-LSA, and bit V of the router-LSA (see Section
A.4.2:RFC2328) is set, set Area A's TransitCapability to TRUE. */
@@ -754,7 +743,8 @@ ospf_spf_next (struct vertex *v, struct ospf_area *area,
if (v->lsa->type == OSPF_ROUTER_LSA)
{
l = (struct router_lsa_link *) p;
-
+ lsa_pos = lsa_pos_next; /* LSA link position */
+ lsa_pos_next++;
p += (ROUTER_LSA_MIN_SIZE +
(l->m[0].tos_count * ROUTER_LSA_TOS_SIZE));
@@ -876,7 +866,7 @@ ospf_spf_next (struct vertex *v, struct ospf_area *area,
w = ospf_vertex_new (w_lsa);
/* Calculate nexthop to W. */
- if (ospf_nexthop_calculation (area, v, w, l, distance))
+ if (ospf_nexthop_calculation (area, v, w, l, distance, lsa_pos))
pqueue_enqueue (w, candidate);
else if (IS_DEBUG_OSPF_EVENT)
zlog_debug ("Nexthop Calc failed");
@@ -896,7 +886,7 @@ ospf_spf_next (struct vertex *v, struct ospf_area *area,
{
/* Found an equal-cost path to W.
* Calculate nexthop of to W from V. */
- ospf_nexthop_calculation (area, v, w, l, distance);
+ ospf_nexthop_calculation (area, v, w, l, distance, lsa_pos);
}
/* less than. */
else
@@ -906,7 +896,7 @@ ospf_spf_next (struct vertex *v, struct ospf_area *area,
* valid nexthop it will call spf_add_parents, which
* will flush the old parents
*/
- if (ospf_nexthop_calculation (area, v, w, l, distance))
+ if (ospf_nexthop_calculation (area, v, w, l, distance, lsa_pos))
/* Decrease the key of the node in the heap.
* trickle-sort it up towards root, just in case this
* node should now be the new root due the cost change.
@@ -1045,6 +1035,7 @@ ospf_rtrs_free (struct route_table *rtrs)
route_table_finish (rtrs);
}
+#if 0
static void
ospf_rtrs_print (struct route_table *rtrs)
{
@@ -1104,6 +1095,7 @@ ospf_rtrs_print (struct route_table *rtrs)
zlog_debug ("ospf_rtrs_print() end");
}
+#endif
/* Calculating the shortest-path tree for an area. */
static void
@@ -1269,9 +1261,6 @@ ospf_spf_calculate_timer (struct thread *thread)
/* If new Router Route is installed,
then schedule re-calculate External routes. */
- if (1)
- ospf_ase_calculate_schedule (ospf);
-
ospf_ase_calculate_timer_add (ospf);
/* Update routing table. */
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index c81d18d6..434dae2a 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -2839,6 +2839,9 @@ show_ip_ospf_interface_sub (struct vty *vty, struct ospf *ospf,
vty_out (vty, " Internet Address %s/%d,",
inet_ntoa (oi->address->u.prefix4), oi->address->prefixlen);
+ vty_out(vty, " Unnumbered: %s,",
+ CHECK_FLAG(oi->ifp->status, ZEBRA_INTERFACE_UNNUMBERED) ? "YES" : "NO");
+
if (oi->connected->destination || oi->type == OSPF_IFTYPE_VIRTUALLINK)
{
struct in_addr *dest;
@@ -2888,7 +2891,7 @@ show_ip_ospf_interface_sub (struct vty *vty, struct ospf *ospf,
vty_out (vty, " Designated Router (ID) %s,",
inet_ntoa (nbr->router_id));
vty_out (vty, " Interface Address %s%s",
- inet_ntoa (nbr->address.u.prefix4), VTY_NEWLINE);
+ inet_ntoa (nbr->src), VTY_NEWLINE);
}
}
@@ -2907,7 +2910,7 @@ show_ip_ospf_interface_sub (struct vty *vty, struct ospf *ospf,
vty_out (vty, " Backup Designated Router (ID) %s,",
inet_ntoa (nbr->router_id));
vty_out (vty, " Interface Address %s%s",
- inet_ntoa (nbr->address.u.prefix4), VTY_NEWLINE);
+ inet_ntoa (nbr->src), VTY_NEWLINE);
}
}
@@ -3001,39 +3004,37 @@ show_ip_ospf_neighbour_header (struct vty *vty)
static void
show_ip_ospf_neighbor_sub (struct vty *vty, struct ospf_interface *oi)
{
- struct route_node *rn;
+ struct listnode *node;
struct ospf_neighbor *nbr;
char msgbuf[16];
char timebuf[OSPF_TIME_DUMP_SIZE];
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info))
- /* Do not show myself. */
- if (nbr != oi->nbr_self)
- /* Down state is not shown. */
- if (nbr->state != NSM_Down)
- {
- ospf_nbr_state_message (nbr, msgbuf, 16);
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, node, nbr))
+ /* Do not show myself. */
+ if (nbr != oi->nbr_self)
+ /* Down state is not shown. */
+ if (nbr->state != NSM_Down)
+ {
+ ospf_nbr_state_message (nbr, msgbuf, 16);
+ if (nbr->state == NSM_Attempt && nbr->router_id.s_addr == 0)
+ vty_out (vty, "%-15s %3d %-15s ",
+ "-", nbr->priority,
+ msgbuf);
+ else
+ vty_out (vty, "%-15s %3d %-15s ",
+ inet_ntoa (nbr->router_id), nbr->priority,
+ msgbuf);
- if (nbr->state == NSM_Attempt && nbr->router_id.s_addr == 0)
- vty_out (vty, "%-15s %3d %-15s ",
- "-", nbr->priority,
- msgbuf);
- else
- vty_out (vty, "%-15s %3d %-15s ",
- inet_ntoa (nbr->router_id), nbr->priority,
- msgbuf);
-
- vty_out (vty, "%9s ",
- ospf_timer_dump (nbr->t_inactivity, timebuf,
+ vty_out (vty, "%9s ",
+ ospf_timer_dump (nbr->t_inactivity, timebuf,
sizeof(timebuf)));
-
- vty_out (vty, "%-15s ", inet_ntoa (nbr->src));
- vty_out (vty, "%-20s %5ld %5ld %5d%s",
- IF_NAME (oi), ospf_ls_retransmit_count (nbr),
- ospf_ls_request_count (nbr), ospf_db_summary_count (nbr),
- VTY_NEWLINE);
- }
+
+ vty_out (vty, "%-15s ", inet_ntoa (nbr->src));
+ vty_out (vty, "%-20s %5ld %5ld %5d%s",
+ IF_NAME (oi), ospf_ls_retransmit_count (nbr),
+ ospf_ls_request_count (nbr), ospf_db_summary_count (nbr),
+ VTY_NEWLINE);
+ }
}
DEFUN (show_ip_ospf_neighbor,
@@ -3199,7 +3200,7 @@ show_ip_ospf_neighbor_detail_sub (struct vty *vty, struct ospf_interface *oi,
/* Show interface address. */
vty_out (vty, " interface address %s%s",
- inet_ntoa (nbr->address.u.prefix4), VTY_NEWLINE);
+ inet_ntoa (nbr->src), VTY_NEWLINE);
/* Show Area ID. */
vty_out (vty, " In the area %s via interface %s%s",
ospf_area_desc_string (oi->area), oi->ifp->name, VTY_NEWLINE);
@@ -3320,14 +3321,13 @@ DEFUN (show_ip_ospf_neighbor_detail,
for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
{
- struct route_node *rn;
+ struct listnode *nnode;
struct ospf_neighbor *nbr;
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info))
- if (nbr != oi->nbr_self)
- if (nbr->state != NSM_Down)
- show_ip_ospf_neighbor_detail_sub (vty, oi, nbr);
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, nnode, nbr))
+ if (nbr != oi->nbr_self)
+ if (nbr->state != NSM_Down)
+ show_ip_ospf_neighbor_detail_sub (vty, oi, nbr);
}
return CMD_SUCCESS;
@@ -3356,15 +3356,14 @@ DEFUN (show_ip_ospf_neighbor_detail_all,
for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
{
- struct route_node *rn;
+ struct listnode *nnode;
struct ospf_neighbor *nbr;
struct ospf_nbr_nbma *nbr_nbma;
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info))
- if (nbr != oi->nbr_self)
- if (oi->type == OSPF_IFTYPE_NBMA && nbr->state != NSM_Down)
- show_ip_ospf_neighbor_detail_sub (vty, oi, rn->info);
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, nnode, nbr))
+ if (nbr != oi->nbr_self)
+ if (oi->type == OSPF_IFTYPE_NBMA && nbr->state != NSM_Down)
+ show_ip_ospf_neighbor_detail_sub (vty, oi, nbr);
if (oi->type == OSPF_IFTYPE_NBMA)
{
@@ -3393,8 +3392,9 @@ DEFUN (show_ip_ospf_neighbor_int_detail,
struct ospf *ospf;
struct ospf_interface *oi;
struct interface *ifp;
- struct route_node *rn, *nrn;
+ struct route_node *rn;
struct ospf_neighbor *nbr;
+ struct listnode *node;
ifp = if_lookup_by_name (argv[0]);
if (!ifp)
@@ -3413,11 +3413,10 @@ DEFUN (show_ip_ospf_neighbor_int_detail,
for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
if ((oi = rn->info))
- for (nrn = route_top (oi->nbrs); nrn; nrn = route_next (nrn))
- if ((nbr = nrn->info))
- if (nbr != oi->nbr_self)
- if (nbr->state != NSM_Down)
- show_ip_ospf_neighbor_detail_sub (vty, oi, nbr);
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, node, nbr))
+ if (nbr != oi->nbr_self)
+ if (nbr->state != NSM_Down)
+ show_ip_ospf_neighbor_detail_sub (vty, oi, nbr);
return CMD_SUCCESS;
}
@@ -3732,7 +3731,7 @@ show_as_external_lsa_detail (struct vty *vty, struct ospf_lsa *lsa)
return 0;
}
-/* N.B. This function currently seems to be unused. */
+#if 0
static int
show_as_external_lsa_stdvty (struct ospf_lsa *lsa)
{
@@ -3756,6 +3755,7 @@ show_as_external_lsa_stdvty (struct ospf_lsa *lsa)
return 0;
}
+#endif
/* Show AS-NSSA-LSA detail information. */
static int
@@ -4929,17 +4929,16 @@ ALIAS (no_ip_ospf_cost2,
static void
ospf_nbr_timer_update (struct ospf_interface *oi)
{
- struct route_node *rn;
+ struct listnode *node;
struct ospf_neighbor *nbr;
- for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
- if ((nbr = rn->info))
- {
- nbr->v_inactivity = OSPF_IF_PARAM (oi, v_wait);
- nbr->v_db_desc = OSPF_IF_PARAM (oi, retransmit_interval);
- nbr->v_ls_req = OSPF_IF_PARAM (oi, retransmit_interval);
- nbr->v_ls_upd = OSPF_IF_PARAM (oi, retransmit_interval);
- }
+ for (ALL_LIST_ELEMENTS_RO (oi->nbrs, node, nbr))
+ {
+ nbr->v_inactivity = OSPF_IF_PARAM (oi, v_wait);
+ nbr->v_db_desc = OSPF_IF_PARAM (oi, retransmit_interval);
+ nbr->v_ls_req = OSPF_IF_PARAM (oi, retransmit_interval);
+ nbr->v_ls_upd = OSPF_IF_PARAM (oi, retransmit_interval);
+ }
}
static int
@@ -5774,6 +5773,71 @@ ALIAS (no_ip_ospf_transmit_delay,
"OSPF interface commands\n"
"Link state transmit delay\n")
+DEFUN (ip_ospf_area,
+ ip_ospf_area_cmd,
+ "ip ospf area (A.B.C.D|<0-4294967295>)",
+ "IP Information\n"
+ "OSPF interface commands\n"
+ "Enable OSPF on this interface\n"
+ "OSPF area ID in IP address format\n"
+ "OSPF area ID as a decimal value\n")
+{
+ struct interface *ifp = vty->index;
+ int format, ret;
+ struct in_addr area_id;
+ struct ospf *ospf;
+ struct ospf_if_params *params;
+
+ ret = ospf_str2area_id (argv[0], &area_id, &format);
+
+ if (ret < 0)
+ {
+ vty_out (vty, "Please specify area by A.B.C.D|<0-4294967295>%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ params = IF_DEF_PARAMS (ifp);
+ if (OSPF_IF_PARAM_CONFIGURED(params, if_area))
+ {
+ vty_out (vty, "There is already a interface statement.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (memcmp (ifp->name, "VLINK", 5) == 0)
+ {
+ vty_out (vty, "Cannot enable OSPF on a virtual link.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ SET_IF_PARAM (params, if_area);
+ params->if_area = area_id;
+ ospf_interface_set (ifp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_ospf_area,
+ no_ip_ospf_area_cmd,
+ "no ip ospf area",
+ NO_STR
+ "IP Information\n"
+ "OSPF interface commands\n"
+ "Disable OSPF on this interface\n")
+{
+ struct interface *ifp = vty->index;
+ struct ospf *ospf;
+ struct ospf_if_params *params;
+
+ params = IF_DEF_PARAMS (ifp);
+ if (!OSPF_IF_PARAM_CONFIGURED(params, if_area))
+ return CMD_SUCCESS;
+
+ UNSET_IF_PARAM (params, if_area);
+
+ ospf_interface_unset (ifp);
+ return CMD_SUCCESS;
+}
+
+
DEFUN (ospf_redistribute_source_metric_type,
ospf_redistribute_source_metric_type_routemap_cmd,
@@ -7374,6 +7438,123 @@ DEFUN (show_ip_ospf_route,
return CMD_SUCCESS;
}
+DEFUN (ip_ospf_host,
+ ip_ospf_host_cmd,
+ "ip ospf host A.B.C.D area (A.B.C.D|<0-4294967295>|all) cost <0-65535>",
+ "OSPF specific commands\n"
+ "Add a host route to OPSF\n"
+ "Host route in IP address format\n")
+{
+ struct ospf *ospf;
+ u_int32_t cost;
+ struct ospf_area *area = NULL;
+ struct in_addr host_addr, area_id;
+ struct ospf_host_route *host = NULL;
+ struct listnode *node;
+ int ret, format;
+
+ ospf = ospf_lookup ();
+ if (ospf == NULL)
+ {
+ vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE);
+ return CMD_SUCCESS;
+ }
+ ret = inet_aton (argv[0], &host_addr);
+ if (!ret)
+ {
+ vty_out (vty, "Please specify host route by A.B.C.D%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ cost = strtol (argv[2], NULL, 10);
+ /* cost range is <0-65535>. */
+ if (cost < 0 || cost > 65535)
+ {
+ vty_out (vty, "cost is invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (strcmp(argv[1], "all") != 0)
+ {
+ VTY_GET_OSPF_AREA_ID (area_id, format, argv[1]); /* returns if error */
+ area = ospf_area_get (ospf, area_id, format);
+ }
+
+
+ for (ALL_LIST_ELEMENTS_RO (ospf->hostlist, node, host))
+ if (IPV4_ADDR_SAME (&host_addr, &host->host_addr) &&
+ area == host->area)
+ break;
+
+ if (!node)
+ {
+ host = XCALLOC (MTYPE_OSPF_TOP, sizeof (struct ospf_host_route));
+ listnode_add (ospf->hostlist, host);
+ }
+ host->host_addr = host_addr;
+ host->cost = cost;
+ host->area = area;
+
+ if (!area)
+ for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+ ospf_router_lsa_timer_add(area);
+ else
+ ospf_router_lsa_timer_add(area);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_ospf_host,
+ no_ip_ospf_host_cmd,
+ "no ip ospf host A.B.C.D area (A.B.C.D|<0-4294967295>|all)",
+ NO_STR
+ "OSPF specific commands\n"
+ "Host route in IP address format\n")
+{
+ struct ospf *ospf;
+ struct ospf_area *area = NULL;
+ struct in_addr host_addr, area_id;
+ struct ospf_host_route *host = NULL;
+ struct listnode *node;
+ int ret, format;
+
+ ospf = ospf_lookup ();
+ if (ospf == NULL)
+ {
+ vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE);
+ return CMD_SUCCESS;
+ }
+ ret = inet_aton (argv[0], &host_addr);
+ if (!ret)
+ {
+ vty_out (vty, "Please specify host route by A.B.C.D%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (strcmp(argv[1], "all") != 0)
+ {
+ VTY_GET_OSPF_AREA_ID (area_id, format, argv[1]);
+ area = ospf_area_get (ospf, area_id, format);
+ }
+
+ for (ALL_LIST_ELEMENTS_RO (ospf->hostlist, node, host))
+ if (IPV4_ADDR_SAME (&host_addr, &host->host_addr) &&
+ area == host->area)
+ break;
+
+ if (node)
+ {
+ listnode_delete (ospf->hostlist, host);
+ XFREE (MTYPE_OSPF_TOP, host);
+ if (!area)
+ for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+ ospf_router_lsa_timer_add(area);
+ else
+ ospf_router_lsa_timer_add(area);
+ }
+ return CMD_SUCCESS;
+}
+
const char *ospf_abr_type_str[] =
{
@@ -7581,6 +7762,14 @@ config_write_interface (struct vty *vty)
vty_out (vty, "%s", VTY_NEWLINE);
}
+ /* Area print. */
+ if (OSPF_IF_PARAM_CONFIGURED (params, if_area))
+ {
+ vty_out (vty, " ip ospf area %s%s",
+ inet_ntoa (params->if_area),
+ VTY_NEWLINE);
+ }
+
/* MTU ignore print. */
if (OSPF_IF_PARAM_CONFIGURED (params, mtu_ignore) &&
params->mtu_ignore != OSPF_MTU_IGNORE_DEFAULT)
@@ -7956,6 +8145,7 @@ ospf_config_write (struct vty *vty)
struct interface *ifp;
struct ospf_interface *oi;
struct listnode *node;
+ struct ospf_host_route *host;
int write = 0;
ospf = ospf_lookup ();
@@ -8023,7 +8213,20 @@ ospf_config_write (struct vty *vty)
/* passive-interface print. */
if (ospf->passive_interface_default == OSPF_IF_PASSIVE)
vty_out (vty, " passive-interface default%s", VTY_NEWLINE);
-
+
+ for (ALL_LIST_ELEMENTS_RO (ospf->hostlist, node, host))
+ {
+ char buf[28];
+
+ if (host->area)
+ area_id2str(buf, sizeof(buf), host->area);
+ else
+ strcpy(buf, "all");
+ vty_out (vty, " ip ospf host %s area %s cost %d%s",
+ inet_ntoa (host->host_addr),
+ buf, host->cost, VTY_NEWLINE);
+ }
+
for (ALL_LIST_ELEMENTS_RO (om->iflist, node, ifp))
if (OSPF_IF_PARAM_CONFIGURED (IF_DEF_PARAMS (ifp), passive_interface)
&& IF_DEF_PARAMS (ifp)->passive_interface !=
@@ -8223,6 +8426,10 @@ ospf_vty_if_init (void)
install_element (INTERFACE_NODE, &no_ip_ospf_transmit_delay_addr_cmd);
install_element (INTERFACE_NODE, &no_ip_ospf_transmit_delay_cmd);
+ /* "ip ospf area" commands. */
+ install_element (INTERFACE_NODE, &ip_ospf_area_cmd);
+ install_element (INTERFACE_NODE, &no_ip_ospf_area_cmd);
+
/* These commands are compatibitliy for previous version. */
install_element (INTERFACE_NODE, &ospf_authentication_key_cmd);
install_element (INTERFACE_NODE, &no_ospf_authentication_key_cmd);
@@ -8509,6 +8716,10 @@ ospf_vty_init (void)
install_element (OSPF_NODE, &no_ospf_neighbor_priority_cmd);
install_element (OSPF_NODE, &no_ospf_neighbor_poll_interval_cmd);
+ /* "ip ospf host" commands. */
+ install_element (OSPF_NODE, &ip_ospf_host_cmd);
+ install_element (OSPF_NODE, &no_ip_ospf_host_cmd);
+
/* Init interface related vty commands. */
ospf_vty_if_init ();
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index af8c8f0f..484aba98 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -133,8 +133,9 @@ ospf_interface_delete (int command, struct zclient *zclient,
if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
zlog_debug
- ("Zebra: interface delete %s index %d flags %lld metric %d mtu %d",
- ifp->name, ifp->ifindex, ifp->flags, ifp->metric, ifp->mtu);
+ ("Zebra: interface delete %s index %d flags %#llx metric %d mtu %d",
+ ifp->name, ifp->ifindex, (unsigned long long) ifp->flags,
+ ifp->metric, ifp->mtu);
#ifdef HAVE_SNMP
ospf_snmp_if_delete (ifp);
@@ -195,6 +196,17 @@ ospf_interface_state_up (int command, struct zclient *zclient,
ospf_if_recalculate_output_cost (ifp);
}
+ if (CHECK_FLAG(if_tmp.status ^ ifp->status, ZEBRA_INTERFACE_UNNUMBERED))
+ {
+ if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+ zlog_debug ("Zebra: Interface[%s] Unnumbered state change %d -> %d.",
+ ifp->name,
+ if_tmp.status & ZEBRA_INTERFACE_UNNUMBERED,
+ ifp->status & ZEBRA_INTERFACE_UNNUMBERED);
+
+ ospf_if_reset (ifp);
+ }
+
if (if_tmp.mtu != ifp->mtu)
{
if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
@@ -964,7 +976,7 @@ ospf_distribute_list_update_timer (struct thread *thread)
/* Update distribute-list and set timer to apply access-list. */
void
-ospf_distribute_list_update (struct ospf *ospf, int type)
+ospf_distribute_list_update (struct ospf *ospf, unsigned long type)
{
struct route_table *rt;
diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h
index 3efd8958..b6419c8b 100644
--- a/ospfd/ospf_zebra.h
+++ b/ospfd/ospf_zebra.h
@@ -56,7 +56,7 @@ extern int ospf_redistribute_check (struct ospf *, struct external_info *,
int *);
extern int ospf_distribute_check_connected (struct ospf *,
struct external_info *);
-extern void ospf_distribute_list_update (struct ospf *, int);
+extern void ospf_distribute_list_update (struct ospf *, unsigned long);
extern int ospf_is_type_redistributed (int);
extern void ospf_distance_reset (struct ospf *);
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index a7553e73..35c6b02b 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -231,7 +231,7 @@ ospf_new (void)
}
new->t_read = thread_add_read (master, ospf_read, new, new->fd);
new->oi_write_q = list_new ();
-
+ new->hostlist = list_new(); /* host route list */
return new;
}
@@ -401,6 +401,7 @@ ospf_finish_final (struct ospf *ospf)
struct ospf_lsa *lsa;
struct ospf_interface *oi;
struct ospf_area *area;
+ struct ospf_host_route *host;
struct ospf_vl_data *vl_data;
struct listnode *node, *nnode;
int i;
@@ -471,6 +472,12 @@ ospf_finish_final (struct ospf *ospf)
ospf_area_free (area);
}
+ for (ALL_LIST_ELEMENTS (ospf->hostlist, node, nnode, host))
+ {
+ listnode_delete (ospf->hostlist, host);
+ XFREE (MTYPE_OSPF_TOP, host);
+ }
+
/* Cancel all timers. */
OSPF_TIMER_OFF (ospf->t_external_lsa);
OSPF_TIMER_OFF (ospf->t_router_lsa_update);
@@ -646,8 +653,32 @@ ospf_area_free (struct ospf_area *area)
void
ospf_area_check_free (struct ospf *ospf, struct in_addr area_id)
{
+ struct interface *ifp;
+ struct listnode *node;
+ struct ospf_if_params *params;
+ struct route_node *rn;
+ struct ospf_network *network;
+ struct ospf_host_route *host;
struct ospf_area *area;
+ /* Check if any interface is a member of this area */
+ for (ALL_LIST_ELEMENTS_RO (om->iflist, node, ifp))
+ {
+ params = IF_DEF_PARAMS (ifp);
+ if (OSPF_IF_PARAM_CONFIGURED(params, if_area)
+ && IPV4_ADDR_SAME (&area_id, &params->if_area))
+ return;
+ }
+ /* Check if any network is a member of this area */
+ for (rn = route_top (ospf->networks); rn; rn = route_next (rn))
+ {
+ network = rn->info;
+ if (network && IPV4_ADDR_SAME (&area_id, &network->area_id))
+ {
+ route_unlock_node (rn);
+ return;
+ }
+ }
area = ospf_area_lookup_by_area_id (ospf, area_id);
if (area &&
listcount (area->oiflist) == 0 &&
@@ -660,6 +691,9 @@ ospf_area_check_free (struct ospf *ospf, struct in_addr area_id)
IMPORT_NAME (area) == NULL &&
area->auth_type == OSPF_AUTH_NULL)
{
+ for (ALL_LIST_ELEMENTS_RO (area->ospf->hostlist, node, host))
+ if (!host->area || host->area == area)
+ return;
listnode_delete (ospf->areas, area);
ospf_area_free (area);
}
@@ -701,6 +735,67 @@ ospf_area_add_if (struct ospf_area *area, struct ospf_interface *oi)
listnode_add (area->oiflist, oi);
}
+void add_ospf_interface(struct interface *ifp, struct ospf_area *area,
+ struct connected *co)
+{
+ struct ospf_interface *oi;
+
+ oi = ospf_if_new (area->ospf, ifp, co->address);
+ oi->connected = co;
+
+ oi->area = area;
+
+ oi->params = ospf_lookup_if_params (ifp, oi->address->u.prefix4);
+ oi->output_cost = ospf_if_get_output_cost (oi);
+
+ /* Add pseudo neighbor. */
+ ospf_nbr_add_self (oi);
+
+ /* Relate ospf interface to ospf instance. */
+ oi->ospf = area->ospf;
+
+ /* update network type as interface flag */
+ /* If network type is specified previously,
+ skip network type setting. */
+ oi->type = IF_DEF_PARAMS (ifp)->type;
+
+ ospf_area_add_if (oi->area, oi);
+
+ /* if router_id is not configured, dont bring up
+ * interfaces.
+ * ospf_router_id_update() will call ospf_if_update
+ * whenever r-id is configured instead.
+ */
+ if ((area->ospf->router_id.s_addr != 0)
+ && if_is_operative (ifp))
+ ospf_if_up (oi);
+}
+
+void update_redistributed(struct ospf *ospf, int add_to_ospf)
+{
+ struct route_node *rn;
+ struct external_info *ei;
+
+ if (ospf_is_type_redistributed (ZEBRA_ROUTE_CONNECT))
+ if (EXTERNAL_INFO (ZEBRA_ROUTE_CONNECT))
+ for (rn = route_top (EXTERNAL_INFO (ZEBRA_ROUTE_CONNECT));
+ rn; rn = route_next (rn))
+ if ((ei = rn->info) != NULL)
+ if (add_to_ospf)
+ {
+ if (ospf_external_info_find_lsa (ospf, &ei->p))
+ if (!ospf_distribute_check_connected (ospf, ei))
+ ospf_external_lsa_flush (ospf, ei->type, &ei->p,
+ ei->ifindex /*, ei->nexthop */);
+ }
+ else
+ {
+ if (!ospf_external_info_find_lsa (ospf, &ei->p))
+ if (ospf_distribute_check_connected (ospf, ei))
+ ospf_external_lsa_originate (ospf, ei);
+ }
+}
+
void
ospf_area_del_if (struct ospf_area *area, struct ospf_interface *oi)
{
@@ -754,16 +849,7 @@ ospf_network_set (struct ospf *ospf, struct prefix_ipv4 *p,
ospf_network_run ((struct prefix *)p, area);
/* Update connected redistribute. */
- if (ospf_is_type_redistributed (ZEBRA_ROUTE_CONNECT))
- if (EXTERNAL_INFO (ZEBRA_ROUTE_CONNECT))
- for (rn = route_top (EXTERNAL_INFO (ZEBRA_ROUTE_CONNECT));
- rn; rn = route_next (rn))
- if ((ei = rn->info) != NULL)
- if (ospf_external_info_find_lsa (ospf, &ei->p))
- if (!ospf_distribute_check_connected (ospf, ei))
- ospf_external_lsa_flush (ospf, ei->type, &ei->p,
- ei->ifindex /*, ei->nexthop */);
-
+ update_redistributed(ospf, 1); /* interfaces possibly added */
ospf_area_check_free (ospf, area_id);
return 1;
@@ -795,12 +881,17 @@ ospf_network_unset (struct ospf *ospf, struct prefix_ipv4 *p,
/* Find interfaces that not configured already. */
for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi))
{
+ struct ospf_if_params *params;
int found = 0;
struct connected *co = oi->connected;
if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
- continue;
-
+ continue;
+
+ params = IF_DEF_PARAMS (oi->ifp);
+ if (OSPF_IF_PARAM_CONFIGURED(params, if_area))
+ continue;
+
for (rn = route_top (ospf->networks); rn; rn = route_next (rn))
{
if (rn->info == NULL)
@@ -819,18 +910,103 @@ ospf_network_unset (struct ospf *ospf, struct prefix_ipv4 *p,
}
/* Update connected redistribute. */
- if (ospf_is_type_redistributed (ZEBRA_ROUTE_CONNECT))
- if (EXTERNAL_INFO (ZEBRA_ROUTE_CONNECT))
- for (rn = route_top (EXTERNAL_INFO (ZEBRA_ROUTE_CONNECT));
- rn; rn = route_next (rn))
- if ((ei = rn->info) != NULL)
- if (!ospf_external_info_find_lsa (ospf, &ei->p))
- if (ospf_distribute_check_connected (ospf, ei))
- ospf_external_lsa_originate (ospf, ei);
+ update_redistributed(ospf, 0); /* interfaces possibly removed */
+ return 1;
+}
+
+int
+ospf_interface_set (struct interface *ifp)
+{
+ struct ospf_area *area;
+ struct route_node *rn;
+ struct external_info *ei;
+ struct listnode *cnode;
+ struct connected *co;
+ struct ospf *ospf;
+ struct ospf_if_params *params;
+ struct in_addr area_id;
+ int ret = OSPF_AREA_ID_FORMAT_ADDRESS;
+
+ if ((ospf = ospf_lookup ()) == NULL)
+ return 1; /* Ospf not ready yet */
+ params = IF_DEF_PARAMS (ifp);
+ area_id = params->if_area;
+
+ area = ospf_area_get (ospf, area_id, ret);
+
+ for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, co))
+ {
+ struct ospf_interface *oi;
+
+ if (CHECK_FLAG(co->flags,ZEBRA_IFA_SECONDARY))
+ continue;
+
+ oi = ospf_if_table_lookup(ifp, co->address);
+ if (oi)
+ { /* Just adjust area for existing interface */
+ ospf_area_del_if (oi->area, oi);
+ oi->area = area;
+ ospf_area_add_if (oi->area, oi);
+ }
+ else
+ {
+ add_ospf_interface(ifp, area, co);
+ }
+ }
+
+ /* Update connected redistribute. */
+ update_redistributed(ospf, 1); /* interface possibly added */
+ ospf_area_check_free (ospf, area_id);
+
+ return 1;
+}
+
+int
+ospf_interface_unset (struct interface *ifp)
+{
+ struct route_node *rn_oi, *rn;
+ struct ospf *ospf;
+
+ if ((ospf = ospf_lookup ()) == NULL)
+ return 1; /* Ospf not ready yet */
+
+ /* Find interfaces that may need to be removed. */
+ for (rn_oi = route_top (IF_OIFS (ifp)); rn_oi; rn_oi = route_next (rn_oi))
+ {
+ struct ospf_interface *oi;
+ struct connected *co;
+ int found = 0;
+
+ if ( (oi = rn_oi->info) == NULL)
+ continue;
+ if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+ continue;
+ co = oi->connected;
+
+ for (rn = route_top (ospf->networks); rn; rn = route_next (rn))
+ {
+ if (rn->info == NULL)
+ continue;
+
+ if (ospf_network_match_iface(co,&rn->p))
+ {
+ found = 1;
+ route_unlock_node (rn);
+ break;
+ }
+ }
+
+ if (found == 0)
+ ospf_if_free (oi);
+ }
+
+ /* Update connected redistribute. */
+ update_redistributed(ospf, 0); /* interfaces possibly removed */
return 1;
}
+
/* Check whether interface matches given network
* returns: 1, true. 0, false
*/
@@ -862,39 +1038,9 @@ ospf_network_run_interface (struct prefix *p, struct ospf_area *area,
if (p->family == co->address->family
&& ! ospf_if_table_lookup(ifp, co->address)
&& ospf_network_match_iface(co,p))
- {
- struct ospf_interface *oi;
-
- oi = ospf_if_new (area->ospf, ifp, co->address);
- oi->connected = co;
-
- oi->area = area;
-
- oi->params = ospf_lookup_if_params (ifp, oi->address->u.prefix4);
- oi->output_cost = ospf_if_get_output_cost (oi);
-
- /* Add pseudo neighbor. */
- ospf_nbr_add_self (oi);
-
- /* Relate ospf interface to ospf instance. */
- oi->ospf = area->ospf;
-
- /* update network type as interface flag */
- /* If network type is specified previously,
- skip network type setting. */
- oi->type = IF_DEF_PARAMS (ifp)->type;
-
- ospf_area_add_if (oi->area, oi);
-
- /* if router_id is not configured, dont bring up
- * interfaces.
- * ospf_router_id_update() will call ospf_if_update
- * whenever r-id is configured instead.
- */
- if ((area->ospf->router_id.s_addr != 0)
- && if_is_operative (ifp))
- ospf_if_up (oi);
- }
+ {
+ add_ospf_interface(ifp, area, co);
+ }
}
}
@@ -953,15 +1099,22 @@ ospf_if_update (struct ospf *ospf, struct interface *ifp)
/* OSPF must be on and Router-ID must be configured. */
if (!ospf || ospf->router_id.s_addr == 0)
return;
-
- /* Run each netowrk for this interface. */
- for (rn = route_top (ospf->networks); rn; rn = route_next (rn))
- if (rn->info != NULL)
- {
- network = (struct ospf_network *) rn->info;
- area = ospf_area_get (ospf, network->area_id, network->format);
- ospf_network_run_interface (&rn->p, area, ifp);
- }
+
+ if (OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS (ifp), if_area))
+ {
+ ospf_interface_set (ifp);
+ }
+ else
+ {
+ /* Run each netowrk for this interface. */
+ for (rn = route_top (ospf->networks); rn; rn = route_next (rn))
+ if (rn->info != NULL)
+ {
+ network = (struct ospf_network *) rn->info;
+ area = ospf_area_get (ospf, network->area_id, network->format);
+ ospf_network_run_interface (&rn->p, area, ifp);
+ }
+ }
}
void
@@ -1205,6 +1358,7 @@ ospf_area_nssa_translator_role_set (struct ospf *ospf, struct in_addr area_id,
return 1;
}
+#if 0
/* XXX: unused? Leave for symmetry? */
static int
ospf_area_nssa_translator_role_unset (struct ospf *ospf,
@@ -1222,6 +1376,7 @@ ospf_area_nssa_translator_role_unset (struct ospf *ospf,
return 1;
}
+#endif
int
ospf_area_export_list_set (struct ospf *ospf,
@@ -1397,8 +1552,6 @@ ospf_nbr_nbma_add (struct ospf_nbr_nbma *nbr_nbma,
struct ospf_interface *oi)
{
struct ospf_neighbor *nbr;
- struct route_node *rn;
- struct prefix p;
if (oi->type != OSPF_IFTYPE_NBMA)
return;
@@ -1406,37 +1559,27 @@ ospf_nbr_nbma_add (struct ospf_nbr_nbma *nbr_nbma,
if (nbr_nbma->nbr != NULL)
return;
- if (IPV4_ADDR_SAME (&oi->nbr_self->address.u.prefix4, &nbr_nbma->addr))
+ if (IPV4_ADDR_SAME (&oi->nbr_self->src, &nbr_nbma->addr))
return;
nbr_nbma->oi = oi;
listnode_add (oi->nbr_nbma, nbr_nbma);
/* Get neighbor information from table. */
- p.family = AF_INET;
- p.prefixlen = IPV4_MAX_BITLEN;
- p.u.prefix4 = nbr_nbma->addr;
-
- rn = route_node_get (oi->nbrs, (struct prefix *)&p);
- if (rn->info)
+ nbr = ospf_nbr_lookup_by_addr (oi->nbrs, &nbr_nbma->addr);
+ if (nbr)
{
- nbr = rn->info;
nbr->nbr_nbma = nbr_nbma;
nbr_nbma->nbr = nbr;
-
- route_unlock_node (rn);
}
else
{
- nbr = rn->info = ospf_nbr_new (oi);
+ nbr = ospf_nbr_new (oi);
nbr->state = NSM_Down;
nbr->src = nbr_nbma->addr;
nbr->nbr_nbma = nbr_nbma;
nbr->priority = nbr_nbma->priority;
- nbr->address = p;
-
nbr_nbma->nbr = nbr;
-
OSPF_NSM_EVENT_EXECUTE (nbr, NSM_Start);
}
}
diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h
index 64e91cef..6cde1099 100644
--- a/ospfd/ospfd.h
+++ b/ospfd/ospfd.h
@@ -274,6 +274,9 @@ struct ospf
struct stream *ibuf;
struct list *oi_write_q;
+ /* queue for AS-External route calculation */
+ struct work_queue *ase_calc_queue;
+
/* Distribute lists out of other route sources. */
struct
{
@@ -329,6 +332,9 @@ struct ospf
u_int32_t rx_lsa_count;
struct route_table *distance_table;
+
+ /* Host route list */
+ struct list *hostlist;
};
/* OSPF area structure. */
@@ -609,6 +615,9 @@ extern struct ospf_area *ospf_area_lookup_by_area_id (struct ospf *,
extern void ospf_area_add_if (struct ospf_area *, struct ospf_interface *);
extern void ospf_area_del_if (struct ospf_area *, struct ospf_interface *);
+extern int ospf_interface_set (struct interface *ifp);
+extern int ospf_interface_unset (struct interface *ifp);
+
extern void ospf_route_map_init (void);
extern void ospf_snmp_init (void);
diff --git a/pimd/.gitignore b/pimd/.gitignore
new file mode 100644
index 00000000..b27998ea
--- /dev/null
+++ b/pimd/.gitignore
@@ -0,0 +1,19 @@
+Makefile
+Makefile.in
+*.o
+pimd
+test_igmpv3_join
+pimd.conf
+libpim.a
+tags
+TAGS
+.deps
+.nfs*
+*.lo
+*.la
+*.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
+
diff --git a/pimd/AUTHORS b/pimd/AUTHORS
new file mode 100644
index 00000000..f6135a41
--- /dev/null
+++ b/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-
diff --git a/pimd/CAVEATS b/pimd/CAVEATS
new file mode 100644
index 00000000..c436d552
--- /dev/null
+++ b/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 FIXED (S,G) Assert state machine (RFC 4601, section 4.6.1) is not
+ implemented. See also TODO T6. See also CAVEAT C10.
+
+C8 It is not possible to disable join suppression in order to
+ explicitly track the join membership of individual downstream
+ routers.
+ - IGMPv3 Explicit Membership Tracking is not supported.
+ When explicit tracking is enabled on a router, the router can
+ individually track the Internet Group Management Protocol (IGMP)
+ membership state of all reporting hosts. This feature allows the
+ router to achieve minimal leave latencies when hosts leave a
+ multicast group or channel. Example:
+ conf t
+ interface eth0
+ ip igmp explicit-tracking
+
+C9 Only IPv4 Address Family (number=1) is supported in the PIM Address
+ Family field.
+ See also RFC 4601: 5.1. PIM Address Family
+ See also CAVEAT C6.
+ See also http://www.iana.org/assignments/address-family-numbers
+
+C10 FIXED Assert metric depends on metric_preference and
+ route_metric. Those parameters should be fetched from RIB
+ (zebra). See also pim_rpf.c, pim_rpf_update().
+
+C11 SSM Mapping is not supported
+
+ SSM Mapping Overview:
+
+ SSM mapping introduces a means for the last hop router to discover
+ sources sending to groups. When SSM mapping is configured, if a
+ router receives an IGMPv1 or IGMPv2 membership report for a
+ particular group G, the router translates this report into one or
+ more (S, G) channel memberships for the well-known sources
+ associated with this group.
+
+ When the router receives an IGMPv1 or IGMPv2 membership report for
+ a group G, the router uses SSM mapping to determine one or more
+ source IP addresses for the group G. SSM mapping then translates
+ the membership report as an IGMPv3 report INCLUDE (G, [S1, G],
+ [S2, G]...[Sn, G] and continues as if it had received an IGMPv3
+ report. The router then sends out PIM joins toward (S1, G) to (Sn,
+ G) and continues to be joined to these groups as long as it
+ continues to receive the IGMPv1 or IGMPv2 membership reports and
+ as long as the SSM mapping for the group remains the same. SSM
+ mapping, thus, enables you to leverage SSM for video delivery to
+ legacy STBs that do not support IGMPv3 or for applications that do
+ not take advantage of the IGMPv3 host stack.
+
+ SSM mapping enables the last hop router to determine the source
+ addresses either by a statically configured table on the router or
+ by consulting a DNS server. When the statically configured table
+ is changed, or when the DNS mapping changes, the router will leave
+ the current sources associated with the joined groups.
+
+C12 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-
diff --git a/pimd/COMMANDS b/pimd/COMMANDS
new file mode 100644
index 00000000..8cfa671e
--- /dev/null
+++ b/pimd/COMMANDS
@@ -0,0 +1,66 @@
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+global configuration commands:
+ ip multicast-routing Enable IP multicast forwarding
+ ip ssmpingd Enable ssmpingd operation
+
+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
+ show ip ssmpingd ssmpingd operation
+
+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
+ debug ssmpingd ssmpingd activity
+ show debugging State of each debugging option
+ test igmp receive report Test reception of IGMPv3 report
+ test pim receive assert Test reception of PIM assert
+ test pim receive dump Test reception of PIM packet dump
+ test pim receive hello Test reception of PIM hello
+ test pim receive join Test reception of PIM join
+ test pim receive prune Test reception of PIM prune
+ test pim receive upcall Test reception of kernel upcall
+
+statistics commands:
+ show memory pim PIM memory statistics
+
+-x-
diff --git a/pimd/COPYING b/pimd/COPYING
new file mode 100644
index 00000000..3912109b
--- /dev/null
+++ b/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.
diff --git a/pimd/DEBUG b/pimd/DEBUG
new file mode 100644
index 00000000..119c46f5
--- /dev/null
+++ b/pimd/DEBUG
@@ -0,0 +1,61 @@
+# $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.
+
+ - Two easy options to test IGMPv3 joins from the receiver host:
+
+ 1) Configure pimd on the receiver host with "ip igmp join":
+
+ interface eth0
+ ip pim ssm
+ ip igmp join 239.1.1.1 1.1.1.1
+
+ 2) Use the test_igmpv3_join command-line utility:
+
+ test_igmpv3_join eth0 239.1.1.1 1.1.1.1
+
+ - The following command generates a 100-kbps multicast stream for
+ channel 1.1.1.1,239.1.1.1 with TTL 10 and 1000-byte payload per UDP
+ packet (to avoid fragmentation):
+
+ nepim -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-
diff --git a/pimd/LINUX_KERNEL_MROUTE_MFC b/pimd/LINUX_KERNEL_MROUTE_MFC
new file mode 100644
index 00000000..e87e567f
--- /dev/null
+++ b/pimd/LINUX_KERNEL_MROUTE_MFC
@@ -0,0 +1,26 @@
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+#
+# The Linux Kernel MFC (Multicast Forwarding Cache)
+#
+
+# Check Linux kernel multicast interfaces:
+cat /proc/net/dev_mcast
+
+# Check that interface eth0 is forwarding multicast:
+cat /proc/sys/net/ipv4/conf/eth0/mc_forwarding
+
+# Check Linux kernel multicast VIFs:
+cat /proc/net/ip_mr_vif
+Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote
+
+# Check Linux kernel MFC:
+# Oifs format = vifi:TTL
+cat /proc/net/ip_mr_cache
+Group Origin Iif Pkts Bytes Wrong Oifs
+
+# iproute2 can display the MFC:
+ip mroute show
+(2.2.2.2, 239.2.2.2) Iif: eth1 Oifs: eth0
+
+# -- end-of-file --
diff --git a/pimd/Makefile.am b/pimd/Makefile.am
new file mode 100644
index 00000000..4aae6378
--- /dev/null
+++ b/pimd/Makefile.am
@@ -0,0 +1,76 @@
+## Process this file with automake to produce Makefile.in.
+## $QuaggaId: $Format:%an, %ai, %h$ $
+
+# qpimd - pimd for quagga
+# Copyright (C) 2008 Everton da Silva Marques
+#
+# qpimd is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2,
+# or (at your option) any later version.
+#
+# qpimd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with qpimd; see the file COPYING. If not, write
+# to the Free Software Foundation, Inc., 59 Temple Place - Suite
+# 330, Boston, MA 02111-1307, USA.
+
+# PIM_DEBUG_BYDEFAULT: Automatically enables all pimd "debug ..." commands
+# PIM_ZCLIENT_DEBUG: Support for internal ZEBRA client debugging
+# PIM_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
+bin_PROGRAMS = test_igmpv3_join
+
+libpim_a_SOURCES = \
+ pimd.c pim_version.c pim_cmd.c pim_signals.c pim_iface.c \
+ pim_vty.c pim_igmp.c pim_sock.c pim_zebra.c \
+ pim_igmpv3.c pim_str.c pim_mroute.c pim_util.c pim_time.c \
+ pim_oil.c pim_zlookup.c pim_pim.c pim_tlv.c pim_neighbor.c \
+ pim_hello.c pim_ifchannel.c pim_join.c pim_assert.c \
+ pim_msg.c pim_upstream.c pim_rpf.c pim_rand.c pim_macro.c \
+ pim_igmp_join.c pim_ssmpingd.c pim_int.c
+
+noinst_HEADERS = \
+ pimd.h pim_version.h pim_cmd.h pim_signals.h pim_iface.h \
+ pim_vty.h pim_igmp.h pim_sock.h pim_zebra.h \
+ pim_igmpv3.h pim_str.h pim_mroute.h pim_util.h pim_time.h \
+ pim_oil.h pim_zlookup.h pim_pim.h pim_tlv.h pim_neighbor.h \
+ pim_hello.h pim_ifchannel.h pim_join.h pim_assert.h \
+ pim_msg.h pim_upstream.h pim_rpf.h pim_rand.h pim_macro.h \
+ pim_igmp_join.h pim_ssmpingd.h pim_int.h
+
+pimd_SOURCES = \
+ pim_main.c $(libpim_a_SOURCES)
+
+test_igmpv3_join_SOURCES = \
+ test_igmpv3_join.c pim_igmp_join.c
+
+pimd_LDADD = ../lib/libzebra.la @LIBCAP@
+
+examplesdir = $(exampledir)
+dist_examples_DATA = pimd.conf.sample
diff --git a/pimd/README b/pimd/README
new file mode 100644
index 00000000..e42ceda2
--- /dev/null
+++ b/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 --
diff --git a/pimd/TODO b/pimd/TODO
new file mode 100644
index 00000000..1dc561dd
--- /dev/null
+++ b/pimd/TODO
@@ -0,0 +1,373 @@
+# $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://tools.ietf.org/html/draft-ietf-mboned-lightweight-igmpv3-mldv2-05
+ http://www.ietf.org/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt
+ http://www.ietf.org/html.charters/mboned-charter.html
+
+T41 DONE ssmping support
+
+ See also:
+ http://www.venaas.no/multicast/ssmping/
+ draft-ietf-mboned-ssmping-07
+ http://tools.ietf.org/html/draft-ietf-mboned-ssmping-07
+
+ Example:
+
+ debug ssmpingd
+
+ conf t
+ ip ssmpingd 1.1.1.1
+
+ show ip ssmpingd
+
+-x-
diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c
new file mode 100644
index 00000000..52168d69
--- /dev/null
+++ b/pimd/pim_assert.c
@@ -0,0 +1,804 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+
+#include "pimd.h"
+#include "pim_str.h"
+#include "pim_tlv.h"
+#include "pim_msg.h"
+#include "pim_pim.h"
+#include "pim_int.h"
+#include "pim_time.h"
+#include "pim_iface.h"
+#include "pim_hello.h"
+#include "pim_macro.h"
+#include "pim_assert.h"
+#include "pim_ifchannel.h"
+
+static int assert_action_a3(struct pim_ifchannel *ch);
+static void assert_action_a2(struct pim_ifchannel *ch,
+ struct pim_assert_metric winner_metric);
+static void assert_action_a6(struct pim_ifchannel *ch,
+ struct pim_assert_metric winner_metric);
+
+void pim_ifassert_winner_set(struct pim_ifchannel *ch,
+ enum pim_ifassert_state new_state,
+ struct in_addr winner,
+ struct pim_assert_metric winner_metric)
+{
+ int winner_changed = (ch->ifassert_winner.s_addr != winner.s_addr);
+ int metric_changed = !pim_assert_metric_match(&ch->ifassert_winner_metric,
+ &winner_metric);
+
+ if (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 = pim_read_uint32_host(curr);
+
+ msg_metric.rpt_bit_flag = msg_metric.metric_preference & 0x80000000; /* save highest bit */
+ msg_metric.metric_preference &= ~0x80000000; /* clear highest bit */
+
+ curr += 4;
+
+ /*
+ Parse assert route metric
+ */
+
+ msg_metric.route_metric = pim_read_uint32_host(curr);
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char neigh_str[100];
+ char source_str[100];
+ char group_str[100];
+ pim_inet4_dump("<neigh?>", src_addr, neigh_str, sizeof(neigh_str));
+ pim_inet4_dump("<src?>", msg_source_addr.u.prefix4, source_str, sizeof(source_str));
+ pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4, group_str, sizeof(group_str));
+ zlog_debug("%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
+ __PRETTY_FUNCTION__, neigh_str, ifp->name,
+ source_str, group_str,
+ msg_metric.metric_preference,
+ msg_metric.route_metric,
+ PIM_FORCE_BOOLEAN(msg_metric.rpt_bit_flag));
+ }
+
+ msg_metric.ip_address = src_addr;
+
+ return dispatch_assert(ifp,
+ msg_source_addr.u.prefix4,
+ msg_group_addr.u.prefix4,
+ msg_metric);
+}
+
+/*
+ RFC 4601: 4.6.3. Assert Metrics
+
+ Assert metrics are defined as:
+
+ When comparing assert_metrics, the rpt_bit_flag, metric_preference,
+ and route_metric field are compared in order, where the first lower
+ value wins. If all fields are equal, the primary IP address of the
+ router that sourced the Assert message is used as a tie-breaker,
+ with the highest IP address winning.
+*/
+int pim_assert_metric_better(const struct pim_assert_metric *m1,
+ const struct pim_assert_metric *m2)
+{
+ if (m1->rpt_bit_flag < m2->rpt_bit_flag)
+ return 1;
+ if (m1->rpt_bit_flag > m2->rpt_bit_flag)
+ return 0;
+
+ if (m1->metric_preference < m2->metric_preference)
+ return 1;
+ if (m1->metric_preference > m2->metric_preference)
+ return 0;
+
+ if (m1->route_metric < m2->route_metric)
+ return 1;
+ if (m1->route_metric > m2->route_metric)
+ return 0;
+
+ return ntohl(m1->ip_address.s_addr) > ntohl(m2->ip_address.s_addr);
+}
+
+int pim_assert_metric_match(const struct pim_assert_metric *m1,
+ const struct pim_assert_metric *m2)
+{
+ if (m1->rpt_bit_flag != m2->rpt_bit_flag)
+ return 0;
+ if (m1->metric_preference != m2->metric_preference)
+ return 0;
+ if (m1->route_metric != m2->route_metric)
+ return 0;
+
+ return m1->ip_address.s_addr == m2->ip_address.s_addr;
+}
+
+int pim_assert_build_msg(uint8_t *pim_msg, int buf_size,
+ struct interface *ifp,
+ struct in_addr group_addr,
+ struct in_addr source_addr,
+ uint32_t metric_preference,
+ uint32_t route_metric,
+ uint32_t rpt_bit_flag)
+{
+ uint8_t *buf_pastend = pim_msg + buf_size;
+ uint8_t *pim_msg_curr;
+ int pim_msg_size;
+ int remain;
+
+ pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* skip room for pim header */
+
+ /* Encode group */
+ remain = buf_pastend - pim_msg_curr;
+ pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
+ remain,
+ group_addr);
+ if (!pim_msg_curr) {
+ char group_str[100];
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ zlog_warn("%s: failure encoding group address %s: space left=%d",
+ __PRETTY_FUNCTION__, group_str, remain);
+ return -1;
+ }
+
+ /* Encode source */
+ remain = buf_pastend - pim_msg_curr;
+ pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
+ remain,
+ source_addr);
+ if (!pim_msg_curr) {
+ char source_str[100];
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: failure encoding source address %s: space left=%d",
+ __PRETTY_FUNCTION__, source_str, remain);
+ return -2;
+ }
+
+ /* Metric preference */
+ pim_write_uint32(pim_msg_curr, rpt_bit_flag ?
+ metric_preference | 0x80000000 :
+ metric_preference);
+ pim_msg_curr += 4;
+
+ /* Route metric */
+ pim_write_uint32(pim_msg_curr, route_metric);
+ pim_msg_curr += 4;
+
+ /*
+ Add PIM header
+ */
+ pim_msg_size = pim_msg_curr - pim_msg;
+ pim_msg_build_header(pim_msg, pim_msg_size,
+ PIM_MSG_TYPE_ASSERT);
+
+ return pim_msg_size;
+}
+
+static int pim_assert_do(struct pim_ifchannel *ch,
+ struct pim_assert_metric metric)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ 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);
+}
+
diff --git a/pimd/pim_assert.h b/pimd/pim_assert.h
new file mode 100644
index 00000000..feeb91df
--- /dev/null
+++ b/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(uint8_t *pim_msg, int buf_size,
+ struct interface *ifp,
+ struct in_addr group_addr,
+ struct in_addr source_addr,
+ uint32_t metric_preference,
+ uint32_t route_metric,
+ uint32_t rpt_bit_flag);
+
+int pim_assert_send(struct pim_ifchannel *ch);
+
+int assert_action_a1(struct pim_ifchannel *ch);
+void assert_action_a4(struct pim_ifchannel *ch);
+void assert_action_a5(struct pim_ifchannel *ch);
+
+#endif /* PIM_ASSERT_H */
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
new file mode 100644
index 00000000..23ad11f6
--- /dev/null
+++ b/pimd/pim_cmd.c
@@ -0,0 +1,4284 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <sys/ioctl.h>
+
+#include <zebra.h>
+
+#include "command.h"
+#include "if.h"
+#include "prefix.h"
+
+#include "pimd.h"
+#include "pim_cmd.h"
+#include "pim_iface.h"
+#include "pim_vty.h"
+#include "pim_mroute.h"
+#include "pim_str.h"
+#include "pim_igmpv3.h"
+#include "pim_sock.h"
+#include "pim_time.h"
+#include "pim_util.h"
+#include "pim_oil.h"
+#include "pim_neighbor.h"
+#include "pim_pim.h"
+#include "pim_ifchannel.h"
+#include "pim_hello.h"
+#include "pim_msg.h"
+#include "pim_upstream.h"
+#include "pim_rpf.h"
+#include "pim_macro.h"
+#include "pim_ssmpingd.h"
+
+static struct cmd_node pim_global_node = {
+ PIM_NODE,
+ "",
+ 1 /* vtysh ? yes */
+};
+
+static struct cmd_node interface_node = {
+ INTERFACE_NODE,
+ "%s(config-if)# ",
+ 1 /* vtysh ? yes */
+};
+
+static void pim_if_membership_clear(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ if (PIM_IF_TEST_PIM(pim_ifp->options) &&
+ PIM_IF_TEST_IGMP(pim_ifp->options)) {
+ return;
+ }
+
+ pim_ifchannel_membership_clear(ifp);
+}
+
+/*
+ When PIM is disabled on interface, IGMPv3 local membership
+ information is not injected into PIM interface state.
+
+ The function pim_if_membership_refresh() fetches all IGMPv3 local
+ membership information into PIM. It is intented to be called
+ whenever PIM is enabled on the interface in order to collect missed
+ local membership information.
+ */
+static void pim_if_membership_refresh(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *sock_node;
+ struct igmp_sock *igmp;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ if (!PIM_IF_TEST_PIM(pim_ifp->options))
+ return;
+ if (!PIM_IF_TEST_IGMP(pim_ifp->options))
+ return;
+
+ /*
+ First clear off membership from all PIM (S,G) entries on the
+ interface
+ */
+
+ pim_ifchannel_membership_clear(ifp);
+
+ /*
+ Then restore PIM (S,G) membership from all IGMPv3 (S,G) entries on
+ the interface
+ */
+
+ /* scan igmp sockets */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+ struct listnode *grpnode;
+ struct igmp_group *grp;
+
+ /* scan igmp groups */
+ for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) {
+ struct listnode *srcnode;
+ struct igmp_source *src;
+
+ /* scan group sources */
+ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) {
+
+ if (IGMP_SOURCE_TEST_FORWARDING(src->source_flags)) {
+ pim_ifchannel_local_membership_add(ifp,
+ src->source_addr,
+ grp->group_addr);
+ }
+
+ } /* scan group sources */
+ } /* scan igmp groups */
+ } /* scan igmp sockets */
+
+ /*
+ Finally delete every PIM (S,G) entry lacking all state info
+ */
+
+ pim_ifchannel_delete_on_noinfo(ifp);
+
+}
+
+static void pim_show_assert(struct vty *vty)
+{
+ struct listnode *ifnode;
+ struct interface *ifp;
+ time_t now;
+
+ now = pim_time_monotonic_sec();
+
+ vty_out(vty,
+ "Interface Address Source Group State Winner Uptime Timer%s",
+ VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+ struct listnode *ch_node;
+ struct pim_ifchannel *ch;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+ char ch_src_str[100];
+ char ch_grp_str[100];
+ char winner_str[100];
+ char uptime[10];
+ char timer[10];
+
+ pim_inet4_dump("<ch_src?>", ch->source_addr,
+ ch_src_str, sizeof(ch_src_str));
+ pim_inet4_dump("<ch_grp?>", ch->group_addr,
+ ch_grp_str, sizeof(ch_grp_str));
+ pim_inet4_dump("<assrt_win?>", ch->ifassert_winner,
+ winner_str, sizeof(winner_str));
+
+ pim_time_uptime(uptime, sizeof(uptime), now - ch->ifassert_creation);
+ pim_time_timer_to_mmss(timer, sizeof(timer),
+ ch->t_ifassert_timer);
+
+ vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %-15s %-8s %-5s%s",
+ ifp->name,
+ inet_ntoa(ifaddr),
+ ch_src_str,
+ ch_grp_str,
+ pim_ifchannel_ifassert_name(ch->ifassert_state),
+ winner_str,
+ uptime,
+ timer,
+ VTY_NEWLINE);
+ } /* scan interface channels */
+ } /* scan interfaces */
+}
+
+static void pim_show_assert_internal(struct vty *vty)
+{
+ struct listnode *ifnode;
+ struct interface *ifp;
+
+ vty_out(vty,
+ "CA: CouldAssert%s"
+ "ECA: Evaluate CouldAssert%s"
+ "ATD: AssertTrackingDesired%s"
+ "eATD: Evaluate AssertTrackingDesired%s%s",
+ VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+ vty_out(vty,
+ "Interface Address Source Group CA eCA ATD eATD%s",
+ VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+ struct listnode *ch_node;
+ struct pim_ifchannel *ch;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+ char ch_src_str[100];
+ char ch_grp_str[100];
+
+ pim_inet4_dump("<ch_src?>", ch->source_addr,
+ ch_src_str, sizeof(ch_src_str));
+ pim_inet4_dump("<ch_grp?>", ch->group_addr,
+ ch_grp_str, sizeof(ch_grp_str));
+ vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-3s %-3s %-4s%s",
+ ifp->name,
+ inet_ntoa(ifaddr),
+ ch_src_str,
+ ch_grp_str,
+ PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags) ? "yes" : "no",
+ pim_macro_ch_could_assert_eval(ch) ? "yes" : "no",
+ PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags) ? "yes" : "no",
+ pim_macro_assert_tracking_desired_eval(ch) ? "yes" : "no",
+ VTY_NEWLINE);
+ } /* scan interface channels */
+ } /* scan interfaces */
+}
+
+static void pim_show_assert_metric(struct vty *vty)
+{
+ struct listnode *ifnode;
+ struct interface *ifp;
+
+ vty_out(vty,
+ "Interface Address Source Group RPT Pref Metric Address %s",
+ VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+ struct listnode *ch_node;
+ struct pim_ifchannel *ch;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+ char ch_src_str[100];
+ char ch_grp_str[100];
+ char addr_str[100];
+ struct pim_assert_metric am;
+
+ am = pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address);
+
+ pim_inet4_dump("<ch_src?>", ch->source_addr,
+ ch_src_str, sizeof(ch_src_str));
+ pim_inet4_dump("<ch_grp?>", ch->group_addr,
+ ch_grp_str, sizeof(ch_grp_str));
+ pim_inet4_dump("<addr?>", am.ip_address,
+ addr_str, sizeof(addr_str));
+
+ vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %4u %6u %-15s%s",
+ ifp->name,
+ inet_ntoa(ifaddr),
+ ch_src_str,
+ ch_grp_str,
+ am.rpt_bit_flag ? "yes" : "no",
+ am.metric_preference,
+ am.route_metric,
+ addr_str,
+ VTY_NEWLINE);
+ } /* scan interface channels */
+ } /* scan interfaces */
+}
+
+static void pim_show_assert_winner_metric(struct vty *vty)
+{
+ struct listnode *ifnode;
+ struct interface *ifp;
+
+ vty_out(vty,
+ "Interface Address Source Group RPT Pref Metric Address %s",
+ VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+ struct listnode *ch_node;
+ struct pim_ifchannel *ch;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+ char ch_src_str[100];
+ char ch_grp_str[100];
+ char addr_str[100];
+ struct pim_assert_metric *am;
+ char pref_str[5];
+ char metr_str[7];
+
+ am = &ch->ifassert_winner_metric;
+
+ pim_inet4_dump("<ch_src?>", ch->source_addr,
+ ch_src_str, sizeof(ch_src_str));
+ pim_inet4_dump("<ch_grp?>", ch->group_addr,
+ ch_grp_str, sizeof(ch_grp_str));
+ pim_inet4_dump("<addr?>", am->ip_address,
+ addr_str, sizeof(addr_str));
+
+ if (am->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX)
+ snprintf(pref_str, sizeof(pref_str), "INFI");
+ else
+ snprintf(pref_str, sizeof(pref_str), "%4u", am->metric_preference);
+
+ if (am->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX)
+ snprintf(metr_str, sizeof(metr_str), "INFI");
+ else
+ snprintf(metr_str, sizeof(metr_str), "%6u", am->route_metric);
+
+ vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-4s %-6s %-15s%s",
+ ifp->name,
+ inet_ntoa(ifaddr),
+ ch_src_str,
+ ch_grp_str,
+ am->rpt_bit_flag ? "yes" : "no",
+ pref_str,
+ metr_str,
+ addr_str,
+ VTY_NEWLINE);
+ } /* scan interface channels */
+ } /* scan interfaces */
+}
+
+static void pim_show_membership(struct vty *vty)
+{
+ struct listnode *ifnode;
+ struct interface *ifp;
+
+ vty_out(vty,
+ "Interface Address Source Group Membership%s",
+ VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+ struct listnode *ch_node;
+ struct pim_ifchannel *ch;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+ char ch_src_str[100];
+ char ch_grp_str[100];
+
+ pim_inet4_dump("<ch_src?>", ch->source_addr,
+ ch_src_str, sizeof(ch_src_str));
+ pim_inet4_dump("<ch_grp?>", ch->group_addr,
+ ch_grp_str, sizeof(ch_grp_str));
+
+ vty_out(vty, "%-9s %-15s %-15s %-15s %-10s%s",
+ ifp->name,
+ inet_ntoa(ifaddr),
+ ch_src_str,
+ ch_grp_str,
+ ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO ?
+ "NOINFO" : "INCLUDE",
+ VTY_NEWLINE);
+ } /* scan interface channels */
+ } /* scan interfaces */
+
+}
+
+static void igmp_show_interfaces(struct vty *vty)
+{
+ struct listnode *node;
+ struct interface *ifp;
+ time_t now;
+
+ now = pim_time_monotonic_sec();
+
+ vty_out(vty,
+ "Interface Address ifIndex Socket Uptime Multi Broad MLoop AllMu Prmsc Del%s",
+ VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct listnode *sock_node;
+ struct igmp_sock *igmp;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+ char uptime[10];
+ int mloop;
+
+ pim_time_uptime(uptime, sizeof(uptime), now - igmp->sock_creation);
+
+ mloop = pim_socket_mcastloop_get(igmp->fd);
+
+ vty_out(vty, "%-9s %-15s %7d %6d %8s %5s %5s %5s %5s %5s %3s%s",
+ ifp->name,
+ inet_ntoa(igmp->ifaddr),
+ ifp->ifindex,
+ igmp->fd,
+ uptime,
+ if_is_multicast(ifp) ? "yes" : "no",
+ if_is_broadcast(ifp) ? "yes" : "no",
+ (mloop < 0) ? "?" : (mloop ? "yes" : "no"),
+ (ifp->flags & IFF_ALLMULTI) ? "yes" : "no",
+ (ifp->flags & IFF_PROMISC) ? "yes" : "no",
+ PIM_IF_IS_DELETED(ifp) ? "yes" : "no",
+ VTY_NEWLINE);
+ }
+ }
+}
+
+static void show_interface_address(struct vty *vty)
+{
+ struct listnode *ifpnode;
+ struct interface *ifp;
+
+ vty_out(vty,
+ "Interface Primary Secondary %s",
+ VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifpnode, ifp)) {
+ struct listnode *ifcnode;
+ struct connected *ifc;
+ struct in_addr pri_addr;
+ char pri_addr_str[100];
+
+ pri_addr = pim_find_primary_addr(ifp);
+
+ pim_inet4_dump("<pri?>", pri_addr, pri_addr_str, sizeof(pri_addr_str));
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, ifcnode, ifc)) {
+ char sec_addr_str[100];
+ struct prefix *p = ifc->address;
+
+ if (p->family != AF_INET)
+ continue;
+
+ if (p->u.prefix4.s_addr == pri_addr.s_addr) {
+ sec_addr_str[0] = '\0';
+ }
+ else {
+ pim_inet4_dump("<sec?>", p->u.prefix4, sec_addr_str, sizeof(sec_addr_str));
+ }
+
+ vty_out(vty, "%-9s %-15s %-15s%s",
+ ifp->name,
+ pri_addr_str,
+ sec_addr_str,
+ VTY_NEWLINE);
+ }
+ }
+}
+
+static void pim_show_dr(struct vty *vty)
+{
+ struct listnode *node;
+ struct interface *ifp;
+ time_t now;
+
+ now = pim_time_monotonic_sec();
+
+ vty_out(vty,
+ "NonPri: Number of neighbors missing DR Priority hello option%s%s",
+ VTY_NEWLINE, VTY_NEWLINE);
+
+ vty_out(vty, "Interface Address DR Uptime Elections NonPri%s", VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+ char dr_str[100];
+ char dr_uptime[10];
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ if (pim_ifp->pim_sock_fd < 0)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ pim_time_uptime(dr_uptime, sizeof(dr_uptime),
+ now - pim_ifp->pim_dr_election_last);
+
+ pim_inet4_dump("<dr?>", pim_ifp->pim_dr_addr,
+ dr_str, sizeof(dr_str));
+
+ vty_out(vty, "%-9s %-15s %-15s %8s %9d %6d%s",
+ ifp->name,
+ inet_ntoa(ifaddr),
+ dr_str,
+ dr_uptime,
+ pim_ifp->pim_dr_election_count,
+ pim_ifp->pim_dr_num_nondrpri_neighbors,
+ VTY_NEWLINE);
+ }
+}
+
+static void pim_show_hello(struct vty *vty)
+{
+ struct listnode *node;
+ struct interface *ifp;
+ time_t now;
+
+ now = pim_time_monotonic_sec();
+
+ vty_out(vty, "Interface Address Period Timer StatStart Recv Rfail Send Sfail%s", VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+ char hello_period[10];
+ char hello_timer[10];
+ char stat_uptime[10];
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ if (pim_ifp->pim_sock_fd < 0)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ pim_time_timer_to_mmss(hello_timer, sizeof(hello_timer), pim_ifp->t_pim_hello_timer);
+ pim_time_mmss(hello_period, sizeof(hello_period), pim_ifp->pim_hello_period);
+ pim_time_uptime(stat_uptime, sizeof(stat_uptime), now - pim_ifp->pim_ifstat_start);
+
+ vty_out(vty, "%-9s %-15s %6s %5s %9s %4u %5u %4u %5u%s",
+ ifp->name,
+ inet_ntoa(ifaddr),
+ hello_period,
+ hello_timer,
+ stat_uptime,
+ pim_ifp->pim_ifstat_hello_recv,
+ pim_ifp->pim_ifstat_hello_recvfail,
+ pim_ifp->pim_ifstat_hello_sent,
+ pim_ifp->pim_ifstat_hello_sendfail,
+ VTY_NEWLINE);
+ }
+}
+
+static void pim_show_interfaces(struct vty *vty)
+{
+ struct listnode *node;
+ struct interface *ifp;
+ time_t now;
+
+ now = pim_time_monotonic_sec();
+
+ vty_out(vty, "Interface Address ifIndex Socket Uptime Multi Broad MLoop AllMu Prmsc Del%s", VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+ char uptime[10];
+ int mloop;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ if (pim_ifp->pim_sock_fd < 0)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ pim_time_uptime(uptime, sizeof(uptime), now - pim_ifp->pim_sock_creation);
+
+ mloop = pim_socket_mcastloop_get(pim_ifp->pim_sock_fd);
+
+ vty_out(vty, "%-9s %-15s %7d %6d %8s %5s %5s %5s %5s %5s %3s%s",
+ ifp->name,
+ inet_ntoa(ifaddr),
+ ifp->ifindex,
+ pim_ifp->pim_sock_fd,
+ uptime,
+ if_is_multicast(ifp) ? "yes" : "no",
+ if_is_broadcast(ifp) ? "yes" : "no",
+ (mloop < 0) ? "?" : (mloop ? "yes" : "no"),
+ (ifp->flags & IFF_ALLMULTI) ? "yes" : "no",
+ (ifp->flags & IFF_PROMISC) ? "yes" : "no",
+ PIM_IF_IS_DELETED(ifp) ? "yes" : "no",
+ VTY_NEWLINE);
+ }
+}
+
+static void pim_show_join(struct vty *vty)
+{
+ struct listnode *ifnode;
+ struct interface *ifp;
+ time_t now;
+
+ now = pim_time_monotonic_sec();
+
+ vty_out(vty,
+ "Interface Address Source Group State Uptime Expire Prune%s",
+ VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+ struct listnode *ch_node;
+ struct pim_ifchannel *ch;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+ char ch_src_str[100];
+ char ch_grp_str[100];
+ char uptime[10];
+ char expire[10];
+ char prune[10];
+
+ pim_inet4_dump("<ch_src?>", ch->source_addr,
+ ch_src_str, sizeof(ch_src_str));
+ pim_inet4_dump("<ch_grp?>", ch->group_addr,
+ ch_grp_str, sizeof(ch_grp_str));
+
+ pim_time_uptime(uptime, sizeof(uptime), now - ch->ifjoin_creation);
+ pim_time_timer_to_mmss(expire, sizeof(expire),
+ ch->t_ifjoin_expiry_timer);
+ pim_time_timer_to_mmss(prune, sizeof(prune),
+ ch->t_ifjoin_prune_pending_timer);
+
+ vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %8s %-6s %5s%s",
+ ifp->name,
+ inet_ntoa(ifaddr),
+ ch_src_str,
+ ch_grp_str,
+ pim_ifchannel_ifjoin_name(ch->ifjoin_state),
+ uptime,
+ expire,
+ prune,
+ VTY_NEWLINE);
+ } /* scan interface channels */
+ } /* scan interfaces */
+
+}
+
+static void pim_show_neighbors(struct vty *vty)
+{
+ struct listnode *node;
+ struct interface *ifp;
+ time_t now;
+
+ now = pim_time_monotonic_sec();
+
+ vty_out(vty,
+ "Recv flags: H=holdtime L=lan_prune_delay P=dr_priority G=generation_id A=address_list%s"
+ " T=can_disable_join_suppression%s%s",
+ VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+ vty_out(vty, "Interface Address Neighbor Uptime Timer Holdt DrPri GenId Recv %s", VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+ struct listnode *neighnode;
+ struct pim_neighbor *neigh;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ if (pim_ifp->pim_sock_fd < 0)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
+ char uptime[10];
+ char holdtime[10];
+ char expire[10];
+ char neigh_src_str[100];
+ char recv[7];
+
+ pim_inet4_dump("<src?>", neigh->source_addr,
+ neigh_src_str, sizeof(neigh_src_str));
+ pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation);
+ pim_time_mmss(holdtime, sizeof(holdtime), neigh->holdtime);
+ pim_time_timer_to_mmss(expire, sizeof(expire), neigh->t_expire_timer);
+
+ recv[0] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_HOLDTIME) ? 'H' : ' ';
+ recv[1] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY) ? 'L' : ' ';
+ recv[2] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY) ? 'P' : ' ';
+ recv[3] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ? 'G' : ' ';
+ recv[4] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_ADDRESS_LIST) ? 'A' : ' ';
+ recv[5] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION) ? 'T' : ' ';
+ recv[6] = '\0';
+
+ vty_out(vty, "%-9s %-15s %-15s %8s %5s %5s %5u %08x %6s%s",
+ ifp->name,
+ inet_ntoa(ifaddr),
+ neigh_src_str,
+ uptime,
+ expire,
+ holdtime,
+ neigh->dr_priority,
+ neigh->generation_id,
+ recv,
+ VTY_NEWLINE);
+ }
+
+
+ }
+}
+
+static void pim_show_lan_prune_delay(struct vty *vty)
+{
+ struct listnode *node;
+ struct interface *ifp;
+
+ vty_out(vty,
+ "PrDly=propagation_delay (msec) OvInt=override_interval (msec)%s"
+ "HiDly=highest_propagation_delay (msec) HiInt=highest_override_interval (msec)%s"
+ "NoDly=number_of_non_lan_delay_neighbors%s"
+ "T=t_bit LPD=lan_prune_delay_hello_option%s%s",
+ VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+ vty_out(vty, "Interface Address PrDly OvInt NoDly HiDly HiInt T Neighbor LPD PrDly OvInt T%s", VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+ struct listnode *neighnode;
+ struct pim_neighbor *neigh;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ if (pim_ifp->pim_sock_fd < 0)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
+ char neigh_src_str[100];
+
+ pim_inet4_dump("<src?>", neigh->source_addr,
+ neigh_src_str, sizeof(neigh_src_str));
+
+ vty_out(vty, "%-9s %-15s %5u %5u %5u %5u %5u %1u %-15s %-3s %5u %5u %1u%s",
+ ifp->name,
+ inet_ntoa(ifaddr),
+ pim_ifp->pim_propagation_delay_msec,
+ pim_ifp->pim_override_interval_msec,
+ pim_ifp->pim_number_of_nonlandelay_neighbors,
+ pim_ifp->pim_neighbors_highest_propagation_delay_msec,
+ pim_ifp->pim_neighbors_highest_override_interval_msec,
+ PIM_FORCE_BOOLEAN(PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options)),
+ neigh_src_str,
+ PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY) ? "yes" : "no",
+ neigh->propagation_delay_msec,
+ neigh->override_interval_msec,
+ PIM_FORCE_BOOLEAN(PIM_OPTION_IS_SET(neigh->hello_options,
+ PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION)),
+ VTY_NEWLINE);
+ }
+
+ }
+}
+
+static void pim_show_jp_override_interval(struct vty *vty)
+{
+ struct listnode *node;
+ struct interface *ifp;
+
+ vty_out(vty,
+ "EffPDelay=effective_propagation_delay (msec)%s"
+ "EffOvrInt=override_interval (msec)%s"
+ "JPOvrInt=jp_override_interval (msec)%s%s",
+ VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+ vty_out(vty, "Interface Address LAN_Delay EffPDelay EffOvrInt JPOvrInt%s", VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ if (pim_ifp->pim_sock_fd < 0)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ vty_out(vty, "%-9s %-15s %-9s %9u %9u %8u%s",
+ ifp->name,
+ inet_ntoa(ifaddr),
+ pim_if_lan_delay_enabled(ifp) ? "enabled" : "disabled",
+ pim_if_effective_propagation_delay_msec(ifp),
+ pim_if_effective_override_interval_msec(ifp),
+ pim_if_jp_override_interval_msec(ifp),
+ VTY_NEWLINE);
+ }
+}
+
+static void pim_show_neighbors_secondary(struct vty *vty)
+{
+ struct listnode *node;
+ struct interface *ifp;
+
+ vty_out(vty, "Interface Address Neighbor Secondary %s", VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+ struct listnode *neighnode;
+ struct pim_neighbor *neigh;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ if (pim_ifp->pim_sock_fd < 0)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
+ char neigh_src_str[100];
+ struct listnode *prefix_node;
+ struct prefix *p;
+
+ if (!neigh->prefix_list)
+ continue;
+
+ pim_inet4_dump("<src?>", neigh->source_addr,
+ neigh_src_str, sizeof(neigh_src_str));
+
+ for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, prefix_node, p)) {
+ char neigh_sec_str[100];
+
+ if (p->family != AF_INET)
+ continue;
+
+ pim_inet4_dump("<src?>", p->u.prefix4,
+ neigh_sec_str, sizeof(neigh_sec_str));
+
+ vty_out(vty, "%-9s %-15s %-15s %-15s%s",
+ ifp->name,
+ inet_ntoa(ifaddr),
+ neigh_src_str,
+ neigh_sec_str,
+ VTY_NEWLINE);
+ }
+ }
+ }
+}
+
+static void pim_show_upstream(struct vty *vty)
+{
+ struct listnode *upnode;
+ struct pim_upstream *up;
+ time_t now;
+
+ now = pim_time_monotonic_sec();
+
+ vty_out(vty, "Source Group State Uptime JoinTimer RefCnt%s", VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, upnode, up)) {
+ char src_str[100];
+ char grp_str[100];
+ char uptime[10];
+ char join_timer[10];
+
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition);
+ pim_time_timer_to_hhmmss(join_timer, sizeof(join_timer), up->t_join_timer);
+
+ vty_out(vty, "%-15s %-15s %-5s %-8s %-9s %6d%s",
+ src_str,
+ grp_str,
+ up->join_state == PIM_UPSTREAM_JOINED ? "Jnd" : "NtJnd",
+ uptime,
+ join_timer,
+ up->ref_count,
+ VTY_NEWLINE);
+ }
+}
+
+static void pim_show_join_desired(struct vty *vty)
+{
+ struct listnode *ifnode;
+ struct listnode *chnode;
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_ifchannel *ch;
+ struct in_addr me_ifaddr;
+ char src_str[100];
+ char grp_str[100];
+
+ vty_out(vty,
+ "Interface Source Group LostAssert Joins PimInclude JoinDesired EvalJD%s",
+ VTY_NEWLINE);
+
+ /* scan all interfaces */
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ continue;
+
+ me_ifaddr = pim_ifp->primary_address;
+
+ /* scan per-interface (S,G) state */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, chnode, ch)) {
+ struct pim_upstream *up = ch->upstream;
+
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+
+ vty_out(vty, "%-9s %-15s %-15s %-10s %-5s %-10s %-11s %-6s%s",
+ ifp->name,
+ src_str,
+ grp_str,
+ pim_macro_ch_lost_assert(ch) ? "yes" : "no",
+ pim_macro_chisin_joins(ch) ? "yes" : "no",
+ pim_macro_chisin_pim_include(ch) ? "yes" : "no",
+ PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags) ? "yes" : "no",
+ pim_upstream_evaluate_join_desired(up) ? "yes" : "no",
+ VTY_NEWLINE);
+ }
+ }
+}
+
+static void pim_show_upstream_rpf(struct vty *vty)
+{
+ struct listnode *upnode;
+ struct pim_upstream *up;
+
+ vty_out(vty,
+ "Source Group RpfIface RibNextHop RpfAddress %s",
+ VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, upnode, up)) {
+ char src_str[100];
+ char grp_str[100];
+ char rpf_nexthop_str[100];
+ char rpf_addr_str[100];
+ struct pim_rpf *rpf;
+ const char *rpf_ifname;
+
+ rpf = &up->rpf;
+
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<nexthop?>", rpf->source_nexthop.mrib_nexthop_addr, rpf_nexthop_str, sizeof(rpf_nexthop_str));
+ pim_inet4_dump("<rpf?>", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
+
+ rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>";
+
+ vty_out(vty, "%-15s %-15s %-8s %-15s %-15s%s",
+ src_str,
+ grp_str,
+ rpf_ifname,
+ rpf_nexthop_str,
+ rpf_addr_str,
+ VTY_NEWLINE);
+ }
+}
+
+static void show_rpf_refresh_stats(struct vty *vty, time_t now)
+{
+ char refresh_uptime[10];
+
+ pim_time_uptime(refresh_uptime, sizeof(refresh_uptime), now - qpim_rpf_cache_refresh_last);
+
+ vty_out(vty,
+ "RPF Cache Refresh Delay: %ld msecs%s"
+ "RPF Cache Refresh Timer: %ld msecs%s"
+ "RPF Cache Refresh Requests: %lld%s"
+ "RPF Cache Refresh Events: %lld%s"
+ "RPF Cache Refresh Last: %s%s",
+ qpim_rpf_cache_refresh_delay_msec, VTY_NEWLINE,
+ pim_time_timer_remain_msec(qpim_rpf_cache_refresher), VTY_NEWLINE,
+ qpim_rpf_cache_refresh_requests, VTY_NEWLINE,
+ qpim_rpf_cache_refresh_events, VTY_NEWLINE,
+ refresh_uptime, VTY_NEWLINE);
+}
+
+static void pim_show_rpf(struct vty *vty)
+{
+ struct listnode *up_node;
+ struct pim_upstream *up;
+ time_t now = pim_time_monotonic_sec();
+
+ show_rpf_refresh_stats(vty, now);
+
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_out(vty,
+ "Source Group RpfIface RpfAddress RibNextHop Metric Pref%s",
+ VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
+ char src_str[100];
+ char grp_str[100];
+ char rpf_addr_str[100];
+ char rib_nexthop_str[100];
+ const char *rpf_ifname;
+ struct pim_rpf *rpf = &up->rpf;
+
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<rpf?>", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
+ pim_inet4_dump("<nexthop?>", rpf->source_nexthop.mrib_nexthop_addr, rib_nexthop_str, sizeof(rib_nexthop_str));
+
+ rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>";
+
+ vty_out(vty, "%-15s %-15s %-8s %-15s %-15s %6d %4d%s",
+ src_str,
+ grp_str,
+ rpf_ifname,
+ rpf_addr_str,
+ rib_nexthop_str,
+ rpf->source_nexthop.mrib_route_metric,
+ rpf->source_nexthop.mrib_metric_preference,
+ VTY_NEWLINE);
+ }
+}
+
+static void igmp_show_querier(struct vty *vty)
+{
+ struct listnode *node;
+ struct interface *ifp;
+
+ vty_out(vty, "Interface Address Querier StartCount Query-Timer Other-Timer%s", VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+ struct pim_interface *pim_ifp = ifp->info;
+ struct listnode *sock_node;
+ struct igmp_sock *igmp;
+
+ if (!pim_ifp)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+ char query_hhmmss[10];
+ char other_hhmmss[10];
+
+ pim_time_timer_to_hhmmss(query_hhmmss, sizeof(query_hhmmss), igmp->t_igmp_query_timer);
+ pim_time_timer_to_hhmmss(other_hhmmss, sizeof(other_hhmmss), igmp->t_other_querier_timer);
+
+ vty_out(vty, "%-9s %-15s %-7s %10d %11s %11s%s",
+ ifp->name,
+ inet_ntoa(igmp->ifaddr),
+ igmp->t_igmp_query_timer ? "THIS" : "OTHER",
+ igmp->startup_query_count,
+ query_hhmmss,
+ other_hhmmss,
+ VTY_NEWLINE);
+ }
+ }
+}
+
+static void igmp_show_groups(struct vty *vty)
+{
+ struct listnode *ifnode;
+ struct interface *ifp;
+ time_t now;
+
+ now = pim_time_monotonic_sec();
+
+ vty_out(vty, "Interface Address Group Mode Timer Srcs V Uptime %s", VTY_NEWLINE);
+
+ /* scan interfaces */
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ struct pim_interface *pim_ifp = ifp->info;
+ struct listnode *sock_node;
+ struct igmp_sock *igmp;
+
+ if (!pim_ifp)
+ continue;
+
+ /* scan igmp sockets */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+ char ifaddr_str[100];
+ struct listnode *grpnode;
+ struct igmp_group *grp;
+
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+
+ /* scan igmp groups */
+ for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) {
+ char group_str[100];
+ char hhmmss[10];
+ char uptime[10];
+
+ pim_inet4_dump("<group?>", grp->group_addr, group_str, sizeof(group_str));
+ pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss), grp->t_group_timer);
+ pim_time_uptime(uptime, sizeof(uptime), now - grp->group_creation);
+
+ vty_out(vty, "%-9s %-15s %-15s %4s %8s %4d %d %8s%s",
+ ifp->name,
+ ifaddr_str,
+ group_str,
+ grp->group_filtermode_isexcl ? "EXCL" : "INCL",
+ hhmmss,
+ grp->group_source_list ? listcount(grp->group_source_list) : 0,
+ igmp_group_compat_mode(igmp, grp),
+ uptime,
+ VTY_NEWLINE);
+
+ } /* scan igmp groups */
+ } /* scan igmp sockets */
+ } /* scan interfaces */
+}
+
+static void igmp_show_group_retransmission(struct vty *vty)
+{
+ struct listnode *ifnode;
+ struct interface *ifp;
+ time_t now;
+
+ now = pim_time_monotonic_sec();
+
+ vty_out(vty, "Interface Address Group RetTimer Counter RetSrcs%s", VTY_NEWLINE);
+
+ /* scan interfaces */
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ struct pim_interface *pim_ifp = ifp->info;
+ struct listnode *sock_node;
+ struct igmp_sock *igmp;
+
+ if (!pim_ifp)
+ continue;
+
+ /* scan igmp sockets */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+ char ifaddr_str[100];
+ struct listnode *grpnode;
+ struct igmp_group *grp;
+
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+
+ /* scan igmp groups */
+ for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) {
+ char group_str[100];
+ char grp_retr_mmss[10];
+ struct listnode *src_node;
+ struct igmp_source *src;
+ int grp_retr_sources = 0;
+
+ pim_inet4_dump("<group?>", grp->group_addr, group_str, sizeof(group_str));
+ pim_time_timer_to_mmss(grp_retr_mmss, sizeof(grp_retr_mmss), grp->t_group_query_retransmit_timer);
+
+
+ /* count group sources with retransmission state */
+ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, src_node, src)) {
+ if (src->source_query_retransmit_count > 0) {
+ ++grp_retr_sources;
+ }
+ }
+
+ vty_out(vty, "%-9s %-15s %-15s %-8s %7d %7d%s",
+ ifp->name,
+ ifaddr_str,
+ group_str,
+ grp_retr_mmss,
+ grp->group_specific_query_retransmit_count,
+ grp_retr_sources,
+ VTY_NEWLINE);
+
+ } /* scan igmp groups */
+ } /* scan igmp sockets */
+ } /* scan interfaces */
+}
+
+static void igmp_show_parameters(struct vty *vty)
+{
+ struct listnode *ifnode;
+ struct interface *ifp;
+
+ vty_out(vty,
+ "QRV: Robustness Variable SQI: Startup Query Interval%s"
+ "QQI: Query Interval OQPI: Other Querier Present Interval%s"
+ "QRI: Query Response Interval LMQT: Last Member Query Time%s"
+ "GMI: Group Membership Interval OHPI: Older Host Present Interval%s%s",
+ VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+ vty_out(vty,
+ "Interface Address QRV QQI QRI GMI SQI OQPI LMQT OHPI %s",
+ VTY_NEWLINE);
+
+ /* scan interfaces */
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ struct pim_interface *pim_ifp = ifp->info;
+ struct listnode *sock_node;
+ struct igmp_sock *igmp;
+
+ if (!pim_ifp)
+ continue;
+
+ /* scan igmp sockets */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+ char ifaddr_str[100];
+ long gmi_dsec; /* Group Membership Interval */
+ long oqpi_dsec; /* Other Querier Present Interval */
+ int sqi;
+ long lmqt_dsec;
+ long ohpi_dsec;
+ long qri_dsec;
+
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+
+ gmi_dsec = PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
+ igmp->querier_query_interval,
+ pim_ifp->igmp_query_max_response_time_dsec) / 100;
+
+ sqi = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
+
+ oqpi_dsec = PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable,
+ igmp->querier_query_interval,
+ pim_ifp->igmp_query_max_response_time_dsec) / 100;
+
+ lmqt_dsec = PIM_IGMP_LMQT_MSEC(pim_ifp->igmp_query_max_response_time_dsec,
+ igmp->querier_robustness_variable) / 100;
+
+ ohpi_dsec = PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
+ igmp->querier_query_interval,
+ pim_ifp->igmp_query_max_response_time_dsec);
+
+ qri_dsec = pim_ifp->igmp_query_max_response_time_dsec;
+
+ vty_out(vty,
+ "%-9s %-15s %3d %3d %3ld.%ld %3ld.%ld %3d %3ld.%ld %3ld.%ld %3ld.%ld%s",
+ ifp->name,
+ ifaddr_str,
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval,
+ qri_dsec / 10, qri_dsec % 10,
+ gmi_dsec / 10, gmi_dsec % 10,
+ sqi,
+ oqpi_dsec / 10, oqpi_dsec % 10,
+ lmqt_dsec / 10, lmqt_dsec % 10,
+ ohpi_dsec / 10, ohpi_dsec % 10,
+ VTY_NEWLINE);
+
+ } /* scan igmp sockets */
+ } /* scan interfaces */
+}
+
+static void igmp_show_sources(struct vty *vty)
+{
+ struct listnode *ifnode;
+ struct interface *ifp;
+ time_t now;
+
+ now = pim_time_monotonic_sec();
+
+ vty_out(vty, "Interface Address Group Source Timer Fwd Uptime %s", VTY_NEWLINE);
+
+ /* scan interfaces */
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ struct pim_interface *pim_ifp = ifp->info;
+ struct listnode *sock_node;
+ struct igmp_sock *igmp;
+
+ if (!pim_ifp)
+ continue;
+
+ /* scan igmp sockets */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+ char ifaddr_str[100];
+ struct listnode *grpnode;
+ struct igmp_group *grp;
+
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+
+ /* scan igmp groups */
+ for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) {
+ char group_str[100];
+ struct listnode *srcnode;
+ struct igmp_source *src;
+
+ pim_inet4_dump("<group?>", grp->group_addr, group_str, sizeof(group_str));
+
+ /* scan group sources */
+ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) {
+ char source_str[100];
+ char mmss[10];
+ char uptime[10];
+
+ pim_inet4_dump("<source?>", src->source_addr, source_str, sizeof(source_str));
+
+ pim_time_timer_to_mmss(mmss, sizeof(mmss), src->t_source_timer);
+
+ pim_time_uptime(uptime, sizeof(uptime), now - src->source_creation);
+
+ vty_out(vty, "%-9s %-15s %-15s %-15s %5s %3s %8s%s",
+ ifp->name,
+ ifaddr_str,
+ group_str,
+ source_str,
+ mmss,
+ IGMP_SOURCE_TEST_FORWARDING(src->source_flags) ? "Y" : "N",
+ uptime,
+ VTY_NEWLINE);
+
+ } /* scan group sources */
+ } /* scan igmp groups */
+ } /* scan igmp sockets */
+ } /* scan interfaces */
+}
+
+static void igmp_show_source_retransmission(struct vty *vty)
+{
+ struct listnode *ifnode;
+ struct interface *ifp;
+ time_t now;
+
+ now = pim_time_monotonic_sec();
+
+ vty_out(vty, "Interface Address Group Source Counter%s", VTY_NEWLINE);
+
+ /* scan interfaces */
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ struct pim_interface *pim_ifp = ifp->info;
+ struct listnode *sock_node;
+ struct igmp_sock *igmp;
+
+ if (!pim_ifp)
+ continue;
+
+ /* scan igmp sockets */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+ char ifaddr_str[100];
+ struct listnode *grpnode;
+ struct igmp_group *grp;
+
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+
+ /* scan igmp groups */
+ for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) {
+ char group_str[100];
+ struct listnode *srcnode;
+ struct igmp_source *src;
+
+ pim_inet4_dump("<group?>", grp->group_addr, group_str, sizeof(group_str));
+
+ /* scan group sources */
+ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) {
+ char source_str[100];
+
+ pim_inet4_dump("<source?>", src->source_addr, source_str, sizeof(source_str));
+
+ vty_out(vty, "%-9s %-15s %-15s %-15s %7d%s",
+ ifp->name,
+ ifaddr_str,
+ group_str,
+ source_str,
+ src->source_query_retransmit_count,
+ VTY_NEWLINE);
+
+ } /* scan group sources */
+ } /* scan igmp groups */
+ } /* scan igmp sockets */
+ } /* scan interfaces */
+}
+
+static void clear_igmp_interfaces()
+{
+ struct listnode *ifnode;
+ struct listnode *ifnextnode;
+ struct interface *ifp;
+
+ for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+ pim_if_addr_del_all_igmp(ifp);
+ }
+
+ for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+ pim_if_addr_add_all(ifp);
+ }
+}
+
+static void clear_pim_interfaces()
+{
+ struct listnode *ifnode;
+ struct listnode *ifnextnode;
+ struct interface *ifp;
+
+ for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+ if (ifp->info) {
+ pim_neighbor_delete_all(ifp, "interface cleared");
+ }
+ }
+}
+
+static void clear_interfaces()
+{
+ clear_igmp_interfaces();
+ clear_pim_interfaces();
+}
+
+DEFUN (pim_interface,
+ pim_interface_cmd,
+ "interface IFNAME",
+ "Select an interface to configure\n"
+ "Interface's name\n")
+{
+ struct interface *ifp;
+ const char *ifname = argv[0];
+ size_t sl;
+
+ sl = strlen(ifname);
+ if (sl > INTERFACE_NAMSIZ) {
+ vty_out(vty, "%% Interface name %s is invalid: length exceeds "
+ "%d characters%s",
+ ifname, INTERFACE_NAMSIZ, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ ifp = if_lookup_by_name_len(ifname, sl);
+ if (!ifp) {
+ vty_out(vty, "%% Interface %s does not exist%s", ifname, VTY_NEWLINE);
+
+ /* Returning here would prevent pimd from booting when there are
+ interface commands in pimd.conf, since all interfaces are
+ unknown at pimd boot time (the zebra daemon has not been
+ contacted for interface discovery). */
+
+ ifp = if_get_by_name_len(ifname, sl);
+ if (!ifp) {
+ vty_out(vty, "%% Could not create interface %s%s", ifname, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ vty->index = ifp;
+ vty->node = INTERFACE_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (clear_ip_interfaces,
+ clear_ip_interfaces_cmd,
+ "clear ip interfaces",
+ CLEAR_STR
+ IP_STR
+ "Reset interfaces\n")
+{
+ clear_interfaces();
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (clear_ip_igmp_interfaces,
+ clear_ip_igmp_interfaces_cmd,
+ "clear ip igmp interfaces",
+ CLEAR_STR
+ IP_STR
+ CLEAR_IP_IGMP_STR
+ "Reset IGMP interfaces\n")
+{
+ clear_igmp_interfaces();
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (clear_ip_pim_interfaces,
+ clear_ip_pim_interfaces_cmd,
+ "clear ip pim interfaces",
+ CLEAR_STR
+ IP_STR
+ CLEAR_IP_PIM_STR
+ "Reset PIM interfaces\n")
+{
+ clear_pim_interfaces();
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_interface,
+ show_ip_igmp_interface_cmd,
+ "show ip igmp interface",
+ SHOW_STR
+ IP_STR
+ IGMP_STR
+ "IGMP interface information\n")
+{
+ igmp_show_interfaces(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_groups,
+ show_ip_igmp_groups_cmd,
+ "show ip igmp groups",
+ SHOW_STR
+ IP_STR
+ IGMP_STR
+ IGMP_GROUP_STR)
+{
+ igmp_show_groups(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_groups_retransmissions,
+ show_ip_igmp_groups_retransmissions_cmd,
+ "show ip igmp groups retransmissions",
+ SHOW_STR
+ IP_STR
+ IGMP_STR
+ IGMP_GROUP_STR
+ "IGMP group retransmissions\n")
+{
+ igmp_show_group_retransmission(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_parameters,
+ show_ip_igmp_parameters_cmd,
+ "show ip igmp parameters",
+ SHOW_STR
+ IP_STR
+ IGMP_STR
+ "IGMP parameters information\n")
+{
+ igmp_show_parameters(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_sources,
+ show_ip_igmp_sources_cmd,
+ "show ip igmp sources",
+ SHOW_STR
+ IP_STR
+ IGMP_STR
+ IGMP_SOURCE_STR)
+{
+ igmp_show_sources(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_sources_retransmissions,
+ show_ip_igmp_sources_retransmissions_cmd,
+ "show ip igmp sources retransmissions",
+ SHOW_STR
+ IP_STR
+ IGMP_STR
+ IGMP_SOURCE_STR
+ "IGMP source retransmissions\n")
+{
+ igmp_show_source_retransmission(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_querier,
+ show_ip_igmp_querier_cmd,
+ "show ip igmp querier",
+ SHOW_STR
+ IP_STR
+ IGMP_STR
+ "IGMP querier information\n")
+{
+ igmp_show_querier(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_address,
+ show_ip_pim_address_cmd,
+ "show ip pim address",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM interface address\n")
+{
+ show_interface_address(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_assert,
+ show_ip_pim_assert_cmd,
+ "show ip pim assert",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM interface assert\n")
+{
+ pim_show_assert(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_assert_internal,
+ show_ip_pim_assert_internal_cmd,
+ "show ip pim assert-internal",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM interface internal assert state\n")
+{
+ pim_show_assert_internal(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_assert_metric,
+ show_ip_pim_assert_metric_cmd,
+ "show ip pim assert-metric",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM interface assert metric\n")
+{
+ pim_show_assert_metric(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_assert_winner_metric,
+ show_ip_pim_assert_winner_metric_cmd,
+ "show ip pim assert-winner-metric",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM interface assert winner metric\n")
+{
+ pim_show_assert_winner_metric(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_dr,
+ show_ip_pim_dr_cmd,
+ "show ip pim designated-router",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM interface designated router\n")
+{
+ pim_show_dr(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_hello,
+ show_ip_pim_hello_cmd,
+ "show ip pim hello",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM interface hello information\n")
+{
+ pim_show_hello(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_interface,
+ show_ip_pim_interface_cmd,
+ "show ip pim interface",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM interface information\n")
+{
+ pim_show_interfaces(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_join,
+ show_ip_pim_join_cmd,
+ "show ip pim join",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM interface join information\n")
+{
+ pim_show_join(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_lan_prune_delay,
+ show_ip_pim_lan_prune_delay_cmd,
+ "show ip pim lan-prune-delay",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM neighbors LAN prune delay parameters\n")
+{
+ pim_show_lan_prune_delay(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_local_membership,
+ show_ip_pim_local_membership_cmd,
+ "show ip pim local-membership",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM interface local-membership\n")
+{
+ pim_show_membership(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_jp_override_interval,
+ show_ip_pim_jp_override_interval_cmd,
+ "show ip pim jp-override-interval",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM interface J/P override interval\n")
+{
+ pim_show_jp_override_interval(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_neighbor,
+ show_ip_pim_neighbor_cmd,
+ "show ip pim neighbor",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM neighbor information\n")
+{
+ pim_show_neighbors(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_secondary,
+ show_ip_pim_secondary_cmd,
+ "show ip pim secondary",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM neighbor addresses\n")
+{
+ pim_show_neighbors_secondary(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_upstream,
+ show_ip_pim_upstream_cmd,
+ "show ip pim upstream",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM upstream information\n")
+{
+ pim_show_upstream(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_upstream_join_desired,
+ show_ip_pim_upstream_join_desired_cmd,
+ "show ip pim upstream-join-desired",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM upstream join-desired\n")
+{
+ pim_show_join_desired(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_upstream_rpf,
+ show_ip_pim_upstream_rpf_cmd,
+ "show ip pim upstream-rpf",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM upstream source rpf\n")
+{
+ pim_show_upstream_rpf(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_rpf,
+ show_ip_pim_rpf_cmd,
+ "show ip pim rpf",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ "PIM cached source rpf information\n")
+{
+ pim_show_rpf(vty);
+
+ return CMD_SUCCESS;
+}
+
+static void show_multicast_interfaces(struct vty *vty)
+{
+ struct listnode *node;
+ struct interface *ifp;
+
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_out(vty, "Interface Address ifi Vif PktsIn PktsOut BytesIn BytesOut%s",
+ VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+ struct sioc_vif_req vreq;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ memset(&vreq, 0, sizeof(vreq));
+ vreq.vifi = pim_ifp->mroute_vif_index;
+
+ if (ioctl(qpim_mroute_socket_fd, SIOCGETVIFCNT, &vreq)) {
+ int e = errno;
+ vty_out(vty,
+ "ioctl(SIOCGETVIFCNT=%d) failure for interface %s vif_index=%d: errno=%d: %s%s",
+ SIOCGETVIFCNT,
+ ifp->name,
+ pim_ifp->mroute_vif_index,
+ e,
+ safe_strerror(e),
+ VTY_NEWLINE);
+ continue;
+ }
+
+ ifaddr = pim_ifp->primary_address;
+
+ vty_out(vty, "%-9s %-15s %3d %3d %7lu %7lu %10lu %10lu%s",
+ ifp->name,
+ inet_ntoa(ifaddr),
+ ifp->ifindex,
+ pim_ifp->mroute_vif_index,
+ vreq.icount,
+ vreq.ocount,
+ vreq.ibytes,
+ vreq.obytes,
+ VTY_NEWLINE);
+ }
+}
+
+DEFUN (show_ip_multicast,
+ show_ip_multicast_cmd,
+ "show ip multicast",
+ SHOW_STR
+ IP_STR
+ "Multicast global information\n")
+{
+ time_t now = pim_time_monotonic_sec();
+
+ if (PIM_MROUTE_IS_ENABLED) {
+ char uptime[10];
+
+ vty_out(vty, "Mroute socket descriptor: %d%s",
+ qpim_mroute_socket_fd,
+ VTY_NEWLINE);
+
+ pim_time_uptime(uptime, sizeof(uptime), now - qpim_mroute_socket_creation);
+ vty_out(vty, "Mroute socket uptime: %s%s",
+ uptime,
+ VTY_NEWLINE);
+ }
+ else {
+ vty_out(vty, "Multicast disabled%s",
+ VTY_NEWLINE);
+ }
+
+ vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "Current highest VifIndex: %d%s",
+ qpim_mroute_oif_highest_vif_index,
+ VTY_NEWLINE);
+ vty_out(vty, "Maximum highest VifIndex: %d%s",
+ MAXVIFS - 1,
+ VTY_NEWLINE);
+
+ vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "Upstream Join Timer: %d secs%s",
+ qpim_t_periodic,
+ VTY_NEWLINE);
+ vty_out(vty, "Join/Prune Holdtime: %d secs%s",
+ PIM_JP_HOLDTIME,
+ VTY_NEWLINE);
+
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ show_rpf_refresh_stats(vty, now);
+
+ show_multicast_interfaces(vty);
+
+ return CMD_SUCCESS;
+}
+
+static void show_mroute(struct vty *vty)
+{
+ struct listnode *node;
+ struct channel_oil *c_oil;
+ time_t now;
+
+ vty_out(vty, "Proto: I=IGMP P=PIM%s%s", VTY_NEWLINE, VTY_NEWLINE);
+
+ vty_out(vty, "Source Group Proto Input iVifI Output oVifI TTL Uptime %s",
+ VTY_NEWLINE);
+
+ now = pim_time_monotonic_sec();
+
+ for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
+ char group_str[100];
+ char source_str[100];
+ int oif_vif_index;
+
+ pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+
+ for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) {
+ struct interface *ifp_in;
+ struct interface *ifp_out;
+ char oif_uptime[10];
+ int ttl;
+ char proto[5];
+
+ ttl = c_oil->oil.mfcc_ttls[oif_vif_index];
+ if (ttl < 1)
+ continue;
+
+ ifp_in = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent);
+ ifp_out = pim_if_find_by_vif_index(oif_vif_index);
+
+ pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - c_oil->oif_creation[oif_vif_index]);
+
+ proto[0] = '\0';
+ if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM) {
+ strcat(proto, "P");
+ }
+ if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) {
+ strcat(proto, "I");
+ }
+
+ vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s",
+ source_str,
+ group_str,
+ proto,
+ ifp_in ? ifp_in->name : "<iif?>",
+ c_oil->oil.mfcc_parent,
+ ifp_out ? ifp_out->name : "<oif?>",
+ oif_vif_index,
+ ttl,
+ oif_uptime,
+ VTY_NEWLINE);
+ }
+ }
+}
+
+DEFUN (show_ip_mroute,
+ show_ip_mroute_cmd,
+ "show ip mroute",
+ SHOW_STR
+ IP_STR
+ MROUTE_STR)
+{
+ show_mroute(vty);
+ return CMD_SUCCESS;
+}
+
+static void show_mroute_count(struct vty *vty)
+{
+ struct listnode *node;
+ struct channel_oil *c_oil;
+
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_out(vty, "Source Group Packets Bytes WrongIf %s",
+ VTY_NEWLINE);
+
+ for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
+ char group_str[100];
+ char source_str[100];
+ struct sioc_sg_req sgreq;
+
+ memset(&sgreq, 0, sizeof(sgreq));
+ sgreq.src = c_oil->oil.mfcc_origin;
+ sgreq.grp = c_oil->oil.mfcc_mcastgrp;
+
+ pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+
+ if (ioctl(qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq)) {
+ int e = errno;
+ vty_out(vty,
+ "ioctl(SIOCGETSGCNT=%d) failure for (S,G)=(%s,%s): errno=%d: %s%s",
+ SIOCGETSGCNT,
+ source_str,
+ group_str,
+ e,
+ safe_strerror(e),
+ VTY_NEWLINE);
+ continue;
+ }
+
+ vty_out(vty, "%-15s %-15s %7ld %10ld %7ld %s",
+ source_str,
+ group_str,
+ sgreq.pktcnt,
+ sgreq.bytecnt,
+ sgreq.wrong_if,
+ VTY_NEWLINE);
+
+ }
+}
+
+DEFUN (show_ip_mroute_count,
+ show_ip_mroute_count_cmd,
+ "show ip mroute count",
+ SHOW_STR
+ IP_STR
+ MROUTE_STR
+ "Route and packet count data\n")
+{
+ show_mroute_count(vty);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_route,
+ show_ip_route_cmd,
+ "show ip route A.B.C.D",
+ SHOW_STR
+ IP_STR
+ ROUTE_STR
+ "Unicast address\n")
+{
+ struct in_addr addr;
+ const char *addr_str;
+ struct pim_nexthop nexthop;
+ char nexthop_addr_str[100];
+ int result;
+
+ addr_str = argv[0];
+ result = inet_pton(AF_INET, addr_str, &addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad unicast address %s: errno=%d: %s%s",
+ addr_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (pim_nexthop_lookup(&nexthop, addr)) {
+ vty_out(vty, "Failure querying RIB nexthop for unicast address %s%s",
+ addr_str, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, "Address NextHop Interface Metric Preference%s",
+ VTY_NEWLINE);
+
+ pim_inet4_dump("<nexthop?>", nexthop.mrib_nexthop_addr,
+ nexthop_addr_str, sizeof(nexthop_addr_str));
+
+ vty_out(vty, "%-15s %-15s %-9s %6d %10d%s",
+ addr_str,
+ nexthop_addr_str,
+ nexthop.interface ? nexthop.interface->name : "<ifname?>",
+ nexthop.mrib_route_metric,
+ nexthop.mrib_metric_preference,
+ VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+static void show_ssmpingd(struct vty *vty)
+{
+ struct listnode *node;
+ struct ssmpingd_sock *ss;
+ time_t now;
+
+ vty_out(vty, "Source Socket Address Port Uptime Requests%s",
+ VTY_NEWLINE);
+
+ if (!qpim_ssmpingd_list)
+ return;
+
+ now = pim_time_monotonic_sec();
+
+ for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) {
+ char source_str[100];
+ char ss_uptime[10];
+ struct sockaddr_in bind_addr;
+ int len = sizeof(bind_addr);
+ char bind_addr_str[100];
+
+ pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
+
+ if (pim_socket_getsockname(ss->sock_fd, (struct sockaddr *) &bind_addr, &len)) {
+ vty_out(vty, "%% Failure reading socket name for ssmpingd source %s on fd=%d%s",
+ source_str, ss->sock_fd, VTY_NEWLINE);
+ }
+
+ pim_inet4_dump("<addr?>", bind_addr.sin_addr, bind_addr_str, sizeof(bind_addr_str));
+ pim_time_uptime(ss_uptime, sizeof(ss_uptime), now - ss->creation);
+
+ vty_out(vty, "%-15s %6d %-15s %5d %8s %8lld%s",
+ source_str,
+ ss->sock_fd,
+ bind_addr_str,
+ ntohs(bind_addr.sin_port),
+ ss_uptime,
+ ss->requests,
+ VTY_NEWLINE);
+ }
+}
+
+DEFUN (show_ip_ssmpingd,
+ show_ip_ssmpingd_cmd,
+ "show ip ssmpingd",
+ SHOW_STR
+ IP_STR
+ SHOW_SSMPINGD_STR)
+{
+ show_ssmpingd(vty);
+ return CMD_SUCCESS;
+}
+
+static void mroute_add_all()
+{
+ struct listnode *node;
+ struct channel_oil *c_oil;
+
+ for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
+ if (pim_mroute_add(&c_oil->oil)) {
+ /* just log warning */
+ char source_str[100];
+ char group_str[100];
+ pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str, group_str);
+ }
+ }
+}
+
+static void mroute_del_all()
+{
+ struct listnode *node;
+ struct channel_oil *c_oil;
+
+ for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
+ if (pim_mroute_del(&c_oil->oil)) {
+ /* just log warning */
+ char source_str[100];
+ char group_str[100];
+ pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str, group_str);
+ }
+ }
+}
+
+DEFUN (ip_multicast_routing,
+ ip_multicast_routing_cmd,
+ PIM_CMD_IP_MULTICAST_ROUTING,
+ IP_STR
+ "Enable IP multicast forwarding\n")
+{
+ pim_mroute_socket_enable();
+ pim_if_add_vif_all();
+ mroute_add_all();
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_multicast_routing,
+ no_ip_multicast_routing_cmd,
+ PIM_CMD_NO " " PIM_CMD_IP_MULTICAST_ROUTING,
+ NO_STR
+ IP_STR
+ "Global IP configuration subcommands\n"
+ "Enable IP multicast forwarding\n")
+{
+ mroute_del_all();
+ pim_if_del_vif_all();
+ pim_mroute_socket_disable();
+ return CMD_SUCCESS;
+}
+
+DEFUN (ip_ssmpingd,
+ ip_ssmpingd_cmd,
+ "ip ssmpingd [A.B.C.D]",
+ IP_STR
+ CONF_SSMPINGD_STR
+ "Source address\n")
+{
+ int result;
+ struct in_addr source_addr;
+ const char *source_str = (argc > 0) ? argv[0] : "0.0.0.0";
+
+ result = inet_pton(AF_INET, source_str, &source_addr);
+ if (result <= 0) {
+ vty_out(vty, "%% Bad source address %s: errno=%d: %s%s",
+ source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ result = pim_ssmpingd_start(source_addr);
+ if (result) {
+ vty_out(vty, "%% Failure starting ssmpingd for source %s: %d%s",
+ source_str, result, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_ssmpingd,
+ no_ip_ssmpingd_cmd,
+ "no ip ssmpingd [A.B.C.D]",
+ NO_STR
+ IP_STR
+ CONF_SSMPINGD_STR
+ "Source address\n")
+{
+ int result;
+ struct in_addr source_addr;
+ const char *source_str = (argc > 0) ? argv[0] : "0.0.0.0";
+
+ result = inet_pton(AF_INET, source_str, &source_addr);
+ if (result <= 0) {
+ vty_out(vty, "%% Bad source address %s: errno=%d: %s%s",
+ source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ result = pim_ssmpingd_stop(source_addr);
+ if (result) {
+ vty_out(vty, "%% Failure stopping ssmpingd for source %s: %d%s",
+ source_str, result, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (interface_ip_igmp,
+ interface_ip_igmp_cmd,
+ "ip igmp",
+ IP_STR
+ IFACE_IGMP_STR)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+
+ ifp = vty->index;
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp) {
+ pim_ifp = pim_if_new(ifp, 1 /* igmp=true */, 0 /* pim=false */);
+ if (!pim_ifp) {
+ vty_out(vty, "Could not enable IGMP on interface %s%s",
+ ifp->name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+ else {
+ PIM_IF_DO_IGMP(pim_ifp->options);
+ }
+
+ pim_if_addr_add_all(ifp);
+ pim_if_membership_refresh(ifp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_igmp,
+ interface_no_ip_igmp_cmd,
+ "no ip igmp",
+ NO_STR
+ IP_STR
+ IFACE_IGMP_STR)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+
+ ifp = vty->index;
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ return CMD_SUCCESS;
+
+ PIM_IF_DONT_IGMP(pim_ifp->options);
+
+ pim_if_membership_clear(ifp);
+
+ pim_if_addr_del_all(ifp);
+
+ if (!PIM_IF_TEST_PIM(pim_ifp->options)) {
+ pim_if_delete(ifp);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (interface_ip_igmp_join,
+ interface_ip_igmp_join_cmd,
+ "ip igmp join A.B.C.D A.B.C.D",
+ IP_STR
+ IFACE_IGMP_STR
+ "IGMP join multicast group\n"
+ "Multicast group address\n"
+ "Source address\n")
+{
+ struct interface *ifp;
+ const char *group_str;
+ const char *source_str;
+ struct in_addr group_addr;
+ struct in_addr source_addr;
+ int result;
+
+ ifp = vty->index;
+
+ /* Group address */
+ group_str = argv[0];
+ result = inet_pton(AF_INET, group_str, &group_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+ group_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Source address */
+ source_str = argv[1];
+ result = inet_pton(AF_INET, source_str, &source_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+ source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ result = pim_if_igmp_join_add(ifp, group_addr, source_addr);
+ if (result) {
+ vty_out(vty, "%% Failure joining IGMP group %s source %s on interface %s: %d%s",
+ group_str, source_str, ifp->name, result, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_igmp_join,
+ interface_no_ip_igmp_join_cmd,
+ "no ip igmp join A.B.C.D A.B.C.D",
+ NO_STR
+ IP_STR
+ IFACE_IGMP_STR
+ "IGMP join multicast group\n"
+ "Multicast group address\n"
+ "Source address\n")
+{
+ struct interface *ifp;
+ const char *group_str;
+ const char *source_str;
+ struct in_addr group_addr;
+ struct in_addr source_addr;
+ int result;
+
+ ifp = vty->index;
+
+ /* Group address */
+ group_str = argv[0];
+ result = inet_pton(AF_INET, group_str, &group_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+ group_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Source address */
+ source_str = argv[1];
+ result = inet_pton(AF_INET, source_str, &source_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+ source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ result = pim_if_igmp_join_del(ifp, group_addr, source_addr);
+ if (result) {
+ vty_out(vty, "%% Failure leaving IGMP group %s source %s on interface %s: %d%s",
+ group_str, source_str, ifp->name, result, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/*
+ CLI reconfiguration affects the interface level (struct pim_interface).
+ This function propagates the reconfiguration to every active socket
+ for that interface.
+ */
+static void igmp_sock_query_interval_reconfig(struct igmp_sock *igmp)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+
+ zassert(igmp);
+
+ /* other querier present? */
+
+ if (igmp->t_other_querier_timer)
+ return;
+
+ /* this is the querier */
+
+ zassert(igmp->interface);
+ zassert(igmp->interface->info);
+
+ ifp = igmp->interface;
+ pim_ifp = ifp->info;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char ifaddr_str[100];
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+ zlog_debug("%s: Querier %s on %s reconfig query_interval=%d",
+ __PRETTY_FUNCTION__,
+ ifaddr_str,
+ ifp->name,
+ pim_ifp->igmp_default_query_interval);
+ }
+
+ /*
+ igmp_startup_mode_on() will reset QQI:
+
+ igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
+ */
+ igmp_startup_mode_on(igmp);
+}
+
+static void igmp_sock_query_reschedule(struct igmp_sock *igmp)
+{
+ if (igmp->t_igmp_query_timer) {
+ /* other querier present */
+ zassert(igmp->t_igmp_query_timer);
+ zassert(!igmp->t_other_querier_timer);
+
+ pim_igmp_general_query_off(igmp);
+ pim_igmp_general_query_on(igmp);
+
+ zassert(igmp->t_igmp_query_timer);
+ zassert(!igmp->t_other_querier_timer);
+ }
+ else {
+ /* this is the querier */
+
+ zassert(!igmp->t_igmp_query_timer);
+ zassert(igmp->t_other_querier_timer);
+
+ pim_igmp_other_querier_timer_off(igmp);
+ pim_igmp_other_querier_timer_on(igmp);
+
+ zassert(!igmp->t_igmp_query_timer);
+ zassert(igmp->t_other_querier_timer);
+ }
+}
+
+static void change_query_interval(struct pim_interface *pim_ifp,
+ int query_interval)
+{
+ struct listnode *sock_node;
+ struct igmp_sock *igmp;
+
+ pim_ifp->igmp_default_query_interval = query_interval;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+ igmp_sock_query_interval_reconfig(igmp);
+ igmp_sock_query_reschedule(igmp);
+ }
+}
+
+static void change_query_max_response_time(struct pim_interface *pim_ifp,
+ int query_max_response_time_dsec)
+{
+ struct listnode *sock_node;
+ struct igmp_sock *igmp;
+
+ pim_ifp->igmp_query_max_response_time_dsec = query_max_response_time_dsec;
+
+ /*
+ Below we modify socket/group/source timers in order to quickly
+ reflect the change. Otherwise, those timers would eventually catch
+ up.
+ */
+
+ /* scan all sockets */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+ struct listnode *grp_node;
+ struct igmp_group *grp;
+
+ /* reschedule socket general query */
+ igmp_sock_query_reschedule(igmp);
+
+ /* scan socket groups */
+ for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grp_node, grp)) {
+ struct listnode *src_node;
+ struct igmp_source *src;
+
+ /* reset group timers for groups in EXCLUDE mode */
+ if (grp->group_filtermode_isexcl) {
+ igmp_group_reset_gmi(grp);
+ }
+
+ /* scan group sources */
+ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, src_node, src)) {
+
+ /* reset source timers for sources with running timers */
+ if (src->t_source_timer) {
+ igmp_source_reset_gmi(igmp, grp, src);
+ }
+ }
+ }
+ }
+}
+
+#define IGMP_QUERY_INTERVAL_MIN (1)
+#define IGMP_QUERY_INTERVAL_MAX (1800)
+
+DEFUN (interface_ip_igmp_query_interval,
+ interface_ip_igmp_query_interval_cmd,
+ PIM_CMD_IP_IGMP_QUERY_INTERVAL " <1-1800>",
+ IP_STR
+ IFACE_IGMP_STR
+ IFACE_IGMP_QUERY_INTERVAL_STR
+ "Query interval in seconds\n")
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ int query_interval;
+ int query_interval_dsec;
+
+ ifp = vty->index;
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp) {
+ vty_out(vty,
+ "IGMP not enabled on interface %s. Please enable IGMP first.%s",
+ ifp->name,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ query_interval = atoi(argv[0]);
+ query_interval_dsec = 10 * query_interval;
+
+ /*
+ It seems we don't need to check bounds since command.c does it
+ already, but we verify them anyway for extra safety.
+ */
+ if (query_interval < IGMP_QUERY_INTERVAL_MIN) {
+ vty_out(vty, "General query interval %d lower than minimum %d%s",
+ query_interval,
+ IGMP_QUERY_INTERVAL_MIN,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (query_interval > IGMP_QUERY_INTERVAL_MAX) {
+ vty_out(vty, "General query interval %d higher than maximum %d%s",
+ query_interval,
+ IGMP_QUERY_INTERVAL_MAX,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (query_interval_dsec <= pim_ifp->igmp_query_max_response_time_dsec) {
+ vty_out(vty,
+ "Can't set general query interval %d dsec <= query max response time %d dsec.%s",
+ query_interval_dsec, pim_ifp->igmp_query_max_response_time_dsec,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ change_query_interval(pim_ifp, query_interval);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_igmp_query_interval,
+ interface_no_ip_igmp_query_interval_cmd,
+ PIM_CMD_NO " " PIM_CMD_IP_IGMP_QUERY_INTERVAL,
+ NO_STR
+ IP_STR
+ IFACE_IGMP_STR
+ IFACE_IGMP_QUERY_INTERVAL_STR)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ int default_query_interval_dsec;
+
+ ifp = vty->index;
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ return CMD_SUCCESS;
+
+ default_query_interval_dsec = IGMP_GENERAL_QUERY_INTERVAL * 10;
+
+ if (default_query_interval_dsec <= pim_ifp->igmp_query_max_response_time_dsec) {
+ vty_out(vty,
+ "Can't set default general query interval %d dsec <= query max response time %d dsec.%s",
+ default_query_interval_dsec, pim_ifp->igmp_query_max_response_time_dsec,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ change_query_interval(pim_ifp, IGMP_GENERAL_QUERY_INTERVAL);
+
+ return CMD_SUCCESS;
+}
+
+#define IGMP_QUERY_MAX_RESPONSE_TIME_MIN (1)
+#define IGMP_QUERY_MAX_RESPONSE_TIME_MAX (25)
+
+DEFUN (interface_ip_igmp_query_max_response_time,
+ interface_ip_igmp_query_max_response_time_cmd,
+ PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME " <1-25>",
+ IP_STR
+ IFACE_IGMP_STR
+ IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR
+ "Query response value in seconds\n")
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ int query_max_response_time;
+
+ ifp = vty->index;
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp) {
+ vty_out(vty,
+ "IGMP not enabled on interface %s. Please enable IGMP first.%s",
+ ifp->name,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ query_max_response_time = atoi(argv[0]);
+
+ /*
+ It seems we don't need to check bounds since command.c does it
+ already, but we verify them anyway for extra safety.
+ */
+ if (query_max_response_time < IGMP_QUERY_MAX_RESPONSE_TIME_MIN) {
+ vty_out(vty, "Query max response time %d sec lower than minimum %d sec%s",
+ query_max_response_time,
+ IGMP_QUERY_MAX_RESPONSE_TIME_MIN,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (query_max_response_time > IGMP_QUERY_MAX_RESPONSE_TIME_MAX) {
+ vty_out(vty, "Query max response time %d sec higher than maximum %d sec%s",
+ query_max_response_time,
+ IGMP_QUERY_MAX_RESPONSE_TIME_MAX,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (query_max_response_time >= pim_ifp->igmp_default_query_interval) {
+ vty_out(vty,
+ "Can't set query max response time %d sec >= general query interval %d sec%s",
+ query_max_response_time, pim_ifp->igmp_default_query_interval,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ change_query_max_response_time(pim_ifp, 10 * query_max_response_time);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_igmp_query_max_response_time,
+ interface_no_ip_igmp_query_max_response_time_cmd,
+ PIM_CMD_NO " " PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME,
+ NO_STR
+ IP_STR
+ IFACE_IGMP_STR
+ IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ int default_query_interval_dsec;
+
+ ifp = vty->index;
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ return CMD_SUCCESS;
+
+ default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval;
+
+ if (IGMP_QUERY_MAX_RESPONSE_TIME_DSEC >= default_query_interval_dsec) {
+ vty_out(vty,
+ "Can't set default query max response time %d dsec >= general query interval %d dsec.%s",
+ IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, default_query_interval_dsec,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ change_query_max_response_time(pim_ifp, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC);
+
+ return CMD_SUCCESS;
+}
+
+#define IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC (10)
+#define IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC (250)
+
+DEFUN (interface_ip_igmp_query_max_response_time_dsec,
+ interface_ip_igmp_query_max_response_time_dsec_cmd,
+ PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC " <10-250>",
+ IP_STR
+ IFACE_IGMP_STR
+ IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR
+ "Query response value in deciseconds\n")
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ int query_max_response_time_dsec;
+ int default_query_interval_dsec;
+
+ ifp = vty->index;
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp) {
+ vty_out(vty,
+ "IGMP not enabled on interface %s. Please enable IGMP first.%s",
+ ifp->name,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ query_max_response_time_dsec = atoi(argv[0]);
+
+ /*
+ It seems we don't need to check bounds since command.c does it
+ already, but we verify them anyway for extra safety.
+ */
+ if (query_max_response_time_dsec < IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC) {
+ vty_out(vty, "Query max response time %d dsec lower than minimum %d dsec%s",
+ query_max_response_time_dsec,
+ IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (query_max_response_time_dsec > IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC) {
+ vty_out(vty, "Query max response time %d dsec higher than maximum %d dsec%s",
+ query_max_response_time_dsec,
+ IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval;
+
+ if (query_max_response_time_dsec >= default_query_interval_dsec) {
+ vty_out(vty,
+ "Can't set query max response time %d dsec >= general query interval %d dsec%s",
+ query_max_response_time_dsec, default_query_interval_dsec,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ change_query_max_response_time(pim_ifp, query_max_response_time_dsec);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_igmp_query_max_response_time_dsec,
+ interface_no_ip_igmp_query_max_response_time_dsec_cmd,
+ PIM_CMD_NO " " PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC,
+ NO_STR
+ IP_STR
+ IFACE_IGMP_STR
+ IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ int default_query_interval_dsec;
+
+ ifp = vty->index;
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ return CMD_SUCCESS;
+
+ default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval;
+
+ if (IGMP_QUERY_MAX_RESPONSE_TIME_DSEC >= default_query_interval_dsec) {
+ vty_out(vty,
+ "Can't set default query max response time %d dsec >= general query interval %d dsec.%s",
+ IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, default_query_interval_dsec,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ change_query_max_response_time(pim_ifp, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (interface_ip_pim_ssm,
+ interface_ip_pim_ssm_cmd,
+ "ip pim ssm",
+ IP_STR
+ PIM_STR
+ IFACE_PIM_STR)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+
+ ifp = vty->index;
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp) {
+ pim_ifp = pim_if_new(ifp, 0 /* igmp=false */, 1 /* pim=true */);
+ if (!pim_ifp) {
+ vty_out(vty, "Could not enable PIM on interface%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+ else {
+ PIM_IF_DO_PIM(pim_ifp->options);
+ }
+
+ pim_if_addr_add_all(ifp);
+ pim_if_membership_refresh(ifp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_pim_ssm,
+ interface_no_ip_pim_ssm_cmd,
+ "no ip pim ssm",
+ NO_STR
+ IP_STR
+ PIM_STR
+ IFACE_PIM_STR)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+
+ ifp = vty->index;
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ return CMD_SUCCESS;
+
+ PIM_IF_DONT_PIM(pim_ifp->options);
+
+ pim_if_membership_clear(ifp);
+
+ /*
+ pim_if_addr_del_all() removes all sockets from
+ pim_ifp->igmp_socket_list.
+ */
+ pim_if_addr_del_all(ifp);
+
+ /*
+ pim_sock_delete() removes all neighbors from
+ pim_ifp->pim_neighbor_list.
+ */
+ pim_sock_delete(ifp, "pim unconfigured on interface");
+
+ if (!PIM_IF_TEST_IGMP(pim_ifp->options)) {
+ pim_if_delete(ifp);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_igmp,
+ debug_igmp_cmd,
+ "debug igmp",
+ DEBUG_STR
+ DEBUG_IGMP_STR)
+{
+ PIM_DO_DEBUG_IGMP_EVENTS;
+ PIM_DO_DEBUG_IGMP_PACKETS;
+ PIM_DO_DEBUG_IGMP_TRACE;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_igmp,
+ no_debug_igmp_cmd,
+ "no debug igmp",
+ NO_STR
+ DEBUG_STR
+ DEBUG_IGMP_STR)
+{
+ PIM_DONT_DEBUG_IGMP_EVENTS;
+ PIM_DONT_DEBUG_IGMP_PACKETS;
+ PIM_DONT_DEBUG_IGMP_TRACE;
+ return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_igmp,
+ undebug_igmp_cmd,
+ "undebug igmp",
+ UNDEBUG_STR
+ DEBUG_IGMP_STR)
+
+DEFUN (debug_igmp_events,
+ debug_igmp_events_cmd,
+ "debug igmp events",
+ DEBUG_STR
+ DEBUG_IGMP_STR
+ DEBUG_IGMP_EVENTS_STR)
+{
+ PIM_DO_DEBUG_IGMP_EVENTS;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_igmp_events,
+ no_debug_igmp_events_cmd,
+ "no debug igmp events",
+ NO_STR
+ DEBUG_STR
+ DEBUG_IGMP_STR
+ DEBUG_IGMP_EVENTS_STR)
+{
+ PIM_DONT_DEBUG_IGMP_EVENTS;
+ return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_igmp_events,
+ undebug_igmp_events_cmd,
+ "undebug igmp events",
+ UNDEBUG_STR
+ DEBUG_IGMP_STR
+ DEBUG_IGMP_EVENTS_STR)
+
+DEFUN (debug_igmp_packets,
+ debug_igmp_packets_cmd,
+ "debug igmp packets",
+ DEBUG_STR
+ DEBUG_IGMP_STR
+ DEBUG_IGMP_PACKETS_STR)
+{
+ PIM_DO_DEBUG_IGMP_PACKETS;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_igmp_packets,
+ no_debug_igmp_packets_cmd,
+ "no debug igmp packets",
+ NO_STR
+ DEBUG_STR
+ DEBUG_IGMP_STR
+ DEBUG_IGMP_PACKETS_STR)
+{
+ PIM_DONT_DEBUG_IGMP_PACKETS;
+ return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_igmp_packets,
+ undebug_igmp_packets_cmd,
+ "undebug igmp packets",
+ UNDEBUG_STR
+ DEBUG_IGMP_STR
+ DEBUG_IGMP_PACKETS_STR)
+
+DEFUN (debug_igmp_trace,
+ debug_igmp_trace_cmd,
+ "debug igmp trace",
+ DEBUG_STR
+ DEBUG_IGMP_STR
+ DEBUG_IGMP_TRACE_STR)
+{
+ PIM_DO_DEBUG_IGMP_TRACE;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_igmp_trace,
+ no_debug_igmp_trace_cmd,
+ "no debug igmp trace",
+ NO_STR
+ DEBUG_STR
+ DEBUG_IGMP_STR
+ DEBUG_IGMP_TRACE_STR)
+{
+ PIM_DONT_DEBUG_IGMP_TRACE;
+ return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_igmp_trace,
+ undebug_igmp_trace_cmd,
+ "undebug igmp trace",
+ UNDEBUG_STR
+ DEBUG_IGMP_STR
+ DEBUG_IGMP_TRACE_STR)
+
+DEFUN (debug_pim,
+ debug_pim_cmd,
+ "debug pim",
+ DEBUG_STR
+ DEBUG_PIM_STR)
+{
+ PIM_DO_DEBUG_PIM_EVENTS;
+ PIM_DO_DEBUG_PIM_PACKETS;
+ PIM_DO_DEBUG_PIM_TRACE;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim,
+ no_debug_pim_cmd,
+ "no debug pim",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIM_STR)
+{
+ PIM_DONT_DEBUG_PIM_EVENTS;
+ PIM_DONT_DEBUG_PIM_PACKETS;
+ PIM_DONT_DEBUG_PIM_TRACE;
+
+ PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND;
+ PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV;
+
+ return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_pim,
+ undebug_pim_cmd,
+ "undebug pim",
+ UNDEBUG_STR
+ DEBUG_PIM_STR)
+
+DEFUN (debug_pim_events,
+ debug_pim_events_cmd,
+ "debug pim events",
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_EVENTS_STR)
+{
+ PIM_DO_DEBUG_PIM_EVENTS;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim_events,
+ no_debug_pim_events_cmd,
+ "no debug pim events",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_EVENTS_STR)
+{
+ PIM_DONT_DEBUG_PIM_EVENTS;
+ return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_pim_events,
+ undebug_pim_events_cmd,
+ "undebug pim events",
+ UNDEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_EVENTS_STR)
+
+DEFUN (debug_pim_packets,
+ debug_pim_packets_cmd,
+ "debug pim packets",
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_PACKETS_STR)
+{
+ PIM_DO_DEBUG_PIM_PACKETS;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim_packets,
+ no_debug_pim_packets_cmd,
+ "no debug pim packets",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_PACKETS_STR)
+{
+ PIM_DONT_DEBUG_PIM_PACKETS;
+ return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_pim_packets,
+ undebug_pim_packets_cmd,
+ "undebug pim packets",
+ UNDEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_PACKETS_STR)
+
+DEFUN (debug_pim_packetdump_send,
+ debug_pim_packetdump_send_cmd,
+ "debug pim packet-dump send",
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_PACKETDUMP_STR
+ DEBUG_PIM_PACKETDUMP_SEND_STR)
+{
+ PIM_DO_DEBUG_PIM_PACKETDUMP_SEND;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim_packetdump_send,
+ no_debug_pim_packetdump_send_cmd,
+ "no debug pim packet-dump send",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_PACKETDUMP_STR
+ DEBUG_PIM_PACKETDUMP_SEND_STR)
+{
+ PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND;
+ return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_pim_packetdump_send,
+ undebug_pim_packetdump_send_cmd,
+ "undebug pim packet-dump send",
+ UNDEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_PACKETDUMP_STR
+ DEBUG_PIM_PACKETDUMP_SEND_STR)
+
+DEFUN (debug_pim_packetdump_recv,
+ debug_pim_packetdump_recv_cmd,
+ "debug pim packet-dump receive",
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_PACKETDUMP_STR
+ DEBUG_PIM_PACKETDUMP_RECV_STR)
+{
+ PIM_DO_DEBUG_PIM_PACKETDUMP_RECV;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim_packetdump_recv,
+ no_debug_pim_packetdump_recv_cmd,
+ "no debug pim packet-dump receive",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_PACKETDUMP_STR
+ DEBUG_PIM_PACKETDUMP_RECV_STR)
+{
+ PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV;
+ return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_pim_packetdump_recv,
+ undebug_pim_packetdump_recv_cmd,
+ "undebug pim packet-dump receive",
+ UNDEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_PACKETDUMP_STR
+ DEBUG_PIM_PACKETDUMP_RECV_STR)
+
+DEFUN (debug_pim_trace,
+ debug_pim_trace_cmd,
+ "debug pim trace",
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_TRACE_STR)
+{
+ PIM_DO_DEBUG_PIM_TRACE;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim_trace,
+ no_debug_pim_trace_cmd,
+ "no debug pim trace",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_TRACE_STR)
+{
+ PIM_DONT_DEBUG_PIM_TRACE;
+ return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_pim_trace,
+ undebug_pim_trace_cmd,
+ "undebug pim trace",
+ UNDEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_TRACE_STR)
+
+DEFUN (debug_ssmpingd,
+ debug_ssmpingd_cmd,
+ "debug ssmpingd",
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_SSMPINGD_STR)
+{
+ PIM_DO_DEBUG_SSMPINGD;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ssmpingd,
+ no_debug_ssmpingd_cmd,
+ "no debug ssmpingd",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_SSMPINGD_STR)
+{
+ PIM_DONT_DEBUG_SSMPINGD;
+ return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_ssmpingd,
+ undebug_ssmpingd_cmd,
+ "undebug ssmpingd",
+ UNDEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_SSMPINGD_STR)
+
+DEFUN (debug_pim_zebra,
+ debug_pim_zebra_cmd,
+ "debug pim zebra",
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_ZEBRA_STR)
+{
+ PIM_DO_DEBUG_ZEBRA;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim_zebra,
+ no_debug_pim_zebra_cmd,
+ "no debug pim zebra",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_ZEBRA_STR)
+{
+ PIM_DONT_DEBUG_ZEBRA;
+ return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_pim_zebra,
+ undebug_pim_zebra_cmd,
+ "undebug pim zebra",
+ UNDEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_ZEBRA_STR)
+
+DEFUN (show_debugging,
+ show_debugging_cmd,
+ "show debugging",
+ SHOW_STR
+ "State of each debugging option\n")
+{
+ pim_debug_config_write(vty);
+ return CMD_SUCCESS;
+}
+
+static struct igmp_sock *find_igmp_sock_by_fd(int fd)
+{
+ struct listnode *ifnode;
+ struct interface *ifp;
+
+ /* scan all interfaces */
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ struct pim_interface *pim_ifp;
+ struct igmp_sock *igmp;
+
+ if (!ifp->info)
+ continue;
+
+ pim_ifp = ifp->info;
+
+ /* lookup igmp socket under current interface */
+ igmp = igmp_sock_lookup_by_fd(pim_ifp->igmp_socket_list, fd);
+ if (igmp)
+ return igmp;
+ }
+
+ return 0;
+}
+
+DEFUN (test_igmp_receive_report,
+ test_igmp_receive_report_cmd,
+ "test igmp receive report <0-65535> A.B.C.D <1-6> .LINE",
+ "Test\n"
+ "Test IGMP protocol\n"
+ "Test IGMP message\n"
+ "Test IGMP report\n"
+ "Socket\n"
+ "IGMP group address\n"
+ "Record type\n"
+ "Sources\n")
+{
+ char buf[1000];
+ char *igmp_msg;
+ struct ip *ip_hdr;
+ size_t ip_hlen; /* ip header length in bytes */
+ int ip_msg_len;
+ int igmp_msg_len;
+ const char *socket;
+ int socket_fd;
+ const char *grp_str;
+ struct in_addr grp_addr;
+ const char *record_type_str;
+ int record_type;
+ const char *src_str;
+ int result;
+ struct igmp_sock *igmp;
+ char *group_record;
+ int num_sources;
+ struct in_addr *sources;
+ struct in_addr *src_addr;
+ int argi;
+
+ socket = argv[0];
+ socket_fd = atoi(socket);
+ igmp = find_igmp_sock_by_fd(socket_fd);
+ if (!igmp) {
+ vty_out(vty, "Could not find IGMP socket %s: fd=%d%s",
+ socket, socket_fd, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ grp_str = argv[1];
+ result = inet_pton(AF_INET, grp_str, &grp_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+ grp_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ record_type_str = argv[2];
+ record_type = atoi(record_type_str);
+
+ /*
+ Tweak IP header
+ */
+ ip_hdr = (struct ip *) buf;
+ ip_hdr->ip_p = PIM_IP_PROTO_IGMP;
+ ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
+ ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */
+ ip_hdr->ip_src = igmp->ifaddr;
+ ip_hdr->ip_dst = igmp->ifaddr;
+
+ /*
+ Build IGMP v3 report message
+ */
+ igmp_msg = buf + ip_hlen;
+ group_record = igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
+ *igmp_msg = PIM_IGMP_V3_MEMBERSHIP_REPORT; /* type */
+ *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
+ *(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET) = htons(1); /* one group record */
+ *(uint8_t *) (group_record + IGMP_V3_GROUP_RECORD_TYPE_OFFSET) = record_type;
+ *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET) = grp_addr;
+
+ /* Scan LINE sources */
+ sources = (struct in_addr *) (group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET);
+ src_addr = sources;
+ for (argi = 3; argi < argc; ++argi,++src_addr) {
+ src_str = argv[argi];
+ result = inet_pton(AF_INET, src_str, src_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+ src_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+ num_sources = src_addr - sources;
+
+ *(uint16_t *)(group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET) = htons(num_sources);
+
+ igmp_msg_len = IGMP_V3_MSG_MIN_SIZE + (num_sources << 4); /* v3 report for one single group record */
+
+ /* compute checksum */
+ *(uint16_t *)(igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = pim_inet_checksum(igmp_msg, igmp_msg_len);
+
+ /* "receive" message */
+
+ ip_msg_len = ip_hlen + igmp_msg_len;
+ result = pim_igmp_packet(igmp, buf, ip_msg_len);
+ if (result) {
+ vty_out(vty, "pim_igmp_packet(len=%d) returned: %d%s",
+ ip_msg_len, result, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int hexval(uint8_t ch)
+{
+ return isdigit(ch) ? (ch - '0') : (10 + tolower(ch) - 'a');
+}
+
+DEFUN (test_pim_receive_dump,
+ test_pim_receive_dump_cmd,
+ "test pim receive dump INTERFACE A.B.C.D .LINE",
+ "Test\n"
+ "Test PIM protocol\n"
+ "Test PIM message reception\n"
+ "Test PIM packet dump reception from neighbor\n"
+ "Interface\n"
+ "Neighbor address\n"
+ "Packet dump\n")
+{
+ char buf[1000];
+ char *pim_msg;
+ struct ip *ip_hdr;
+ size_t ip_hlen; /* ip header length in bytes */
+ int ip_msg_len;
+ int pim_msg_size;
+ const char *neigh_str;
+ struct in_addr neigh_addr;
+ const char *ifname;
+ struct interface *ifp;
+ int argi;
+ int result;
+
+ /* Find interface */
+ ifname = argv[0];
+ ifp = if_lookup_by_name(ifname);
+ if (!ifp) {
+ vty_out(vty, "No such interface name %s%s",
+ ifname, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Neighbor address */
+ neigh_str = argv[1];
+ result = inet_pton(AF_INET, neigh_str, &neigh_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s",
+ neigh_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /*
+ Tweak IP header
+ */
+ ip_hdr = (struct ip *) buf;
+ ip_hdr->ip_p = PIM_IP_PROTO_PIM;
+ ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
+ ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */
+ ip_hdr->ip_src = neigh_addr;
+ ip_hdr->ip_dst = qpim_all_pim_routers_addr;
+
+ /*
+ Build PIM hello message
+ */
+ pim_msg = buf + ip_hlen;
+ pim_msg_size = 0;
+
+ /* Scan LINE dump into buffer */
+ for (argi = 2; argi < argc; ++argi) {
+ const char *str = argv[argi];
+ int str_len = strlen(str);
+ int str_last = str_len - 1;
+ int i;
+
+ if (str_len % 2) {
+ vty_out(vty, "%% Uneven hex array arg %d=%s%s",
+ argi, str, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ for (i = 0; i < str_last; i += 2) {
+ uint8_t octet;
+ int left;
+ uint8_t h1 = str[i];
+ uint8_t h2 = str[i + 1];
+
+ if (!isxdigit(h1) || !isxdigit(h2)) {
+ vty_out(vty, "%% Non-hex octet %c%c at hex array arg %d=%s%s",
+ h1, h2, argi, str, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ octet = (hexval(h1) << 4) + hexval(h2);
+
+ left = sizeof(buf) - ip_hlen - pim_msg_size;
+ if (left < 1) {
+ vty_out(vty, "%% Overflow buf_size=%d buf_left=%d at hex array arg %d=%s octet %02x%s",
+ sizeof(buf), left, argi, str, octet, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ pim_msg[pim_msg_size++] = octet;
+ }
+ }
+
+ ip_msg_len = ip_hlen + pim_msg_size;
+
+ vty_out(vty, "Receiving: buf_size=%d ip_msg_size=%d pim_msg_size=%d%s",
+ sizeof(buf), ip_msg_len, pim_msg_size, VTY_NEWLINE);
+
+ /* "receive" message */
+
+ result = pim_pim_packet(ifp, buf, ip_msg_len);
+ if (result) {
+ vty_out(vty, "%% pim_pim_packet(len=%d) returned failure: %d%s",
+ ip_msg_len, result, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (test_pim_receive_hello,
+ test_pim_receive_hello_cmd,
+ "test pim receive hello INTERFACE A.B.C.D <0-65535> <0-65535> <0-65535> <0-32767> <0-65535> <0-1>[LINE]",
+ "Test\n"
+ "Test PIM protocol\n"
+ "Test PIM message reception\n"
+ "Test PIM hello reception from neighbor\n"
+ "Interface\n"
+ "Neighbor address\n"
+ "Neighbor holdtime\n"
+ "Neighbor DR priority\n"
+ "Neighbor generation ID\n"
+ "Neighbor propagation delay (msec)\n"
+ "Neighbor override interval (msec)\n"
+ "Neighbor LAN prune delay T-bit\n"
+ "Neighbor secondary addresses\n")
+{
+ char buf[1000];
+ char *pim_msg;
+ struct ip *ip_hdr;
+ size_t ip_hlen; /* ip header length in bytes */
+ int ip_msg_len;
+ int pim_tlv_size;
+ int pim_msg_size;
+ const char *neigh_str;
+ struct in_addr neigh_addr;
+ const char *ifname;
+ struct interface *ifp;
+ uint16_t neigh_holdtime;
+ uint16_t neigh_propagation_delay;
+ uint16_t neigh_override_interval;
+ int neigh_can_disable_join_suppression;
+ uint32_t neigh_dr_priority;
+ uint32_t neigh_generation_id;
+ int argi;
+ int result;
+
+ /* Find interface */
+ ifname = argv[0];
+ ifp = if_lookup_by_name(ifname);
+ if (!ifp) {
+ vty_out(vty, "No such interface name %s%s",
+ ifname, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Neighbor address */
+ neigh_str = argv[1];
+ result = inet_pton(AF_INET, neigh_str, &neigh_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s",
+ neigh_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ neigh_holdtime = atoi(argv[2]);
+ neigh_dr_priority = atoi(argv[3]);
+ neigh_generation_id = atoi(argv[4]);
+ neigh_propagation_delay = atoi(argv[5]);
+ neigh_override_interval = atoi(argv[6]);
+ neigh_can_disable_join_suppression = atoi(argv[7]);
+
+ /*
+ Tweak IP header
+ */
+ ip_hdr = (struct ip *) buf;
+ ip_hdr->ip_p = PIM_IP_PROTO_PIM;
+ ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
+ ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */
+ ip_hdr->ip_src = neigh_addr;
+ ip_hdr->ip_dst = qpim_all_pim_routers_addr;
+
+ /*
+ Build PIM hello message
+ */
+ pim_msg = buf + ip_hlen;
+
+ /* Scan LINE addresses */
+ for (argi = 8; argi < argc; ++argi) {
+ const char *sec_str = argv[argi];
+ struct in_addr sec_addr;
+ result = inet_pton(AF_INET, sec_str, &sec_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad neighbor secondary address %s: errno=%d: %s%s",
+ sec_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty_out(vty,
+ "FIXME WRITEME consider neighbor secondary address %s%s",
+ sec_str, VTY_NEWLINE);
+ }
+
+ pim_tlv_size = pim_hello_build_tlv(ifp->name,
+ pim_msg + PIM_PIM_MIN_LEN,
+ sizeof(buf) - ip_hlen - PIM_PIM_MIN_LEN,
+ neigh_holdtime,
+ neigh_dr_priority,
+ neigh_generation_id,
+ neigh_propagation_delay,
+ neigh_override_interval,
+ neigh_can_disable_join_suppression,
+ 0 /* FIXME secondary address list */);
+ if (pim_tlv_size < 0) {
+ vty_out(vty, "pim_hello_build_tlv() returned failure: %d%s",
+ pim_tlv_size, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN;
+
+ pim_msg_build_header(pim_msg, pim_msg_size,
+ PIM_MSG_TYPE_HELLO);
+
+ /* "receive" message */
+
+ ip_msg_len = ip_hlen + pim_msg_size;
+ result = pim_pim_packet(ifp, buf, ip_msg_len);
+ if (result) {
+ vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s",
+ ip_msg_len, result, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (test_pim_receive_assert,
+ test_pim_receive_assert_cmd,
+ "test pim receive assert INTERFACE A.B.C.D A.B.C.D A.B.C.D <0-65535> <0-65535> <0-1>",
+ "Test\n"
+ "Test PIM protocol\n"
+ "Test PIM message reception\n"
+ "Test reception of PIM assert\n"
+ "Interface\n"
+ "Neighbor address\n"
+ "Assert multicast group address\n"
+ "Assert unicast source address\n"
+ "Assert metric preference\n"
+ "Assert route metric\n"
+ "Assert RPT bit flag\n")
+{
+ char buf[1000];
+ char *buf_pastend = buf + sizeof(buf);
+ char *pim_msg;
+ struct ip *ip_hdr;
+ size_t ip_hlen; /* ip header length in bytes */
+ int ip_msg_len;
+ int pim_msg_size;
+ const char *neigh_str;
+ struct in_addr neigh_addr;
+ const char *group_str;
+ struct in_addr group_addr;
+ const char *source_str;
+ struct in_addr source_addr;
+ const char *ifname;
+ struct interface *ifp;
+ uint32_t assert_metric_preference;
+ uint32_t assert_route_metric;
+ uint32_t assert_rpt_bit_flag;
+ int remain;
+ int result;
+
+ /* Find interface */
+ ifname = argv[0];
+ ifp = if_lookup_by_name(ifname);
+ if (!ifp) {
+ vty_out(vty, "No such interface name %s%s",
+ ifname, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Neighbor address */
+ neigh_str = argv[1];
+ result = inet_pton(AF_INET, neigh_str, &neigh_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s",
+ neigh_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Group address */
+ group_str = argv[2];
+ result = inet_pton(AF_INET, group_str, &group_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+ group_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Source address */
+ source_str = argv[3];
+ result = inet_pton(AF_INET, source_str, &source_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+ source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ assert_metric_preference = atoi(argv[4]);
+ assert_route_metric = atoi(argv[5]);
+ assert_rpt_bit_flag = atoi(argv[6]);
+
+ remain = buf_pastend - buf;
+ if (remain < (int) sizeof(struct ip)) {
+ vty_out(vty, "No room for ip header: buf_size=%d < ip_header_size=%d%s",
+ remain, sizeof(struct ip), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /*
+ Tweak IP header
+ */
+ ip_hdr = (struct ip *) buf;
+ ip_hdr->ip_p = PIM_IP_PROTO_PIM;
+ ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
+ ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */
+ ip_hdr->ip_src = neigh_addr;
+ ip_hdr->ip_dst = qpim_all_pim_routers_addr;
+
+ /*
+ Build PIM assert message
+ */
+ pim_msg = buf + ip_hlen; /* skip ip header */
+
+ pim_msg_size = pim_assert_build_msg(pim_msg, buf_pastend - pim_msg, ifp,
+ group_addr, source_addr,
+ assert_metric_preference,
+ assert_route_metric,
+ assert_rpt_bit_flag);
+ if (pim_msg_size < 0) {
+ vty_out(vty, "Failure building PIM assert message: size=%d%s",
+ pim_msg_size, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* "receive" message */
+
+ ip_msg_len = ip_hlen + pim_msg_size;
+ result = pim_pim_packet(ifp, buf, ip_msg_len);
+ if (result) {
+ vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s",
+ ip_msg_len, result, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int recv_joinprune(struct vty *vty,
+ const char *argv[],
+ int src_is_join)
+{
+ char buf[1000];
+ const char *buf_pastend = buf + sizeof(buf);
+ char *pim_msg;
+ char *pim_msg_curr;
+ int pim_msg_size;
+ struct ip *ip_hdr;
+ size_t ip_hlen; /* ip header length in bytes */
+ int ip_msg_len;
+ uint16_t neigh_holdtime;
+ const char *neigh_dst_str;
+ struct in_addr neigh_dst_addr;
+ const char *neigh_src_str;
+ struct in_addr neigh_src_addr;
+ const char *group_str;
+ struct in_addr group_addr;
+ const char *source_str;
+ struct in_addr source_addr;
+ const char *ifname;
+ struct interface *ifp;
+ int result;
+ int remain;
+ uint16_t num_joined;
+ uint16_t num_pruned;
+
+ /* Find interface */
+ ifname = argv[0];
+ ifp = if_lookup_by_name(ifname);
+ if (!ifp) {
+ vty_out(vty, "No such interface name %s%s",
+ ifname, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ neigh_holdtime = atoi(argv[1]);
+
+ /* Neighbor destination address */
+ neigh_dst_str = argv[2];
+ result = inet_pton(AF_INET, neigh_dst_str, &neigh_dst_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad neighbor destination address %s: errno=%d: %s%s",
+ neigh_dst_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Neighbor source address */
+ neigh_src_str = argv[3];
+ result = inet_pton(AF_INET, neigh_src_str, &neigh_src_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad neighbor source address %s: errno=%d: %s%s",
+ neigh_src_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Multicast group address */
+ group_str = argv[4];
+ result = inet_pton(AF_INET, group_str, &group_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+ group_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Multicast source address */
+ source_str = argv[5];
+ result = inet_pton(AF_INET, source_str, &source_addr);
+ if (result <= 0) {
+ vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+ source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /*
+ Tweak IP header
+ */
+ ip_hdr = (struct ip *) buf;
+ ip_hdr->ip_p = PIM_IP_PROTO_PIM;
+ ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
+ ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */
+ ip_hdr->ip_src = neigh_src_addr;
+ ip_hdr->ip_dst = qpim_all_pim_routers_addr;
+
+ /*
+ Build PIM message
+ */
+ pim_msg = buf + ip_hlen;
+
+ /* skip room for pim header */
+ pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN;
+
+ remain = buf_pastend - pim_msg_curr;
+ pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
+ remain,
+ neigh_dst_addr);
+ if (!pim_msg_curr) {
+ vty_out(vty, "Failure encoding destination address %s: space left=%d%s",
+ neigh_dst_str, remain, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ remain = buf_pastend - pim_msg_curr;
+ if (remain < 4) {
+ vty_out(vty, "Group will not fit: space left=%d%s",
+ remain, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ *pim_msg_curr = 0; /* reserved */
+ ++pim_msg_curr;
+ *pim_msg_curr = 1; /* number of groups */
+ ++pim_msg_curr;
+ *((uint16_t *) pim_msg_curr) = htons(neigh_holdtime);
+ ++pim_msg_curr;
+ ++pim_msg_curr;
+
+ 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) {
+ vty_out(vty, "Failure encoding group address %s: space left=%d%s",
+ group_str, remain, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ remain = buf_pastend - pim_msg_curr;
+ if (remain < 4) {
+ vty_out(vty, "Sources will not fit: space left=%d%s",
+ remain, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (src_is_join) {
+ num_joined = 1;
+ num_pruned = 0;
+ }
+ else {
+ num_joined = 0;
+ num_pruned = 1;
+ }
+
+ /* number of joined sources */
+ *((uint16_t *) pim_msg_curr) = htons(num_joined);
+ ++pim_msg_curr;
+ ++pim_msg_curr;
+
+ /* number of pruned sources */
+ *((uint16_t *) pim_msg_curr) = htons(num_pruned);
+ ++pim_msg_curr;
+ ++pim_msg_curr;
+
+ remain = buf_pastend - pim_msg_curr;
+ pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr,
+ remain,
+ source_addr);
+ if (!pim_msg_curr) {
+ vty_out(vty, "Failure encoding source address %s: space left=%d%s",
+ source_str, remain, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Add PIM header */
+
+ pim_msg_size = pim_msg_curr - pim_msg;
+
+ pim_msg_build_header(pim_msg, pim_msg_size,
+ PIM_MSG_TYPE_JOIN_PRUNE);
+
+ /*
+ "Receive" message
+ */
+
+ ip_msg_len = ip_hlen + pim_msg_size;
+ result = pim_pim_packet(ifp, buf, ip_msg_len);
+ if (result) {
+ vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s",
+ ip_msg_len, result, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (test_pim_receive_join,
+ test_pim_receive_join_cmd,
+ "test pim receive join INTERFACE <0-65535> A.B.C.D A.B.C.D A.B.C.D A.B.C.D",
+ "Test\n"
+ "Test PIM protocol\n"
+ "Test PIM message reception\n"
+ "Test PIM join reception from neighbor\n"
+ "Interface\n"
+ "Neighbor holdtime\n"
+ "Upstream neighbor unicast destination address\n"
+ "Downstream neighbor unicast source address\n"
+ "Multicast group address\n"
+ "Unicast source address\n")
+{
+ return recv_joinprune(vty, argv, 1 /* src_is_join=true */);
+}
+
+DEFUN (test_pim_receive_prune,
+ test_pim_receive_prune_cmd,
+ "test pim receive prune INTERFACE <0-65535> A.B.C.D A.B.C.D A.B.C.D A.B.C.D",
+ "Test\n"
+ "Test PIM protocol\n"
+ "Test PIM message reception\n"
+ "Test PIM prune reception from neighbor\n"
+ "Interface\n"
+ "Neighbor holdtime\n"
+ "Upstream neighbor unicast destination address\n"
+ "Downstream neighbor unicast source address\n"
+ "Multicast group address\n"
+ "Unicast source address\n")
+{
+ return recv_joinprune(vty, argv, 0 /* src_is_join=false */);
+}
+
+DEFUN (test_pim_receive_upcall,
+ test_pim_receive_upcall_cmd,
+ "test pim receive upcall (nocache|wrongvif|wholepkt) <0-65535> A.B.C.D A.B.C.D",
+ "Test\n"
+ "Test PIM protocol\n"
+ "Test PIM message reception\n"
+ "Test reception of kernel upcall\n"
+ "NOCACHE kernel upcall\n"
+ "WRONGVIF kernel upcall\n"
+ "WHOLEPKT kernel upcall\n"
+ "Input interface vif index\n"
+ "Multicast group address\n"
+ "Multicast source address\n")
+{
+ struct igmpmsg msg;
+ const char *upcall_type;
+ const char *group_str;
+ const char *source_str;
+ int result;
+
+ upcall_type = argv[0];
+
+ if (upcall_type[0] == 'n')
+ msg.im_msgtype = IGMPMSG_NOCACHE;
+ else if (upcall_type[1] == 'r')
+ msg.im_msgtype = IGMPMSG_WRONGVIF;
+ else if (upcall_type[1] == 'h')
+ msg.im_msgtype = IGMPMSG_WHOLEPKT;
+ else {
+ vty_out(vty, "Unknown kernel upcall type: %s%s",
+ upcall_type, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ msg.im_vif = atoi(argv[1]);
+
+ /* Group address */
+ group_str = argv[2];
+ result = inet_pton(AF_INET, group_str, &msg.im_dst);
+ if (result <= 0) {
+ vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+ group_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Source address */
+ source_str = argv[3];
+ result = inet_pton(AF_INET, source_str, &msg.im_src);
+ if (result <= 0) {
+ vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+ source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ msg.im_mbz = 0; /* Must be zero */
+
+ result = pim_mroute_msg(-1, (char *) &msg, sizeof(msg));
+ if (result) {
+ vty_out(vty, "pim_mroute_msg(len=%d) returned failure: %d%s",
+ sizeof(msg), result, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+void pim_cmd_init()
+{
+ install_node (&pim_global_node, pim_global_config_write); /* PIM_NODE */
+ install_node (&interface_node, pim_interface_config_write); /* INTERFACE_NODE */
+
+ install_element (CONFIG_NODE, &ip_multicast_routing_cmd);
+ install_element (CONFIG_NODE, &no_ip_multicast_routing_cmd);
+ install_element (CONFIG_NODE, &ip_ssmpingd_cmd);
+ install_element (CONFIG_NODE, &no_ip_ssmpingd_cmd);
+#if 0
+ install_element (CONFIG_NODE, &interface_cmd); /* from if.h */
+#else
+ install_element (CONFIG_NODE, &pim_interface_cmd);
+#endif
+ install_element (CONFIG_NODE, &no_interface_cmd); /* from if.h */
+
+ install_default (INTERFACE_NODE);
+ install_element (INTERFACE_NODE, &interface_ip_igmp_cmd);
+ install_element (INTERFACE_NODE, &interface_no_ip_igmp_cmd);
+ install_element (INTERFACE_NODE, &interface_ip_igmp_join_cmd);
+ install_element (INTERFACE_NODE, &interface_no_ip_igmp_join_cmd);
+ install_element (INTERFACE_NODE, &interface_ip_igmp_query_interval_cmd);
+ install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_interval_cmd);
+ install_element (INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_cmd);
+ install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_cmd);
+ install_element (INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_dsec_cmd);
+ install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_dsec_cmd);
+ install_element (INTERFACE_NODE, &interface_ip_pim_ssm_cmd);
+ install_element (INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd);
+
+ install_element (VIEW_NODE, &show_ip_igmp_interface_cmd);
+ install_element (VIEW_NODE, &show_ip_igmp_parameters_cmd);
+ install_element (VIEW_NODE, &show_ip_igmp_groups_cmd);
+ install_element (VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd);
+ install_element (VIEW_NODE, &show_ip_igmp_sources_cmd);
+ install_element (VIEW_NODE, &show_ip_igmp_sources_retransmissions_cmd);
+ install_element (VIEW_NODE, &show_ip_igmp_querier_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_assert_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_assert_internal_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_assert_metric_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_assert_winner_metric_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_dr_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_hello_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_interface_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_join_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_jp_override_interval_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_lan_prune_delay_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_local_membership_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_neighbor_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_rpf_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_secondary_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_upstream_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_upstream_join_desired_cmd);
+ install_element (VIEW_NODE, &show_ip_pim_upstream_rpf_cmd);
+ install_element (VIEW_NODE, &show_ip_multicast_cmd);
+ install_element (VIEW_NODE, &show_ip_mroute_cmd);
+ install_element (VIEW_NODE, &show_ip_mroute_count_cmd);
+ install_element (VIEW_NODE, &show_ip_route_cmd);
+ install_element (VIEW_NODE, &show_ip_ssmpingd_cmd);
+ install_element (VIEW_NODE, &show_debugging_cmd);
+
+ install_element (ENABLE_NODE, &clear_ip_interfaces_cmd);
+ install_element (ENABLE_NODE, &clear_ip_igmp_interfaces_cmd);
+ install_element (ENABLE_NODE, &clear_ip_pim_interfaces_cmd);
+
+ install_element (ENABLE_NODE, &show_ip_igmp_interface_cmd);
+ install_element (ENABLE_NODE, &show_ip_igmp_parameters_cmd);
+ install_element (ENABLE_NODE, &show_ip_igmp_groups_cmd);
+ install_element (ENABLE_NODE, &show_ip_igmp_groups_retransmissions_cmd);
+ install_element (ENABLE_NODE, &show_ip_igmp_sources_cmd);
+ install_element (ENABLE_NODE, &show_ip_igmp_sources_retransmissions_cmd);
+ install_element (ENABLE_NODE, &show_ip_igmp_querier_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_address_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_assert_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_assert_internal_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_assert_metric_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_assert_winner_metric_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_dr_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_hello_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_interface_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_join_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_jp_override_interval_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_lan_prune_delay_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_local_membership_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_neighbor_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_rpf_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_secondary_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_upstream_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_upstream_join_desired_cmd);
+ install_element (ENABLE_NODE, &show_ip_pim_upstream_rpf_cmd);
+ install_element (ENABLE_NODE, &show_ip_multicast_cmd);
+ install_element (ENABLE_NODE, &show_ip_mroute_cmd);
+ install_element (ENABLE_NODE, &show_ip_mroute_count_cmd);
+ install_element (ENABLE_NODE, &show_ip_route_cmd);
+ install_element (ENABLE_NODE, &show_ip_ssmpingd_cmd);
+ install_element (ENABLE_NODE, &show_debugging_cmd);
+
+ install_element (ENABLE_NODE, &test_igmp_receive_report_cmd);
+ install_element (ENABLE_NODE, &test_pim_receive_assert_cmd);
+ install_element (ENABLE_NODE, &test_pim_receive_dump_cmd);
+ install_element (ENABLE_NODE, &test_pim_receive_hello_cmd);
+ install_element (ENABLE_NODE, &test_pim_receive_join_cmd);
+ install_element (ENABLE_NODE, &test_pim_receive_prune_cmd);
+ install_element (ENABLE_NODE, &test_pim_receive_upcall_cmd);
+
+ install_element (ENABLE_NODE, &debug_igmp_cmd);
+ install_element (ENABLE_NODE, &no_debug_igmp_cmd);
+ install_element (ENABLE_NODE, &undebug_igmp_cmd);
+ install_element (ENABLE_NODE, &debug_igmp_events_cmd);
+ install_element (ENABLE_NODE, &no_debug_igmp_events_cmd);
+ install_element (ENABLE_NODE, &undebug_igmp_events_cmd);
+ install_element (ENABLE_NODE, &debug_igmp_packets_cmd);
+ install_element (ENABLE_NODE, &no_debug_igmp_packets_cmd);
+ install_element (ENABLE_NODE, &undebug_igmp_packets_cmd);
+ install_element (ENABLE_NODE, &debug_igmp_trace_cmd);
+ install_element (ENABLE_NODE, &no_debug_igmp_trace_cmd);
+ install_element (ENABLE_NODE, &undebug_igmp_trace_cmd);
+ install_element (ENABLE_NODE, &debug_pim_cmd);
+ install_element (ENABLE_NODE, &no_debug_pim_cmd);
+ install_element (ENABLE_NODE, &undebug_pim_cmd);
+ install_element (ENABLE_NODE, &debug_pim_events_cmd);
+ install_element (ENABLE_NODE, &no_debug_pim_events_cmd);
+ install_element (ENABLE_NODE, &undebug_pim_events_cmd);
+ install_element (ENABLE_NODE, &debug_pim_packets_cmd);
+ install_element (ENABLE_NODE, &no_debug_pim_packets_cmd);
+ install_element (ENABLE_NODE, &undebug_pim_packets_cmd);
+ install_element (ENABLE_NODE, &debug_pim_packetdump_send_cmd);
+ install_element (ENABLE_NODE, &no_debug_pim_packetdump_send_cmd);
+ install_element (ENABLE_NODE, &undebug_pim_packetdump_send_cmd);
+ install_element (ENABLE_NODE, &debug_pim_packetdump_recv_cmd);
+ install_element (ENABLE_NODE, &no_debug_pim_packetdump_recv_cmd);
+ install_element (ENABLE_NODE, &undebug_pim_packetdump_recv_cmd);
+ install_element (ENABLE_NODE, &debug_pim_trace_cmd);
+ install_element (ENABLE_NODE, &no_debug_pim_trace_cmd);
+ install_element (ENABLE_NODE, &undebug_pim_trace_cmd);
+ install_element (ENABLE_NODE, &debug_ssmpingd_cmd);
+ install_element (ENABLE_NODE, &no_debug_ssmpingd_cmd);
+ install_element (ENABLE_NODE, &undebug_ssmpingd_cmd);
+ install_element (ENABLE_NODE, &debug_pim_zebra_cmd);
+ install_element (ENABLE_NODE, &no_debug_pim_zebra_cmd);
+ install_element (ENABLE_NODE, &undebug_pim_zebra_cmd);
+
+ install_element (CONFIG_NODE, &debug_igmp_cmd);
+ install_element (CONFIG_NODE, &no_debug_igmp_cmd);
+ install_element (CONFIG_NODE, &undebug_igmp_cmd);
+ install_element (CONFIG_NODE, &debug_igmp_events_cmd);
+ install_element (CONFIG_NODE, &no_debug_igmp_events_cmd);
+ install_element (CONFIG_NODE, &undebug_igmp_events_cmd);
+ install_element (CONFIG_NODE, &debug_igmp_packets_cmd);
+ install_element (CONFIG_NODE, &no_debug_igmp_packets_cmd);
+ install_element (CONFIG_NODE, &undebug_igmp_packets_cmd);
+ install_element (CONFIG_NODE, &debug_igmp_trace_cmd);
+ install_element (CONFIG_NODE, &no_debug_igmp_trace_cmd);
+ install_element (CONFIG_NODE, &undebug_igmp_trace_cmd);
+ install_element (CONFIG_NODE, &debug_pim_cmd);
+ install_element (CONFIG_NODE, &no_debug_pim_cmd);
+ install_element (CONFIG_NODE, &undebug_pim_cmd);
+ install_element (CONFIG_NODE, &debug_pim_events_cmd);
+ install_element (CONFIG_NODE, &no_debug_pim_events_cmd);
+ install_element (CONFIG_NODE, &undebug_pim_events_cmd);
+ install_element (CONFIG_NODE, &debug_pim_packets_cmd);
+ install_element (CONFIG_NODE, &no_debug_pim_packets_cmd);
+ install_element (CONFIG_NODE, &undebug_pim_packets_cmd);
+ install_element (CONFIG_NODE, &debug_pim_trace_cmd);
+ install_element (CONFIG_NODE, &no_debug_pim_trace_cmd);
+ install_element (CONFIG_NODE, &undebug_pim_trace_cmd);
+ install_element (CONFIG_NODE, &debug_ssmpingd_cmd);
+ install_element (CONFIG_NODE, &no_debug_ssmpingd_cmd);
+ install_element (CONFIG_NODE, &undebug_ssmpingd_cmd);
+ install_element (CONFIG_NODE, &debug_pim_zebra_cmd);
+ install_element (CONFIG_NODE, &no_debug_pim_zebra_cmd);
+ install_element (CONFIG_NODE, &undebug_pim_zebra_cmd);
+}
diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h
new file mode 100644
index 00000000..fd1a62fa
--- /dev/null
+++ b/pimd/pim_cmd.h
@@ -0,0 +1,62 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_CMD_H
+#define PIM_CMD_H
+
+#define PIM_STR "PIM information\n"
+#define IGMP_STR "IGMP information\n"
+#define IGMP_GROUP_STR "IGMP groups information\n"
+#define IGMP_SOURCE_STR "IGMP sources information\n"
+#define CONF_SSMPINGD_STR "Enable ssmpingd operation\n"
+#define SHOW_SSMPINGD_STR "ssmpingd operation\n"
+#define IFACE_PIM_STR "Enable PIM SSM operation\n"
+#define IFACE_IGMP_STR "Enable IGMP operation\n"
+#define IFACE_IGMP_QUERY_INTERVAL_STR "IGMP host query interval\n"
+#define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR "IGMP max query response value (seconds)\n"
+#define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR "IGMP max query response value (deciseconds)\n"
+#define DEBUG_IGMP_STR "IGMP protocol activity\n"
+#define DEBUG_IGMP_EVENTS_STR "IGMP protocol events\n"
+#define DEBUG_IGMP_PACKETS_STR "IGMP protocol packets\n"
+#define DEBUG_IGMP_TRACE_STR "IGMP internal daemon activity\n"
+#define DEBUG_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_PACKETDUMP_STR "PIM packet dump\n"
+#define DEBUG_PIM_PACKETDUMP_SEND_STR "Dump sent packets\n"
+#define DEBUG_PIM_PACKETDUMP_RECV_STR "Dump received packets\n"
+#define DEBUG_PIM_TRACE_STR "PIM internal daemon activity\n"
+#define DEBUG_PIM_ZEBRA_STR "ZEBRA protocol activity\n"
+#define DEBUG_SSMPINGD_STR "ssmpingd activity\n"
+#define CLEAR_IP_IGMP_STR "IGMP clear commands\n"
+#define CLEAR_IP_PIM_STR "PIM clear commands\n"
+#define MROUTE_STR "IP multicast routing table\n"
+
+#define 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 */
diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c
new file mode 100644
index 00000000..545b3b12
--- /dev/null
+++ b/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 > left=%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) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ remain,
+ option_type, option_len,
+ src_str, ifp->name);
+ }
+
+ switch (option_type) {
+ case PIM_MSG_OPTION_TYPE_HOLDTIME:
+ if (pim_tlv_parse_holdtime(ifp->name, src_addr,
+ &hello_options,
+ &hello_option_holdtime,
+ option_len,
+ tlv_curr)) {
+ FREE_ADDR_LIST_THEN_RETURN(-3);
+ }
+ break;
+ case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
+ if (pim_tlv_parse_lan_prune_delay(ifp->name, src_addr,
+ &hello_options,
+ &hello_option_propagation_delay,
+ &hello_option_override_interval,
+ option_len,
+ tlv_curr)) {
+ FREE_ADDR_LIST_THEN_RETURN(-4);
+ }
+ break;
+ case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
+ if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
+ &hello_options,
+ &hello_option_dr_priority,
+ option_len,
+ tlv_curr)) {
+ FREE_ADDR_LIST_THEN_RETURN(-5);
+ }
+ break;
+ case PIM_MSG_OPTION_TYPE_GENERATION_ID:
+ if (pim_tlv_parse_generation_id(ifp->name, src_addr,
+ &hello_options,
+ &hello_option_generation_id,
+ option_len,
+ tlv_curr)) {
+ FREE_ADDR_LIST_THEN_RETURN(-6);
+ }
+ break;
+ case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
+ if (pim_tlv_parse_addr_list(ifp->name, src_addr,
+ &hello_options,
+ &hello_option_addr_list,
+ option_len,
+ tlv_curr)) {
+ return -7;
+ }
+ break;
+ case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
+ __PRETTY_FUNCTION__,
+ option_type, option_len,
+ src_str, ifp->name);
+ }
+ break;
+ default:
+ {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
+ __PRETTY_FUNCTION__,
+ option_type, option_len,
+ src_str, ifp->name);
+ }
+ }
+
+ tlv_curr += option_len;
+ }
+
+ /*
+ Check received PIM hello options
+ */
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime",
+ ifp->name, src_addr,
+ PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME),
+ hello_option_holdtime);
+ tlv_trace_uint16(__PRETTY_FUNCTION__, "propagation_delay",
+ ifp->name, src_addr,
+ PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
+ hello_option_propagation_delay);
+ tlv_trace_uint16(__PRETTY_FUNCTION__, "override_interval",
+ ifp->name, src_addr,
+ PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
+ hello_option_override_interval);
+ tlv_trace_bool(__PRETTY_FUNCTION__, "can_disable_join_suppression",
+ ifp->name, src_addr,
+ PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
+ PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
+ tlv_trace_uint32(__PRETTY_FUNCTION__, "dr_priority",
+ ifp->name, src_addr,
+ PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_DR_PRIORITY),
+ hello_option_dr_priority);
+ tlv_trace_uint32_hex(__PRETTY_FUNCTION__, "generation_id",
+ ifp->name, src_addr,
+ PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID),
+ hello_option_generation_id);
+ tlv_trace_list(__PRETTY_FUNCTION__, "address_list",
+ ifp->name, src_addr,
+ PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_ADDRESS_LIST),
+ hello_option_addr_list);
+ }
+
+ if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: PIM hello missing holdtime from %s on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, ifp->name);
+ }
+
+ /*
+ New neighbor?
+ */
+
+ neigh = pim_neighbor_find(ifp, src_addr);
+ if (!neigh) {
+ /* Add as new neighbor */
+
+ neigh = pim_neighbor_add(ifp, src_addr,
+ hello_options,
+ hello_option_holdtime,
+ hello_option_propagation_delay,
+ hello_option_override_interval,
+ hello_option_dr_priority,
+ hello_option_generation_id,
+ hello_option_addr_list);
+ if (!neigh) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: failure creating PIM neighbor %s on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, ifp->name);
+ FREE_ADDR_LIST_THEN_RETURN(-8);
+ }
+
+ /* actual addr list has been saved under neighbor */
+ return 0;
+ }
+
+ /*
+ Received generation ID ?
+ */
+
+ if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) {
+ /* GenID mismatch ? */
+ if (!PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ||
+ (hello_option_generation_id != neigh->generation_id)) {
+
+ /* GenID changed */
+
+ pim_upstream_rpf_genid_changed(neigh->source_addr);
+
+ /* GenID mismatch, then replace neighbor */
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
+ __PRETTY_FUNCTION__,
+ hello_option_generation_id,
+ neigh->generation_id,
+ src_str, ifp->name);
+ }
+
+ pim_upstream_rpf_genid_changed(neigh->source_addr);
+
+ pim_neighbor_delete(ifp, neigh, "GenID mismatch");
+ neigh = pim_neighbor_add(ifp, src_addr,
+ hello_options,
+ hello_option_holdtime,
+ hello_option_propagation_delay,
+ hello_option_override_interval,
+ hello_option_dr_priority,
+ hello_option_generation_id,
+ hello_option_addr_list);
+ if (!neigh) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: failure re-creating PIM neighbor %s on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, ifp->name);
+ FREE_ADDR_LIST_THEN_RETURN(-9);
+ }
+ /* actual addr list is saved under neighbor */
+ return 0;
+
+ } /* GenId mismatch: replace neighbor */
+
+ } /* GenId received */
+
+ /*
+ Update existing neighbor
+ */
+
+ pim_neighbor_update(neigh,
+ hello_options,
+ hello_option_holdtime,
+ hello_option_dr_priority,
+ hello_option_addr_list);
+ /* actual addr list is saved under neighbor */
+ return 0;
+}
+
+int pim_hello_build_tlv(const char *ifname,
+ char *tlv_buf, int tlv_buf_size,
+ uint16_t holdtime,
+ uint32_t dr_priority,
+ uint32_t generation_id,
+ uint16_t propagation_delay,
+ uint16_t override_interval,
+ int can_disable_join_suppression,
+ struct list *ifconnected)
+{
+ char *curr = tlv_buf;
+ char *pastend = tlv_buf + tlv_buf_size;
+ char *tmp;
+
+ /*
+ * Append options
+ */
+
+ /* Holdtime */
+ curr = pim_tlv_append_uint16(curr,
+ pastend,
+ PIM_MSG_OPTION_TYPE_HOLDTIME,
+ holdtime);
+ if (!curr) {
+ zlog_warn("%s: could not set PIM hello Holdtime option for interface %s",
+ __PRETTY_FUNCTION__, ifname);
+ return -1;
+ }
+
+ /* LAN Prune Delay */
+ tmp = pim_tlv_append_2uint16(curr,
+ pastend,
+ PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
+ propagation_delay,
+ override_interval);
+ if (!tmp) {
+ zlog_warn("%s: could not set PIM LAN Prune Delay option for interface %s",
+ __PRETTY_FUNCTION__, ifname);
+ return -1;
+ }
+ if (can_disable_join_suppression) {
+ *((uint8_t*)(curr) + 4) |= 0x80; /* enable T bit */
+ }
+ curr = tmp;
+
+ /* DR Priority */
+ curr = pim_tlv_append_uint32(curr,
+ pastend,
+ PIM_MSG_OPTION_TYPE_DR_PRIORITY,
+ dr_priority);
+ if (!curr) {
+ zlog_warn("%s: could not set PIM hello DR Priority option for interface %s",
+ __PRETTY_FUNCTION__, ifname);
+ return -2;
+ }
+
+ /* Generation ID */
+ curr = pim_tlv_append_uint32(curr,
+ pastend,
+ PIM_MSG_OPTION_TYPE_GENERATION_ID,
+ generation_id);
+ if (!curr) {
+ zlog_warn("%s: could not set PIM hello Generation ID option for interface %s",
+ __PRETTY_FUNCTION__, ifname);
+ return -3;
+ }
+
+ /* Secondary Address List */
+ if (ifconnected) {
+ curr = pim_tlv_append_addrlist_ucast(curr,
+ pastend,
+ ifconnected);
+ if (!curr) {
+ zlog_warn("%s: could not set PIM hello Secondary Address List option for interface %s",
+ __PRETTY_FUNCTION__, ifname);
+ return -4;
+ }
+ }
+
+ return curr - tlv_buf;
+}
+
+/*
+ RFC 4601: 4.3.1. Sending Hello Messages
+
+ Thus, if a router needs to send a Join/Prune or Assert message on an
+ interface on which it has not yet sent a Hello message with the
+ currently configured IP address, then it MUST immediately send the
+ relevant Hello message without waiting for the Hello Timer to
+ expire, followed by the Join/Prune or Assert message.
+*/
+void pim_hello_require(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+
+ zassert(ifp);
+
+ pim_ifp = ifp->info;
+
+ zassert(pim_ifp);
+
+ if (pim_ifp->pim_ifstat_hello_sent)
+ return;
+
+ pim_hello_restart_now(ifp); /* Send hello and restart timer */
+}
diff --git a/pimd/pim_hello.h b/pimd/pim_hello.h
new file mode 100644
index 00000000..90a11ba6
--- /dev/null
+++ b/pimd/pim_hello.h
@@ -0,0 +1,46 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_HELLO_H
+#define PIM_HELLO_H
+
+#include <zebra.h>
+
+#include "if.h"
+
+int pim_hello_recv(struct interface *ifp,
+ struct in_addr src_addr,
+ char *tlv_buf, int tlv_buf_size);
+
+int pim_hello_build_tlv(const char *ifname,
+ char *tlv_buf, int tlv_buf_size,
+ uint16_t holdtime,
+ uint32_t dr_priority,
+ uint32_t generation_id,
+ uint16_t propagation_delay,
+ uint16_t override_interval,
+ int can_disable_join_suppression,
+ struct list *ifconnected);
+
+void pim_hello_require(struct interface *ifp);
+
+#endif /* PIM_HELLO_H */
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
new file mode 100644
index 00000000..7806c805
--- /dev/null
+++ b/pimd/pim_iface.c
@@ -0,0 +1,1134 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "if.h"
+#include "log.h"
+#include "vty.h"
+#include "memory.h"
+#include "prefix.h"
+
+#include "pimd.h"
+#include "pim_iface.h"
+#include "pim_igmp.h"
+#include "pim_mroute.h"
+#include "pim_oil.h"
+#include "pim_str.h"
+#include "pim_pim.h"
+#include "pim_neighbor.h"
+#include "pim_ifchannel.h"
+#include "pim_rand.h"
+#include "pim_sock.h"
+#include "pim_ssmpingd.h"
+
+static void pim_if_igmp_join_del_all(struct interface *ifp);
+
+void pim_if_init()
+{
+ if_init();
+}
+
+static void *if_list_clean(struct pim_interface *pim_ifp)
+{
+ if (pim_ifp->igmp_join_list) {
+ list_delete(pim_ifp->igmp_join_list);
+ }
+
+ if (pim_ifp->igmp_socket_list) {
+ list_delete(pim_ifp->igmp_socket_list);
+ }
+
+ if (pim_ifp->pim_neighbor_list) {
+ list_delete(pim_ifp->pim_neighbor_list);
+ }
+
+ if (pim_ifp->pim_ifchannel_list) {
+ list_delete(pim_ifp->pim_ifchannel_list);
+ }
+
+ XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
+
+ return 0;
+}
+
+struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim)
+{
+ struct pim_interface *pim_ifp;
+
+ zassert(ifp);
+ zassert(!ifp->info);
+
+ pim_ifp = XMALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp));
+ if (!pim_ifp) {
+ zlog_err("PIM XMALLOC(%d) failure", sizeof(*pim_ifp));
+ return 0;
+ }
+
+ pim_ifp->options = 0;
+ pim_ifp->mroute_vif_index = -1;
+
+ pim_ifp->igmp_default_robustness_variable = IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
+ pim_ifp->igmp_default_query_interval = IGMP_GENERAL_QUERY_INTERVAL;
+ pim_ifp->igmp_query_max_response_time_dsec = IGMP_QUERY_MAX_RESPONSE_TIME_DSEC;
+ pim_ifp->igmp_specific_query_max_response_time_dsec = IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC;
+
+ /*
+ RFC 3376: 8.3. Query Response Interval
+ The number of seconds represented by the [Query Response Interval]
+ must be less than the [Query Interval].
+ */
+ zassert(pim_ifp->igmp_query_max_response_time_dsec < pim_ifp->igmp_default_query_interval);
+
+ if (pim)
+ PIM_IF_DO_PIM(pim_ifp->options);
+ if (igmp)
+ PIM_IF_DO_IGMP(pim_ifp->options);
+
+#if 0
+ /* FIXME: Should join? */
+ PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(pim_ifp->options);
+#endif
+
+ pim_ifp->igmp_join_list = 0;
+ pim_ifp->igmp_socket_list = 0;
+ pim_ifp->pim_neighbor_list = 0;
+ pim_ifp->pim_ifchannel_list = 0;
+
+ /* list of struct igmp_sock */
+ pim_ifp->igmp_socket_list = list_new();
+ if (!pim_ifp->igmp_socket_list) {
+ zlog_err("%s %s: failure: igmp_socket_list=list_new()",
+ __FILE__, __PRETTY_FUNCTION__);
+ return if_list_clean(pim_ifp);
+ }
+ pim_ifp->igmp_socket_list->del = (void (*)(void *)) igmp_sock_free;
+
+ /* list of struct pim_neighbor */
+ pim_ifp->pim_neighbor_list = list_new();
+ if (!pim_ifp->pim_neighbor_list) {
+ zlog_err("%s %s: failure: pim_neighbor_list=list_new()",
+ __FILE__, __PRETTY_FUNCTION__);
+ return if_list_clean(pim_ifp);
+ }
+ pim_ifp->pim_neighbor_list->del = (void (*)(void *)) pim_neighbor_free;
+
+ /* list of struct pim_ifchannel */
+ pim_ifp->pim_ifchannel_list = list_new();
+ if (!pim_ifp->pim_ifchannel_list) {
+ zlog_err("%s %s: failure: pim_ifchannel_list=list_new()",
+ __FILE__, __PRETTY_FUNCTION__);
+ return if_list_clean(pim_ifp);
+ }
+ pim_ifp->pim_ifchannel_list->del = (void (*)(void *)) pim_ifchannel_free;
+
+ ifp->info = pim_ifp;
+
+ pim_sock_reset(ifp);
+
+ zassert(PIM_IF_TEST_PIM(pim_ifp->options) || PIM_IF_TEST_IGMP(pim_ifp->options));
+
+ if (PIM_MROUTE_IS_ENABLED) {
+ pim_if_add_vif(ifp);
+ }
+
+ return pim_ifp;
+}
+
+void pim_if_delete(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+
+ zassert(ifp);
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ if (pim_ifp->igmp_join_list) {
+ pim_if_igmp_join_del_all(ifp);
+ }
+ zassert(!pim_ifp->igmp_join_list);
+
+ zassert(pim_ifp->igmp_socket_list);
+ zassert(!listcount(pim_ifp->igmp_socket_list));
+
+ zassert(pim_ifp->pim_neighbor_list);
+ zassert(!listcount(pim_ifp->pim_neighbor_list));
+
+ zassert(pim_ifp->pim_ifchannel_list);
+ zassert(!listcount(pim_ifp->pim_ifchannel_list));
+
+ if (PIM_MROUTE_IS_ENABLED) {
+ pim_if_del_vif(ifp);
+ }
+
+ list_delete(pim_ifp->igmp_socket_list);
+ list_delete(pim_ifp->pim_neighbor_list);
+ list_delete(pim_ifp->pim_ifchannel_list);
+
+ XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
+
+ ifp->info = 0;
+}
+
+void pim_if_update_could_assert(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *node;
+ struct listnode *next_node;
+ struct pim_ifchannel *ch;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
+ pim_ifchannel_update_could_assert(ch);
+ }
+}
+
+static void pim_if_update_my_assert_metric(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *node;
+ struct listnode *next_node;
+ struct pim_ifchannel *ch;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
+ pim_ifchannel_update_my_assert_metric(ch);
+ }
+}
+
+static void pim_addr_change(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ pim_if_dr_election(ifp); /* Done TODO T30 */
+ 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 */
+ pim_if_update_assert_tracking_desired(ifp); /* depends on DR, join_desired */
+
+ /*
+ RFC 4601: 4.3.1. Sending Hello Messages
+
+ 1) 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).
+ -- FIXME See CAVEAT C13
+
+ 2) After an interface has changed its IP address, it MUST send a
+ Hello message with its new IP address.
+ -- DONE below
+
+ 3) 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.
+ -- FIXME See TODO T31
+ */
+ pim_ifp->pim_ifstat_hello_sent = 0; /* reset hello counter */
+ if (pim_ifp->pim_sock_fd < 0)
+ return;
+ pim_hello_restart_now(ifp); /* send hello and restart timer */
+}
+
+static void on_primary_address_change(struct interface *ifp,
+ const char *caller,
+ struct in_addr old_addr,
+ struct in_addr new_addr)
+{
+ struct pim_interface *pim_ifp;
+
+ {
+ char old_str[100];
+ char new_str[100];
+ pim_inet4_dump("<old?>", old_addr, old_str, sizeof(old_str));
+ pim_inet4_dump("<new?>", new_addr, new_str, sizeof(new_str));
+ zlog_info("%s: %s: primary address changed from %s to %s on interface %s",
+ __PRETTY_FUNCTION__, caller,
+ old_str, new_str, ifp->name);
+ }
+
+ pim_ifp = ifp->info;
+
+ if (pim_ifp) {
+ if (PIM_IF_TEST_PIM(pim_ifp->options)) {
+ pim_addr_change(ifp);
+ }
+ }
+}
+
+static void detect_primary_address_change(struct interface *ifp,
+ const char *caller)
+{
+ struct pim_interface *pim_ifp;
+ struct in_addr new_prim_addr;
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ return;
+
+ new_prim_addr = pim_find_primary_addr(ifp);
+
+ if (PIM_DEBUG_ZEBRA) {
+ char new_prim_str[100];
+ char old_prim_str[100];
+ pim_inet4_dump("<new?>", new_prim_addr, new_prim_str, sizeof(new_prim_str));
+ pim_inet4_dump("<old?>", pim_ifp->primary_address, old_prim_str, sizeof(old_prim_str));
+ zlog_debug("%s: old primary addr %s, new primary addr %s on interface %s",
+ __PRETTY_FUNCTION__,
+ old_prim_str, new_prim_str, ifp->name);
+ }
+
+ if (new_prim_addr.s_addr != pim_ifp->primary_address.s_addr) {
+ struct in_addr old_addr = pim_ifp->primary_address;
+ pim_ifp->primary_address = new_prim_addr;
+
+ on_primary_address_change(ifp, caller, old_addr, new_prim_addr);
+ }
+}
+
+void pim_if_addr_add(struct connected *ifc)
+{
+ struct pim_interface *pim_ifp;
+ struct interface *ifp;
+ struct in_addr ifaddr;
+
+ zassert(ifc);
+
+ ifp = ifc->ifp;
+ zassert(ifp);
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ return;
+
+ if (!if_is_operative(ifp))
+ return;
+
+ ifaddr = ifc->address->u.prefix4;
+
+ detect_primary_address_change(ifp, __PRETTY_FUNCTION__);
+
+ if (PIM_IF_TEST_IGMP(pim_ifp->options)) {
+ struct igmp_sock *igmp;
+
+ /* lookup IGMP socket */
+ igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list,
+ ifaddr);
+ if (!igmp) {
+ /* if addr new, add IGMP socket */
+ pim_igmp_sock_add(pim_ifp->igmp_socket_list, ifaddr, ifp);
+ }
+ } /* igmp */
+
+ if (PIM_IF_TEST_PIM(pim_ifp->options)) {
+
+ /* Interface has a valid primary address ? */
+ if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) {
+
+ /* Interface has a valid socket ? */
+ if (pim_ifp->pim_sock_fd < 0) {
+ if (pim_sock_add(ifp)) {
+ zlog_warn("Failure creating PIM socket for interface %s",
+ ifp->name);
+ }
+ }
+
+ }
+ } /* pim */
+
+ if (PIM_MROUTE_IS_ENABLED) {
+ /*
+ PIM or IGMP is enabled on interface, and there is at least one
+ address assigned, then try to create a vif_index.
+ */
+ if (pim_ifp->mroute_vif_index < 0) {
+ pim_if_add_vif(ifp);
+ }
+ }
+}
+
+static void pim_if_addr_del_igmp(struct connected *ifc)
+{
+ struct pim_interface *pim_ifp = ifc->ifp->info;
+ struct igmp_sock *igmp;
+ struct in_addr ifaddr;
+
+ if (ifc->address->family != AF_INET) {
+ /* non-IPv4 address */
+ return;
+ }
+
+ if (!pim_ifp) {
+ /* IGMP not enabled on interface */
+ return;
+ }
+
+ ifaddr = ifc->address->u.prefix4;
+
+ /* lookup IGMP socket */
+ igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list,
+ ifaddr);
+ if (igmp) {
+ /* if addr found, del IGMP socket */
+ igmp_sock_delete(igmp);
+ }
+}
+
+static void pim_if_addr_del_pim(struct connected *ifc)
+{
+ struct pim_interface *pim_ifp = ifc->ifp->info;
+
+ if (ifc->address->family != AF_INET) {
+ /* non-IPv4 address */
+ return;
+ }
+
+ if (!pim_ifp) {
+ /* PIM not enabled on interface */
+ return;
+ }
+
+ if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) {
+ /* Interface keeps a valid primary address */
+ return;
+ }
+
+ if (pim_ifp->pim_sock_fd < 0) {
+ /* Interface does not hold a valid socket any longer */
+ return;
+ }
+
+ /*
+ pim_sock_delete() closes the socket, stops read and timer threads,
+ and kills all neighbors.
+ */
+ pim_sock_delete(ifc->ifp, "last address has been removed from interface");
+}
+
+void pim_if_addr_del(struct connected *ifc)
+{
+ struct interface *ifp;
+
+ zassert(ifc);
+ ifp = ifc->ifp;
+ zassert(ifp);
+
+ detect_primary_address_change(ifp, __PRETTY_FUNCTION__);
+
+ pim_if_addr_del_igmp(ifc);
+ pim_if_addr_del_pim(ifc);
+}
+
+void pim_if_addr_add_all(struct interface *ifp)
+{
+ struct connected *ifc;
+ struct listnode *node;
+ struct listnode *nextnode;
+
+ /* PIM/IGMP enabled ? */
+ if (!ifp->info)
+ return;
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) {
+ struct prefix *p = ifc->address;
+
+ if (p->family != AF_INET)
+ continue;
+
+ pim_if_addr_add(ifc);
+ }
+}
+
+void pim_if_addr_del_all(struct interface *ifp)
+{
+ struct connected *ifc;
+ struct listnode *node;
+ struct listnode *nextnode;
+
+ /* PIM/IGMP enabled ? */
+ if (!ifp->info)
+ return;
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) {
+ struct prefix *p = ifc->address;
+
+ if (p->family != AF_INET)
+ continue;
+
+ pim_if_addr_del(ifc);
+ }
+}
+
+void pim_if_addr_del_all_igmp(struct interface *ifp)
+{
+ struct connected *ifc;
+ struct listnode *node;
+ struct listnode *nextnode;
+
+ /* PIM/IGMP enabled ? */
+ if (!ifp->info)
+ return;
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) {
+ struct prefix *p = ifc->address;
+
+ if (p->family != AF_INET)
+ continue;
+
+ pim_if_addr_del_igmp(ifc);
+ }
+}
+
+void pim_if_addr_del_all_pim(struct interface *ifp)
+{
+ struct connected *ifc;
+ struct listnode *node;
+ struct listnode *nextnode;
+
+ /* PIM/IGMP enabled ? */
+ if (!ifp->info)
+ return;
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) {
+ struct prefix *p = ifc->address;
+
+ if (p->family != AF_INET)
+ continue;
+
+ pim_if_addr_del_pim(ifc);
+ }
+}
+
+static struct in_addr find_first_addr(struct interface *ifp)
+{
+ struct connected *ifc;
+ struct listnode *node;
+ struct in_addr addr;
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
+ struct prefix *p = ifc->address;
+
+ if (p->family != AF_INET)
+ continue;
+
+ if (PIM_INADDR_IS_ANY(p->u.prefix4)) {
+ zlog_warn("%s: null IPv4 address connected to interface %s",
+ __PRETTY_FUNCTION__, ifp->name);
+ continue;
+ }
+
+ return p->u.prefix4;
+ }
+
+ addr.s_addr = PIM_NET_INADDR_ANY;
+
+ return addr;
+}
+
+struct in_addr pim_find_primary_addr(struct interface *ifp)
+{
+ return find_first_addr(ifp);
+}
+
+/*
+ pim_if_add_vif() uses ifindex as vif_index
+
+ see also pim_if_find_vifindex_by_ifindex()
+ */
+int pim_if_add_vif(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp = ifp->info;
+ struct in_addr ifaddr;
+
+ zassert(pim_ifp);
+
+ if (pim_ifp->mroute_vif_index > 0) {
+ zlog_warn("%s: vif_index=%d > 0 on interface %s ifindex=%d",
+ __PRETTY_FUNCTION__,
+ pim_ifp->mroute_vif_index, ifp->name, ifp->ifindex);
+ return -1;
+ }
+
+ if (ifp->ifindex < 1) {
+ zlog_warn("%s: ifindex=%d < 1 on interface %s",
+ __PRETTY_FUNCTION__,
+ ifp->ifindex, ifp->name);
+ return -2;
+ }
+
+ if (ifp->ifindex >= MAXVIFS) {
+ zlog_warn("%s: ifindex=%d >= MAXVIFS=%d on interface %s",
+ __PRETTY_FUNCTION__,
+ ifp->ifindex, MAXVIFS, ifp->name);
+ return -3;
+ }
+
+ ifaddr = pim_ifp->primary_address;
+ if (PIM_INADDR_IS_ANY(ifaddr)) {
+ zlog_warn("%s: could not get address for interface %s ifindex=%d",
+ __PRETTY_FUNCTION__,
+ ifp->name, ifp->ifindex);
+ return -4;
+ }
+
+ if (pim_mroute_add_vif(ifp->ifindex, ifaddr)) {
+ /* pim_mroute_add_vif reported error */
+ return -5;
+ }
+
+ pim_ifp->mroute_vif_index = ifp->ifindex;
+
+ /*
+ Update highest vif_index
+ */
+ if (pim_ifp->mroute_vif_index > qpim_mroute_oif_highest_vif_index) {
+ qpim_mroute_oif_highest_vif_index = pim_ifp->mroute_vif_index;
+ }
+
+ return 0;
+}
+
+static int iflist_find_highest_vif_index()
+{
+ struct listnode *ifnode;
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ int highest_vif_index = -1;
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ continue;
+
+ if (pim_ifp->mroute_vif_index > highest_vif_index) {
+ highest_vif_index = pim_ifp->mroute_vif_index;
+ }
+ }
+
+ return highest_vif_index;
+}
+
+int pim_if_del_vif(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp = ifp->info;
+ int old_vif_index;
+
+ if (pim_ifp->mroute_vif_index < 1) {
+ zlog_warn("%s: vif_index=%d < 1 on interface %s ifindex=%d",
+ __PRETTY_FUNCTION__,
+ pim_ifp->mroute_vif_index, ifp->name, ifp->ifindex);
+ return -1;
+ }
+
+ if (pim_mroute_del_vif(pim_ifp->mroute_vif_index)) {
+ /* pim_mroute_del_vif reported error */
+ return -2;
+ }
+
+ /*
+ Update highest vif_index
+ */
+
+ /* save old vif_index in order to compare with highest below */
+ old_vif_index = pim_ifp->mroute_vif_index;
+
+ pim_ifp->mroute_vif_index = -1;
+
+ if (old_vif_index == qpim_mroute_oif_highest_vif_index) {
+ qpim_mroute_oif_highest_vif_index = iflist_find_highest_vif_index();
+ }
+
+ return 0;
+}
+
+void pim_if_add_vif_all()
+{
+ struct listnode *ifnode;
+ struct listnode *ifnextnode;
+ struct interface *ifp;
+
+ for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+ if (!ifp->info)
+ continue;
+
+ pim_if_add_vif(ifp);
+ }
+}
+
+void pim_if_del_vif_all()
+{
+ struct listnode *ifnode;
+ struct listnode *ifnextnode;
+ struct interface *ifp;
+
+ for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+ if (!ifp->info)
+ continue;
+
+ pim_if_del_vif(ifp);
+ }
+}
+
+struct interface *pim_if_find_by_vif_index(int vif_index)
+{
+ struct listnode *ifnode;
+ struct interface *ifp;
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+ if (ifp->info) {
+ struct pim_interface *pim_ifp;
+ pim_ifp = ifp->info;
+ if (vif_index == pim_ifp->mroute_vif_index)
+ return ifp;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ pim_if_add_vif() uses ifindex as vif_index
+ */
+int pim_if_find_vifindex_by_ifindex(int ifindex)
+{
+ return ifindex;
+}
+
+int pim_if_lan_delay_enabled(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+ zassert(pim_ifp->pim_number_of_nonlandelay_neighbors >= 0);
+
+ return pim_ifp->pim_number_of_nonlandelay_neighbors == 0;
+}
+
+uint16_t pim_if_effective_propagation_delay_msec(struct interface *ifp)
+{
+ if (pim_if_lan_delay_enabled(ifp)) {
+ struct pim_interface *pim_ifp;
+ pim_ifp = ifp->info;
+ return pim_ifp->pim_neighbors_highest_propagation_delay_msec;
+ }
+ else {
+ return PIM_DEFAULT_PROPAGATION_DELAY_MSEC;
+ }
+}
+
+uint16_t pim_if_effective_override_interval_msec(struct interface *ifp)
+{
+ if (pim_if_lan_delay_enabled(ifp)) {
+ struct pim_interface *pim_ifp;
+ pim_ifp = ifp->info;
+ return pim_ifp->pim_neighbors_highest_override_interval_msec;
+ }
+ else {
+ return PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC;
+ }
+}
+
+int pim_if_t_override_msec(struct interface *ifp)
+{
+ int effective_override_interval_msec;
+ int t_override_msec;
+
+ effective_override_interval_msec =
+ pim_if_effective_override_interval_msec(ifp);
+
+ t_override_msec = pim_rand_next(0, effective_override_interval_msec);
+
+ return t_override_msec;
+}
+
+uint16_t pim_if_jp_override_interval_msec(struct interface *ifp)
+{
+ return pim_if_effective_propagation_delay_msec(ifp) +
+ pim_if_effective_override_interval_msec(ifp);
+}
+
+/*
+ RFC 4601: 4.1.6. State Summarization Macros
+
+ The function NBR( I, A ) uses information gathered through PIM Hello
+ messages to map the IP address A of a directly connected PIM
+ neighbor router on interface I to the primary IP address of the same
+ router (Section 4.3.4). The primary IP address of a neighbor is the
+ address that it uses as the source of its PIM Hello messages.
+*/
+struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp,
+ struct in_addr addr)
+{
+ struct listnode *neighnode;
+ struct pim_neighbor *neigh;
+ struct pim_interface *pim_ifp;
+
+ zassert(ifp);
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp) {
+ zlog_warn("%s: multicast not enabled on interface %s",
+ __PRETTY_FUNCTION__,
+ ifp->name);
+ return 0;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
+
+ /* primary address ? */
+ if (neigh->source_addr.s_addr == addr.s_addr)
+ return neigh;
+
+ /* secondary address ? */
+ if (pim_neighbor_find_secondary(neigh, addr))
+ return neigh;
+ }
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug("%s: neighbor not found for address %s on interface %s",
+ __PRETTY_FUNCTION__,
+ addr_str, ifp->name);
+ }
+
+ return 0;
+}
+
+long pim_if_t_suppressed_msec(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ long t_suppressed_msec;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ /* join suppression disabled ? */
+ if (PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options))
+ return 0;
+
+ /* t_suppressed = t_periodic * rand(1.1, 1.4) */
+
+ t_suppressed_msec = qpim_t_periodic * pim_rand_next(1100, 1400);
+
+ return t_suppressed_msec;
+}
+
+static void igmp_join_free(struct igmp_join *ij)
+{
+ XFREE(MTYPE_PIM_IGMP_JOIN, ij);
+}
+
+static struct igmp_join *igmp_join_find(struct list *join_list,
+ struct in_addr group_addr,
+ struct in_addr source_addr)
+{
+ struct listnode *node;
+ struct igmp_join *ij;
+
+ zassert(join_list);
+
+ for (ALL_LIST_ELEMENTS_RO(join_list, node, ij)) {
+ if ((group_addr.s_addr == ij->group_addr.s_addr) &&
+ (source_addr.s_addr == ij->source_addr.s_addr))
+ return ij;
+ }
+
+ return 0;
+}
+
+static int igmp_join_sock(const char *ifname,
+ int ifindex,
+ struct in_addr group_addr,
+ struct in_addr source_addr)
+{
+ int join_fd;
+
+ join_fd = pim_socket_raw(IPPROTO_IGMP);
+ if (join_fd < 0) {
+ return -1;
+ }
+
+ if (pim_socket_join_source(join_fd, ifindex, group_addr, source_addr, ifname)) {
+ close(join_fd);
+ return -2;
+ }
+
+ return join_fd;
+}
+
+static struct igmp_join *igmp_join_new(struct interface *ifp,
+ struct in_addr group_addr,
+ struct in_addr source_addr)
+{
+ struct pim_interface *pim_ifp;
+ struct igmp_join *ij;
+ int join_fd;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ join_fd = igmp_join_sock(ifp->name, ifp->ifindex, group_addr, source_addr);
+ if (join_fd < 0) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s",
+ __PRETTY_FUNCTION__,
+ group_str, source_str, ifp->name);
+ return 0;
+ }
+
+ ij = XMALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij));
+ if (!ij) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_err("%s: XMALLOC(%d) failure for IGMP group %s source %s on interface %s",
+ __PRETTY_FUNCTION__,
+ sizeof(*ij), group_str, source_str, ifp->name);
+ close(join_fd);
+ return 0;
+ }
+
+ ij->sock_fd = join_fd;
+ ij->group_addr = group_addr;
+ ij->source_addr = source_addr;
+
+ listnode_add(pim_ifp->igmp_join_list, ij);
+
+ return ij;
+}
+
+int pim_if_igmp_join_add(struct interface *ifp,
+ struct in_addr group_addr,
+ struct in_addr source_addr)
+{
+ struct pim_interface *pim_ifp;
+ struct igmp_join *ij;
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp) {
+ zlog_warn("%s: multicast not enabled on interface %s",
+ __PRETTY_FUNCTION__,
+ ifp->name);
+ return -1;
+ }
+
+ if (!pim_ifp->igmp_join_list) {
+ pim_ifp->igmp_join_list = list_new();
+ if (!pim_ifp->igmp_join_list) {
+ zlog_err("%s %s: failure: igmp_join_list=list_new()",
+ __FILE__, __PRETTY_FUNCTION__);
+ return -2;
+ }
+ pim_ifp->igmp_join_list->del = (void (*)(void *)) igmp_join_free;
+ }
+
+ ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr);
+ if (ij) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: can't re-join existing IGMP group %s source %s on interface %s",
+ __PRETTY_FUNCTION__,
+ group_str, source_str, ifp->name);
+ return -3;
+ }
+
+ ij = igmp_join_new(ifp, group_addr, source_addr);
+ if (!ij) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: igmp_join_new() failure for IGMP group %s source %s on interface %s",
+ __PRETTY_FUNCTION__,
+ group_str, source_str, ifp->name);
+ return -4;
+ }
+
+ return 0;
+}
+
+
+
+int pim_if_igmp_join_del(struct interface *ifp,
+ struct in_addr group_addr,
+ struct in_addr source_addr)
+{
+ struct pim_interface *pim_ifp;
+ struct igmp_join *ij;
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp) {
+ zlog_warn("%s: multicast not enabled on interface %s",
+ __PRETTY_FUNCTION__,
+ ifp->name);
+ return -1;
+ }
+
+ if (!pim_ifp->igmp_join_list) {
+ zlog_warn("%s: no IGMP join on interface %s",
+ __PRETTY_FUNCTION__,
+ ifp->name);
+ return -2;
+ }
+
+ ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr);
+ if (!ij) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: could not find IGMP group %s source %s on interface %s",
+ __PRETTY_FUNCTION__,
+ group_str, source_str, ifp->name);
+ return -3;
+ }
+
+ if (close(ij->sock_fd)) {
+ int e = errno;
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s",
+ __PRETTY_FUNCTION__,
+ ij->sock_fd, group_str, source_str, ifp->name, e, safe_strerror(e));
+ /* warning only */
+ }
+ listnode_delete(pim_ifp->igmp_join_list, ij);
+ igmp_join_free(ij);
+ if (listcount(pim_ifp->igmp_join_list) < 1) {
+ list_delete(pim_ifp->igmp_join_list);
+ pim_ifp->igmp_join_list = 0;
+ }
+
+ return 0;
+}
+
+static void pim_if_igmp_join_del_all(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *node;
+ struct listnode *nextnode;
+ struct igmp_join *ij;
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp) {
+ zlog_warn("%s: multicast not enabled on interface %s",
+ __PRETTY_FUNCTION__,
+ ifp->name);
+ return;
+ }
+
+ if (!pim_ifp->igmp_join_list)
+ return;
+
+ for (ALL_LIST_ELEMENTS(pim_ifp->igmp_join_list, node, nextnode, ij))
+ pim_if_igmp_join_del(ifp, ij->group_addr, ij->source_addr);
+}
+
+/*
+ RFC 4601
+
+ Transitions from "I am Assert Loser" State
+
+ Current Winner's GenID Changes or NLT Expires
+
+ The Neighbor Liveness Timer associated with the current winner
+ expires or we receive a Hello message from the current winner
+ reporting a different GenID from the one it previously reported.
+ This indicates that the current winner's interface or router has
+ gone down (and may have come back up), and so we must assume it no
+ longer knows it was the winner.
+ */
+void pim_if_assert_on_neighbor_down(struct interface *ifp,
+ struct in_addr neigh_addr)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *node;
+ struct listnode *next_node;
+ struct pim_ifchannel *ch;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
+ /* Is (S,G,I) assert loser ? */
+ if (ch->ifassert_state != PIM_IFASSERT_I_AM_LOSER)
+ continue;
+ /* Dead neighbor was winner ? */
+ if (ch->ifassert_winner.s_addr != neigh_addr.s_addr)
+ continue;
+
+ assert_action_a5(ch);
+ }
+}
+
+void pim_if_update_join_desired(struct pim_interface *pim_ifp)
+{
+ struct listnode *ch_node;
+ struct pim_ifchannel *ch;
+
+ /* clear off flag from interface's upstreams */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+ PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(ch->upstream->flags);
+ }
+
+ /* scan per-interface (S,G,I) state on this I interface */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+ struct pim_upstream *up = ch->upstream;
+
+ if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(up->flags))
+ continue;
+
+ /* update join_desired for the global (S,G) state */
+ pim_upstream_update_join_desired(up);
+ PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(up->flags);
+ }
+}
+
+void pim_if_update_assert_tracking_desired(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *node;
+ struct listnode *next_node;
+ struct pim_ifchannel *ch;
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ return;
+
+ for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
+ pim_ifchannel_update_assert_tracking_desired(ch);
+ }
+}
diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h
new file mode 100644
index 00000000..0a702c27
--- /dev/null
+++ b/pimd/pim_iface.h
@@ -0,0 +1,160 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_IFACE_H
+#define PIM_IFACE_H
+
+#include <zebra.h>
+
+#include "if.h"
+#include "vty.h"
+
+#include "pim_igmp.h"
+#include "pim_upstream.h"
+
+#define PIM_IF_MASK_PIM (1 << 0)
+#define PIM_IF_MASK_IGMP (1 << 1)
+#define PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS (1 << 2)
+#define PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION (1 << 3)
+
+#define PIM_IF_IS_DELETED(ifp) ((ifp)->ifindex == IFINDEX_INTERNAL)
+
+#define PIM_IF_TEST_PIM(options) (PIM_IF_MASK_PIM & (options))
+#define PIM_IF_TEST_IGMP(options) (PIM_IF_MASK_IGMP & (options))
+#define PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(options) (PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS & (options))
+#define PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) (PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION & (options))
+
+#define PIM_IF_DO_PIM(options) ((options) |= PIM_IF_MASK_PIM)
+#define PIM_IF_DO_IGMP(options) ((options) |= PIM_IF_MASK_IGMP)
+#define PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(options) ((options) |= PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS)
+#define PIM_IF_DO_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) ((options) |= PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION)
+
+#define PIM_IF_DONT_PIM(options) ((options) &= ~PIM_IF_MASK_PIM)
+#define PIM_IF_DONT_IGMP(options) ((options) &= ~PIM_IF_MASK_IGMP)
+#define PIM_IF_DONT_IGMP_LISTEN_ALLROUTERS(options) ((options) &= ~PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS)
+#define PIM_IF_DONT_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) ((options) &= ~PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION)
+
+struct pim_interface {
+ uint32_t options; /* bit vector */
+ int mroute_vif_index;
+ struct in_addr primary_address; /* remember addr to detect change */
+
+ int igmp_default_robustness_variable; /* IGMPv3 QRV */
+ int igmp_default_query_interval; /* IGMPv3 secs between general queries */
+ int igmp_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs for general queries */
+ int igmp_specific_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs for specific queries */
+ struct list *igmp_socket_list; /* list of struct igmp_sock */
+ struct list *igmp_join_list; /* list of struct igmp_join */
+
+ int pim_sock_fd; /* PIM socket file descriptor */
+ struct thread *t_pim_sock_read; /* thread for reading PIM socket */
+ int64_t pim_sock_creation; /* timestamp of PIM socket creation */
+
+ struct thread *t_pim_hello_timer;
+ int pim_hello_period;
+ int pim_default_holdtime;
+ int pim_triggered_hello_delay;
+ uint32_t pim_generation_id;
+ uint16_t pim_propagation_delay_msec; /* config */
+ uint16_t pim_override_interval_msec; /* config */
+ struct list *pim_neighbor_list; /* list of struct pim_neighbor */
+ struct list *pim_ifchannel_list; /* list of struct pim_ifchannel */
+
+ /* neighbors without lan_delay */
+ int pim_number_of_nonlandelay_neighbors;
+ uint16_t pim_neighbors_highest_propagation_delay_msec;
+ uint16_t pim_neighbors_highest_override_interval_msec;
+
+ /* DR Election */
+ int64_t pim_dr_election_last; /* timestamp */
+ int pim_dr_election_count;
+ struct in_addr pim_dr_addr;
+ uint32_t pim_dr_priority; /* config */
+ int pim_dr_num_nondrpri_neighbors; /* neighbors without dr_pri */
+
+ int64_t pim_ifstat_start; /* start timestamp for stats */
+ uint32_t pim_ifstat_hello_sent;
+ uint32_t pim_ifstat_hello_sendfail;
+ uint32_t pim_ifstat_hello_recv;
+ uint32_t pim_ifstat_hello_recvfail;
+};
+
+/*
+ if default_holdtime is set (>= 0), use it;
+ otherwise default_holdtime is 3.5 * hello_period
+ */
+#define PIM_IF_DEFAULT_HOLDTIME(pim_ifp) \
+ (((pim_ifp)->pim_default_holdtime < 0) ? \
+ ((pim_ifp)->pim_hello_period * 7 / 2) : \
+ ((pim_ifp)->pim_default_holdtime))
+
+void pim_if_init(void);
+
+struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim);
+void pim_if_delete(struct interface *ifp);
+void pim_if_addr_add(struct connected *ifc);
+void pim_if_addr_del(struct connected *ifc);
+void pim_if_addr_add_all(struct interface *ifp);
+void pim_if_addr_del_all(struct interface *ifp);
+void pim_if_addr_del_all_igmp(struct interface *ifp);
+void pim_if_addr_del_all_pim(struct interface *ifp);
+
+int pim_if_add_vif(struct interface *ifp);
+int pim_if_del_vif(struct interface *ifp);
+void pim_if_add_vif_all(void);
+void pim_if_del_vif_all(void);
+
+struct interface *pim_if_find_by_vif_index(int vif_index);
+int pim_if_find_vifindex_by_ifindex(int ifindex);
+
+int pim_if_lan_delay_enabled(struct interface *ifp);
+uint16_t pim_if_effective_propagation_delay_msec(struct interface *ifp);
+uint16_t pim_if_effective_override_interval_msec(struct interface *ifp);
+uint16_t pim_if_jp_override_interval_msec(struct interface *ifp);
+struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp,
+ struct in_addr addr);
+
+long pim_if_t_suppressed_msec(struct interface *ifp);
+int pim_if_t_override_msec(struct interface *ifp);
+
+struct in_addr pim_find_primary_addr(struct interface *ifp);
+
+int pim_if_igmp_join_add(struct interface *ifp,
+ struct in_addr group_addr,
+ struct in_addr source_addr);
+int pim_if_igmp_join_del(struct interface *ifp,
+ struct in_addr group_addr,
+ struct in_addr source_addr);
+
+void pim_if_update_could_assert(struct interface *ifp);
+
+void pim_if_assert_on_neighbor_down(struct interface *ifp,
+ struct in_addr neigh_addr);
+
+void pim_if_rpf_interface_changed(struct interface *old_rpf_ifp,
+ struct pim_upstream *up);
+
+void pim_if_update_join_desired(struct pim_interface *pim_ifp);
+
+void pim_if_update_assert_tracking_desired(struct interface *ifp);
+
+#endif /* PIM_IFACE_H */
diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c
new file mode 100644
index 00000000..7f946cfd
--- /dev/null
+++ b/pimd/pim_ifchannel.c
@@ -0,0 +1,893 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "thread.h"
+#include "memory.h"
+
+#include "pimd.h"
+#include "pim_str.h"
+#include "pim_iface.h"
+#include "pim_ifchannel.h"
+#include "pim_zebra.h"
+#include "pim_time.h"
+#include "pim_msg.h"
+#include "pim_pim.h"
+#include "pim_join.h"
+#include "pim_rpf.h"
+#include "pim_macro.h"
+
+void pim_ifchannel_free(struct pim_ifchannel *ch)
+{
+ zassert(!ch->t_ifjoin_expiry_timer);
+ zassert(!ch->t_ifjoin_prune_pending_timer);
+ zassert(!ch->t_ifassert_timer);
+
+ XFREE(MTYPE_PIM_IFCHANNEL, ch);
+}
+
+void pim_ifchannel_delete(struct pim_ifchannel *ch)
+{
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ch->interface->info;
+ zassert(pim_ifp);
+
+ if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) {
+ pim_upstream_update_join_desired(ch->upstream);
+ }
+
+ pim_upstream_del(ch->upstream);
+
+ THREAD_OFF(ch->t_ifjoin_expiry_timer);
+ THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
+ THREAD_OFF(ch->t_ifassert_timer);
+
+ /*
+ notice that listnode_delete() can't be moved
+ into pim_ifchannel_free() because the later is
+ called by list_delete_all_node()
+ */
+ listnode_delete(pim_ifp->pim_ifchannel_list, ch);
+
+ pim_ifchannel_free(ch);
+}
+
+#define IFCHANNEL_NOINFO(ch) \
+ ( \
+ ((ch)->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO) \
+ && \
+ ((ch)->ifjoin_state == PIM_IFJOIN_NOINFO) \
+ && \
+ ((ch)->ifassert_state == PIM_IFASSERT_NOINFO) \
+ )
+
+static void delete_on_noinfo(struct pim_ifchannel *ch)
+{
+ if (IFCHANNEL_NOINFO(ch)) {
+
+ /* In NOINFO state, timers should have been cleared */
+ zassert(!ch->t_ifjoin_expiry_timer);
+ zassert(!ch->t_ifjoin_prune_pending_timer);
+ zassert(!ch->t_ifassert_timer);
+
+ pim_ifchannel_delete(ch);
+ }
+}
+
+void pim_ifchannel_ifjoin_switch(const char *caller,
+ struct pim_ifchannel *ch,
+ enum pim_ifjoin_state new_state)
+{
+ enum pim_ifjoin_state old_state = ch->ifjoin_state;
+
+ if (old_state == new_state) {
+ zlog_debug("%s calledby %s: non-transition on state %d (%s)",
+ __PRETTY_FUNCTION__, caller, new_state,
+ pim_ifchannel_ifjoin_name(new_state));
+ return;
+ }
+
+ zassert(old_state != new_state);
+
+ ch->ifjoin_state = new_state;
+
+ /* Transition to/from NOINFO ? */
+ if (
+ (old_state == PIM_IFJOIN_NOINFO)
+ ||
+ (new_state == PIM_IFJOIN_NOINFO)
+ ) {
+
+ if (PIM_DEBUG_PIM_EVENTS) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+ zlog_debug("PIM_IFCHANNEL_%s: (S,G)=(%s,%s) on interface %s",
+ ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"),
+ src_str, grp_str, ch->interface->name);
+ }
+
+ /*
+ Record uptime of state transition to/from NOINFO
+ */
+ ch->ifjoin_creation = pim_time_monotonic_sec();
+
+ pim_upstream_update_join_desired(ch->upstream);
+ pim_ifchannel_update_could_assert(ch);
+ pim_ifchannel_update_assert_tracking_desired(ch);
+ }
+}
+
+const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state)
+{
+ switch (ifjoin_state) {
+ case PIM_IFJOIN_NOINFO: return "NOINFO";
+ case PIM_IFJOIN_JOIN: return "JOIN";
+ case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP";
+ }
+
+ return "ifjoin_bad_state";
+}
+
+const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
+{
+ switch (ifassert_state) {
+ case PIM_IFASSERT_NOINFO: return "NOINFO";
+ case PIM_IFASSERT_I_AM_WINNER: return "WINNER";
+ case PIM_IFASSERT_I_AM_LOSER: return "LOSER";
+ }
+
+ return "ifassert_bad_state";
+}
+
+/*
+ RFC 4601: 4.6.5. Assert State Macros
+
+ AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
+ defaults to Infinity when in the NoInfo state.
+*/
+void reset_ifassert_state(struct pim_ifchannel *ch)
+{
+ THREAD_OFF(ch->t_ifassert_timer);
+
+ pim_ifassert_winner_set(ch,
+ PIM_IFASSERT_NOINFO,
+ qpim_inaddr_any,
+ qpim_infinite_assert_metric);
+}
+
+static struct pim_ifchannel *pim_ifchannel_new(struct interface *ifp,
+ struct in_addr source_addr,
+ struct in_addr group_addr)
+{
+ struct pim_ifchannel *ch;
+ struct pim_interface *pim_ifp;
+ struct pim_upstream *up;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ up = pim_upstream_add(source_addr, group_addr);
+ if (!up) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+ zlog_err("%s: could not attach upstream (S,G)=(%s,%s) on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, ifp->name);
+ return 0;
+ }
+
+ ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
+ if (!ch) {
+ zlog_err("%s: PIM XMALLOC(%d) failure",
+ __PRETTY_FUNCTION__, sizeof(*ch));
+ return 0;
+ }
+
+ ch->flags = 0;
+ ch->upstream = up;
+ ch->interface = ifp;
+ ch->source_addr = source_addr;
+ ch->group_addr = group_addr;
+ ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
+
+ ch->ifjoin_state = PIM_IFJOIN_NOINFO;
+ ch->t_ifjoin_expiry_timer = 0;
+ ch->t_ifjoin_prune_pending_timer = 0;
+ ch->ifjoin_creation = 0;
+
+ /* Assert state */
+ ch->t_ifassert_timer = 0;
+ reset_ifassert_state(ch);
+ if (pim_macro_ch_could_assert_eval(ch))
+ PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
+ else
+ PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
+
+ if (pim_macro_assert_tracking_desired_eval(ch))
+ PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
+ else
+ PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
+
+ ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
+
+ /* Attach to list */
+ listnode_add(pim_ifp->pim_ifchannel_list, ch);
+
+ zassert(IFCHANNEL_NOINFO(ch));
+
+ return ch;
+}
+
+struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
+ struct in_addr source_addr,
+ struct in_addr group_addr)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *ch_node;
+ struct pim_ifchannel *ch;
+
+ zassert(ifp);
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+ zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str,
+ ifp->name);
+ return 0;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+ if (
+ (source_addr.s_addr == ch->source_addr.s_addr) &&
+ (group_addr.s_addr == ch->group_addr.s_addr)
+ ) {
+ return ch;
+ }
+ }
+
+ return 0;
+}
+
+static void ifmembership_set(struct pim_ifchannel *ch,
+ enum pim_ifmembership membership)
+{
+ if (ch->local_ifmembership == membership)
+ return;
+
+ {
+ 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) membership now is %s on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str,
+ membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO",
+ ch->interface->name);
+ }
+
+ ch->local_ifmembership = membership;
+
+ pim_upstream_update_join_desired(ch->upstream);
+ pim_ifchannel_update_could_assert(ch);
+ pim_ifchannel_update_assert_tracking_desired(ch);
+}
+
+
+void pim_ifchannel_membership_clear(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *ch_node;
+ struct pim_ifchannel *ch;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+ ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
+ }
+}
+
+void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *node;
+ struct listnode *next_node;
+ struct pim_ifchannel *ch;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
+ delete_on_noinfo(ch);
+ }
+}
+
+struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
+ struct in_addr source_addr,
+ struct in_addr group_addr)
+{
+ struct pim_ifchannel *ch;
+ char src_str[100];
+ char grp_str[100];
+
+ ch = pim_ifchannel_find(ifp, source_addr, group_addr);
+ if (ch)
+ return ch;
+
+ ch = pim_ifchannel_new(ifp, source_addr, group_addr);
+ if (ch)
+ return ch;
+
+ pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+ zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=(%s,%s) on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, ifp->name);
+
+ return 0;
+}
+
+static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
+{
+ pim_forward_stop(ch);
+ pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
+ delete_on_noinfo(ch);
+}
+
+static int on_ifjoin_expiry_timer(struct thread *t)
+{
+ struct pim_ifchannel *ch;
+
+ zassert(t);
+ ch = THREAD_ARG(t);
+ zassert(ch);
+
+ ch->t_ifjoin_expiry_timer = 0;
+
+ zassert(ch->ifjoin_state == PIM_IFJOIN_JOIN);
+
+ ifjoin_to_noinfo(ch);
+ /* ch may have been deleted */
+
+ return 0;
+}
+
+static void prune_echo(struct interface *ifp,
+ struct in_addr source_addr,
+ struct in_addr group_addr)
+{
+ struct pim_interface *pim_ifp;
+ struct in_addr neigh_dst_addr;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ neigh_dst_addr = pim_ifp->primary_address;
+
+ if (PIM_DEBUG_PIM_EVENTS) {
+ char source_str[100];
+ char group_str[100];
+ char neigh_dst_str[100];
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<neigh?>", neigh_dst_addr, neigh_dst_str, sizeof(neigh_dst_str));
+ zlog_debug("%s: sending PruneEcho(S,G)=(%s,%s) to upstream=%s on interface %s",
+ __PRETTY_FUNCTION__, source_str, group_str, neigh_dst_str, ifp->name);
+ }
+
+ pim_joinprune_send(ifp, neigh_dst_addr, source_addr, group_addr,
+ 0 /* boolean: send_join=false (prune) */);
+}
+
+static int on_ifjoin_prune_pending_timer(struct thread *t)
+{
+ struct pim_ifchannel *ch;
+ int send_prune_echo; /* boolean */
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct in_addr ch_source;
+ struct in_addr ch_group;
+
+ zassert(t);
+ ch = THREAD_ARG(t);
+ zassert(ch);
+
+ ch->t_ifjoin_prune_pending_timer = 0;
+
+ zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING);
+
+ /* Send PruneEcho(S,G) ? */
+ ifp = ch->interface;
+ pim_ifp = ifp->info;
+ send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1);
+
+ /* Save (S,G) */
+ ch_source = ch->source_addr;
+ ch_group = ch->group_addr;
+
+ ifjoin_to_noinfo(ch);
+ /* from here ch may have been deleted */
+
+ if (send_prune_echo)
+ prune_echo(ifp, ch_source, ch_group);
+
+ return 0;
+}
+
+static void check_recv_upstream(int is_join,
+ struct interface *recv_ifp,
+ struct in_addr upstream,
+ struct in_addr source_addr,
+ struct in_addr group_addr,
+ uint8_t source_flags,
+ int holdtime)
+{
+ struct pim_upstream *up;
+
+ /* Upstream (S,G) in Joined state ? */
+ up = pim_upstream_find(source_addr, group_addr);
+ if (!up)
+ return;
+ if (up->join_state != PIM_UPSTREAM_JOINED)
+ return;
+
+ /* Upstream (S,G) in Joined state */
+
+ if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
+ /* RPF'(S,G) not found */
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+ zlog_warn("%s %s: RPF'(%s,%s) not found",
+ __FILE__, __PRETTY_FUNCTION__,
+ src_str, grp_str);
+ return;
+ }
+
+ /* upstream directed to RPF'(S,G) ? */
+ if (upstream.s_addr != up->rpf.rpf_addr.s_addr) {
+ char src_str[100];
+ char grp_str[100];
+ char up_str[100];
+ char rpf_str[100];
+ pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
+ pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
+ zlog_warn("%s %s: (S,G)=(%s,%s) upstream=%s not directed to RPF'(S,G)=%s on interface %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ src_str, grp_str,
+ up_str, rpf_str, recv_ifp->name);
+ return;
+ }
+ /* upstream directed to RPF'(S,G) */
+
+ if (is_join) {
+ /* Join(S,G) to RPF'(S,G) */
+ pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime);
+ return;
+ }
+
+ /* Prune to RPF'(S,G) */
+
+ if (source_flags & PIM_RPT_BIT_MASK) {
+ if (source_flags & PIM_WILDCARD_BIT_MASK) {
+ /* Prune(*,G) to RPF'(S,G) */
+ pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)",
+ up, up->rpf.rpf_addr);
+ return;
+ }
+
+ /* Prune(S,G,rpt) to RPF'(S,G) */
+ pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
+ up, up->rpf.rpf_addr);
+ return;
+ }
+
+ /* Prune(S,G) to RPF'(S,G) */
+ pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up,
+ up->rpf.rpf_addr);
+}
+
+static int nonlocal_upstream(int is_join,
+ struct interface *recv_ifp,
+ struct in_addr upstream,
+ struct in_addr source_addr,
+ struct in_addr group_addr,
+ uint8_t source_flags,
+ uint16_t holdtime)
+{
+ struct pim_interface *recv_pim_ifp;
+ int is_local; /* boolean */
+
+ recv_pim_ifp = recv_ifp->info;
+ zassert(recv_pim_ifp);
+
+ is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char up_str[100];
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
+ pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+ zlog_warn("%s: recv %s (S,G)=(%s,%s) to %s upstream=%s on %s",
+ __PRETTY_FUNCTION__,
+ is_join ? "join" : "prune",
+ src_str, grp_str,
+ is_local ? "local" : "non-local",
+ up_str, recv_ifp->name);
+ }
+
+ if (is_local)
+ return 0;
+
+ /*
+ Since recv upstream addr was not directed to our primary
+ address, check if we should react to it in any way.
+ */
+ check_recv_upstream(is_join, recv_ifp, upstream, source_addr, group_addr,
+ source_flags, holdtime);
+
+ return 1; /* non-local */
+}
+
+void pim_ifchannel_join_add(struct interface *ifp,
+ struct in_addr neigh_addr,
+ struct in_addr upstream,
+ struct in_addr source_addr,
+ struct in_addr group_addr,
+ uint8_t source_flags,
+ uint16_t holdtime)
+{
+ struct pim_interface *pim_ifp;
+ struct pim_ifchannel *ch;
+
+ if (nonlocal_upstream(1 /* join */, ifp, upstream,
+ source_addr, group_addr, source_flags, holdtime)) {
+ return;
+ }
+
+ ch = pim_ifchannel_add(ifp, source_addr, group_addr);
+ if (!ch)
+ return;
+
+ /*
+ RFC 4601: 4.6.1. (S,G) Assert Message State Machine
+
+ Transitions from "I am Assert Loser" State
+
+ Receive Join(S,G) on Interface I
+
+ We receive a Join(S,G) that has the Upstream Neighbor Address
+ field set to my primary IP address on interface I. The action is
+ to transition to NoInfo state, delete this (S,G) assert state
+ (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
+ to operate.
+
+ Notice: The nonlocal_upstream() test above ensures the upstream
+ address of the join message is our primary address.
+ */
+ if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
+ char src_str[100];
+ char grp_str[100];
+ char neigh_str[100];
+ pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
+ zlog_warn("%s: Assert Loser recv Join(%s,%s) from %s on %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, neigh_str, ifp->name);
+
+ assert_action_a5(ch);
+ }
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ switch (ch->ifjoin_state) {
+ case PIM_IFJOIN_NOINFO:
+ pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
+ if (pim_macro_chisin_oiflist(ch)) {
+ pim_forward_start(ch);
+ }
+ break;
+ case PIM_IFJOIN_JOIN:
+ zassert(!ch->t_ifjoin_prune_pending_timer);
+
+ /*
+ In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
+ previously received join message with holdtime=0xFFFF.
+ */
+ if (ch->t_ifjoin_expiry_timer) {
+ unsigned long remain =
+ thread_timer_remain_second(ch->t_ifjoin_expiry_timer);
+ if (remain > holdtime) {
+ /*
+ RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
+
+ Transitions from Join State
+
+ The (S,G) downstream state machine on interface I remains in
+ Join state, and the Expiry Timer (ET) is restarted, set to
+ maximum of its current value and the HoldTime from the
+ triggering Join/Prune message.
+
+ Conclusion: Do not change the ET if the current value is
+ higher than the received join holdtime.
+ */
+ return;
+ }
+ }
+ THREAD_OFF(ch->t_ifjoin_expiry_timer);
+ break;
+ case PIM_IFJOIN_PRUNE_PENDING:
+ zassert(!ch->t_ifjoin_expiry_timer);
+ zassert(ch->t_ifjoin_prune_pending_timer);
+ THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
+ pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
+ break;
+ }
+
+ zassert(!IFCHANNEL_NOINFO(ch));
+
+ if (holdtime != 0xFFFF) {
+ THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
+ on_ifjoin_expiry_timer,
+ ch, holdtime);
+ }
+}
+
+void pim_ifchannel_prune(struct interface *ifp,
+ struct in_addr upstream,
+ struct in_addr source_addr,
+ struct in_addr group_addr,
+ uint8_t source_flags,
+ uint16_t holdtime)
+{
+ struct pim_ifchannel *ch;
+ int jp_override_interval_msec;
+
+ if (nonlocal_upstream(0 /* prune */, ifp, upstream,
+ source_addr, group_addr, source_flags, holdtime)) {
+ return;
+ }
+
+ ch = pim_ifchannel_add(ifp, source_addr, group_addr);
+ if (!ch)
+ return;
+
+ switch (ch->ifjoin_state) {
+ case PIM_IFJOIN_NOINFO:
+ case PIM_IFJOIN_PRUNE_PENDING:
+ /* nothing to do */
+ break;
+ case PIM_IFJOIN_JOIN:
+ {
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ifp->info;
+
+ zassert(ch->t_ifjoin_expiry_timer);
+ zassert(!ch->t_ifjoin_prune_pending_timer);
+
+ THREAD_OFF(ch->t_ifjoin_expiry_timer);
+
+ pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
+
+ if (listcount(pim_ifp->pim_neighbor_list) > 1) {
+ jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
+ }
+ else {
+ jp_override_interval_msec = 0; /* schedule to expire immediately */
+ /* If we called ifjoin_prune() directly instead, care should
+ be taken not to use "ch" afterwards since it would be
+ deleted. */
+ }
+
+ THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
+ on_ifjoin_prune_pending_timer,
+ ch, jp_override_interval_msec);
+
+ zassert(!ch->t_ifjoin_expiry_timer);
+ zassert(ch->t_ifjoin_prune_pending_timer);
+ }
+ break;
+ }
+
+}
+
+void pim_ifchannel_local_membership_add(struct interface *ifp,
+ struct in_addr source_addr,
+ struct in_addr group_addr)
+{
+ struct pim_ifchannel *ch;
+ struct pim_interface *pim_ifp;
+
+ /* PIM enabled on interface? */
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ return;
+ if (!PIM_IF_TEST_PIM(pim_ifp->options))
+ return;
+
+ ch = pim_ifchannel_add(ifp, source_addr, group_addr);
+ if (!ch) {
+ return;
+ }
+
+ ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
+
+ zassert(!IFCHANNEL_NOINFO(ch));
+}
+
+void pim_ifchannel_local_membership_del(struct interface *ifp,
+ struct in_addr source_addr,
+ struct in_addr group_addr)
+{
+ struct pim_ifchannel *ch;
+ struct pim_interface *pim_ifp;
+
+ /* PIM enabled on interface? */
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ return;
+ if (!PIM_IF_TEST_PIM(pim_ifp->options))
+ return;
+
+ ch = pim_ifchannel_find(ifp, source_addr, group_addr);
+ if (!ch)
+ return;
+
+ ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
+
+ delete_on_noinfo(ch);
+}
+
+void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
+{
+ int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
+ int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
+
+ if (new_couldassert == old_couldassert)
+ return;
+
+ {
+ 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: CouldAssert(%s,%s,%s) changed from %d to %d",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, ch->interface->name,
+ old_couldassert, new_couldassert);
+ }
+
+ if (new_couldassert) {
+ /* CouldAssert(S,G,I) switched from FALSE to TRUE */
+ PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
+ }
+ else {
+ /* CouldAssert(S,G,I) switched from TRUE to FALSE */
+ PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
+
+ if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
+ assert_action_a4(ch);
+ }
+ }
+
+ pim_ifchannel_update_my_assert_metric(ch);
+}
+
+/*
+ my_assert_metric may be affected by:
+
+ CouldAssert(S,G)
+ pim_ifp->primary_address
+ rpf->source_nexthop.mrib_metric_preference;
+ rpf->source_nexthop.mrib_route_metric;
+ */
+void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
+{
+ struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch);
+
+ if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
+ return;
+
+ {
+ char src_str[100];
+ char grp_str[100];
+ char old_addr_str[100];
+ char new_addr_str[100];
+ pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str));
+ pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str));
+ zlog_info("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, ch->interface->name,
+ ch->ifassert_my_metric.rpt_bit_flag,
+ ch->ifassert_my_metric.metric_preference,
+ ch->ifassert_my_metric.route_metric,
+ old_addr_str,
+ my_metric_new.rpt_bit_flag,
+ my_metric_new.metric_preference,
+ my_metric_new.route_metric,
+ new_addr_str);
+ }
+
+ ch->ifassert_my_metric = my_metric_new;
+
+ if (pim_assert_metric_better(&ch->ifassert_my_metric,
+ &ch->ifassert_winner_metric)) {
+ assert_action_a5(ch);
+ }
+}
+
+void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
+{
+ int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
+ int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
+
+ if (new_atd == old_atd)
+ return;
+
+ {
+ 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: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, ch->interface->name,
+ old_atd, new_atd);
+ }
+
+ if (new_atd) {
+ /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
+ PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
+ }
+ else {
+ /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
+ PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
+
+ if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
+ assert_action_a5(ch);
+ }
+ }
+}
diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h
new file mode 100644
index 00000000..e6f1c294
--- /dev/null
+++ b/pimd/pim_ifchannel.h
@@ -0,0 +1,145 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_IFCHANNEL_H
+#define PIM_IFCHANNEL_H
+
+#include <zebra.h>
+
+#include "if.h"
+
+#include "pim_upstream.h"
+
+enum pim_ifmembership {
+ PIM_IFMEMBERSHIP_NOINFO,
+ PIM_IFMEMBERSHIP_INCLUDE
+};
+
+enum pim_ifjoin_state {
+ PIM_IFJOIN_NOINFO,
+ PIM_IFJOIN_JOIN,
+ PIM_IFJOIN_PRUNE_PENDING
+};
+
+enum pim_ifassert_state {
+ PIM_IFASSERT_NOINFO,
+ PIM_IFASSERT_I_AM_WINNER,
+ PIM_IFASSERT_I_AM_LOSER
+};
+
+struct pim_assert_metric {
+ uint32_t rpt_bit_flag;
+ uint32_t metric_preference;
+ uint32_t route_metric;
+ struct in_addr ip_address; /* neighbor router that sourced the Assert message */
+};
+
+/*
+ Flag to detect change in CouldAssert(S,G,I)
+*/
+#define PIM_IF_FLAG_MASK_COULD_ASSERT (1 << 0)
+#define PIM_IF_FLAG_TEST_COULD_ASSERT(flags) ((flags) & PIM_IF_FLAG_MASK_COULD_ASSERT)
+#define PIM_IF_FLAG_SET_COULD_ASSERT(flags) ((flags) |= PIM_IF_FLAG_MASK_COULD_ASSERT)
+#define PIM_IF_FLAG_UNSET_COULD_ASSERT(flags) ((flags) &= ~PIM_IF_FLAG_MASK_COULD_ASSERT)
+/*
+ Flag to detect change in AssertTrackingDesired(S,G,I)
+*/
+#define PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED (1 << 1)
+#define PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(flags) ((flags) & PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED)
+#define PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(flags) ((flags) |= PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED)
+#define PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(flags) ((flags) &= ~PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED)
+
+/*
+ Per-interface (S,G) state
+*/
+struct pim_ifchannel {
+ struct in_addr source_addr; /* (S,G) source key */
+ struct in_addr group_addr; /* (S,G) group key */
+ struct interface *interface; /* backpointer to interface */
+ uint32_t flags;
+
+ /* IGMPv3 determined interface has local members for (S,G) ? */
+ enum pim_ifmembership local_ifmembership;
+
+ /* Per-interface (S,G) Join/Prune State (Section 4.1.4 of RFC4601) */
+ enum pim_ifjoin_state ifjoin_state;
+ struct thread *t_ifjoin_expiry_timer;
+ struct thread *t_ifjoin_prune_pending_timer;
+ int64_t ifjoin_creation; /* Record uptime of ifjoin state */
+
+ /* Per-interface (S,G) Assert State (Section 4.6.1 of RFC4601) */
+ enum pim_ifassert_state ifassert_state;
+ struct thread *t_ifassert_timer;
+ struct in_addr ifassert_winner;
+ struct pim_assert_metric ifassert_winner_metric;
+ int64_t ifassert_creation; /* Record uptime of ifassert state */
+ struct pim_assert_metric ifassert_my_metric;
+
+ /* Upstream (S,G) state */
+ struct pim_upstream *upstream;
+};
+
+void pim_ifchannel_free(struct pim_ifchannel *ch);
+void pim_ifchannel_delete(struct pim_ifchannel *ch);
+void pim_ifchannel_membership_clear(struct interface *ifp);
+void pim_ifchannel_delete_on_noinfo(struct interface *ifp);
+struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
+ struct in_addr source_addr,
+ struct in_addr group_addr);
+struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
+ struct in_addr source_addr,
+ struct in_addr group_addr);
+void pim_ifchannel_join_add(struct interface *ifp,
+ struct in_addr neigh_addr,
+ struct in_addr upstream,
+ struct in_addr source_addr,
+ struct in_addr group_addr,
+ uint8_t source_flags,
+ uint16_t holdtime);
+void pim_ifchannel_prune(struct interface *ifp,
+ struct in_addr upstream,
+ struct in_addr source_addr,
+ struct in_addr group_addr,
+ uint8_t source_flags,
+ uint16_t holdtime);
+void pim_ifchannel_local_membership_add(struct interface *ifp,
+ struct in_addr source_addr,
+ struct in_addr group_addr);
+void pim_ifchannel_local_membership_del(struct interface *ifp,
+ struct in_addr source_addr,
+ struct in_addr group_addr);
+
+void pim_ifchannel_ifjoin_switch(const char *caller,
+ struct pim_ifchannel *ch,
+ enum pim_ifjoin_state new_state);
+const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state);
+const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state);
+
+int pim_ifchannel_isin_oiflist(struct pim_ifchannel *ch);
+
+void reset_ifassert_state(struct pim_ifchannel *ch);
+
+void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch);
+void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch);
+void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch);
+
+#endif /* PIM_IFCHANNEL_H */
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
new file mode 100644
index 00000000..a0a4aa81
--- /dev/null
+++ b/pimd/pim_igmp.c
@@ -0,0 +1,1413 @@
+/*
+ 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 "memory.h"
+
+#include "pimd.h"
+#include "pim_igmp.h"
+#include "pim_igmpv3.h"
+#include "pim_iface.h"
+#include "pim_sock.h"
+#include "pim_mroute.h"
+#include "pim_str.h"
+#include "pim_util.h"
+#include "pim_time.h"
+#include "pim_zebra.h"
+
+#define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE (1)
+#define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE (2)
+#define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3)
+#define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4)
+#define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES (5)
+#define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES (6)
+
+static void group_timer_off(struct igmp_group *group);
+
+static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
+ struct in_addr group_addr);
+
+static int igmp_sock_open(struct in_addr ifaddr, int ifindex, uint32_t pim_options)
+{
+ int fd;
+ int join = 0;
+ struct in_addr group;
+
+ fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, 1 /* loop=true */);
+ if (fd < 0)
+ return -1;
+
+ if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) {
+ if (inet_aton(PIM_ALL_ROUTERS, &group)) {
+ if (!pim_socket_join(fd, group, ifaddr, ifindex))
+ ++join;
+ }
+ else {
+ zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
+ __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+ PIM_ALL_ROUTERS, errno, safe_strerror(errno));
+ }
+ }
+
+ /*
+ IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1
+ IGMP routers must receive general queries for querier election.
+ */
+ if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
+ if (!pim_socket_join(fd, group, ifaddr, ifindex))
+ ++join;
+ }
+ else {
+ zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
+ __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+ PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
+ }
+
+ if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
+ if (!pim_socket_join(fd, group, ifaddr, ifindex)) {
+ ++join;
+ }
+ }
+ else {
+ zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
+ __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+ PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
+ }
+
+ if (!join) {
+ zlog_err("IGMP socket fd=%d could not join any group on interface address %s",
+ fd, inet_ntoa(ifaddr));
+ close(fd);
+ fd = -1;
+ }
+
+ return fd;
+}
+
+#undef IGMP_SOCK_DUMP
+
+#ifdef IGMP_SOCK_DUMP
+static void igmp_sock_dump(array_t *igmp_sock_array)
+{
+ int size = array_size(igmp_sock_array);
+ for (int i = 0; i < size; ++i) {
+
+ struct igmp_sock *igmp = array_get(igmp_sock_array, i);
+
+ zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d",
+ __FILE__, __PRETTY_FUNCTION__,
+ i, size,
+ inet_ntoa(igmp->ifaddr),
+ igmp->fd);
+ }
+}
+#endif
+
+struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
+ struct in_addr ifaddr)
+{
+ struct listnode *sock_node;
+ struct igmp_sock *igmp;
+
+#ifdef IGMP_SOCK_DUMP
+ igmp_sock_dump(igmp_sock_list);
+#endif
+
+ for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
+ if (ifaddr.s_addr == igmp->ifaddr.s_addr)
+ return igmp;
+
+ return 0;
+}
+
+struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list,
+ int fd)
+{
+ struct listnode *sock_node;
+ struct igmp_sock *igmp;
+
+ for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
+ if (fd == igmp->fd)
+ return igmp;
+
+ return 0;
+}
+
+static int pim_igmp_other_querier_expire(struct thread *t)
+{
+ struct igmp_sock *igmp;
+
+ zassert(t);
+ igmp = THREAD_ARG(t);
+ zassert(igmp);
+
+ zassert(igmp->t_other_querier_timer);
+ zassert(!igmp->t_igmp_query_timer);
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char ifaddr_str[100];
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+ zlog_debug("%s: Querier %s resuming",
+ __PRETTY_FUNCTION__,
+ ifaddr_str);
+ }
+
+ igmp->t_other_querier_timer = 0;
+
+ /*
+ We are the current querier, then
+ re-start sending general queries.
+ */
+ pim_igmp_general_query_on(igmp);
+
+ return 0;
+}
+
+void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)
+{
+ long other_querier_present_interval_msec;
+ struct pim_interface *pim_ifp;
+
+ zassert(igmp);
+ zassert(igmp->interface);
+ zassert(igmp->interface->info);
+
+ pim_ifp = igmp->interface->info;
+
+ if (igmp->t_other_querier_timer) {
+ /*
+ There is other querier present already,
+ then reset the other-querier-present timer.
+ */
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char ifaddr_str[100];
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+ zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present",
+ ifaddr_str);
+ }
+
+ THREAD_OFF(igmp->t_other_querier_timer);
+ zassert(!igmp->t_other_querier_timer);
+ }
+ else {
+ /*
+ We are the current querier, then stop sending general queries:
+ igmp->t_igmp_query_timer = 0;
+ */
+ pim_igmp_general_query_off(igmp);
+ }
+
+ /*
+ Since this socket is starting the other-querier-present timer,
+ there should not be periodic query timer for this socket.
+ */
+ zassert(!igmp->t_igmp_query_timer);
+
+ /*
+ RFC 3376: 8.5. Other Querier Present Interval
+
+ The Other Querier Present Interval is the length of time that must
+ pass before a multicast router decides that there is no longer
+ another multicast router which should be the querier. This value
+ MUST be ((the Robustness Variable) times (the Query Interval)) plus
+ (one half of one Query Response Interval).
+
+ other_querier_present_interval_msec = \
+ igmp->querier_robustness_variable * \
+ 1000 * igmp->querier_query_interval + \
+ 100 * (pim_ifp->query_max_response_time_dsec >> 1);
+ */
+ other_querier_present_interval_msec =
+ PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable,
+ igmp->querier_query_interval,
+ pim_ifp->igmp_query_max_response_time_dsec);
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char ifaddr_str[100];
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+ zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
+ ifaddr_str,
+ other_querier_present_interval_msec / 1000,
+ other_querier_present_interval_msec % 1000);
+ }
+
+ THREAD_TIMER_MSEC_ON(master, igmp->t_other_querier_timer,
+ pim_igmp_other_querier_expire,
+ igmp, other_querier_present_interval_msec);
+}
+
+void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp)
+{
+ zassert(igmp);
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ if (igmp->t_other_querier_timer) {
+ char ifaddr_str[100];
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+ zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
+ ifaddr_str, igmp->fd, igmp->interface->name);
+ }
+ }
+ THREAD_OFF(igmp->t_other_querier_timer);
+ zassert(!igmp->t_other_querier_timer);
+}
+
+static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
+ int max_resp_code,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ uint8_t resv_s_qrv;
+ uint8_t s_flag;
+ uint8_t qrv;
+ struct in_addr group_addr;
+ uint16_t recv_checksum;
+ uint16_t checksum;
+ int i;
+
+ group_addr = *(struct in_addr *)(igmp_msg + 4);
+
+ ifp = igmp->interface;
+ pim_ifp = ifp->info;
+
+ recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
+
+ /* for computing checksum */
+ *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
+
+ checksum = pim_inet_checksum(igmp_msg, igmp_msg_len);
+ if (checksum != recv_checksum) {
+ zlog_warn("Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
+ query_version, from_str, ifp->name, recv_checksum, checksum);
+ return -1;
+ }
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+ zlog_debug("Recv IGMP query v%d from %s on %s: size=%d checksum=%x group=%s",
+ query_version, from_str, ifp->name,
+ igmp_msg_len, checksum, group_str);
+ }
+
+ /*
+ RFC 3376: 6.6.2. Querier Election
+
+ When a router receives a query with a lower IP address, it sets
+ the Other-Querier-Present timer to Other Querier Present Interval
+ and ceases to send queries on the network if it was the previously
+ elected querier.
+ */
+ if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char ifaddr_str[100];
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+ zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)",
+ ifp->name,
+ ifaddr_str, ntohl(igmp->ifaddr.s_addr),
+ from_str, ntohl(from.s_addr));
+ }
+
+ pim_igmp_other_querier_timer_on(igmp);
+ }
+
+ /*
+ RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
+
+ Routers adopt the QRV value from the most recently received Query
+ as their own [Robustness Variable] value, unless that most
+ recently received QRV was zero, in which case the receivers use
+ the default [Robustness Variable] value specified in section 8.1
+ or a statically configured value.
+ */
+ resv_s_qrv = igmp_msg[8];
+ qrv = 7 & resv_s_qrv;
+ igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
+
+ /*
+ RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
+
+ Multicast routers that are not the current querier adopt the QQI
+ value from the most recently received Query as their own [Query
+ Interval] value, unless that most recently received QQI was zero,
+ in which case the receiving routers use the default.
+ */
+ if (igmp->t_other_querier_timer) {
+ /* other querier present */
+ uint8_t qqic;
+ uint16_t qqi;
+ qqic = igmp_msg[9];
+ qqi = igmp_msg_decode8to16(qqic);
+ igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char ifaddr_str[100];
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+ zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
+ ifaddr_str,
+ qqi ? "recv-non-default" : "default",
+ igmp->querier_query_interval,
+ qqic,
+ from_str);
+ }
+ }
+
+ /*
+ RFC 3376: 6.6.1. Timer Updates
+
+ When a router sends or receives a query with a clear Suppress
+ Router-Side Processing flag, it must update its timers to reflect
+ the correct timeout values for the group or sources being queried.
+
+ General queries don't trigger timer update.
+ */
+ s_flag = (1 << 3) & resv_s_qrv;
+ if (!s_flag) {
+ /* s_flag is clear */
+
+ if (PIM_INADDR_IS_ANY(group_addr)) {
+ /* this is a general query */
+
+ /* log that general query should have the s_flag set */
+ zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear",
+ query_version, from_str, ifp->name);
+ }
+ else {
+ struct igmp_group *group;
+
+ /* this is a non-general query: perform timer updates */
+
+ group = find_group_by_addr(igmp, group_addr);
+ if (group) {
+ int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
+
+ /*
+ RFC 3376: 6.6.1. Timer Updates
+ Query Q(G,A): Source Timer for sources in A are lowered to LMQT
+ Query Q(G): Group Timer is lowered to LMQT
+ */
+ if (recv_num_sources < 1) {
+ /* Query Q(G): Group Timer is lowered to LMQT */
+
+ igmp_group_timer_lower_to_lmqt(group);
+ }
+ else {
+ /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
+
+ /* Scan sources in query and lower their timers to LMQT */
+ struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET);
+ for (i = 0; i < recv_num_sources; ++i) {
+ struct in_addr src_addr = sources[i];
+ struct igmp_source *src = igmp_find_source_by_addr(group, src_addr);
+ if (src) {
+ igmp_source_timer_lower_to_lmqt(src);
+ }
+ }
+ }
+
+ }
+ else {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+ zlog_warn("IGMP query v%d from %s on %s: could not find group %s for timer update",
+ query_version, from_str, ifp->name, group_str);
+ }
+ }
+ } /* s_flag is clear: timer updates */
+
+ return 0;
+}
+
+static int igmp_v3_report(struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ uint16_t recv_checksum;
+ uint16_t checksum;
+ int num_groups;
+ uint8_t *group_record;
+ uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
+ struct interface *ifp = igmp->interface;
+ int i;
+
+ if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
+ from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE);
+ return -1;
+ }
+
+ recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
+
+ /* for computing checksum */
+ *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
+
+ checksum = pim_inet_checksum(igmp_msg, igmp_msg_len);
+ if (checksum != recv_checksum) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
+ from_str, ifp->name, recv_checksum, checksum);
+ return -1;
+ }
+
+ num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
+ if (num_groups < 1) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
+ from_str, ifp->name);
+ return -1;
+ }
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
+ from_str, ifp->name, igmp_msg_len, checksum, num_groups);
+ }
+
+ group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
+
+ /* Scan groups */
+ for (i = 0; i < num_groups; ++i) {
+ struct in_addr rec_group;
+ uint8_t *sources;
+ uint8_t *src;
+ int rec_type;
+ int rec_auxdatalen;
+ int rec_num_sources;
+ int j;
+
+ if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
+ from_str, ifp->name);
+ return -1;
+ }
+
+ rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
+ rec_auxdatalen = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
+ rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
+
+ rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET);
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
+ from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group));
+ }
+
+ /* Scan sources */
+
+ sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
+
+ for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
+
+ if ((src + 4) > report_pastend) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
+ from_str, ifp->name);
+ return -1;
+ }
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ char src_str[200];
+
+ if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
+ sprintf(src_str, "<source?>");
+
+ zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
+ from_str, ifp->name, i, inet_ntoa(rec_group), src_str);
+ }
+ } /* for (sources) */
+
+ switch (rec_type) {
+ case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
+ igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
+ igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
+ igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
+ igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
+ igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
+ igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ default:
+ zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
+ from_str, ifp->name, rec_type);
+ }
+
+ group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
+
+ } /* for (group records) */
+
+ return 0;
+}
+
+static void on_trace(const char *label,
+ struct interface *ifp, struct in_addr from)
+{
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char from_str[100];
+ pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
+ zlog_debug("%s: from %s on %s",
+ label, from_str, ifp->name);
+ }
+}
+
+static int igmp_v2_report(struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ struct interface *ifp = igmp->interface;
+ struct igmp_group *group;
+ struct in_addr group_addr;
+
+ on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
+
+ if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
+ zlog_warn("Recv IGMP report v2 from %s on %s: size=%d other than correct=%d",
+ from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
+ return -1;
+ }
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ zlog_warn("%s %s: FIXME WRITEME",
+ __FILE__, __PRETTY_FUNCTION__);
+ }
+
+ group_addr = *(struct in_addr *)(igmp_msg + 4);
+
+ /* non-existant group is created as INCLUDE {empty} */
+ group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
+ if (!group) {
+ return -1;
+ }
+
+ group->last_igmp_v2_report_dsec = pim_time_monotonic_dsec();
+
+ return 0;
+}
+
+static int igmp_v2_leave(struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ struct interface *ifp = igmp->interface;
+
+ on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
+
+ if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
+ zlog_warn("Recv IGMP leave v2 from %s on %s: size=%d other than correct=%d",
+ from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
+ return -1;
+ }
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ zlog_warn("%s %s: FIXME WRITEME",
+ __FILE__, __PRETTY_FUNCTION__);
+ }
+
+ return 0;
+}
+
+static int igmp_v1_report(struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ struct interface *ifp = igmp->interface;
+ struct igmp_group *group;
+ struct in_addr group_addr;
+
+ on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
+
+ if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
+ zlog_warn("Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
+ from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
+ return -1;
+ }
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ zlog_warn("%s %s: FIXME WRITEME",
+ __FILE__, __PRETTY_FUNCTION__);
+ }
+
+ group_addr = *(struct in_addr *)(igmp_msg + 4);
+
+ /* non-existant group is created as INCLUDE {empty} */
+ group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
+ if (!group) {
+ return -1;
+ }
+
+ group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
+
+ return 0;
+}
+
+int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
+{
+ struct ip *ip_hdr;
+ size_t ip_hlen; /* ip header length in bytes */
+ char *igmp_msg;
+ int igmp_msg_len;
+ int msg_type;
+ char from_str[100];
+ char to_str[100];
+
+ if (len < sizeof(*ip_hdr)) {
+ zlog_warn("IGMP packet size=%d shorter than minimum=%d",
+ len, sizeof(*ip_hdr));
+ return -1;
+ }
+
+ ip_hdr = (struct ip *) buf;
+
+ pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str , sizeof(from_str));
+ pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str , sizeof(to_str));
+
+ ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ zlog_debug("Recv IP packet from %s to %s on %s: size=%d ip_header_size=%d ip_proto=%d",
+ from_str, to_str, igmp->interface->name, len, ip_hlen, ip_hdr->ip_p);
+ }
+
+ if (ip_hdr->ip_p != PIM_IP_PROTO_IGMP) {
+ zlog_warn("IP packet protocol=%d is not IGMP=%d",
+ ip_hdr->ip_p, PIM_IP_PROTO_IGMP);
+ return -1;
+ }
+
+ if (ip_hlen < PIM_IP_HEADER_MIN_LEN) {
+ zlog_warn("IP packet header size=%d shorter than minimum=%d",
+ ip_hlen, PIM_IP_HEADER_MIN_LEN);
+ return -1;
+ }
+ if (ip_hlen > PIM_IP_HEADER_MAX_LEN) {
+ zlog_warn("IP packet header size=%d greater than maximum=%d",
+ ip_hlen, PIM_IP_HEADER_MAX_LEN);
+ return -1;
+ }
+
+ igmp_msg = buf + ip_hlen;
+ msg_type = *igmp_msg;
+ igmp_msg_len = len - ip_hlen;
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
+ from_str, to_str, igmp->interface->name, ip_hdr->ip_ttl, msg_type,
+ igmp_msg_len);
+ }
+
+ if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
+ zlog_warn("IGMP message size=%d shorter than minimum=%d",
+ igmp_msg_len, PIM_IGMP_MIN_LEN);
+ return -1;
+ }
+
+ switch (msg_type) {
+ case PIM_IGMP_MEMBERSHIP_QUERY:
+ {
+ int max_resp_code = igmp_msg[1];
+ int query_version;
+
+ /*
+ RFC 3376: 7.1. Query Version Distinctions
+ IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero
+ IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero
+ IGMPv3 Query: length >= 12 octets
+ */
+
+ if (igmp_msg_len == 8) {
+ query_version = max_resp_code ? 2 : 1;
+ }
+ else if (igmp_msg_len >= 12) {
+ query_version = 3;
+ }
+ else {
+ zlog_warn("Unknown IGMP query version");
+ return -1;
+ }
+
+ return recv_igmp_query(igmp, query_version, max_resp_code,
+ ip_hdr->ip_src, from_str,
+ igmp_msg, igmp_msg_len);
+ }
+
+ case PIM_IGMP_V3_MEMBERSHIP_REPORT:
+ return igmp_v3_report(igmp, ip_hdr->ip_src, from_str,
+ igmp_msg, igmp_msg_len);
+
+ case PIM_IGMP_V2_MEMBERSHIP_REPORT:
+ return igmp_v2_report(igmp, ip_hdr->ip_src, from_str,
+ igmp_msg, igmp_msg_len);
+
+ case PIM_IGMP_V1_MEMBERSHIP_REPORT:
+ return igmp_v1_report(igmp, ip_hdr->ip_src, from_str,
+ igmp_msg, igmp_msg_len);
+
+ case PIM_IGMP_V2_LEAVE_GROUP:
+ return igmp_v2_leave(igmp, ip_hdr->ip_src, from_str,
+ igmp_msg, igmp_msg_len);
+ }
+
+ zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
+
+ return -1;
+}
+
+static int pim_igmp_general_query(struct thread *t);
+
+void pim_igmp_general_query_on(struct igmp_sock *igmp)
+{
+ struct pim_interface *pim_ifp;
+ int startup_mode;
+ int query_interval;
+
+ zassert(igmp);
+ zassert(igmp->interface);
+
+ /*
+ Since this socket is starting as querier,
+ there should not exist a timer for other-querier-present.
+ */
+ zassert(!igmp->t_other_querier_timer);
+ pim_ifp = igmp->interface->info;
+ zassert(pim_ifp);
+
+ /*
+ RFC 3376: 8.6. Startup Query Interval
+
+ The Startup Query Interval is the interval between General Queries
+ sent by a Querier on startup. Default: 1/4 the Query Interval.
+ */
+ startup_mode = igmp->startup_query_count > 0;
+ if (startup_mode) {
+ --igmp->startup_query_count;
+
+ /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */
+ query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
+ }
+ else {
+ query_interval = igmp->querier_query_interval;
+ }
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char ifaddr_str[100];
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+ zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
+ ifaddr_str,
+ query_interval,
+ startup_mode ? "startup" : "non-startup",
+ igmp->fd);
+ }
+ igmp->t_igmp_query_timer = 0;
+ zassert(!igmp->t_igmp_query_timer);
+ THREAD_TIMER_ON(master, igmp->t_igmp_query_timer,
+ pim_igmp_general_query,
+ igmp, query_interval);
+}
+
+void pim_igmp_general_query_off(struct igmp_sock *igmp)
+{
+ zassert(igmp);
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ if (igmp->t_igmp_query_timer) {
+ char ifaddr_str[100];
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+ zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
+ ifaddr_str, igmp->fd, igmp->interface->name);
+ }
+ }
+ THREAD_OFF(igmp->t_igmp_query_timer);
+ zassert(!igmp->t_igmp_query_timer);
+}
+
+/* Issue IGMP general query */
+static int pim_igmp_general_query(struct thread *t)
+{
+ char query_buf[PIM_IGMP_BUFSIZE_WRITE];
+ struct igmp_sock *igmp;
+ struct in_addr dst_addr;
+ struct in_addr group_addr;
+ struct pim_interface *pim_ifp;
+
+ zassert(t);
+
+ igmp = THREAD_ARG(t);
+
+ zassert(igmp);
+ zassert(igmp->interface);
+ zassert(igmp->interface->info);
+
+ pim_ifp = igmp->interface->info;
+
+ /*
+ RFC3376: 4.1.12. IP Destination Addresses for Queries
+
+ In IGMPv3, General Queries are sent with an IP destination address
+ of 224.0.0.1, the all-systems multicast address. Group-Specific
+ and Group-and-Source-Specific Queries are sent with an IP
+ destination address equal to the multicast address of interest.
+ */
+
+ dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+ group_addr.s_addr = PIM_NET_INADDR_ANY;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char querier_str[100];
+ char dst_str[100];
+ pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
+ sizeof(querier_str));
+ pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+ zlog_debug("Querier %s issuing IGMP general query to %s on %s",
+ querier_str, dst_str, igmp->interface->name);
+ }
+
+ pim_igmp_send_membership_query(0 /* igmp_group */,
+ igmp->fd,
+ igmp->interface->name,
+ query_buf,
+ sizeof(query_buf),
+ 0 /* num_sources */,
+ dst_addr,
+ group_addr,
+ pim_ifp->igmp_query_max_response_time_dsec,
+ 1 /* s_flag: always set for general queries */,
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
+
+ pim_igmp_general_query_on(igmp);
+
+ return 0;
+}
+
+static int pim_igmp_read(struct thread *t);
+
+static void igmp_read_on(struct igmp_sock *igmp)
+{
+ zassert(igmp);
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ zlog_debug("Scheduling READ event on IGMP socket fd=%d",
+ igmp->fd);
+ }
+ igmp->t_igmp_read = 0;
+ zassert(!igmp->t_igmp_read);
+ THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd);
+}
+
+static int pim_igmp_read(struct thread *t)
+{
+ struct igmp_sock *igmp;
+ int fd;
+ struct sockaddr_in from;
+ struct sockaddr_in to;
+ socklen_t fromlen = sizeof(from);
+ socklen_t tolen = sizeof(to);
+ char buf[PIM_IGMP_BUFSIZE_READ];
+ int len;
+ int ifindex = -1;
+ int result = -1; /* defaults to bad */
+
+ zassert(t);
+
+ igmp = THREAD_ARG(t);
+
+ zassert(igmp);
+
+ fd = THREAD_FD(t);
+
+ zassert(fd == igmp->fd);
+
+ len = pim_socket_recvfromto(fd, buf, sizeof(buf),
+ &from, &fromlen,
+ &to, &tolen,
+ &ifindex);
+ if (len < 0) {
+ zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s",
+ fd, errno, safe_strerror(errno));
+ goto done;
+ }
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ char from_str[100];
+ char to_str[100];
+
+ if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
+ sprintf(from_str, "<from?>");
+ if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
+ sprintf(to_str, "<to?>");
+
+ zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
+ len, from_str, to_str, fd, ifindex, igmp->interface->ifindex);
+ }
+
+#ifdef PIM_CHECK_RECV_IFINDEX_SANITY
+ /* ifindex sanity check */
+ if (ifindex != (int) igmp->interface->ifindex) {
+ char from_str[100];
+ char to_str[100];
+ struct interface *ifp;
+
+ if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
+ sprintf(from_str, "<from?>");
+ if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
+ sprintf(to_str, "<to?>");
+
+ ifp = if_lookup_by_index(ifindex);
+ if (ifp) {
+ zassert(ifindex == (int) ifp->ifindex);
+ }
+
+#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
+ zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
+ from_str, to_str, fd,
+ ifindex, ifp ? ifp->name : "<if-notfound>",
+ igmp->interface->ifindex, igmp->interface->name);
+#endif
+ goto done;
+ }
+#endif
+
+ if (pim_igmp_packet(igmp, buf, len)) {
+ goto done;
+ }
+
+ result = 0; /* good */
+
+ done:
+ igmp_read_on(igmp);
+
+ return result;
+}
+
+static void sock_close(struct igmp_sock *igmp)
+{
+ pim_igmp_other_querier_timer_off(igmp);
+ pim_igmp_general_query_off(igmp);
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ if (igmp->t_igmp_read) {
+ zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s",
+ inet_ntoa(igmp->ifaddr), igmp->fd,
+ igmp->interface->name);
+ }
+ }
+ THREAD_OFF(igmp->t_igmp_read);
+ zassert(!igmp->t_igmp_read);
+
+ if (close(igmp->fd)) {
+ zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
+ inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name,
+ errno, safe_strerror(errno));
+ }
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
+ inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name);
+ }
+}
+
+void igmp_startup_mode_on(struct igmp_sock *igmp)
+{
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = igmp->interface->info;
+
+ /*
+ RFC 3376: 8.7. Startup Query Count
+
+ The Startup Query Count is the number of Queries sent out on
+ startup, separated by the Startup Query Interval. Default: the
+ Robustness Variable.
+ */
+ igmp->startup_query_count = igmp->querier_robustness_variable;
+
+ /*
+ Since we're (re)starting, reset QQI to default Query Interval
+ */
+ igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
+}
+
+static void igmp_group_free(struct igmp_group *group)
+{
+ zassert(!group->t_group_query_retransmit_timer);
+ zassert(!group->t_group_timer);
+ zassert(group->group_source_list);
+ zassert(!listcount(group->group_source_list));
+
+ list_free(group->group_source_list);
+
+ XFREE(MTYPE_PIM_IGMP_GROUP, group);
+}
+
+static void igmp_group_delete(struct igmp_group *group)
+{
+ struct listnode *src_node;
+ struct listnode *src_nextnode;
+ struct igmp_source *src;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ zlog_debug("Deleting IGMP group %s from socket %d interface %s",
+ group_str,
+ group->group_igmp_sock->fd,
+ group->group_igmp_sock->interface->name);
+ }
+
+ for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, src)) {
+ igmp_source_delete(src);
+ }
+
+ if (group->t_group_query_retransmit_timer) {
+ THREAD_OFF(group->t_group_query_retransmit_timer);
+ zassert(!group->t_group_query_retransmit_timer);
+ }
+
+ group_timer_off(group);
+ listnode_delete(group->group_igmp_sock->igmp_group_list, group);
+ igmp_group_free(group);
+}
+
+void igmp_group_delete_empty_include(struct igmp_group *group)
+{
+ zassert(!group->group_filtermode_isexcl);
+ zassert(!listcount(group->group_source_list));
+
+ igmp_group_delete(group);
+}
+
+void igmp_sock_free(struct igmp_sock *igmp)
+{
+ zassert(!igmp->t_igmp_read);
+ zassert(!igmp->t_igmp_query_timer);
+ zassert(!igmp->t_other_querier_timer);
+ zassert(igmp->igmp_group_list);
+ zassert(!listcount(igmp->igmp_group_list));
+
+ list_free(igmp->igmp_group_list);
+
+ XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
+}
+
+void igmp_sock_delete(struct igmp_sock *igmp)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *grp_node;
+ struct listnode *grp_nextnode;
+ struct igmp_group *grp;
+
+ for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, grp)) {
+ igmp_group_delete(grp);
+ }
+
+ sock_close(igmp);
+
+ pim_ifp = igmp->interface->info;
+
+ listnode_delete(pim_ifp->igmp_socket_list, igmp);
+
+ igmp_sock_free(igmp);
+}
+
+static struct igmp_sock *igmp_sock_new(int fd,
+ struct in_addr ifaddr,
+ struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ struct igmp_sock *igmp;
+
+ pim_ifp = ifp->info;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s",
+ fd, inet_ntoa(ifaddr), ifp->name);
+ }
+
+ igmp = XMALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
+ if (!igmp) {
+ zlog_warn("%s %s: XMALLOC() failure",
+ __FILE__, __PRETTY_FUNCTION__);
+ return 0;
+ }
+
+ igmp->igmp_group_list = list_new();
+ if (!igmp->igmp_group_list) {
+ zlog_err("%s %s: failure: igmp_group_list = list_new()",
+ __FILE__, __PRETTY_FUNCTION__);
+ return 0;
+ }
+ igmp->igmp_group_list->del = (void (*)(void *)) igmp_group_free;
+
+ igmp->fd = fd;
+ igmp->interface = ifp;
+ igmp->ifaddr = ifaddr;
+ igmp->t_igmp_read = 0;
+ igmp->t_igmp_query_timer = 0;
+ igmp->t_other_querier_timer = 0; /* no other querier present */
+ igmp->querier_robustness_variable = pim_ifp->igmp_default_robustness_variable;
+ igmp->sock_creation = pim_time_monotonic_sec();
+
+ /*
+ igmp_startup_mode_on() will reset QQI:
+
+ igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
+ */
+ igmp_startup_mode_on(igmp);
+
+ igmp_read_on(igmp);
+ pim_igmp_general_query_on(igmp);
+
+ return igmp;
+}
+
+struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
+ struct in_addr ifaddr,
+ struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ struct igmp_sock *igmp;
+ int fd;
+
+ pim_ifp = ifp->info;
+
+ fd = igmp_sock_open(ifaddr, ifp->ifindex, pim_ifp->options);
+ if (fd < 0) {
+ zlog_warn("Could not open IGMP socket for %s on %s",
+ inet_ntoa(ifaddr), ifp->name);
+ return 0;
+ }
+
+ igmp = igmp_sock_new(fd, ifaddr, ifp);
+ if (!igmp) {
+ zlog_err("%s %s: igmp_sock_new() failure",
+ __FILE__, __PRETTY_FUNCTION__);
+ close(fd);
+ return 0;
+ }
+
+ listnode_add(igmp_sock_list, igmp);
+
+#ifdef IGMP_SOCK_DUMP
+ igmp_sock_dump(igmp_sock_array);
+#endif
+
+ return igmp;
+}
+
+/*
+ RFC 3376: 6.5. Switching Router Filter-Modes
+
+ When a router's filter-mode for a group is EXCLUDE and the group
+ timer expires, the router filter-mode for the group transitions to
+ INCLUDE.
+
+ A router uses source records with running source timers as its state
+ for the switch to a filter-mode of INCLUDE. If there are any source
+ records with source timers greater than zero (i.e., requested to be
+ forwarded), a router switches to filter-mode of INCLUDE using those
+ source records. Source records whose timers are zero (from the
+ previous EXCLUDE mode) are deleted.
+ */
+static int igmp_group_timer(struct thread *t)
+{
+ struct igmp_group *group;
+
+ zassert(t);
+ group = THREAD_ARG(t);
+ zassert(group);
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ zlog_debug("%s: Timer for group %s on interface %s",
+ __PRETTY_FUNCTION__,
+ group_str, group->group_igmp_sock->interface->name);
+ }
+
+ zassert(group->group_filtermode_isexcl);
+
+ group->t_group_timer = 0;
+ group->group_filtermode_isexcl = 0;
+
+ /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
+ igmp_anysource_forward_stop(group);
+
+ igmp_source_delete_expired(group->group_source_list);
+
+ zassert(!group->t_group_timer);
+ zassert(!group->group_filtermode_isexcl);
+
+ /*
+ RFC 3376: 6.2.2. Definition of Group Timers
+
+ If there are no more source records for the group, delete group
+ record.
+ */
+ if (listcount(group->group_source_list) < 1) {
+ igmp_group_delete_empty_include(group);
+ }
+
+ return 0;
+}
+
+static void group_timer_off(struct igmp_group *group)
+{
+ if (!group->t_group_timer)
+ return;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ zlog_debug("Cancelling TIMER event for group %s on %s",
+ group_str, group->group_igmp_sock->interface->name);
+ }
+
+ THREAD_OFF(group->t_group_timer);
+ zassert(!group->t_group_timer);
+}
+
+void igmp_group_timer_on(struct igmp_group *group,
+ long interval_msec, const char *ifname)
+{
+ group_timer_off(group);
+
+ if (PIM_DEBUG_IGMP_EVENTS) {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
+ interval_msec / 1000,
+ interval_msec % 1000,
+ group_str, ifname);
+ }
+
+ /*
+ RFC 3376: 6.2.2. Definition of Group Timers
+
+ The group timer is only used when a group is in EXCLUDE mode and
+ it represents the time for the *filter-mode* of the group to
+ expire and switch to INCLUDE mode.
+ */
+ zassert(group->group_filtermode_isexcl);
+
+ THREAD_TIMER_MSEC_ON(master, group->t_group_timer,
+ igmp_group_timer,
+ group, interval_msec);
+}
+
+static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
+ struct in_addr group_addr)
+{
+ struct igmp_group *group;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, node, group))
+ if (group_addr.s_addr == group->group_addr.s_addr)
+ return group;
+
+ return 0;
+}
+
+struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
+ struct in_addr group_addr,
+ const char *ifname)
+{
+ struct igmp_group *group;
+
+ group = find_group_by_addr(igmp, group_addr);
+ if (group) {
+ return group;
+ }
+
+ /*
+ Non-existant group is created as INCLUDE {empty}:
+
+ RFC 3376 - 5.1. Action on Change of Interface State
+
+ If no interface state existed for that multicast address before
+ the change (i.e., the change consisted of creating a new
+ per-interface record), or if no state exists after the change
+ (i.e., the change consisted of deleting a per-interface record),
+ then the "non-existent" state is considered to have a filter mode
+ of INCLUDE and an empty source list.
+ */
+
+ group = XMALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
+ if (!group) {
+ zlog_warn("%s %s: XMALLOC() failure",
+ __FILE__, __PRETTY_FUNCTION__);
+ return 0; /* error, not found, could not create */
+ }
+
+ group->group_source_list = list_new();
+ if (!group->group_source_list) {
+ zlog_warn("%s %s: list_new() failure",
+ __FILE__, __PRETTY_FUNCTION__);
+ XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */
+ return 0; /* error, not found, could not initialize */
+ }
+ group->group_source_list->del = (void (*)(void *)) igmp_source_free;
+
+ group->t_group_timer = 0;
+ group->t_group_query_retransmit_timer = 0;
+ group->group_specific_query_retransmit_count = 0;
+ group->group_addr = group_addr;
+ group->group_igmp_sock = igmp;
+ group->last_igmp_v1_report_dsec = -1;
+ group->last_igmp_v2_report_dsec = -1;
+ group->group_creation = pim_time_monotonic_sec();
+
+ /* initialize new group as INCLUDE {empty} */
+ group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
+
+ listnode_add(igmp->igmp_group_list, group);
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ zlog_debug("Creating new IGMP group %s on socket %d interface %s",
+ group_str, group->group_igmp_sock->fd, ifname);
+ }
+
+ /*
+ RFC 3376: 6.2.2. Definition of Group Timers
+
+ The group timer is only used when a group is in EXCLUDE mode and
+ it represents the time for the *filter-mode* of the group to
+ expire and switch to INCLUDE mode.
+ */
+ zassert(!group->group_filtermode_isexcl); /* INCLUDE mode */
+ zassert(!group->t_group_timer); /* group timer == 0 */
+
+ /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
+ igmp_anysource_forward_stop(group);
+
+ return group;
+}
diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h
new file mode 100644
index 00000000..95e60bf2
--- /dev/null
+++ b/pimd/pim_igmp.h
@@ -0,0 +1,175 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_IGMP_H
+#define PIM_IGMP_H
+
+#include <netinet/in.h>
+
+#include <zebra.h>
+#include "vty.h"
+#include "linklist.h"
+
+/*
+ The following sizes are likely to support
+ any message sent within local MTU.
+*/
+#define PIM_IGMP_BUFSIZE_READ (20000)
+#define PIM_IGMP_BUFSIZE_WRITE (20000)
+
+#define PIM_IGMP_MEMBERSHIP_QUERY (0x11)
+#define PIM_IGMP_V1_MEMBERSHIP_REPORT (0x12)
+#define PIM_IGMP_V2_MEMBERSHIP_REPORT (0x16)
+#define PIM_IGMP_V2_LEAVE_GROUP (0x17)
+#define PIM_IGMP_V3_MEMBERSHIP_REPORT (0x22)
+
+#define IGMP_V3_REPORT_HEADER_SIZE (8)
+#define IGMP_V3_GROUP_RECORD_MIN_SIZE (8)
+#define IGMP_V3_MSG_MIN_SIZE (IGMP_V3_REPORT_HEADER_SIZE + \
+ IGMP_V3_GROUP_RECORD_MIN_SIZE)
+#define IGMP_V12_MSG_SIZE (8)
+
+#define IGMP_V3_GROUP_RECORD_TYPE_OFFSET (0)
+#define IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET (1)
+#define IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET (2)
+#define IGMP_V3_GROUP_RECORD_GROUP_OFFSET (4)
+#define IGMP_V3_GROUP_RECORD_SOURCE_OFFSET (8)
+
+/* RFC 3376: 8.1. Robustness Variable - Default: 2 */
+#define IGMP_DEFAULT_ROBUSTNESS_VARIABLE (2)
+
+/* RFC 3376: 8.2. Query Interval - Default: 125 seconds */
+#define IGMP_GENERAL_QUERY_INTERVAL (125)
+
+/* RFC 3376: 8.3. Query Response Interval - Default: 100 deciseconds */
+#define IGMP_QUERY_MAX_RESPONSE_TIME_DSEC (100)
+
+/* RFC 3376: 8.8. Last Member Query Interval - Default: 10 deciseconds */
+#define IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC (10)
+
+struct igmp_join {
+ struct in_addr group_addr;
+ struct in_addr source_addr;
+ int sock_fd;
+};
+
+struct igmp_sock {
+ int fd;
+ struct interface *interface;
+ struct in_addr ifaddr;
+ time_t sock_creation;
+
+ struct thread *t_igmp_read; /* read: IGMP sockets */
+ struct thread *t_igmp_query_timer; /* timer: issue IGMP general queries */
+ struct thread *t_other_querier_timer; /* timer: other querier present */
+
+ int querier_query_interval; /* QQI */
+ int querier_robustness_variable; /* QRV */
+ int startup_query_count;
+
+ struct list *igmp_group_list; /* list of struct igmp_group */
+};
+
+struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
+ struct in_addr ifaddr);
+struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list,
+ int fd);
+struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
+ struct in_addr ifaddr,
+ struct interface *ifp);
+void igmp_sock_delete(struct igmp_sock *igmp);
+void igmp_sock_free(struct igmp_sock *igmp);
+
+int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len);
+
+void pim_igmp_general_query_on(struct igmp_sock *igmp);
+void pim_igmp_general_query_off(struct igmp_sock *igmp);
+void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp);
+void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp);
+
+#define IGMP_SOURCE_MASK_FORWARDING (1 << 0)
+#define IGMP_SOURCE_MASK_DELETE (1 << 1)
+#define IGMP_SOURCE_MASK_SEND (1 << 2)
+#define IGMP_SOURCE_TEST_FORWARDING(flags) ((flags) & IGMP_SOURCE_MASK_FORWARDING)
+#define IGMP_SOURCE_TEST_DELETE(flags) ((flags) & IGMP_SOURCE_MASK_DELETE)
+#define IGMP_SOURCE_TEST_SEND(flags) ((flags) & IGMP_SOURCE_MASK_SEND)
+#define IGMP_SOURCE_DO_FORWARDING(flags) ((flags) |= IGMP_SOURCE_MASK_FORWARDING)
+#define IGMP_SOURCE_DO_DELETE(flags) ((flags) |= IGMP_SOURCE_MASK_DELETE)
+#define IGMP_SOURCE_DO_SEND(flags) ((flags) |= IGMP_SOURCE_MASK_SEND)
+#define IGMP_SOURCE_DONT_FORWARDING(flags) ((flags) &= ~IGMP_SOURCE_MASK_FORWARDING)
+#define IGMP_SOURCE_DONT_DELETE(flags) ((flags) &= ~IGMP_SOURCE_MASK_DELETE)
+#define IGMP_SOURCE_DONT_SEND(flags) ((flags) &= ~IGMP_SOURCE_MASK_SEND)
+
+struct igmp_source {
+ struct in_addr source_addr;
+ struct thread *t_source_timer;
+ struct igmp_group *source_group; /* back pointer */
+ time_t source_creation;
+ uint32_t source_flags;
+ struct channel_oil *source_channel_oil;
+
+ /*
+ RFC 3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
+ */
+ int source_query_retransmit_count;
+};
+
+struct igmp_group {
+ /*
+ RFC 3376: 6.2.2. Definition of Group Timers
+
+ The group timer is only used when a group is in EXCLUDE mode and it
+ represents the time for the *filter-mode* of the group to expire and
+ switch to INCLUDE mode.
+ */
+ struct thread *t_group_timer;
+
+ /* Shared between group-specific and
+ group-and-source-specific retransmissions */
+ struct thread *t_group_query_retransmit_timer;
+
+ /* Counter exclusive for group-specific retransmissions
+ (not used by group-and-source-specific retransmissions,
+ since sources have their counters) */
+ int group_specific_query_retransmit_count;
+
+ struct in_addr group_addr;
+ int group_filtermode_isexcl; /* 0=INCLUDE, 1=EXCLUDE */
+ struct list *group_source_list; /* list of struct igmp_source */
+ time_t group_creation;
+ struct igmp_sock *group_igmp_sock; /* back pointer */
+ int64_t last_igmp_v1_report_dsec;
+ int64_t last_igmp_v2_report_dsec;
+};
+
+struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
+ struct in_addr group_addr,
+ const char *ifname);
+
+void igmp_group_delete_empty_include(struct igmp_group *group);
+
+void igmp_startup_mode_on(struct igmp_sock *igmp);
+
+void igmp_group_timer_on(struct igmp_group *group,
+ long interval_msec, const char *ifname);
+
+#endif /* PIM_IGMP_H */
diff --git a/pimd/pim_igmp_join.c b/pimd/pim_igmp_join.c
new file mode 100644
index 00000000..ead246c3
--- /dev/null
+++ b/pimd/pim_igmp_join.c
@@ -0,0 +1,65 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "pim_igmp_join.h"
+
+#ifndef MCAST_JOIN_SOURCE_GROUP
+#define MCAST_JOIN_SOURCE_GROUP 46
+struct group_source_req
+{
+ uint32_t gsr_interface;
+ struct sockaddr_storage gsr_group;
+ struct sockaddr_storage gsr_source;
+};
+#endif
+
+int pim_igmp_join_source(int fd, int ifindex,
+ struct in_addr group_addr,
+ struct in_addr source_addr)
+{
+ struct group_source_req req;
+ struct sockaddr_in *group_sa = (struct sockaddr_in *) &req.gsr_group;
+ struct sockaddr_in *source_sa = (struct sockaddr_in *) &req.gsr_source;
+
+ memset(group_sa, 0, sizeof(*group_sa));
+ group_sa->sin_family = AF_INET;
+ group_sa->sin_addr = group_addr;
+ group_sa->sin_port = htons(0);
+
+ memset(source_sa, 0, sizeof(*source_sa));
+ source_sa->sin_family = AF_INET;
+ source_sa->sin_addr = source_addr;
+ source_sa->sin_port = htons(0);
+
+ req.gsr_interface = ifindex;
+
+ return setsockopt(fd, IPPROTO_IP, MCAST_JOIN_SOURCE_GROUP,
+ &req, sizeof(req));
+
+ return 0;
+}
diff --git a/pimd/pim_igmp_join.h b/pimd/pim_igmp_join.h
new file mode 100644
index 00000000..1127af12
--- /dev/null
+++ b/pimd/pim_igmp_join.h
@@ -0,0 +1,32 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_IGMP_JOIN_H
+#define PIM_IGMP_JOIN_H
+
+#include <netinet/in.h>
+
+int pim_igmp_join_source(int fd, int ifindex,
+ struct in_addr group_addr,
+ struct in_addr source_addr);
+
+#endif /* PIM_IGMP_JOIN_H */
diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c
new file mode 100644
index 00000000..7e76f442
--- /dev/null
+++ b/pimd/pim_igmpv3.c
@@ -0,0 +1,1728 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+#include "log.h"
+#include "memory.h"
+
+#include "pimd.h"
+#include "pim_iface.h"
+#include "pim_igmp.h"
+#include "pim_igmpv3.h"
+#include "pim_str.h"
+#include "pim_util.h"
+#include "pim_time.h"
+#include "pim_zebra.h"
+#include "pim_oil.h"
+
+static void group_retransmit_timer_on(struct igmp_group *group);
+static long igmp_group_timer_remain_msec(struct igmp_group *group);
+static long igmp_source_timer_remain_msec(struct igmp_source *source);
+static void group_query_send(struct igmp_group *group);
+static void source_query_send_by_flag(struct igmp_group *group,
+ int num_sources_tosend);
+
+static void on_trace(const char *label,
+ struct interface *ifp, struct in_addr from,
+ struct in_addr group_addr,
+ int num_sources, struct in_addr *sources)
+{
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char from_str[100];
+ char group_str[100];
+
+ pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
+ pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+
+ zlog_debug("%s: from %s on %s: group=%s sources=%d",
+ label, from_str, ifp->name, group_str, num_sources);
+ }
+}
+
+int igmp_group_compat_mode(const struct igmp_sock *igmp,
+ const struct igmp_group *group)
+{
+ struct pim_interface *pim_ifp;
+ int64_t now_dsec;
+ long older_host_present_interval_dsec;
+
+ zassert(igmp);
+ zassert(igmp->interface);
+ zassert(igmp->interface->info);
+
+ pim_ifp = igmp->interface->info;
+
+ /*
+ RFC 3376: 8.13. Older Host Present Interval
+
+ This value MUST be ((the Robustness Variable) times (the Query
+ Interval)) plus (one Query Response Interval).
+
+ older_host_present_interval_dsec = \
+ igmp->querier_robustness_variable * \
+ 10 * igmp->querier_query_interval + \
+ pim_ifp->query_max_response_time_dsec;
+ */
+ older_host_present_interval_dsec =
+ PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
+ igmp->querier_query_interval,
+ pim_ifp->igmp_query_max_response_time_dsec);
+
+ now_dsec = pim_time_monotonic_dsec();
+ if (now_dsec < 1) {
+ /* broken timer logged by pim_time_monotonic_dsec() */
+ return 3;
+ }
+
+ if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec)
+ return 1; /* IGMPv1 */
+
+ if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec)
+ return 2; /* IGMPv2 */
+
+ return 3; /* IGMPv3 */
+}
+
+void igmp_group_reset_gmi(struct igmp_group *group)
+{
+ long group_membership_interval_msec;
+ struct pim_interface *pim_ifp;
+ struct igmp_sock *igmp;
+ struct interface *ifp;
+
+ igmp = group->group_igmp_sock;
+ ifp = igmp->interface;
+ pim_ifp = ifp->info;
+
+ /*
+ RFC 3376: 8.4. Group Membership Interval
+
+ The Group Membership Interval is the amount of time that must pass
+ before a multicast router decides there are no more members of a
+ group or a particular source on a network.
+
+ This value MUST be ((the Robustness Variable) times (the Query
+ Interval)) plus (one Query Response Interval).
+
+ group_membership_interval_msec = querier_robustness_variable *
+ (1000 * querier_query_interval) +
+ 100 * query_response_interval_dsec;
+ */
+ group_membership_interval_msec =
+ PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
+ igmp->querier_query_interval,
+ pim_ifp->igmp_query_max_response_time_dsec);
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
+ group_str,
+ group_membership_interval_msec / 1000,
+ group_membership_interval_msec % 1000,
+ ifp->name);
+ }
+
+ /*
+ RFC 3376: 6.2.2. Definition of Group Timers
+
+ The group timer is only used when a group is in EXCLUDE mode and
+ it represents the time for the *filter-mode* of the group to
+ expire and switch to INCLUDE mode.
+ */
+ zassert(group->group_filtermode_isexcl);
+
+ igmp_group_timer_on(group, group_membership_interval_msec, ifp->name);
+}
+
+static int igmp_source_timer(struct thread *t)
+{
+ struct igmp_source *source;
+ struct igmp_group *group;
+
+ zassert(t);
+ source = THREAD_ARG(t);
+ zassert(source);
+
+ group = source->source_group;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+ zlog_debug("%s: Source timer expired for group %s source %s on %s",
+ __PRETTY_FUNCTION__,
+ group_str, source_str,
+ group->group_igmp_sock->interface->name);
+ }
+
+ zassert(source->t_source_timer);
+ source->t_source_timer = 0;
+
+ /*
+ RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
+
+ Group
+ Filter-Mode Source Timer Value Action
+ ----------- ------------------ ------
+ INCLUDE TIMER == 0 Suggest to stop forwarding
+ traffic from source and
+ remove source record. If
+ there are no more source
+ records for the group, delete
+ group record.
+
+ EXCLUDE TIMER == 0 Suggest to not forward
+ traffic from source
+ (DO NOT remove record)
+
+ Source timer switched from (T > 0) to (T == 0): disable forwarding.
+ */
+
+ zassert(!source->t_source_timer);
+
+ if (group->group_filtermode_isexcl) {
+ /* EXCLUDE mode */
+
+ igmp_source_forward_stop(source);
+ }
+ else {
+ /* INCLUDE mode */
+
+ /* igmp_source_delete() will stop forwarding source */
+ igmp_source_delete(source);
+
+ /*
+ If there are no more source records for the group, delete group
+ record.
+ */
+ if (!listcount(group->group_source_list)) {
+ igmp_group_delete_empty_include(group);
+ }
+ }
+
+ return 0;
+}
+
+static void source_timer_off(struct igmp_group *group,
+ struct igmp_source *source)
+{
+ if (!source->t_source_timer)
+ return;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+ zlog_debug("Cancelling TIMER event for group %s source %s on %s",
+ group_str, source_str,
+ group->group_igmp_sock->interface->name);
+ }
+
+ THREAD_OFF(source->t_source_timer);
+ zassert(!source->t_source_timer);
+}
+
+static void igmp_source_timer_on(struct igmp_group *group,
+ struct igmp_source *source,
+ long interval_msec)
+{
+ source_timer_off(group, source);
+
+ if (PIM_DEBUG_IGMP_EVENTS) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+ zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
+ interval_msec / 1000,
+ interval_msec % 1000,
+ group_str, source_str,
+ group->group_igmp_sock->interface->name);
+ }
+
+ THREAD_TIMER_MSEC_ON(master, source->t_source_timer,
+ igmp_source_timer,
+ source, interval_msec);
+ zassert(source->t_source_timer);
+
+ /*
+ RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
+
+ Source timer switched from (T == 0) to (T > 0): enable forwarding.
+ */
+ igmp_source_forward_start(source);
+}
+
+void igmp_source_reset_gmi(struct igmp_sock *igmp,
+ struct igmp_group *group,
+ struct igmp_source *source)
+{
+ long group_membership_interval_msec;
+ struct pim_interface *pim_ifp;
+ struct interface *ifp;
+
+ ifp = igmp->interface;
+ pim_ifp = ifp->info;
+
+ group_membership_interval_msec =
+ PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
+ igmp->querier_query_interval,
+ pim_ifp->igmp_query_max_response_time_dsec);
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ char source_str[100];
+
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+
+ zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
+ source_str,
+ group_membership_interval_msec / 1000,
+ group_membership_interval_msec % 1000,
+ group_str,
+ ifp->name);
+ }
+
+ igmp_source_timer_on(group, source,
+ group_membership_interval_msec);
+}
+
+static void source_mark_delete_flag(struct list *source_list)
+{
+ struct listnode *src_node;
+ struct igmp_source *src;
+
+ for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
+ IGMP_SOURCE_DO_DELETE(src->source_flags);
+ }
+}
+
+static void source_mark_send_flag(struct list *source_list)
+{
+ struct listnode *src_node;
+ struct igmp_source *src;
+
+ for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
+ IGMP_SOURCE_DO_SEND(src->source_flags);
+ }
+}
+
+static int source_mark_send_flag_by_timer(struct list *source_list)
+{
+ struct listnode *src_node;
+ struct igmp_source *src;
+ int num_marked_sources = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
+ /* Is source timer running? */
+ if (src->t_source_timer) {
+ IGMP_SOURCE_DO_SEND(src->source_flags);
+ ++num_marked_sources;
+ }
+ else {
+ IGMP_SOURCE_DONT_SEND(src->source_flags);
+ }
+ }
+
+ return num_marked_sources;
+}
+
+static void source_clear_send_flag(struct list *source_list)
+{
+ struct listnode *src_node;
+ struct igmp_source *src;
+
+ for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
+ IGMP_SOURCE_DONT_SEND(src->source_flags);
+ }
+}
+
+/*
+ Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
+*/
+static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group)
+{
+ zassert(group->group_filtermode_isexcl);
+
+ if (listcount(group->group_source_list) < 1) {
+ igmp_anysource_forward_start(group);
+ }
+}
+
+void igmp_source_free(struct igmp_source *source)
+{
+ /* make sure there is no source timer running */
+ zassert(!source->t_source_timer);
+
+ XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);
+}
+
+static void source_channel_oil_detach(struct igmp_source *source)
+{
+ if (source->source_channel_oil) {
+ pim_channel_oil_del(source->source_channel_oil);
+ source->source_channel_oil = 0;
+ }
+}
+
+void igmp_source_delete(struct igmp_source *source)
+{
+ struct igmp_group *group;
+
+ group = source->source_group;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+ zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
+ source_str, group_str,
+ group->group_igmp_sock->fd,
+ group->group_igmp_sock->interface->name);
+ }
+
+ source_timer_off(group, source);
+ igmp_source_forward_stop(source);
+
+ /* make sure forwarding is disabled */
+ zassert(!IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
+
+ source_channel_oil_detach(source);
+
+ /*
+ notice that listnode_delete() can't be moved
+ into igmp_source_free() because the later is
+ called by list_delete_all_node()
+ */
+ listnode_delete(group->group_source_list, source);
+
+ igmp_source_free(source);
+
+ if (group->group_filtermode_isexcl) {
+ group_exclude_fwd_anysrc_ifempty(group);
+ }
+}
+
+static void source_delete_by_flag(struct list *source_list)
+{
+ struct listnode *src_node;
+ struct listnode *src_nextnode;
+ struct igmp_source *src;
+
+ for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
+ if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
+ igmp_source_delete(src);
+}
+
+void igmp_source_delete_expired(struct list *source_list)
+{
+ struct listnode *src_node;
+ struct listnode *src_nextnode;
+ struct igmp_source *src;
+
+ for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
+ if (!src->t_source_timer)
+ igmp_source_delete(src);
+}
+
+struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
+ struct in_addr src_addr)
+{
+ struct listnode *src_node;
+ struct igmp_source *src;
+
+ for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
+ if (src_addr.s_addr == src->source_addr.s_addr)
+ return src;
+
+ return 0;
+}
+
+static struct igmp_source *source_new(struct igmp_group *group,
+ struct in_addr src_addr,
+ const char *ifname)
+{
+ struct igmp_source *src;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str));
+ zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
+ source_str, group_str,
+ group->group_igmp_sock->fd,
+ ifname);
+ }
+
+ src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
+ if (!src) {
+ zlog_warn("%s %s: XMALLOC() failure",
+ __FILE__, __PRETTY_FUNCTION__);
+ return 0; /* error, not found, could not create */
+ }
+
+ src->t_source_timer = 0;
+ src->source_group = group; /* back pointer */
+ src->source_addr = src_addr;
+ src->source_creation = pim_time_monotonic_sec();
+ src->source_flags = 0;
+ src->source_query_retransmit_count = 0;
+ src->source_channel_oil = 0;
+
+ listnode_add(group->group_source_list, src);
+
+ zassert(!src->t_source_timer); /* source timer == 0 */
+
+ /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
+ igmp_anysource_forward_stop(group);
+
+ return src;
+}
+
+static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,
+ struct igmp_group *group,
+ struct in_addr src_addr,
+ const char *ifname)
+{
+ struct igmp_source *src;
+
+ src = igmp_find_source_by_addr(group, src_addr);
+ if (src) {
+ return src;
+ }
+
+ src = source_new(group, src_addr, ifname);
+ if (!src) {
+ return 0;
+ }
+
+ return src;
+}
+
+static void allow(struct igmp_sock *igmp, struct in_addr from,
+ struct in_addr group_addr,
+ int num_sources, struct in_addr *sources)
+{
+ struct interface *ifp = igmp->interface;
+ struct pim_interface *pim_ifp;
+ struct igmp_group *group;
+ int i;
+
+ pim_ifp = ifp->info;
+
+ /* non-existant group is created as INCLUDE {empty} */
+ group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
+ if (!group) {
+ return;
+ }
+
+ /* scan received sources */
+ for (i = 0; i < num_sources; ++i) {
+ struct igmp_source *source;
+ struct in_addr *src_addr;
+
+ src_addr = sources + i;
+
+ source = add_source_by_addr(igmp, group, *src_addr, ifp->name);
+ if (!source) {
+ continue;
+ }
+
+ /*
+ RFC 3376: 6.4.1. Reception of Current-State Records
+
+ When receiving IS_IN reports for groups in EXCLUDE mode is
+ sources should be moved from set with (timers = 0) to set with
+ (timers > 0).
+
+ igmp_source_reset_gmi() below, resetting the source timers to
+ GMI, accomplishes this.
+ */
+ igmp_source_reset_gmi(igmp, group, source);
+
+ } /* scan received sources */
+}
+
+void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
+ struct in_addr group_addr,
+ int num_sources, struct in_addr *sources)
+{
+ on_trace(__PRETTY_FUNCTION__,
+ igmp->interface, from, group_addr, num_sources, sources);
+
+ allow(igmp, from, group_addr, num_sources, sources);
+}
+
+static void isex_excl(struct igmp_group *group,
+ int num_sources, struct in_addr *sources)
+{
+ int i;
+
+ /* EXCLUDE mode */
+ zassert(group->group_filtermode_isexcl);
+
+ /* E.1: set deletion flag for known sources (X,Y) */
+ source_mark_delete_flag(group->group_source_list);
+
+ /* scan received sources (A) */
+ for (i = 0; i < num_sources; ++i) {
+ struct igmp_source *source;
+ struct in_addr *src_addr;
+
+ src_addr = sources + i;
+
+ /* E.2: lookup reported source from (A) in (X,Y) */
+ source = igmp_find_source_by_addr(group, *src_addr);
+ if (source) {
+ /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
+ IGMP_SOURCE_DONT_DELETE(source->source_flags);
+ }
+ else {
+ /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
+ source = source_new(group, *src_addr,
+ group->group_igmp_sock->interface->name);
+ if (!source) {
+ /* ugh, internal malloc failure, skip source */
+ continue;
+ }
+ zassert(!source->t_source_timer); /* timer == 0 */
+ igmp_source_reset_gmi(group->group_igmp_sock, group, source);
+ zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
+ }
+
+ } /* scan received sources */
+
+ /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
+ source_delete_by_flag(group->group_source_list);
+}
+
+static void isex_incl(struct igmp_group *group,
+ int num_sources, struct in_addr *sources)
+{
+ int i;
+
+ /* INCLUDE mode */
+ zassert(!group->group_filtermode_isexcl);
+
+ /* I.1: set deletion flag for known sources (A) */
+ source_mark_delete_flag(group->group_source_list);
+
+ /* scan received sources (B) */
+ for (i = 0; i < num_sources; ++i) {
+ struct igmp_source *source;
+ struct in_addr *src_addr;
+
+ src_addr = sources + i;
+
+ /* I.2: lookup reported source (B) */
+ source = igmp_find_source_by_addr(group, *src_addr);
+ if (source) {
+ /* I.3: if found, clear deletion flag (A*B) */
+ IGMP_SOURCE_DONT_DELETE(source->source_flags);
+ }
+ else {
+ /* I.4: if not found, create source with timer=0 (B-A) */
+ source = source_new(group, *src_addr,
+ group->group_igmp_sock->interface->name);
+ if (!source) {
+ /* ugh, internal malloc failure, skip source */
+ continue;
+ }
+ zassert(!source->t_source_timer); /* (B-A) timer=0 */
+ }
+
+ } /* scan received sources */
+
+ /* I.5: delete all sources marked with deletion flag (A-B) */
+ source_delete_by_flag(group->group_source_list);
+
+ group->group_filtermode_isexcl = 1; /* boolean=true */
+
+ zassert(group->group_filtermode_isexcl);
+
+ group_exclude_fwd_anysrc_ifempty(group);
+}
+
+void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
+ struct in_addr group_addr,
+ int num_sources, struct in_addr *sources)
+{
+ struct interface *ifp = igmp->interface;
+ struct pim_interface *pim_ifp;
+ struct igmp_group *group;
+
+ on_trace(__PRETTY_FUNCTION__,
+ ifp, from, group_addr, num_sources, sources);
+
+ pim_ifp = ifp->info;
+
+ /* non-existant group is created as INCLUDE {empty} */
+ group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
+ if (!group) {
+ return;
+ }
+
+ if (group->group_filtermode_isexcl) {
+ /* EXCLUDE mode */
+ isex_excl(group, num_sources, sources);
+ }
+ else {
+ /* INCLUDE mode */
+ isex_incl(group, num_sources, sources);
+ zassert(group->group_filtermode_isexcl);
+ }
+
+ zassert(group->group_filtermode_isexcl);
+
+ igmp_group_reset_gmi(group);
+}
+
+static void toin_incl(struct igmp_group *group,
+ int num_sources, struct in_addr *sources)
+{
+ struct igmp_sock *igmp = group->group_igmp_sock;
+ int num_sources_tosend = listcount(group->group_source_list);
+ int i;
+
+ /* Set SEND flag for all known sources (A) */
+ source_mark_send_flag(group->group_source_list);
+
+ /* Scan received sources (B) */
+ for (i = 0; i < num_sources; ++i) {
+ struct igmp_source *source;
+ struct in_addr *src_addr;
+
+ src_addr = sources + i;
+
+ /* Lookup reported source (B) */
+ source = igmp_find_source_by_addr(group, *src_addr);
+ if (source) {
+ /* If found, clear SEND flag (A*B) */
+ IGMP_SOURCE_DONT_SEND(source->source_flags);
+ --num_sources_tosend;
+ }
+ else {
+ /* If not found, create new source */
+ source = source_new(group, *src_addr,
+ group->group_igmp_sock->interface->name);
+ if (!source) {
+ /* ugh, internal malloc failure, skip source */
+ continue;
+ }
+ }
+
+ /* (B)=GMI */
+ igmp_source_reset_gmi(igmp, group, source);
+ }
+
+ /* Send sources marked with SEND flag: Q(G,A-B) */
+ if (num_sources_tosend > 0) {
+ source_query_send_by_flag(group, num_sources_tosend);
+ }
+}
+
+static void toin_excl(struct igmp_group *group,
+ int num_sources, struct in_addr *sources)
+{
+ struct igmp_sock *igmp = group->group_igmp_sock;
+ int num_sources_tosend;
+ int i;
+
+ /* Set SEND flag for X (sources with timer > 0) */
+ num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list);
+
+ /* Scan received sources (A) */
+ for (i = 0; i < num_sources; ++i) {
+ struct igmp_source *source;
+ struct in_addr *src_addr;
+
+ src_addr = sources + i;
+
+ /* Lookup reported source (A) */
+ source = igmp_find_source_by_addr(group, *src_addr);
+ if (source) {
+ if (source->t_source_timer) {
+ /* If found and timer running, clear SEND flag (X*A) */
+ IGMP_SOURCE_DONT_SEND(source->source_flags);
+ --num_sources_tosend;
+ }
+ }
+ else {
+ /* If not found, create new source */
+ source = source_new(group, *src_addr,
+ group->group_igmp_sock->interface->name);
+ if (!source) {
+ /* ugh, internal malloc failure, skip source */
+ continue;
+ }
+ }
+
+ /* (A)=GMI */
+ igmp_source_reset_gmi(igmp, group, source);
+ }
+
+ /* Send sources marked with SEND flag: Q(G,X-A) */
+ if (num_sources_tosend > 0) {
+ source_query_send_by_flag(group, num_sources_tosend);
+ }
+
+ /* Send Q(G) */
+ group_query_send(group);
+}
+
+void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
+ struct in_addr group_addr,
+ int num_sources, struct in_addr *sources)
+{
+ struct interface *ifp = igmp->interface;
+ struct pim_interface *pim_ifp;
+ struct igmp_group *group;
+
+ on_trace(__PRETTY_FUNCTION__,
+ ifp, from, group_addr, num_sources, sources);
+
+ pim_ifp = ifp->info;
+
+ /* non-existant group is created as INCLUDE {empty} */
+ group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
+ if (!group) {
+ return;
+ }
+
+ if (group->group_filtermode_isexcl) {
+ /* EXCLUDE mode */
+ toin_excl(group, num_sources, sources);
+ }
+ else {
+ /* INCLUDE mode */
+ toin_incl(group, num_sources, sources);
+ }
+}
+
+static void toex_incl(struct igmp_group *group,
+ int num_sources, struct in_addr *sources)
+{
+ int num_sources_tosend = 0;
+ int i;
+
+ zassert(!group->group_filtermode_isexcl);
+
+ /* Set DELETE flag for all known sources (A) */
+ source_mark_delete_flag(group->group_source_list);
+
+ /* Clear off SEND flag from all known sources (A) */
+ source_clear_send_flag(group->group_source_list);
+
+ /* Scan received sources (B) */
+ for (i = 0; i < num_sources; ++i) {
+ struct igmp_source *source;
+ struct in_addr *src_addr;
+
+ src_addr = sources + i;
+
+ /* Lookup reported source (B) */
+ source = igmp_find_source_by_addr(group, *src_addr);
+ if (source) {
+ /* If found, clear deletion flag: (A*B) */
+ IGMP_SOURCE_DONT_DELETE(source->source_flags);
+ /* and set SEND flag (A*B) */
+ IGMP_SOURCE_DO_SEND(source->source_flags);
+ ++num_sources_tosend;
+ }
+ else {
+ /* If source not found, create source with timer=0: (B-A)=0 */
+ source = source_new(group, *src_addr,
+ group->group_igmp_sock->interface->name);
+ if (!source) {
+ /* ugh, internal malloc failure, skip source */
+ continue;
+ }
+ zassert(!source->t_source_timer); /* (B-A) timer=0 */
+ }
+
+ } /* Scan received sources (B) */
+
+ group->group_filtermode_isexcl = 1; /* boolean=true */
+
+ /* Delete all sources marked with DELETE flag (A-B) */
+ source_delete_by_flag(group->group_source_list);
+
+ /* Send sources marked with SEND flag: Q(G,A*B) */
+ if (num_sources_tosend > 0) {
+ source_query_send_by_flag(group, num_sources_tosend);
+ }
+
+ zassert(group->group_filtermode_isexcl);
+
+ group_exclude_fwd_anysrc_ifempty(group);
+}
+
+static void toex_excl(struct igmp_group *group,
+ int num_sources, struct in_addr *sources)
+{
+ int num_sources_tosend = 0;
+ int i;
+
+ /* set DELETE flag for all known sources (X,Y) */
+ source_mark_delete_flag(group->group_source_list);
+
+ /* clear off SEND flag from all known sources (X,Y) */
+ source_clear_send_flag(group->group_source_list);
+
+ /* scan received sources (A) */
+ for (i = 0; i < num_sources; ++i) {
+ struct igmp_source *source;
+ struct in_addr *src_addr;
+
+ src_addr = sources + i;
+
+ /* lookup reported source (A) in known sources (X,Y) */
+ source = igmp_find_source_by_addr(group, *src_addr);
+ if (source) {
+ /* if found, clear off DELETE flag from reported source (A) */
+ IGMP_SOURCE_DONT_DELETE(source->source_flags);
+ }
+ else {
+ /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
+ long group_timer_msec;
+ source = source_new(group, *src_addr,
+ group->group_igmp_sock->interface->name);
+ if (!source) {
+ /* ugh, internal malloc failure, skip source */
+ continue;
+ }
+
+ zassert(!source->t_source_timer); /* timer == 0 */
+ group_timer_msec = igmp_group_timer_remain_msec(group);
+ igmp_source_timer_on(group, source, group_timer_msec);
+ zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
+
+ /* make sure source is created with DELETE flag unset */
+ zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
+ }
+
+ /* make sure reported source has DELETE flag unset */
+ zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
+
+ if (source->t_source_timer) {
+ /* if source timer>0 mark SEND flag: Q(G,A-Y) */
+ IGMP_SOURCE_DO_SEND(source->source_flags);
+ ++num_sources_tosend;
+ }
+
+ } /* scan received sources (A) */
+
+ /*
+ delete all sources marked with DELETE flag:
+ Delete (X-A)
+ Delete (Y-A)
+ */
+ source_delete_by_flag(group->group_source_list);
+
+ /* send sources marked with SEND flag: Q(G,A-Y) */
+ if (num_sources_tosend > 0) {
+ source_query_send_by_flag(group, num_sources_tosend);
+ }
+}
+
+void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
+ struct in_addr group_addr,
+ int num_sources, struct in_addr *sources)
+{
+ struct interface *ifp = igmp->interface;
+ struct pim_interface *pim_ifp;
+ struct igmp_group *group;
+
+ on_trace(__PRETTY_FUNCTION__,
+ ifp, from, group_addr, num_sources, sources);
+
+ pim_ifp = ifp->info;
+
+ /* non-existant group is created as INCLUDE {empty} */
+ group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
+ if (!group) {
+ return;
+ }
+
+ if (group->group_filtermode_isexcl) {
+ /* EXCLUDE mode */
+ toex_excl(group, num_sources, sources);
+ }
+ else {
+ /* INCLUDE mode */
+ toex_incl(group, num_sources, sources);
+ zassert(group->group_filtermode_isexcl);
+ }
+ zassert(group->group_filtermode_isexcl);
+
+ /* Group Timer=GMI */
+ igmp_group_reset_gmi(group);
+}
+
+void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
+ struct in_addr group_addr,
+ int num_sources, struct in_addr *sources)
+{
+ on_trace(__PRETTY_FUNCTION__,
+ igmp->interface, from, group_addr, num_sources, sources);
+
+ allow(igmp, from, group_addr, num_sources, sources);
+}
+
+/*
+ RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
+
+ When transmitting a group specific query, if the group timer is
+ larger than LMQT, the "Suppress Router-Side Processing" bit is set
+ in the query message.
+*/
+static void group_retransmit_group(struct igmp_group *group)
+{
+ char query_buf[PIM_IGMP_BUFSIZE_WRITE];
+ struct igmp_sock *igmp;
+ struct pim_interface *pim_ifp;
+ long lmqc; /* Last Member Query Count */
+ long lmqi_msec; /* Last Member Query Interval */
+ long lmqt_msec; /* Last Member Query Time */
+ int s_flag;
+
+ igmp = group->group_igmp_sock;
+ pim_ifp = igmp->interface->info;
+
+ lmqc = igmp->querier_robustness_variable;
+ lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
+ lmqt_msec = lmqc * lmqi_msec;
+
+ /*
+ RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
+
+ When transmitting a group specific query, if the group timer is
+ larger than LMQT, the "Suppress Router-Side Processing" bit is set
+ in the query message.
+ */
+ s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
+ group_str, igmp->interface->name, s_flag,
+ group->group_specific_query_retransmit_count);
+ }
+
+ /*
+ RFC3376: 4.1.12. IP Destination Addresses for Queries
+
+ Group-Specific and Group-and-Source-Specific Queries are sent with
+ an IP destination address equal to the multicast address of
+ interest.
+ */
+
+ pim_igmp_send_membership_query(group,
+ igmp->fd,
+ igmp->interface->name,
+ query_buf,
+ sizeof(query_buf),
+ 0 /* num_sources_tosend */,
+ group->group_addr /* dst_addr */,
+ group->group_addr /* group_addr */,
+ pim_ifp->igmp_specific_query_max_response_time_dsec,
+ s_flag,
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
+}
+
+/*
+ RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
+
+ When building a group and source specific query for a group G, two
+ separate query messages are sent for the group. The first one has
+ the "Suppress Router-Side Processing" bit set and contains all the
+ sources with retransmission state and timers greater than LMQT. The
+ second has the "Suppress Router-Side Processing" bit clear and
+ contains all the sources with retransmission state and timers lower
+ or equal to LMQT. If either of the two calculated messages does not
+ contain any sources, then its transmission is suppressed.
+ */
+static int group_retransmit_sources(struct igmp_group *group,
+ int send_with_sflag_set)
+{
+ struct igmp_sock *igmp;
+ struct pim_interface *pim_ifp;
+ long lmqc; /* Last Member Query Count */
+ long lmqi_msec; /* Last Member Query Interval */
+ long lmqt_msec; /* Last Member Query Time */
+ char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
+ char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
+ int query_buf1_max_sources;
+ int query_buf2_max_sources;
+ struct in_addr *source_addr1;
+ struct in_addr *source_addr2;
+ int num_sources_tosend1;
+ int num_sources_tosend2;
+ struct listnode *src_node;
+ struct igmp_source *src;
+ int num_retransmit_sources_left = 0;
+
+ query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
+ query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
+
+ source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
+ source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
+
+ igmp = group->group_igmp_sock;
+ pim_ifp = igmp->interface->info;
+
+ lmqc = igmp->querier_robustness_variable;
+ lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
+ lmqt_msec = lmqc * lmqi_msec;
+
+ /* Scan all group sources */
+ for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
+
+ /* Source has retransmission state? */
+ if (src->source_query_retransmit_count < 1)
+ continue;
+
+ if (--src->source_query_retransmit_count > 0) {
+ ++num_retransmit_sources_left;
+ }
+
+ /* Copy source address into appropriate query buffer */
+ if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
+ *source_addr1 = src->source_addr;
+ ++source_addr1;
+ }
+ else {
+ *source_addr2 = src->source_addr;
+ ++source_addr2;
+ }
+
+ }
+
+ num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
+ num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ zlog_debug("retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
+ group_str, igmp->interface->name,
+ num_sources_tosend1,
+ num_sources_tosend2,
+ send_with_sflag_set,
+ num_retransmit_sources_left);
+ }
+
+ if (num_sources_tosend1 > 0) {
+ /*
+ Send group-and-source-specific query with s_flag set and all
+ sources with timers greater than LMQT.
+ */
+
+ if (send_with_sflag_set) {
+
+ query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
+ if (num_sources_tosend1 > query_buf1_max_sources) {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%d (max_sources=%d)",
+ __PRETTY_FUNCTION__, group_str, igmp->interface->name,
+ num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources);
+ }
+ else {
+ /*
+ RFC3376: 4.1.12. IP Destination Addresses for Queries
+
+ Group-Specific and Group-and-Source-Specific Queries are sent with
+ an IP destination address equal to the multicast address of
+ interest.
+ */
+
+ pim_igmp_send_membership_query(group,
+ igmp->fd,
+ igmp->interface->name,
+ query_buf1,
+ sizeof(query_buf1),
+ num_sources_tosend1,
+ group->group_addr,
+ group->group_addr,
+ pim_ifp->igmp_specific_query_max_response_time_dsec,
+ 1 /* s_flag */,
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
+
+ }
+
+ } /* send_with_sflag_set */
+
+ }
+
+ if (num_sources_tosend2 > 0) {
+ /*
+ Send group-and-source-specific query with s_flag clear and all
+ sources with timers lower or equal to LMQT.
+ */
+
+ query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
+ if (num_sources_tosend2 > query_buf2_max_sources) {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%d (max_sources=%d)",
+ __PRETTY_FUNCTION__, group_str, igmp->interface->name,
+ num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources);
+ }
+ else {
+ /*
+ RFC3376: 4.1.12. IP Destination Addresses for Queries
+
+ Group-Specific and Group-and-Source-Specific Queries are sent with
+ an IP destination address equal to the multicast address of
+ interest.
+ */
+
+ pim_igmp_send_membership_query(group,
+ igmp->fd,
+ igmp->interface->name,
+ query_buf2,
+ sizeof(query_buf2),
+ num_sources_tosend2,
+ group->group_addr,
+ group->group_addr,
+ pim_ifp->igmp_specific_query_max_response_time_dsec,
+ 0 /* s_flag */,
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
+
+ }
+ }
+
+ return num_retransmit_sources_left;
+}
+
+static int igmp_group_retransmit(struct thread *t)
+{
+ struct igmp_group *group;
+ int num_retransmit_sources_left;
+ int send_with_sflag_set; /* boolean */
+
+ zassert(t);
+ group = THREAD_ARG(t);
+ zassert(group);
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ zlog_debug("group_retransmit_timer: group %s on %s",
+ group_str, group->group_igmp_sock->interface->name);
+ }
+
+ /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
+ if (group->group_specific_query_retransmit_count > 0) {
+
+ /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
+ group_retransmit_group(group);
+ --group->group_specific_query_retransmit_count;
+
+ /*
+ RFC3376: 6.6.3.2
+ If a group specific query is scheduled to be transmitted at the
+ same time as a group and source specific query for the same group,
+ then transmission of the group and source specific message with the
+ "Suppress Router-Side Processing" bit set may be suppressed.
+ */
+ send_with_sflag_set = 0; /* boolean=false */
+ }
+ else {
+ send_with_sflag_set = 1; /* boolean=true */
+ }
+
+ /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
+ num_retransmit_sources_left = group_retransmit_sources(group,
+ send_with_sflag_set);
+
+ group->t_group_query_retransmit_timer = 0;
+
+ /*
+ Keep group retransmit timer running if there is any retransmit
+ counter pending
+ */
+ if ((num_retransmit_sources_left > 0) ||
+ (group->group_specific_query_retransmit_count > 0)) {
+ group_retransmit_timer_on(group);
+ }
+
+ return 0;
+}
+
+/*
+ group_retransmit_timer_on:
+ if group retransmit timer isn't running, starts it;
+ otherwise, do nothing
+*/
+static void group_retransmit_timer_on(struct igmp_group *group)
+{
+ struct igmp_sock *igmp;
+ struct pim_interface *pim_ifp;
+ long lmqi_msec; /* Last Member Query Interval */
+
+ /* if group retransmit timer is running, do nothing */
+ if (group->t_group_query_retransmit_timer) {
+ return;
+ }
+
+ igmp = group->group_igmp_sock;
+ pim_ifp = igmp->interface->info;
+
+ lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
+ lmqi_msec / 1000,
+ lmqi_msec % 1000,
+ group_str,
+ igmp->interface->name);
+ }
+
+ THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
+ igmp_group_retransmit,
+ group, lmqi_msec);
+}
+
+static long igmp_group_timer_remain_msec(struct igmp_group *group)
+{
+ return pim_time_timer_remain_msec(group->t_group_timer);
+}
+
+static long igmp_source_timer_remain_msec(struct igmp_source *source)
+{
+ return pim_time_timer_remain_msec(source->t_source_timer);
+}
+
+/*
+ RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
+*/
+static void group_query_send(struct igmp_group *group)
+{
+ long lmqc; /* Last Member Query Count */
+
+ lmqc = group->group_igmp_sock->querier_robustness_variable;
+
+ /* lower group timer to lmqt */
+ igmp_group_timer_lower_to_lmqt(group);
+
+ /* reset retransmission counter */
+ group->group_specific_query_retransmit_count = lmqc;
+
+ /* immediately send group specific query (decrease retransmit counter by 1)*/
+ group_retransmit_group(group);
+
+ /* make sure group retransmit timer is running */
+ group_retransmit_timer_on(group);
+}
+
+/*
+ RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
+*/
+static void source_query_send_by_flag(struct igmp_group *group,
+ int num_sources_tosend)
+{
+ struct igmp_sock *igmp;
+ struct pim_interface *pim_ifp;
+ struct listnode *src_node;
+ struct igmp_source *src;
+ long lmqc; /* Last Member Query Count */
+ long lmqi_msec; /* Last Member Query Interval */
+ long lmqt_msec; /* Last Member Query Time */
+
+ zassert(num_sources_tosend > 0);
+
+ igmp = group->group_igmp_sock;
+ pim_ifp = igmp->interface->info;
+
+ lmqc = igmp->querier_robustness_variable;
+ lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
+ lmqt_msec = lmqc * lmqi_msec;
+
+ /*
+ RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
+
+ (...) for each of the sources in X of group G, with source timer larger
+ than LMQT:
+ o Set number of retransmissions for each source to [Last Member
+ Query Count].
+ o Lower source timer to LMQT.
+ */
+ for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
+ if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
+ /* source "src" in X of group G */
+ if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
+ src->source_query_retransmit_count = lmqc;
+ igmp_source_timer_lower_to_lmqt(src);
+ }
+ }
+ }
+
+ /* send group-and-source specific queries */
+ group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
+
+ /* make sure group retransmit timer is running */
+ group_retransmit_timer_on(group);
+}
+
+static void block_excl(struct igmp_group *group,
+ int num_sources, struct in_addr *sources)
+{
+ int num_sources_tosend = 0;
+ int i;
+
+ /* 1. clear off SEND flag from all known sources (X,Y) */
+ source_clear_send_flag(group->group_source_list);
+
+ /* 2. scan received sources (A) */
+ for (i = 0; i < num_sources; ++i) {
+ struct igmp_source *source;
+ struct in_addr *src_addr;
+
+ src_addr = sources + i;
+
+ /* lookup reported source (A) in known sources (X,Y) */
+ source = igmp_find_source_by_addr(group, *src_addr);
+ if (!source) {
+ /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
+ long group_timer_msec;
+ source = source_new(group, *src_addr,
+ group->group_igmp_sock->interface->name);
+ if (!source) {
+ /* ugh, internal malloc failure, skip source */
+ continue;
+ }
+
+ zassert(!source->t_source_timer); /* timer == 0 */
+ group_timer_msec = igmp_group_timer_remain_msec(group);
+ igmp_source_timer_on(group, source, group_timer_msec);
+ zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
+ }
+
+ if (source->t_source_timer) {
+ /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
+ IGMP_SOURCE_DO_SEND(source->source_flags);
+ ++num_sources_tosend;
+ }
+ }
+
+ /* 5. send sources marked with SEND flag: Q(G,A-Y) */
+ if (num_sources_tosend > 0) {
+ source_query_send_by_flag(group, num_sources_tosend);
+ }
+}
+
+static void block_incl(struct igmp_group *group,
+ int num_sources, struct in_addr *sources)
+{
+ int num_sources_tosend = 0;
+ int i;
+
+ /* 1. clear off SEND flag from all known sources (B) */
+ source_clear_send_flag(group->group_source_list);
+
+ /* 2. scan received sources (A) */
+ for (i = 0; i < num_sources; ++i) {
+ struct igmp_source *source;
+ struct in_addr *src_addr;
+
+ src_addr = sources + i;
+
+ /* lookup reported source (A) in known sources (B) */
+ source = igmp_find_source_by_addr(group, *src_addr);
+ if (source) {
+ /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
+ IGMP_SOURCE_DO_SEND(source->source_flags);
+ ++num_sources_tosend;
+ }
+ }
+
+ /* 4. send sources marked with SEND flag: Q(G,A*B) */
+ if (num_sources_tosend > 0) {
+ source_query_send_by_flag(group, num_sources_tosend);
+ }
+}
+
+void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
+ struct in_addr group_addr,
+ int num_sources, struct in_addr *sources)
+{
+ struct interface *ifp = igmp->interface;
+ struct pim_interface *pim_ifp;
+ struct igmp_group *group;
+
+ on_trace(__PRETTY_FUNCTION__,
+ ifp, from, group_addr, num_sources, sources);
+
+ pim_ifp = ifp->info;
+
+ /* non-existant group is created as INCLUDE {empty} */
+ group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
+ if (!group) {
+ return;
+ }
+
+ if (group->group_filtermode_isexcl) {
+ /* EXCLUDE mode */
+ block_excl(group, num_sources, sources);
+ }
+ else {
+ /* INCLUDE mode */
+ block_incl(group, num_sources, sources);
+ }
+}
+
+void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
+{
+ struct igmp_sock *igmp;
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ char *ifname;
+ int lmqi_dsec; /* Last Member Query Interval */
+ int lmqc; /* Last Member Query Count */
+ int lmqt_msec; /* Last Member Query Time */
+
+ /*
+ RFC 3376: 6.2.2. Definition of Group Timers
+
+ The group timer is only used when a group is in EXCLUDE mode and
+ it represents the time for the *filter-mode* of the group to
+ expire and switch to INCLUDE mode.
+ */
+ if (!group->group_filtermode_isexcl) {
+ return;
+ }
+
+ igmp = group->group_igmp_sock;
+ ifp = igmp->interface;
+ pim_ifp = ifp->info;
+ ifname = ifp->name;
+
+ lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
+ lmqc = igmp->querier_robustness_variable;
+ lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
+ __PRETTY_FUNCTION__,
+ group_str, ifname,
+ lmqc, lmqi_dsec, lmqt_msec);
+ }
+
+ zassert(group->group_filtermode_isexcl);
+
+ igmp_group_timer_on(group, lmqt_msec, ifname);
+}
+
+void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
+{
+ struct igmp_group *group;
+ struct igmp_sock *igmp;
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ char *ifname;
+ int lmqi_dsec; /* Last Member Query Interval */
+ int lmqc; /* Last Member Query Count */
+ int lmqt_msec; /* Last Member Query Time */
+
+ group = source->source_group;
+ igmp = group->group_igmp_sock;
+ ifp = igmp->interface;
+ pim_ifp = ifp->info;
+ ifname = ifp->name;
+
+ lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
+ lmqc = igmp->querier_robustness_variable;
+ lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+ zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
+ __PRETTY_FUNCTION__,
+ group_str, source_str, ifname,
+ lmqc, lmqi_dsec, lmqt_msec);
+ }
+
+ igmp_source_timer_on(group, source, lmqt_msec);
+}
+
+/*
+ Copy sources to message:
+
+ struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
+ if (num_sources > 0) {
+ struct listnode *node;
+ struct igmp_source *src;
+ int i = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
+ sources[i++] = src->source_addr;
+ }
+ }
+*/
+void pim_igmp_send_membership_query(struct igmp_group *group,
+ int fd,
+ const char *ifname,
+ char *query_buf,
+ int query_buf_size,
+ int num_sources,
+ struct in_addr dst_addr,
+ struct in_addr group_addr,
+ int query_max_response_time_dsec,
+ uint8_t s_flag,
+ uint8_t querier_robustness_variable,
+ uint16_t querier_query_interval)
+{
+ ssize_t msg_size;
+ uint8_t max_resp_code;
+ uint8_t qqic;
+ ssize_t sent;
+ struct sockaddr_in to;
+ socklen_t tolen;
+ uint16_t checksum;
+
+ zassert(num_sources >= 0);
+
+ msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
+ if (msg_size > query_buf_size) {
+ zlog_err("%s %s: unable to send: msg_size=%d larger than query_buf_size=%d",
+ __FILE__, __PRETTY_FUNCTION__,
+ msg_size, query_buf_size);
+ return;
+ }
+
+ s_flag = PIM_FORCE_BOOLEAN(s_flag);
+ zassert((s_flag == 0) || (s_flag == 1));
+
+ max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
+ qqic = igmp_msg_encode16to8(querier_query_interval);
+
+ /*
+ RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
+
+ If non-zero, the QRV field contains the [Robustness Variable]
+ value used by the querier, i.e., the sender of the Query. If the
+ querier's [Robustness Variable] exceeds 7, the maximum value of
+ the QRV field, the QRV is set to zero.
+ */
+ if (querier_robustness_variable > 7) {
+ querier_robustness_variable = 0;
+ }
+
+ query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
+ query_buf[1] = max_resp_code;
+ *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
+ *(struct in_addr *)(query_buf + 4) = group_addr;
+ query_buf[8] = (s_flag << 3) | querier_robustness_variable;
+ query_buf[9] = qqic;
+ *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
+
+ checksum = pim_inet_checksum(query_buf, msg_size);
+ *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ char dst_str[100];
+ char group_str[100];
+ pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+ pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+ zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%d s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x",
+ __PRETTY_FUNCTION__,
+ dst_str, ifname, group_str, num_sources,
+ msg_size, s_flag, querier_robustness_variable,
+ querier_query_interval, qqic, checksum);
+ }
+
+#if 0
+ memset(&to, 0, sizeof(to));
+#endif
+ to.sin_family = AF_INET;
+ to.sin_addr = dst_addr;
+#if 0
+ to.sin_port = htons(0);
+#endif
+ tolen = sizeof(to);
+
+ sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT, &to, tolen);
+ if (sent != (ssize_t) msg_size) {
+ int e = errno;
+ char dst_str[100];
+ char group_str[100];
+ pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+ pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+ if (sent < 0) {
+ zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%d: errno=%d: %s",
+ __PRETTY_FUNCTION__,
+ dst_str, ifname, group_str, msg_size,
+ e, safe_strerror(e));
+ }
+ else {
+ zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%d: sent=%d",
+ __PRETTY_FUNCTION__,
+ dst_str, ifname, group_str,
+ msg_size, sent);
+ }
+ return;
+ }
+
+ /*
+ s_flag sanity test: s_flag must be set for general queries
+
+ RFC 3376: 6.6.1. Timer Updates
+
+ When a router sends or receives a query with a clear Suppress
+ Router-Side Processing flag, it must update its timers to reflect
+ the correct timeout values for the group or sources being queried.
+
+ General queries don't trigger timer update.
+ */
+ if (!s_flag) {
+ /* general query? */
+ if (PIM_INADDR_IS_ANY(group_addr)) {
+ char dst_str[100];
+ char group_str[100];
+ pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+ pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+ zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
+ __PRETTY_FUNCTION__,
+ dst_str, ifname, group_str, num_sources);
+ }
+ }
+
+}
diff --git a/pimd/pim_igmpv3.h b/pimd/pim_igmpv3.h
new file mode 100644
index 00000000..bb7e9267
--- /dev/null
+++ b/pimd/pim_igmpv3.h
@@ -0,0 +1,100 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_IGMPV3_H
+#define PIM_IGMPV3_H
+
+#include <zebra.h>
+#include "if.h"
+
+#define IGMP_V3_CHECKSUM_OFFSET (2)
+#define IGMP_V3_REPORT_NUMGROUPS_OFFSET (6)
+#define IGMP_V3_REPORT_GROUPPRECORD_OFFSET (8)
+#define IGMP_V3_NUMSOURCES_OFFSET (10)
+#define IGMP_V3_SOURCES_OFFSET (12)
+
+/* GMI: Group Membership Interval */
+#define PIM_IGMP_GMI_MSEC(qrv,qqi,qri_dsec) ((qrv) * (1000 * (qqi)) + 100 * (qri_dsec))
+
+/* OQPI: Other Querier Present Interval */
+#define PIM_IGMP_OQPI_MSEC(qrv,qqi,qri_dsec) ((qrv) * (1000 * (qqi)) + 100 * ((qri_dsec) >> 1))
+
+/* SQI: Startup Query Interval */
+#define PIM_IGMP_SQI(qi) (((qi) < 4) ? 1 : ((qi) >> 2))
+
+/* LMQT: Last Member Query Time */
+#define PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc) ((lmqc) * (100 * (lmqi_dsec)))
+
+/* OHPI: Older Host Present Interval */
+#define PIM_IGMP_OHPI_DSEC(qrv,qqi,qri_dsec) ((qrv) * (10 * (qqi)) + (qri_dsec))
+
+void igmp_group_reset_gmi(struct igmp_group *group);
+void igmp_source_reset_gmi(struct igmp_sock *igmp,
+ struct igmp_group *group,
+ struct igmp_source *source);
+
+void igmp_source_free(struct igmp_source *source);
+void igmp_source_delete(struct igmp_source *source);
+void igmp_source_delete_expired(struct list *source_list);
+
+int igmp_group_compat_mode(const struct igmp_sock *igmp,
+ const struct igmp_group *group);
+
+void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
+ struct in_addr group_addr,
+ int num_sources, struct in_addr *sources);
+void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
+ struct in_addr group_addr,
+ int num_sources, struct in_addr *sources);
+void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
+ struct in_addr group_addr,
+ int num_sources, struct in_addr *sources);
+void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
+ struct in_addr group_addr,
+ int num_sources, struct in_addr *sources);
+void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
+ struct in_addr group_addr,
+ int num_sources, struct in_addr *sources);
+void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
+ struct in_addr group_addr,
+ int num_sources, struct in_addr *sources);
+
+void igmp_group_timer_lower_to_lmqt(struct igmp_group *group);
+void igmp_source_timer_lower_to_lmqt(struct igmp_source *source);
+
+struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
+ struct in_addr src_addr);
+
+void pim_igmp_send_membership_query(struct igmp_group *group,
+ int fd,
+ const char *ifname,
+ char *query_buf,
+ int query_buf_size,
+ int num_sources,
+ struct in_addr dst_addr,
+ struct in_addr group_addr,
+ int query_max_response_time_dsec,
+ uint8_t s_flag,
+ uint8_t querier_robustness_variable,
+ uint16_t querier_query_interval);
+
+#endif /* PIM_IGMPV3_H */
diff --git a/pimd/pim_int.c b/pimd/pim_int.c
new file mode 100644
index 00000000..2ff1a116
--- /dev/null
+++ b/pimd/pim_int.c
@@ -0,0 +1,44 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <string.h>
+#include <netinet/in.h>
+
+#include "pim_int.h"
+
+uint32_t pim_read_uint32_host(const uint8_t *buf)
+{
+ uint32_t val;
+ memcpy(&val, buf, sizeof(val));
+ /* val is in netorder */
+ val = ntohl(val);
+ /* val is in hostorder */
+ return val;
+}
+
+void pim_write_uint32(uint8_t *buf, uint32_t val_host)
+{
+ /* val_host is in host order */
+ val_host = htonl(val_host);
+ /* val_host is in netorder */
+ memcpy(buf, &val_host, sizeof(val_host));
+}
diff --git a/pimd/pim_int.h b/pimd/pim_int.h
new file mode 100644
index 00000000..d64b1032
--- /dev/null
+++ b/pimd/pim_int.h
@@ -0,0 +1,31 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_INT_H
+#define PIM_INT_H
+
+#include <stdint.h>
+
+uint32_t pim_read_uint32_host(const uint8_t *buf);
+void pim_write_uint32(uint8_t *buf, uint32_t val_host);
+
+#endif /* PIM_INT_H */
diff --git a/pimd/pim_join.c b/pimd/pim_join.c
new file mode 100644
index 00000000..aa3aa789
--- /dev/null
+++ b/pimd/pim_join.c
@@ -0,0 +1,430 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+
+#include "pimd.h"
+#include "pim_str.h"
+#include "pim_tlv.h"
+#include "pim_msg.h"
+#include "pim_pim.h"
+#include "pim_join.h"
+#include "pim_iface.h"
+#include "pim_hello.h"
+#include "pim_ifchannel.h"
+
+static void on_trace(const char *label,
+ struct interface *ifp, struct in_addr src)
+{
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
+ zlog_debug("%s: from %s on %s",
+ label, src_str, ifp->name);
+ }
+}
+
+static void recv_join(struct interface *ifp,
+ struct pim_neighbor *neigh,
+ uint16_t holdtime,
+ struct in_addr upstream,
+ struct in_addr group,
+ struct in_addr source,
+ uint8_t source_flags)
+{
+ if (PIM_DEBUG_PIM_TRACE) {
+ char up_str[100];
+ char src_str[100];
+ char grp_str[100];
+ char neigh_str[100];
+ pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
+ pim_inet4_dump("<src?>", source, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", group, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
+ zlog_warn("%s: join (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str,
+ source_flags & PIM_RPT_BIT_MASK,
+ source_flags & PIM_WILDCARD_BIT_MASK,
+ up_str, holdtime, neigh_str, ifp->name);
+ }
+
+ /* Restart join expiry timer */
+ pim_ifchannel_join_add(ifp, neigh->source_addr, upstream,
+ source, group, source_flags, holdtime);
+}
+
+static void recv_prune(struct interface *ifp,
+ struct pim_neighbor *neigh,
+ uint16_t holdtime,
+ struct in_addr upstream,
+ struct in_addr group,
+ struct in_addr source,
+ uint8_t source_flags)
+{
+ if (PIM_DEBUG_PIM_TRACE) {
+ char up_str[100];
+ char src_str[100];
+ char grp_str[100];
+ char neigh_str[100];
+ pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
+ pim_inet4_dump("<src?>", source, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", group, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
+ zlog_warn("%s: prune (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str,
+ source_flags & PIM_RPT_BIT_MASK,
+ source_flags & PIM_WILDCARD_BIT_MASK,
+ up_str, holdtime, neigh_str, ifp->name);
+ }
+
+ pim_ifchannel_prune(ifp, upstream, source, group, source_flags, holdtime);
+}
+
+int pim_joinprune_recv(struct interface *ifp,
+ struct pim_neighbor *neigh,
+ struct in_addr src_addr,
+ char *tlv_buf, int tlv_buf_size)
+{
+ struct prefix msg_upstream_addr;
+ uint8_t msg_num_groups;
+ uint16_t msg_holdtime;
+ int addr_offset;
+ char *buf;
+ char *pastend;
+ int remain;
+ int group;
+
+ on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
+
+ buf = tlv_buf;
+ pastend = tlv_buf + tlv_buf_size;
+
+ /*
+ Parse ucast addr
+ */
+ addr_offset = pim_parse_addr_ucast(ifp->name, src_addr,
+ &msg_upstream_addr,
+ buf, pastend - buf);
+ if (addr_offset < 1) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
+ __PRETTY_FUNCTION__,
+ src_str, ifp->name);
+ return -1;
+ }
+ buf += addr_offset;
+
+ /*
+ Check upstream address family
+ */
+ if (msg_upstream_addr.family != AF_INET) {
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ msg_upstream_addr.family, src_str, ifp->name);
+ }
+ return -2;
+ }
+
+ remain = pastend - buf;
+ if (remain < 4) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ remain, 4, src_str, ifp->name);
+ return -4;
+ }
+
+ ++buf; /* skip reserved byte */
+ msg_num_groups = *(const uint8_t *) buf;
+ ++buf;
+ msg_holdtime = ntohs(*(const uint16_t *) buf);
+ ++buf;
+ ++buf;
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ char upstream_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
+ upstream_str, sizeof(upstream_str));
+ zlog_warn("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ upstream_str, msg_num_groups, msg_holdtime,
+ src_str, ifp->name);
+ }
+
+ /* Scan groups */
+ for (group = 0; group < msg_num_groups; ++group) {
+ struct prefix msg_group_addr;
+ struct prefix msg_source_addr;
+ uint8_t msg_source_flags;
+ uint16_t msg_num_joined_sources;
+ uint16_t msg_num_pruned_sources;
+ int source;
+
+ addr_offset = pim_parse_addr_group(ifp->name, src_addr,
+ &msg_group_addr,
+ buf, pastend - buf);
+ if (addr_offset < 1) {
+ return -5;
+ }
+ buf += addr_offset;
+
+ remain = pastend - buf;
+ if (remain < 4) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ remain, 4, src_str, ifp->name);
+ return -6;
+ }
+
+ msg_num_joined_sources = ntohs(*(const uint16_t *) buf);
+ buf += 2;
+ msg_num_pruned_sources = ntohs(*(const uint16_t *) buf);
+ buf += 2;
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ char upstream_str[100];
+ char group_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
+ upstream_str, sizeof(upstream_str));
+ pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4,
+ group_str, sizeof(group_str));
+ zlog_warn("%s: join/prune upstream=%s group=%s/%d join_src=%d prune_src=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ upstream_str, group_str, msg_group_addr.prefixlen,
+ msg_num_joined_sources, msg_num_pruned_sources,
+ src_str, ifp->name);
+ }
+
+ /* Scan joined sources */
+ for (source = 0; source < msg_num_joined_sources; ++source) {
+ addr_offset = pim_parse_addr_source(ifp->name, src_addr,
+ &msg_source_addr,
+ &msg_source_flags,
+ buf, pastend - buf);
+ if (addr_offset < 1) {
+ return -7;
+ }
+
+ buf += addr_offset;
+
+ recv_join(ifp, neigh, msg_holdtime,
+ msg_upstream_addr.u.prefix4,
+ msg_group_addr.u.prefix4,
+ msg_source_addr.u.prefix4,
+ msg_source_flags);
+ }
+
+ /* Scan pruned sources */
+ for (source = 0; source < msg_num_pruned_sources; ++source) {
+ addr_offset = pim_parse_addr_source(ifp->name, src_addr,
+ &msg_source_addr,
+ &msg_source_flags,
+ buf, pastend - buf);
+ if (addr_offset < 1) {
+ return -8;
+ }
+
+ buf += addr_offset;
+
+ recv_prune(ifp, neigh, msg_holdtime,
+ msg_upstream_addr.u.prefix4,
+ msg_group_addr.u.prefix4,
+ msg_source_addr.u.prefix4,
+ msg_source_flags);
+ }
+
+ } /* scan groups */
+
+ return 0;
+}
+
+int pim_joinprune_send(struct interface *ifp,
+ struct in_addr upstream_addr,
+ struct in_addr source_addr,
+ struct in_addr group_addr,
+ int send_join)
+{
+ struct pim_interface *pim_ifp;
+ char pim_msg[1000];
+ const char *pastend = pim_msg + sizeof(pim_msg);
+ char *pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* room for pim header */
+ int pim_msg_size;
+ int remain;
+
+ zassert(ifp);
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp) {
+ zlog_warn("%s: multicast not enabled on interface %s",
+ __PRETTY_FUNCTION__,
+ ifp->name);
+ return -1;
+ }
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char source_str[100];
+ char group_str[100];
+ char dst_str[100];
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
+ zlog_debug("%s: sending %s(S,G)=(%s,%s) to upstream=%s on interface %s",
+ __PRETTY_FUNCTION__,
+ send_join ? "Join" : "Prune",
+ source_str, group_str, dst_str, ifp->name);
+ }
+
+ if (PIM_INADDR_IS_ANY(upstream_addr)) {
+ if (PIM_DEBUG_PIM_TRACE) {
+ char source_str[100];
+ char group_str[100];
+ char dst_str[100];
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
+ zlog_debug("%s: %s(S,G)=(%s,%s): upstream=%s is myself on interface %s",
+ __PRETTY_FUNCTION__,
+ send_join ? "Join" : "Prune",
+ source_str, group_str, dst_str, ifp->name);
+ }
+ return 0;
+ }
+
+ /*
+ RFC 4601: 4.3.1. Sending Hello Messages
+
+ Thus, if a router needs to send a Join/Prune or Assert message on
+ an interface on which it has not yet sent a Hello message with the
+ currently configured IP address, then it MUST immediately send the
+ relevant Hello message without waiting for the Hello Timer to
+ expire, followed by the Join/Prune or Assert message.
+ */
+ pim_hello_require(ifp);
+
+ /*
+ Build PIM message
+ */
+
+ remain = pastend - pim_msg_curr;
+ pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
+ remain,
+ upstream_addr);
+ if (!pim_msg_curr) {
+ char dst_str[100];
+ pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
+ zlog_warn("%s: failure encoding destination address %s: space left=%d",
+ __PRETTY_FUNCTION__, dst_str, remain);
+ return -3;
+ }
+
+ remain = pastend - pim_msg_curr;
+ if (remain < 4) {
+ zlog_warn("%s: group will not fit: space left=%d",
+ __PRETTY_FUNCTION__, remain);
+ return -4;
+ }
+
+ *pim_msg_curr = 0; /* reserved */
+ ++pim_msg_curr;
+ *pim_msg_curr = 1; /* number of groups */
+ ++pim_msg_curr;
+ *((uint16_t *) pim_msg_curr) = htons(PIM_JP_HOLDTIME);
+ ++pim_msg_curr;
+ ++pim_msg_curr;
+
+ remain = pastend - pim_msg_curr;
+ pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
+ remain,
+ group_addr);
+ if (!pim_msg_curr) {
+ char group_str[100];
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ zlog_warn("%s: failure encoding group address %s: space left=%d",
+ __PRETTY_FUNCTION__, group_str, remain);
+ return -5;
+ }
+
+ remain = pastend - pim_msg_curr;
+ if (remain < 4) {
+ zlog_warn("%s: sources will not fit: space left=%d",
+ __PRETTY_FUNCTION__, remain);
+ return -6;
+ }
+
+ /* number of joined sources */
+ *((uint16_t *) pim_msg_curr) = htons(send_join ? 1 : 0);
+ ++pim_msg_curr;
+ ++pim_msg_curr;
+
+ /* number of pruned sources */
+ *((uint16_t *) pim_msg_curr) = htons(send_join ? 0 : 1);
+ ++pim_msg_curr;
+ ++pim_msg_curr;
+
+ remain = pastend - pim_msg_curr;
+ pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr,
+ remain,
+ source_addr);
+ if (!pim_msg_curr) {
+ char source_str[100];
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: failure encoding source address %s: space left=%d",
+ __PRETTY_FUNCTION__, source_str, remain);
+ return -7;
+ }
+
+ /* Add PIM header */
+
+ pim_msg_size = pim_msg_curr - pim_msg;
+
+ pim_msg_build_header(pim_msg, pim_msg_size,
+ PIM_MSG_TYPE_JOIN_PRUNE);
+
+ if (pim_msg_send(pim_ifp->pim_sock_fd,
+ qpim_all_pim_routers_addr,
+ pim_msg,
+ pim_msg_size,
+ ifp->name)) {
+ zlog_warn("%s: could not send PIM message on interface %s",
+ __PRETTY_FUNCTION__, ifp->name);
+ return -8;
+ }
+
+ return 0;
+}
diff --git a/pimd/pim_join.h b/pimd/pim_join.h
new file mode 100644
index 00000000..4d9407be
--- /dev/null
+++ b/pimd/pim_join.h
@@ -0,0 +1,43 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_JOIN_H
+#define PIM_JOIN_H
+
+#include <zebra.h>
+
+#include "if.h"
+
+#include "pim_neighbor.h"
+
+int pim_joinprune_recv(struct interface *ifp,
+ struct pim_neighbor *neigh,
+ struct in_addr src_addr,
+ char *tlv_buf, int tlv_buf_size);
+
+int pim_joinprune_send(struct interface *ifp,
+ struct in_addr upstream_addr,
+ struct in_addr source_addr,
+ struct in_addr group_addr,
+ int send_join);
+
+#endif /* PIM_JOIN_H */
diff --git a/pimd/pim_macro.c b/pimd/pim_macro.c
new file mode 100644
index 00000000..3f565325
--- /dev/null
+++ b/pimd/pim_macro.c
@@ -0,0 +1,437 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+
+#include "pim_macro.h"
+#include "pimd.h"
+#include "pim_str.h"
+#include "pim_iface.h"
+#include "pim_ifchannel.h"
+
+#define PIM_IFP_I_am_DR(pim_ifp) ((pim_ifp)->pim_dr_addr.s_addr == (pim_ifp)->primary_address.s_addr)
+
+/*
+ DownstreamJPState(S,G,I) is the per-interface state machine for
+ receiving (S,G) Join/Prune messages.
+
+ DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
+*/
+static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch)
+{
+ return ch->ifjoin_state != PIM_IFJOIN_NOINFO;
+}
+
+/*
+ The clause "local_receiver_include(S,G,I)" is true if the IGMP/MLD
+ module or other local membership mechanism has determined that local
+ members on interface I desire to receive traffic sent specifically
+ by S to G.
+*/
+static int local_receiver_include(const struct pim_ifchannel *ch)
+{
+ /* local_receiver_include(S,G,I) ? */
+ return ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE;
+}
+
+/*
+ RFC 4601: 4.1.6. State Summarization Macros
+
+ The set "joins(S,G)" is the set of all interfaces on which the
+ router has received (S,G) Joins:
+
+ joins(S,G) =
+ { all interfaces I such that
+ DownstreamJPState(S,G,I) is either Join or Prune-Pending }
+
+ DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
+*/
+int pim_macro_chisin_joins(const struct pim_ifchannel *ch)
+{
+ return downstream_jpstate_isjoined(ch);
+}
+
+/*
+ RFC 4601: 4.6.5. Assert State Macros
+
+ The set "lost_assert(S,G)" is the set of all interfaces on which the
+ router has received (S,G) joins but has lost an (S,G) assert.
+
+ lost_assert(S,G) =
+ { all interfaces I such that
+ lost_assert(S,G,I) == TRUE }
+
+ bool lost_assert(S,G,I) {
+ if ( RPF_interface(S) == I ) {
+ return FALSE
+ } else {
+ return ( AssertWinner(S,G,I) != NULL AND
+ AssertWinner(S,G,I) != me AND
+ (AssertWinnerMetric(S,G,I) is better
+ than spt_assert_metric(S,I) )
+ }
+ }
+
+ AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
+ packet that won an Assert.
+*/
+int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_assert_metric spt_assert_metric;
+
+ ifp = ch->interface;
+ if (!ifp) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+ zlog_warn("%s: (S,G)=(%s,%s): null interface",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str);
+ return 0; /* false */
+ }
+
+ /* RPF_interface(S) == I ? */
+ if (ch->upstream->rpf.source_nexthop.interface == ifp)
+ return 0; /* false */
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+ zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, ifp->name);
+ return 0; /* false */
+ }
+
+ if (PIM_INADDR_IS_ANY(ch->ifassert_winner))
+ return 0; /* false */
+
+ /* AssertWinner(S,G,I) == me ? */
+ if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
+ return 0; /* false */
+
+ spt_assert_metric = pim_macro_spt_assert_metric(&ch->upstream->rpf,
+ pim_ifp->primary_address);
+
+ return pim_assert_metric_better(&ch->ifassert_winner_metric,
+ &spt_assert_metric);
+}
+
+/*
+ RFC 4601: 4.1.6. State Summarization Macros
+
+ pim_include(S,G) =
+ { all interfaces I such that:
+ ( (I_am_DR( I ) AND lost_assert(S,G,I) == FALSE )
+ OR AssertWinner(S,G,I) == me )
+ AND local_receiver_include(S,G,I) }
+
+ AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
+ packet that won an Assert.
+*/
+int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch)
+{
+ struct pim_interface *pim_ifp = ch->interface->info;
+
+ if (!pim_ifp) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+ zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, ch->interface->name);
+ return 0; /* false */
+ }
+
+ /* local_receiver_include(S,G,I) ? */
+ if (!local_receiver_include(ch))
+ return 0; /* false */
+
+ /* OR AssertWinner(S,G,I) == me ? */
+ if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
+ return 1; /* true */
+
+ return (
+ /* I_am_DR( I ) ? */
+ PIM_IFP_I_am_DR(pim_ifp)
+ &&
+ /* lost_assert(S,G,I) == FALSE ? */
+ (!pim_macro_ch_lost_assert(ch))
+ );
+}
+
+int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch)
+{
+ if (pim_macro_chisin_joins(ch))
+ return 1; /* true */
+
+ return pim_macro_chisin_pim_include(ch);
+}
+
+/*
+ RFC 4601: 4.6.1. (S,G) Assert Message State Machine
+
+ CouldAssert(S,G,I) =
+ SPTbit(S,G)==TRUE
+ AND (RPF_interface(S) != I)
+ AND (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
+ (+) ( pim_include(*,G) (-) pim_exclude(S,G) )
+ (-) lost_assert(*,G)
+ (+) joins(S,G) (+) pim_include(S,G) ) )
+
+ CouldAssert(S,G,I) is true for downstream interfaces that would be in
+ the inherited_olist(S,G) if (S,G) assert information was not taken
+ into account.
+
+ CouldAssert(S,G,I) may be affected by changes in the following:
+
+ pim_ifp->primary_address
+ pim_ifp->pim_dr_addr
+ ch->ifassert_winner_metric
+ ch->ifassert_winner
+ ch->local_ifmembership
+ ch->ifjoin_state
+ ch->upstream->rpf.source_nexthop.mrib_metric_preference
+ ch->upstream->rpf.source_nexthop.mrib_route_metric
+ ch->upstream->rpf.source_nexthop.interface
+*/
+int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch)
+{
+ struct interface *ifp;
+
+ /* SPTbit(S,G) is always true for PIM-SSM-Only Routers */
+
+ ifp = ch->interface;
+ if (!ifp) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+ zlog_warn("%s: (S,G)=(%s,%s): null interface",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str);
+ return 0; /* false */
+ }
+
+ /* RPF_interface(S) != I ? */
+ if (ch->upstream->rpf.source_nexthop.interface == ifp)
+ return 0; /* false */
+
+ /* I in joins(S,G) (+) pim_include(S,G) ? */
+ return pim_macro_chisin_joins_or_include(ch);
+}
+
+/*
+ RFC 4601: 4.6.3. Assert Metrics
+
+ spt_assert_metric(S,I) gives the assert metric we use if we're
+ sending an assert based on active (S,G) forwarding state:
+
+ assert_metric
+ spt_assert_metric(S,I) {
+ return {0,MRIB.pref(S),MRIB.metric(S),my_ip_address(I)}
+ }
+*/
+struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf,
+ struct in_addr ifaddr)
+{
+ struct pim_assert_metric metric;
+
+ metric.rpt_bit_flag = 0;
+ metric.metric_preference = rpf->source_nexthop.mrib_metric_preference;
+ metric.route_metric = rpf->source_nexthop.mrib_route_metric;
+ metric.ip_address = ifaddr;
+
+ return metric;
+}
+
+/*
+ RFC 4601: 4.6.3. Assert Metrics
+
+ An assert metric for (S,G) to include in (or compare against) an
+ Assert message sent on interface I should be computed using the
+ following pseudocode:
+
+ assert_metric my_assert_metric(S,G,I) {
+ if( CouldAssert(S,G,I) == TRUE ) {
+ return spt_assert_metric(S,I)
+ } else if( CouldAssert(*,G,I) == TRUE ) {
+ return rpt_assert_metric(G,I)
+ } else {
+ return infinite_assert_metric()
+ }
+ }
+*/
+struct pim_assert_metric pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch)
+{
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ch->interface->info;
+
+ if (pim_ifp) {
+ if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
+ return pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address);
+ }
+ }
+
+ return qpim_infinite_assert_metric;
+}
+
+/*
+ RFC 4601 4.2. Data Packet Forwarding Rules
+ RFC 4601 4.8.2. PIM-SSM-Only Routers
+
+ Macro:
+ inherited_olist(S,G) =
+ joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
+*/
+static int pim_macro_chisin_inherited_olist(const struct pim_ifchannel *ch)
+{
+ if (pim_macro_ch_lost_assert(ch))
+ return 0; /* false */
+
+ return pim_macro_chisin_joins_or_include(ch);
+}
+
+/*
+ RFC 4601 4.2. Data Packet Forwarding Rules
+ RFC 4601 4.8.2. PIM-SSM-Only Routers
+
+ Additionally, the Packet forwarding rules of Section 4.2 can be
+ simplified in a PIM-SSM-only router:
+
+ iif is the incoming interface of the packet.
+ oiflist = NULL
+ if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) {
+ oiflist = inherited_olist(S,G)
+ } else if (iif is in inherited_olist(S,G)) {
+ send Assert(S,G) on iif
+ }
+ oiflist = oiflist (-) iif
+ forward packet on all interfaces in oiflist
+
+ Macro:
+ inherited_olist(S,G) =
+ joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
+
+ Note:
+ - The following test is performed as response to WRONGVIF kernel
+ upcall:
+ if (iif is in inherited_olist(S,G)) {
+ send Assert(S,G) on iif
+ }
+ See pim_mroute.c mroute_msg().
+*/
+int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch)
+{
+ if (ch->upstream->join_state != PIM_UPSTREAM_JOINED) {
+ /* oiflist is NULL */
+ return 0; /* false */
+ }
+
+ /* oiflist = oiflist (-) iif */
+ if (ch->interface == ch->upstream->rpf.source_nexthop.interface)
+ return 0; /* false */
+
+ return pim_macro_chisin_inherited_olist(ch);
+}
+
+/*
+ RFC 4601: 4.6.1. (S,G) Assert Message State Machine
+
+ AssertTrackingDesired(S,G,I) =
+ (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
+ (+) ( pim_include(*,G) (-) pim_exclude(S,G) )
+ (-) lost_assert(*,G)
+ (+) joins(S,G) ) )
+ OR (local_receiver_include(S,G,I) == TRUE
+ AND (I_am_DR(I) OR (AssertWinner(S,G,I) == me)))
+ OR ((RPF_interface(S) == I) AND (JoinDesired(S,G) == TRUE))
+ OR ((RPF_interface(RP(G)) == I) AND (JoinDesired(*,G) == TRUE)
+ AND (SPTbit(S,G) == FALSE))
+
+ AssertTrackingDesired(S,G,I) is true on any interface in which an
+ (S,G) assert might affect our behavior.
+*/
+int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch)
+{
+ struct pim_interface *pim_ifp;
+ struct interface *ifp;
+
+ ifp = ch->interface;
+ if (!ifp) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+ zlog_warn("%s: (S,G)=(%s,%s): null interface",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str);
+ return 0; /* false */
+ }
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+ zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, ch->interface->name);
+ return 0; /* false */
+ }
+
+ /* I in joins(S,G) ? */
+ if (pim_macro_chisin_joins(ch))
+ return 1; /* true */
+
+ /* local_receiver_include(S,G,I) ? */
+ if (local_receiver_include(ch)) {
+ /* I_am_DR(I) ? */
+ if (PIM_IFP_I_am_DR(pim_ifp))
+ return 1; /* true */
+
+ /* AssertWinner(S,G,I) == me ? */
+ if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
+ return 1; /* true */
+ }
+
+ /* RPF_interface(S) == I ? */
+ if (ch->upstream->rpf.source_nexthop.interface == ifp) {
+ /* JoinDesired(S,G) ? */
+ if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags))
+ return 1; /* true */
+ }
+
+ return 0; /* false */
+}
+
diff --git a/pimd/pim_macro.h b/pimd/pim_macro.h
new file mode 100644
index 00000000..472fa9b4
--- /dev/null
+++ b/pimd/pim_macro.h
@@ -0,0 +1,44 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_MACRO_H
+#define PIM_MACRO_H
+
+#include <zebra.h>
+
+#include "if.h"
+
+#include "pim_upstream.h"
+#include "pim_ifchannel.h"
+
+int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch);
+int pim_macro_chisin_joins(const struct pim_ifchannel *ch);
+int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch);
+int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch);
+int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch);
+struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf,
+ struct in_addr ifaddr);
+struct pim_assert_metric pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch);
+int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch);
+int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch);
+
+#endif /* PIM_MACRO_H */
diff --git a/pimd/pim_main.c b/pimd/pim_main.c
new file mode 100644
index 00000000..71749303
--- /dev/null
+++ b/pimd/pim_main.c
@@ -0,0 +1,298 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "privs.h"
+#include "version.h"
+#include <getopt.h>
+#include "command.h"
+#include "thread.h"
+#include <signal.h>
+
+#include "memory.h"
+#include "filter.h"
+#include "vty.h"
+#include "sigevent.h"
+#include "version.h"
+#include "paths.h"
+
+#include "pimd.h"
+#include "pim_version.h"
+#include "pim_signals.h"
+#include "pim_zebra.h"
+
+#ifdef PIM_ZCLIENT_DEBUG
+extern int zclient_debug;
+#endif
+
+extern struct host host;
+
+char config_default[MAXPATHLEN];
+
+struct option longopts[] = {
+ { "daemon", no_argument, NULL, 'd'},
+ { "namespace", required_argument, NULL, 'N'},
+ { "config_file", required_argument, NULL, 'f'},
+ { "pid_file", required_argument, NULL, 'i'},
+ { "vty_addr", required_argument, NULL, 'A'},
+ { "vty_port", required_argument, NULL, 'P'},
+ { "version", no_argument, NULL, 'v'},
+ { "debug_zclient", no_argument, NULL, 'Z'},
+ { "help", no_argument, NULL, 'h'},
+ { 0 }
+};
+
+/* pimd privileges */
+zebra_capabilities_t _caps_p [] =
+{
+ ZCAP_NET_ADMIN,
+ ZCAP_SYS_ADMIN,
+ ZCAP_NET_RAW,
+};
+
+/* pimd privileges to run with */
+struct zebra_privs_t pimd_privs =
+{
+#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP)
+ .user = QUAGGA_USER,
+ .group = QUAGGA_GROUP,
+#endif
+#ifdef VTY_GROUP
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = sizeof(_caps_p)/sizeof(_caps_p[0]),
+ .cap_num_i = 0
+};
+
+char* progname;
+static char pid_file_default[MAXPATHLEN];
+const char *pid_file = pid_file_default;
+
+static void usage(int status)
+{
+ if (status != 0)
+ fprintf (stderr, "Try `%s --help' for more information.\n", progname);
+ else {
+ printf ("Usage : %s [OPTION...]\n\
+Daemon which manages PIM.\n\n\
+-d, --daemon Run in daemon mode\n\
+-N, --namespace Insert argument into all paths\n\
+-f, --config_file Set configuration file name\n\
+-i, --pid_file Set process identifier file name\n\
+-A, --vty_addr Set vty's bind address\n\
+-P, --vty_port Set vty's port number\n\
+-v, --version Print program version\n\
+"
+
+#ifdef PIM_ZCLIENT_DEBUG
+"\
+-Z, --debug_zclient Enable zclient debugging\n\
+"
+#endif
+
+"\
+-h, --help Display this help and exit\n\
+\n\
+Report bugs to %s\n", progname, PIMD_BUG_ADDRESS);
+ }
+
+ exit (status);
+}
+
+
+int main(int argc, char** argv, char** envp) {
+ char *p;
+ char *vty_addr = NULL;
+ int vty_port = -1;
+ int daemon_mode = 0;
+ char *config_file = NULL;
+ struct thread thread;
+
+ umask(0027);
+
+ progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
+
+ zlog_default = openzlog(progname, ZLOG_PIM,
+ LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+
+ /* this while just reads the options */
+ while (1) {
+ int opt;
+
+ opt = getopt_long (argc, argv, "dN:f:i:A:P:vZh", longopts, 0);
+
+ if (opt == EOF)
+ break;
+
+ switch (opt) {
+ case 0:
+ break;
+ case 'd':
+ daemon_mode = 1;
+ break;
+ case 'N':
+ path_set_namespace (optarg);
+ break;
+ case 'f':
+ config_file = optarg;
+ break;
+ case 'i':
+ pid_file = optarg;
+ break;
+ case 'A':
+ vty_addr = optarg;
+ break;
+ case 'P':
+ vty_port = atoi (optarg);
+ break;
+ case 'v':
+ printf(PIMD_PROGNAME " version %s\n", PIMD_VERSION);
+ print_version(QUAGGA_PROGNAME);
+ exit (0);
+ break;
+#ifdef PIM_ZCLIENT_DEBUG
+ case 'Z':
+ zclient_debug = 1;
+ break;
+#endif
+ case 'h':
+ usage (0);
+ break;
+ default:
+ usage (1);
+ break;
+ }
+ }
+
+ strcpy (config_default, path_config ("pimd.conf"));
+ strcpy (pid_file_default, path_state ("pimd.pid"));
+
+ master = thread_master_create();
+
+ /*
+ * Temporarily send zlog to stdout
+ */
+ zlog_default->maxlvl[ZLOG_DEST_STDOUT] = zlog_default->default_lvl;
+ zlog_notice("Boot logging temporarily directed to stdout - begin");
+
+ zlog_notice("Quagga %s " PIMD_PROGNAME " %s starting",
+ QUAGGA_VERSION, PIMD_VERSION);
+
+ /*
+ * Initializations
+ */
+ zprivs_init (&pimd_privs);
+ pim_signals_init();
+ cmd_init(1);
+ vty_init(master);
+ memory_init();
+ access_list_init();
+ pim_init();
+ sort_node();
+
+ /*
+ * reset zlog default, then will obey configuration file
+ */
+ zlog_notice("Boot logging temporarily directed to stdout - end");
+#if 0
+ /* this would disable logging to stdout, but config has not been
+ loaded yet to reconfig the logging output */
+ zlog_default->maxlvl[ZLOG_DEST_STDOUT] = ZLOG_DISABLED;
+#endif
+
+ zlog_notice("Loading configuration - begin");
+
+ /* Get configuration file. */
+ vty_read_config(config_file, config_default);
+
+ /*
+ Starting from here zlog_* functions will log according configuration
+ */
+
+ zlog_notice("Loading configuration - end");
+
+ /* Change to the daemon program. */
+ if (daemon_mode) {
+ if (daemon(0, 0)) {
+ zlog_warn("failed to daemonize");
+ }
+ }
+
+ /* Process ID file creation. */
+ pid_output(pid_file);
+
+ /* Create pimd VTY socket */
+ if (vty_port < 0)
+ vty_port = PIMD_VTY_PORT;
+ vty_serv_sock(vty_addr, vty_port, path_state ("pimd.vty"));
+
+ zlog_notice("Quagga %s " PIMD_PROGNAME " %s starting, VTY interface at port TCP %d",
+ QUAGGA_VERSION, PIMD_VERSION, vty_port);
+
+#ifdef PIM_DEBUG_BYDEFAULT
+ zlog_notice("PIM_DEBUG_BYDEFAULT: Enabling all debug commands");
+ PIM_DO_DEBUG_PIM_EVENTS;
+ PIM_DO_DEBUG_PIM_PACKETS;
+ PIM_DO_DEBUG_PIM_TRACE;
+ PIM_DO_DEBUG_IGMP_EVENTS;
+ PIM_DO_DEBUG_IGMP_PACKETS;
+ PIM_DO_DEBUG_IGMP_TRACE;
+ PIM_DO_DEBUG_ZEBRA;
+#endif
+
+#ifdef PIM_ZCLIENT_DEBUG
+ zlog_notice("PIM_ZCLIENT_DEBUG: zclient debugging is supported, mode is %s (see option -Z)",
+ zclient_debug ? "ON" : "OFF");
+#endif
+
+#ifdef PIM_CHECK_RECV_IFINDEX_SANITY
+ zlog_notice("PIM_CHECK_RECV_IFINDEX_SANITY: will match sock/recv ifindex");
+#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
+ zlog_notice("PIM_REPORT_RECV_IFINDEX_MISMATCH: will report sock/recv ifindex mismatch");
+#endif
+#endif
+
+#ifdef PIM_USE_QUAGGA_INET_CHECKSUM
+ zlog_notice("PIM_USE_QUAGGA_INET_CHECKSUM: using Quagga's builtin checksum");
+#endif
+
+#ifdef PIM_UNEXPECTED_KERNEL_UPCALL
+ zlog_notice("PIM_UNEXPECTED_KERNEL_UPCALL: report unexpected kernel upcall");
+#endif
+
+ /*
+ Initialize zclient "update" and "lookup" sockets
+ */
+ pim_zebra_init();
+
+ while (thread_fetch(master, &thread))
+ thread_call(&thread);
+
+ zlog_err("%s %s: thread_fetch() returned NULL, exiting",
+ __FILE__, __PRETTY_FUNCTION__);
+
+ /* never reached */
+ return 0;
+}
diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c
new file mode 100644
index 00000000..f9c291dd
--- /dev/null
+++ b/pimd/pim_mroute.c
@@ -0,0 +1,448 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+#include "log.h"
+#include "privs.h"
+
+#include "pimd.h"
+#include "pim_mroute.h"
+#include "pim_str.h"
+#include "pim_time.h"
+#include "pim_iface.h"
+#include "pim_macro.h"
+
+/* GLOBAL VARS */
+extern struct zebra_privs_t pimd_privs;
+
+static void mroute_read_on(void);
+
+static int pim_mroute_set(int fd, int enable)
+{
+ int err;
+ int opt = enable ? MRT_INIT : MRT_DONE;
+ socklen_t opt_len = sizeof(opt);
+
+ err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len);
+ if (err) {
+ int e = errno;
+ zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e));
+ errno = e;
+ return -1;
+ }
+
+#if 0
+ zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok",
+ __FILE__, __PRETTY_FUNCTION__,
+ fd, opt);
+#endif
+
+ return 0;
+}
+
+int pim_mroute_msg(int fd, const char *buf, int buf_size)
+{
+ struct interface *ifp;
+ const struct ip *ip_hdr;
+ const struct igmpmsg *msg;
+ const char *upcall;
+ char src_str[100];
+ char grp_str[100];
+
+ ip_hdr = (const struct ip *) buf;
+
+ /* kernel upcall must have protocol=0 */
+ if (ip_hdr->ip_p) {
+ /* this is not a kernel upcall */
+#ifdef PIM_UNEXPECTED_KERNEL_UPCALL
+ zlog_warn("%s: not a kernel upcall proto=%d msg_size=%d",
+ __PRETTY_FUNCTION__, ip_hdr->ip_p, buf_size);
+#endif
+ return 0;
+ }
+
+ msg = (const struct igmpmsg *) buf;
+
+ switch (msg->im_msgtype) {
+ case IGMPMSG_NOCACHE: upcall = "NOCACHE"; break;
+ case IGMPMSG_WRONGVIF: upcall = "WRONGVIF"; break;
+ case IGMPMSG_WHOLEPKT: upcall = "WHOLEPKT"; break;
+ default: upcall = "<unknown_upcall?>";
+ }
+ ifp = pim_if_find_by_vif_index(msg->im_vif);
+ pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
+
+ if (msg->im_msgtype == IGMPMSG_WRONGVIF) {
+ struct pim_ifchannel *ch;
+ struct pim_interface *pim_ifp;
+
+ /*
+ Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
+
+ RFC 4601 4.8.2. PIM-SSM-Only Routers
+
+ iif is the incoming interface of the packet.
+ if (iif is in inherited_olist(S,G)) {
+ send Assert(S,G) on iif
+ }
+ */
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ zlog_debug("%s: WRONGVIF from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
+ __PRETTY_FUNCTION__,
+ fd,
+ src_str,
+ grp_str,
+ ifp ? ifp->name : "<ifname?>",
+ msg->im_vif);
+ }
+
+ if (!ifp) {
+ zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, msg->im_vif);
+ return -1;
+ }
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp) {
+ zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, ifp->name);
+ return -2;
+ }
+
+ ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst);
+ if (!ch) {
+ zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, ifp->name);
+ return -3;
+ }
+
+ /*
+ RFC 4601: 4.6.1. (S,G) Assert Message State Machine
+
+ Transitions from NoInfo State
+
+ An (S,G) data packet arrives on interface I, AND
+ CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
+ downstream interface that is in our (S,G) outgoing interface
+ list. We optimistically assume that we will be the assert
+ winner for this (S,G), and so we transition to the "I am Assert
+ Winner" state and perform Actions A1 (below), which will
+ initiate the assert negotiation for (S,G).
+ */
+
+ if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
+ zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, ifp->name);
+ return -4;
+ }
+
+ if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
+ zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, ifp->name);
+ return -5;
+ }
+
+ if (assert_action_a1(ch)) {
+ zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, ifp->name);
+ return -6;
+ }
+
+ return 0;
+ } /* IGMPMSG_WRONGVIF */
+
+ zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
+ __PRETTY_FUNCTION__,
+ upcall,
+ msg->im_msgtype,
+ ip_hdr->ip_p,
+ fd,
+ src_str,
+ grp_str,
+ ifp ? ifp->name : "<ifname?>",
+ msg->im_vif);
+
+ return 0;
+}
+
+static int mroute_read_msg(int fd)
+{
+ const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg));
+ char buf[1000];
+ int rd;
+
+ if (((int) sizeof(buf)) < msg_min_size) {
+ zlog_err("%s: fd=%d: buf size=%d lower than msg_min=%d",
+ __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size);
+ return -1;
+ }
+
+ rd = read(fd, buf, sizeof(buf));
+ if (rd < 0) {
+ zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
+ __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+ return -2;
+ }
+
+ if (rd < msg_min_size) {
+ zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
+ __PRETTY_FUNCTION__, fd, rd, msg_min_size);
+ return -3;
+ }
+
+ return pim_mroute_msg(fd, buf, rd);
+}
+
+static int mroute_read(struct thread *t)
+{
+ int fd;
+ int result;
+
+ zassert(t);
+ zassert(!THREAD_ARG(t));
+
+ fd = THREAD_FD(t);
+ zassert(fd == qpim_mroute_socket_fd);
+
+ result = mroute_read_msg(fd);
+
+ /* Keep reading */
+ qpim_mroute_socket_reader = 0;
+ mroute_read_on();
+
+ return result;
+}
+
+static void mroute_read_on()
+{
+ zassert(!qpim_mroute_socket_reader);
+ zassert(PIM_MROUTE_IS_ENABLED);
+
+ THREAD_READ_ON(master, qpim_mroute_socket_reader,
+ mroute_read, 0, qpim_mroute_socket_fd);
+}
+
+static void mroute_read_off()
+{
+ THREAD_OFF(qpim_mroute_socket_reader);
+}
+
+int pim_mroute_socket_enable()
+{
+ int fd;
+
+ if (PIM_MROUTE_IS_ENABLED)
+ return -1;
+
+ if ( pimd_privs.change (ZPRIVS_RAISE) )
+ zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
+ safe_strerror (errno) );
+
+ fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
+
+ if ( pimd_privs.change (ZPRIVS_LOWER) )
+ zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
+ safe_strerror (errno) );
+
+ if (fd < 0) {
+ zlog_warn("Could not create mroute socket: errno=%d: %s",
+ errno, safe_strerror(errno));
+ return -2;
+ }
+
+ if (pim_mroute_set(fd, 1)) {
+ zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
+ fd, errno, safe_strerror(errno));
+ close(fd);
+ return -3;
+ }
+
+ qpim_mroute_socket_fd = fd;
+ qpim_mroute_socket_creation = pim_time_monotonic_sec();
+ mroute_read_on();
+
+ zassert(PIM_MROUTE_IS_ENABLED);
+
+ return 0;
+}
+
+int pim_mroute_socket_disable()
+{
+ if (PIM_MROUTE_IS_DISABLED)
+ return -1;
+
+ if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
+ zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
+ qpim_mroute_socket_fd, errno, safe_strerror(errno));
+ return -2;
+ }
+
+ if (close(qpim_mroute_socket_fd)) {
+ zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
+ qpim_mroute_socket_fd, errno, safe_strerror(errno));
+ return -3;
+ }
+
+ mroute_read_off();
+ qpim_mroute_socket_fd = -1;
+
+ zassert(PIM_MROUTE_IS_DISABLED);
+
+ return 0;
+}
+
+/*
+ For each network interface (e.g., physical or a virtual tunnel) that
+ would be used for multicast forwarding, a corresponding multicast
+ interface must be added to the kernel.
+ */
+int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr)
+{
+ struct vifctl_ext vc;
+ int err;
+
+ if (PIM_MROUTE_IS_DISABLED) {
+ zlog_warn("%s: global multicast is disabled",
+ __PRETTY_FUNCTION__);
+ return -1;
+ }
+
+ memset(&vc, 0, sizeof(vc));
+ vc.vc.vifc_vifi = vif_index;
+ vc.vc.vifc_flags = 0;
+ vc.vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
+ vc.vc.vifc_rate_limit = 0;
+ vc.ifindex = vif_index;
+ memcpy(&vc.vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vc.vifc_lcl_addr));
+
+#ifdef PIM_DVMRP_TUNNEL
+ if (vc.vifc_flags & VIFF_TUNNEL) {
+ memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
+ }
+#endif
+
+ err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
+ if (err)
+ err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc.vc));
+ if (err) {
+ char ifaddr_str[100];
+ int e = errno;
+
+ pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
+
+ zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s): errno=%d: %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ qpim_mroute_socket_fd, vif_index, ifaddr_str,
+ e, safe_strerror(e));
+ errno = e;
+ return -2;
+ }
+
+ return 0;
+}
+
+int pim_mroute_del_vif(int vif_index)
+{
+ struct vifctl vc;
+ int err;
+
+ if (PIM_MROUTE_IS_DISABLED) {
+ zlog_warn("%s: global multicast is disabled",
+ __PRETTY_FUNCTION__);
+ return -1;
+ }
+
+ memset(&vc, 0, sizeof(vc));
+ vc.vifc_vifi = vif_index;
+
+ err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
+ if (err) {
+ int e = errno;
+ zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ qpim_mroute_socket_fd, vif_index,
+ e, safe_strerror(e));
+ errno = e;
+ return -2;
+ }
+
+ return 0;
+}
+
+int pim_mroute_add(struct mfcctl *mc)
+{
+ int err;
+
+ if (PIM_MROUTE_IS_DISABLED) {
+ zlog_warn("%s: global multicast is disabled",
+ __PRETTY_FUNCTION__);
+ return -1;
+ }
+
+ err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
+ mc, sizeof(*mc));
+ if (err) {
+ int e = errno;
+ zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ qpim_mroute_socket_fd,
+ e, safe_strerror(e));
+ errno = e;
+ return -2;
+ }
+
+ return 0;
+}
+
+int pim_mroute_del(struct mfcctl *mc)
+{
+ int err;
+
+ if (PIM_MROUTE_IS_DISABLED) {
+ zlog_warn("%s: global multicast is disabled",
+ __PRETTY_FUNCTION__);
+ return -1;
+ }
+
+ err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, mc, sizeof(*mc));
+ if (err) {
+ int e = errno;
+ zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ qpim_mroute_socket_fd,
+ e, safe_strerror(e));
+ errno = e;
+ return -2;
+ }
+
+ return 0;
+}
diff --git a/pimd/pim_mroute.h b/pimd/pim_mroute.h
new file mode 100644
index 00000000..9aa48a43
--- /dev/null
+++ b/pimd/pim_mroute.h
@@ -0,0 +1,178 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_MROUTE_H
+#define PIM_MROUTE_H
+
+/*
+ For msghdr.msg_control in Solaris 10
+*/
+#ifndef _XPG4_2
+#define _XPG4_2
+#endif
+#ifndef __EXTENSIONS__
+#define __EXTENSIONS__
+#endif
+
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_IP_MROUTE_H
+#include <netinet/ip_mroute.h>
+#endif
+
+#define PIM_MROUTE_MIN_TTL (1)
+
+/*
+ Below: from <linux/mroute.h>
+*/
+
+#ifndef MAXVIFS
+#define MAXVIFS (32)
+#endif
+
+#ifndef SIOCGETVIFCNT
+#define SIOCGETVIFCNT SIOCPROTOPRIVATE /* IP protocol privates */
+#define SIOCGETSGCNT (SIOCPROTOPRIVATE+1)
+#define SIOCGETRPF (SIOCPROTOPRIVATE+2)
+#endif
+
+#ifndef MRT_INIT
+#define MRT_BASE 200
+#define MRT_INIT (MRT_BASE) /* Activate the kernel mroute code */
+#define MRT_DONE (MRT_BASE+1) /* Shutdown the kernel mroute */
+#define MRT_ADD_VIF (MRT_BASE+2) /* Add a virtual interface */
+#define MRT_DEL_VIF (MRT_BASE+3) /* Delete a virtual interface */
+#define MRT_ADD_MFC (MRT_BASE+4) /* Add a multicast forwarding entry */
+#define MRT_DEL_MFC (MRT_BASE+5) /* Delete a multicast forwarding entry */
+#define MRT_VERSION (MRT_BASE+6) /* Get the kernel multicast version */
+#define MRT_ASSERT (MRT_BASE+7) /* Activate PIM assert mode */
+#define MRT_PIM (MRT_BASE+8) /* enable PIM code */
+#endif
+
+#ifndef HAVE_VIFI_T
+typedef unsigned short vifi_t;
+#endif
+
+#ifndef HAVE_STRUCT_VIFCTL
+struct vifctl {
+ vifi_t vifc_vifi; /* Index of VIF */
+ unsigned char vifc_flags; /* VIFF_ flags */
+ unsigned char vifc_threshold; /* ttl limit */
+ unsigned int vifc_rate_limit; /* Rate limiter values (NI) */
+ struct in_addr vifc_lcl_addr; /* Our address */
+ struct in_addr vifc_rmt_addr; /* IPIP tunnel addr */
+};
+#endif
+
+struct vifctl_ext {
+ struct vifctl vc; /* vifctl */
+ int ifindex; /* interface index */
+};
+
+#ifndef HAVE_STRUCT_MFCCTL
+struct mfcctl {
+ struct in_addr mfcc_origin; /* Origin of mcast */
+ struct in_addr mfcc_mcastgrp; /* Group in question */
+ vifi_t mfcc_parent; /* Where it arrived */
+ unsigned char mfcc_ttls[MAXVIFS]; /* Where it is going */
+ unsigned int mfcc_pkt_cnt; /* pkt count for src-grp */
+ unsigned int mfcc_byte_cnt;
+ unsigned int mfcc_wrong_if;
+ int mfcc_expire;
+};
+#endif
+
+/*
+ * Group count retrieval for mrouted
+ */
+/*
+ struct sioc_sg_req sgreq;
+ memset(&sgreq, 0, sizeof(sgreq));
+ memcpy(&sgreq.src, &source_addr, sizeof(sgreq.src));
+ memcpy(&sgreq.grp, &group_addr, sizeof(sgreq.grp));
+ ioctl(mrouter_s4, SIOCGETSGCNT, &sgreq);
+ */
+#ifndef HAVE_STRUCT_SIOC_SG_REQ
+struct sioc_sg_req {
+ struct in_addr src;
+ struct in_addr grp;
+ unsigned long pktcnt;
+ unsigned long bytecnt;
+ unsigned long wrong_if;
+};
+#endif
+
+/*
+ * To get vif packet counts
+ */
+/*
+ struct sioc_vif_req vreq;
+ memset(&vreq, 0, sizeof(vreq));
+ vreq.vifi = vif_index;
+ ioctl(mrouter_s4, SIOCGETVIFCNT, &vreq);
+ */
+#ifndef HAVE_STRUCT_SIOC_VIF_REQ
+struct sioc_vif_req {
+ vifi_t vifi; /* Which iface */
+ unsigned long icount; /* In packets */
+ unsigned long ocount; /* Out packets */
+ unsigned long ibytes; /* In bytes */
+ unsigned long obytes; /* Out bytes */
+};
+#endif
+
+/*
+ * Pseudo messages used by mrouted
+ */
+#ifndef IGMPMSG_NOCACHE
+#define IGMPMSG_NOCACHE 1 /* Kern cache fill request to mrouted */
+#define IGMPMSG_WRONGVIF 2 /* For PIM assert processing (unused) */
+#define IGMPMSG_WHOLEPKT 3 /* For PIM Register processing */
+#endif
+
+#ifndef HAVE_STRUCT_IGMPMSG
+struct igmpmsg
+{
+ uint32_t unused1,unused2;
+ unsigned char im_msgtype; /* What is this */
+ unsigned char im_mbz; /* Must be zero */
+ unsigned char im_vif; /* Interface (this ought to be a vifi_t!) */
+ unsigned char unused3;
+ struct in_addr im_src,im_dst;
+};
+#endif
+
+/*
+ Above: from <linux/mroute.h>
+*/
+
+int pim_mroute_socket_enable(void);
+int pim_mroute_socket_disable(void);
+
+int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr);
+int pim_mroute_del_vif(int vif_index);
+
+int pim_mroute_add(struct mfcctl *mc);
+int pim_mroute_del(struct mfcctl *mc);
+
+int pim_mroute_msg(int fd, const char *buf, int buf_size);
+
+#endif /* PIM_MROUTE_H */
diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c
new file mode 100644
index 00000000..1105eaca
--- /dev/null
+++ b/pimd/pim_msg.c
@@ -0,0 +1,106 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "pimd.h"
+#include "pim_pim.h"
+#include "pim_msg.h"
+#include "pim_util.h"
+
+void pim_msg_build_header(char *pim_msg, int pim_msg_size,
+ uint8_t pim_msg_type)
+{
+ uint16_t checksum;
+
+ zassert(pim_msg_size >= PIM_PIM_MIN_LEN);
+
+ /*
+ * Write header
+ */
+
+ *(uint8_t *) PIM_MSG_HDR_OFFSET_VERSION(pim_msg) = (PIM_PROTO_VERSION << 4) | pim_msg_type;
+ *(uint8_t *) PIM_MSG_HDR_OFFSET_RESERVED(pim_msg) = 0;
+
+ /*
+ * Compute checksum
+ */
+
+ *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = 0;
+ checksum = pim_inet_checksum(pim_msg, pim_msg_size);
+ *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = checksum;
+}
+
+char *pim_msg_addr_encode_ipv4_ucast(char *buf,
+ int buf_size,
+ struct in_addr addr)
+{
+ const int ENCODED_IPV4_UCAST_SIZE = 6;
+
+ if (buf_size < ENCODED_IPV4_UCAST_SIZE) {
+ return 0;
+ }
+
+ buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */
+ buf[1] = '\0'; /* native encoding */
+ *(struct in_addr *)(buf + 2) = addr;
+
+ return buf + ENCODED_IPV4_UCAST_SIZE;
+}
+
+char *pim_msg_addr_encode_ipv4_group(char *buf,
+ int buf_size,
+ struct in_addr addr)
+{
+ const int ENCODED_IPV4_GROUP_SIZE = 8;
+
+ if (buf_size < ENCODED_IPV4_GROUP_SIZE) {
+ return 0;
+ }
+
+ buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */
+ buf[1] = '\0'; /* native encoding */
+ buf[2] = '\0'; /* reserved */
+ buf[3] = 32; /* mask len */
+ *(struct in_addr *)(buf + 4) = addr;
+
+ return buf + ENCODED_IPV4_GROUP_SIZE;
+}
+
+char *pim_msg_addr_encode_ipv4_source(char *buf,
+ int buf_size,
+ struct in_addr addr)
+{
+ const int ENCODED_IPV4_SOURCE_SIZE = 8;
+
+ if (buf_size < ENCODED_IPV4_SOURCE_SIZE) {
+ return 0;
+ }
+
+ buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */
+ buf[1] = '\0'; /* native encoding */
+ buf[2] = 4; /* reserved = 0 | S bit = 1 | W bit = 0 | R bit = 0 */
+ buf[3] = 32; /* mask len */
+ *(struct in_addr *)(buf + 4) = addr;
+
+ return buf + ENCODED_IPV4_SOURCE_SIZE;
+}
diff --git a/pimd/pim_msg.h b/pimd/pim_msg.h
new file mode 100644
index 00000000..228d6a85
--- /dev/null
+++ b/pimd/pim_msg.h
@@ -0,0 +1,52 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_MSG_H
+#define PIM_MSG_H
+
+#include <netinet/in.h>
+
+/*
+ Number Description
+ ---------- ------------------
+ 0 Reserved
+ 1 IP (IP version 4)
+ 2 IP6 (IP version 6)
+
+ From:
+ http://www.iana.org/assignments/address-family-numbers
+*/
+#define PIM_MSG_ADDRESS_FAMILY_IPV4 (1)
+
+void pim_msg_build_header(char *pim_msg, int pim_msg_size,
+ uint8_t pim_msg_type);
+char *pim_msg_addr_encode_ipv4_ucast(char *buf,
+ int buf_size,
+ struct in_addr addr);
+char *pim_msg_addr_encode_ipv4_group(char *buf,
+ int buf_size,
+ struct in_addr addr);
+char *pim_msg_addr_encode_ipv4_source(char *buf,
+ int buf_size,
+ struct in_addr addr);
+
+#endif /* PIM_MSG_H */
diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c
new file mode 100644
index 00000000..9013b50c
--- /dev/null
+++ b/pimd/pim_neighbor.c
@@ -0,0 +1,715 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+#include "memory.h"
+
+#include "pimd.h"
+#include "pim_neighbor.h"
+#include "pim_time.h"
+#include "pim_str.h"
+#include "pim_iface.h"
+#include "pim_pim.h"
+#include "pim_upstream.h"
+#include "pim_ifchannel.h"
+
+static void dr_election_by_addr(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *node;
+ struct pim_neighbor *neigh;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ pim_ifp->pim_dr_addr = pim_ifp->primary_address;
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ zlog_debug("%s: on interface %s",
+ __PRETTY_FUNCTION__,
+ ifp->name);
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) {
+ if (ntohl(neigh->source_addr.s_addr) > ntohl(pim_ifp->pim_dr_addr.s_addr)) {
+ pim_ifp->pim_dr_addr = neigh->source_addr;
+ }
+ }
+}
+
+static void dr_election_by_pri(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *node;
+ struct pim_neighbor *neigh;
+ uint32_t dr_pri;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ pim_ifp->pim_dr_addr = pim_ifp->primary_address;
+ dr_pri = pim_ifp->pim_dr_priority;
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ zlog_debug("%s: dr pri %u on interface %s",
+ __PRETTY_FUNCTION__,
+ dr_pri, ifp->name);
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) {
+ if (PIM_DEBUG_PIM_TRACE) {
+ zlog_info("%s: neigh pri %u addr %x if dr addr %x",
+ __PRETTY_FUNCTION__,
+ neigh->dr_priority,
+ ntohl(neigh->source_addr.s_addr),
+ ntohl(pim_ifp->pim_dr_addr.s_addr));
+ }
+ if (
+ (neigh->dr_priority > dr_pri) ||
+ (
+ (neigh->dr_priority == dr_pri) &&
+ (ntohl(neigh->source_addr.s_addr) > ntohl(pim_ifp->pim_dr_addr.s_addr))
+ )
+ ) {
+ pim_ifp->pim_dr_addr = neigh->source_addr;
+ dr_pri = neigh->dr_priority;
+ }
+ }
+}
+
+/*
+ RFC 4601: 4.3.2. DR Election
+
+ A router's idea of the current DR on an interface can change when a
+ PIM Hello message is received, when a neighbor times out, or when a
+ router's own DR Priority changes.
+ */
+void pim_if_dr_election(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp = ifp->info;
+ struct in_addr old_dr_addr;
+
+ pim_ifp->pim_dr_election_last = pim_time_monotonic_sec(); /* timestamp */
+ ++pim_ifp->pim_dr_election_count;
+
+ old_dr_addr = pim_ifp->pim_dr_addr;
+
+ if (pim_ifp->pim_dr_num_nondrpri_neighbors) {
+ dr_election_by_addr(ifp);
+ }
+ else {
+ dr_election_by_pri(ifp);
+ }
+
+ /* DR changed ? */
+ if (old_dr_addr.s_addr != pim_ifp->pim_dr_addr.s_addr) {
+ char dr_old_str[100];
+ char dr_new_str[100];
+ pim_inet4_dump("<old_dr?>", old_dr_addr, dr_old_str, sizeof(dr_old_str));
+ pim_inet4_dump("<new_dr?>", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str));
+ zlog_info("%s: DR was %s now is %s on interface %s",
+ __PRETTY_FUNCTION__,
+ dr_old_str, dr_new_str, ifp->name);
+
+ pim_if_update_join_desired(pim_ifp);
+ pim_if_update_could_assert(ifp);
+ pim_if_update_assert_tracking_desired(ifp);
+ }
+}
+
+static void update_dr_priority(struct pim_neighbor *neigh,
+ pim_hello_options hello_options,
+ uint32_t dr_priority)
+{
+ pim_hello_options will_set_pri; /* boolean */
+ pim_hello_options bit_flip; /* boolean */
+ pim_hello_options pri_change; /* boolean */
+
+ will_set_pri = PIM_OPTION_IS_SET(hello_options,
+ PIM_OPTION_MASK_DR_PRIORITY);
+
+ bit_flip =
+ (
+ will_set_pri !=
+ PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY)
+ );
+
+ if (bit_flip) {
+ struct pim_interface *pim_ifp = neigh->interface->info;
+
+ /* update num. of neighbors without dr_pri */
+
+ if (will_set_pri) {
+ --pim_ifp->pim_dr_num_nondrpri_neighbors;
+ }
+ else {
+ ++pim_ifp->pim_dr_num_nondrpri_neighbors;
+ }
+ }
+
+ pri_change =
+ (
+ bit_flip
+ ||
+ (neigh->dr_priority != dr_priority)
+ );
+
+ if (will_set_pri) {
+ neigh->dr_priority = dr_priority;
+ }
+ else {
+ neigh->dr_priority = 0; /* cosmetic unset */
+ }
+
+ if (pri_change) {
+ /*
+ RFC 4601: 4.3.2. DR Election
+
+ A router's idea of the current DR on an interface can change when a
+ PIM Hello message is received, when a neighbor times out, or when a
+ router's own DR Priority changes.
+ */
+ pim_if_dr_election(neigh->interface);
+ }
+}
+
+static int on_neighbor_timer(struct thread *t)
+{
+ struct pim_neighbor *neigh;
+ struct interface *ifp;
+ char msg[100];
+
+ zassert(t);
+ neigh = THREAD_ARG(t);
+ zassert(neigh);
+
+ ifp = neigh->interface;
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
+ zlog_debug("Expired %d sec holdtime for neighbor %s on interface %s",
+ neigh->holdtime, src_str, ifp->name);
+ }
+
+ neigh->t_expire_timer = 0;
+
+ snprintf(msg, sizeof(msg), "%d-sec holdtime expired", neigh->holdtime);
+ pim_neighbor_delete(ifp, neigh, msg);
+
+ /*
+ RFC 4601: 4.3.2. DR Election
+
+ A router's idea of the current DR on an interface can change when a
+ PIM Hello message is received, when a neighbor times out, or when a
+ router's own DR Priority changes.
+ */
+ pim_if_dr_election(ifp);
+
+ return 0;
+}
+
+static void neighbor_timer_off(struct pim_neighbor *neigh)
+{
+ if (PIM_DEBUG_PIM_TRACE) {
+ if (neigh->t_expire_timer) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
+ zlog_debug("%s: cancelling timer for neighbor %s on %s",
+ __PRETTY_FUNCTION__,
+ src_str, neigh->interface->name);
+ }
+ }
+ THREAD_OFF(neigh->t_expire_timer);
+ zassert(!neigh->t_expire_timer);
+}
+
+void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime)
+{
+ neigh->holdtime = holdtime;
+
+ neighbor_timer_off(neigh);
+
+ /*
+ 0xFFFF is request for no holdtime
+ */
+ if (neigh->holdtime == 0xFFFF) {
+ return;
+ }
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
+ zlog_debug("%s: starting %u sec timer for neighbor %s on %s",
+ __PRETTY_FUNCTION__,
+ neigh->holdtime, src_str, neigh->interface->name);
+ }
+
+ THREAD_TIMER_ON(master, neigh->t_expire_timer,
+ on_neighbor_timer,
+ neigh, neigh->holdtime);
+}
+
+static struct pim_neighbor *pim_neighbor_new(struct interface *ifp,
+ struct in_addr source_addr,
+ pim_hello_options hello_options,
+ uint16_t holdtime,
+ uint16_t propagation_delay,
+ uint16_t override_interval,
+ uint32_t dr_priority,
+ uint32_t generation_id,
+ struct list *addr_list)
+{
+ struct pim_interface *pim_ifp;
+ struct pim_neighbor *neigh;
+ char src_str[100];
+
+ zassert(ifp);
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ neigh = XMALLOC(MTYPE_PIM_NEIGHBOR, sizeof(*neigh));
+ if (!neigh) {
+ zlog_err("%s: PIM XMALLOC(%d) failure",
+ __PRETTY_FUNCTION__, sizeof(*neigh));
+ return 0;
+ }
+
+ neigh->creation = pim_time_monotonic_sec();
+ neigh->source_addr = source_addr;
+ neigh->hello_options = hello_options;
+ neigh->propagation_delay_msec = propagation_delay;
+ neigh->override_interval_msec = override_interval;
+ neigh->dr_priority = dr_priority;
+ neigh->generation_id = generation_id;
+ neigh->prefix_list = addr_list;
+ neigh->t_expire_timer = 0;
+ neigh->interface = ifp;
+
+ pim_neighbor_timer_reset(neigh, holdtime);
+
+ pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+
+ if (PIM_DEBUG_PIM_EVENTS) {
+ zlog_debug("%s: creating PIM neighbor %s on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, ifp->name);
+ }
+
+ zlog_info("PIM NEIGHBOR UP: neighbor %s on interface %s",
+ src_str, ifp->name);
+
+ if (neigh->propagation_delay_msec > pim_ifp->pim_neighbors_highest_propagation_delay_msec) {
+ pim_ifp->pim_neighbors_highest_propagation_delay_msec = neigh->propagation_delay_msec;
+ }
+ if (neigh->override_interval_msec > pim_ifp->pim_neighbors_highest_override_interval_msec) {
+ pim_ifp->pim_neighbors_highest_override_interval_msec = neigh->override_interval_msec;
+ }
+
+ if (!PIM_OPTION_IS_SET(neigh->hello_options,
+ PIM_OPTION_MASK_LAN_PRUNE_DELAY)) {
+ /* update num. of neighbors without hello option lan_delay */
+ ++pim_ifp->pim_number_of_nonlandelay_neighbors;
+ }
+
+ if (!PIM_OPTION_IS_SET(neigh->hello_options,
+ PIM_OPTION_MASK_DR_PRIORITY)) {
+ /* update num. of neighbors without hello option dr_pri */
+ ++pim_ifp->pim_dr_num_nondrpri_neighbors;
+ }
+
+ /*
+ RFC 4601: 4.3.2. DR Election
+
+ A router's idea of the current DR on an interface can change when a
+ PIM Hello message is received, when a neighbor times out, or when a
+ router's own DR Priority changes.
+ */
+ pim_if_dr_election(neigh->interface);
+
+ /*
+ RFC 4601: 4.3.1. Sending Hello Messages
+
+ To allow new or rebooting routers to learn of PIM neighbors quickly,
+ when a Hello message is received from a new neighbor, or a Hello
+ message with a new GenID is received from an existing neighbor, a
+ new Hello message should be sent on this interface after a
+ randomized delay between 0 and Triggered_Hello_Delay.
+ */
+ pim_hello_restart_triggered(neigh->interface);
+
+ return neigh;
+}
+
+static void delete_prefix_list(struct pim_neighbor *neigh)
+{
+ if (neigh->prefix_list) {
+
+#ifdef DUMP_PREFIX_LIST
+ struct listnode *p_node;
+ struct prefix *p;
+ char addr_str[10];
+ int list_size = neigh->prefix_list ? (int) listcount(neigh->prefix_list) : -1;
+ int i = 0;
+ for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, p_node, p)) {
+ pim_inet4_dump("<addr?>", p->u.prefix4, addr_str, sizeof(addr_str));
+ zlog_debug("%s: DUMP_PREFIX_LIST neigh=%x prefix_list=%x prefix=%x addr=%s [%d/%d]",
+ __PRETTY_FUNCTION__,
+ (unsigned) neigh, (unsigned) neigh->prefix_list, (unsigned) p,
+ addr_str, i, list_size);
+ ++i;
+ }
+#endif
+
+ list_delete(neigh->prefix_list);
+ neigh->prefix_list = 0;
+ }
+}
+
+void pim_neighbor_free(struct pim_neighbor *neigh)
+{
+ zassert(!neigh->t_expire_timer);
+
+ delete_prefix_list(neigh);
+
+ XFREE(MTYPE_PIM_NEIGHBOR, neigh);
+}
+
+struct pim_neighbor *pim_neighbor_find(struct interface *ifp,
+ struct in_addr source_addr)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *node;
+ struct pim_neighbor *neigh;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) {
+ if (source_addr.s_addr == neigh->source_addr.s_addr) {
+ return neigh;
+ }
+ }
+
+ return 0;
+}
+
+struct pim_neighbor *pim_neighbor_add(struct interface *ifp,
+ struct in_addr source_addr,
+ pim_hello_options hello_options,
+ uint16_t holdtime,
+ uint16_t propagation_delay,
+ uint16_t override_interval,
+ uint32_t dr_priority,
+ uint32_t generation_id,
+ struct list *addr_list)
+{
+ struct pim_interface *pim_ifp;
+ struct pim_neighbor *neigh;
+
+ neigh = pim_neighbor_new(ifp, source_addr,
+ hello_options,
+ holdtime,
+ propagation_delay,
+ override_interval,
+ dr_priority,
+ generation_id,
+ addr_list);
+ if (!neigh) {
+ return 0;
+ }
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ listnode_add(pim_ifp->pim_neighbor_list, neigh);
+
+ return neigh;
+}
+
+static uint16_t
+find_neighbors_next_highest_propagation_delay_msec(struct interface *ifp,
+ struct pim_neighbor *highest_neigh)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *neigh_node;
+ struct pim_neighbor *neigh;
+ uint16_t next_highest_delay_msec;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ next_highest_delay_msec = pim_ifp->pim_propagation_delay_msec;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node, neigh)) {
+ if (neigh == highest_neigh)
+ continue;
+ if (neigh->propagation_delay_msec > next_highest_delay_msec)
+ next_highest_delay_msec = neigh->propagation_delay_msec;
+ }
+
+ return next_highest_delay_msec;
+}
+
+static uint16_t
+find_neighbors_next_highest_override_interval_msec(struct interface *ifp,
+ struct pim_neighbor *highest_neigh)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *neigh_node;
+ struct pim_neighbor *neigh;
+ uint16_t next_highest_interval_msec;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ next_highest_interval_msec = pim_ifp->pim_override_interval_msec;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node, neigh)) {
+ if (neigh == highest_neigh)
+ continue;
+ if (neigh->override_interval_msec > next_highest_interval_msec)
+ next_highest_interval_msec = neigh->override_interval_msec;
+ }
+
+ return next_highest_interval_msec;
+}
+
+void pim_neighbor_delete(struct interface *ifp,
+ struct pim_neighbor *neigh,
+ const char *delete_message)
+{
+ struct pim_interface *pim_ifp;
+ char src_str[100];
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
+ zlog_info("PIM NEIGHBOR DOWN: neighbor %s on interface %s: %s",
+ src_str, ifp->name, delete_message);
+
+ neighbor_timer_off(neigh);
+
+ pim_if_assert_on_neighbor_down(ifp, neigh->source_addr);
+
+ if (!PIM_OPTION_IS_SET(neigh->hello_options,
+ PIM_OPTION_MASK_LAN_PRUNE_DELAY)) {
+ /* update num. of neighbors without hello option lan_delay */
+
+ --pim_ifp->pim_number_of_nonlandelay_neighbors;
+ }
+
+ if (!PIM_OPTION_IS_SET(neigh->hello_options,
+ PIM_OPTION_MASK_DR_PRIORITY)) {
+ /* update num. of neighbors without dr_pri */
+
+ --pim_ifp->pim_dr_num_nondrpri_neighbors;
+ }
+
+ zassert(neigh->propagation_delay_msec <= pim_ifp->pim_neighbors_highest_propagation_delay_msec);
+ zassert(neigh->override_interval_msec <= pim_ifp->pim_neighbors_highest_override_interval_msec);
+
+ if (pim_if_lan_delay_enabled(ifp)) {
+
+ /* will delete a neighbor with highest propagation delay? */
+ if (neigh->propagation_delay_msec == pim_ifp->pim_neighbors_highest_propagation_delay_msec) {
+ /* then find the next highest propagation delay */
+ pim_ifp->pim_neighbors_highest_propagation_delay_msec =
+ find_neighbors_next_highest_propagation_delay_msec(ifp, neigh);
+ }
+
+ /* will delete a neighbor with highest override interval? */
+ if (neigh->override_interval_msec == pim_ifp->pim_neighbors_highest_override_interval_msec) {
+ /* then find the next highest propagation delay */
+ pim_ifp->pim_neighbors_highest_override_interval_msec =
+ find_neighbors_next_highest_override_interval_msec(ifp, neigh);
+ }
+ }
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ zlog_debug("%s: deleting PIM neighbor %s on interface %s",
+ __PRETTY_FUNCTION__,
+ src_str, ifp->name);
+ }
+
+ listnode_delete(pim_ifp->pim_neighbor_list, neigh);
+
+ pim_neighbor_free(neigh);
+}
+
+void pim_neighbor_delete_all(struct interface *ifp,
+ const char *delete_message)
+{
+ struct pim_interface *pim_ifp;
+ struct listnode *neigh_node;
+ struct listnode *neigh_nextnode;
+ struct pim_neighbor *neigh;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ for (ALL_LIST_ELEMENTS(pim_ifp->pim_neighbor_list, neigh_node,
+ neigh_nextnode, neigh)) {
+ pim_neighbor_delete(ifp, neigh, delete_message);
+ }
+}
+
+struct prefix *pim_neighbor_find_secondary(struct pim_neighbor *neigh,
+ struct in_addr addr)
+{
+ struct listnode *node;
+ struct prefix *p;
+
+ if (!neigh->prefix_list)
+ return 0;
+
+ for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, node, p)) {
+ if (p->family == AF_INET) {
+ if (addr.s_addr == p->u.prefix4.s_addr) {
+ return p;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ RFC 4601: 4.3.4. Maintaining Secondary Address Lists
+
+ All the advertised secondary addresses in received Hello messages
+ must be checked against those previously advertised by all other
+ PIM neighbors on that interface. If there is a conflict and the
+ same secondary address was previously advertised by another
+ neighbor, then only the most recently received mapping MUST be
+ maintained, and an error message SHOULD be logged to the
+ administrator in a rate-limited manner.
+*/
+static void delete_from_neigh_addr(struct interface *ifp,
+ struct list *addr_list,
+ struct in_addr neigh_addr)
+{
+ struct listnode *addr_node;
+ struct prefix *addr;
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ zassert(addr_list);
+
+ /*
+ Scan secondary address list
+ */
+ for (ALL_LIST_ELEMENTS_RO(addr_list, addr_node,
+ addr)) {
+ struct listnode *neigh_node;
+ struct pim_neighbor *neigh;
+
+ if (addr->family != AF_INET)
+ continue;
+
+ /*
+ Scan neighbors
+ */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node,
+ neigh)) {
+ {
+ struct prefix *p = pim_neighbor_find_secondary(neigh, addr->u.prefix4);
+ if (p) {
+ char addr_str[100];
+ char this_neigh_str[100];
+ char other_neigh_str[100];
+
+ pim_inet4_dump("<addr?>", addr->u.prefix4, addr_str, sizeof(addr_str));
+ pim_inet4_dump("<neigh1?>", neigh_addr, this_neigh_str, sizeof(this_neigh_str));
+ pim_inet4_dump("<neigh2?>", neigh->source_addr, other_neigh_str, sizeof(other_neigh_str));
+
+ zlog_info("secondary addr %s recvd from neigh %s deleted from neigh %s on %s",
+ addr_str, this_neigh_str, other_neigh_str, ifp->name);
+
+ listnode_delete(neigh->prefix_list, p);
+ prefix_free(p);
+ }
+ }
+
+ } /* scan neighbors */
+
+ } /* scan addr list */
+
+}
+
+void pim_neighbor_update(struct pim_neighbor *neigh,
+ pim_hello_options hello_options,
+ uint16_t holdtime,
+ uint32_t dr_priority,
+ struct list *addr_list)
+{
+ struct pim_interface *pim_ifp = neigh->interface->info;
+
+ /* Received holdtime ? */
+ if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
+ pim_neighbor_timer_reset(neigh, holdtime);
+ }
+ else {
+ pim_neighbor_timer_reset(neigh, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
+ }
+
+#ifdef DUMP_PREFIX_LIST
+ zlog_debug("%s: DUMP_PREFIX_LIST old_prefix_list=%x old_size=%d new_prefix_list=%x new_size=%d",
+ __PRETTY_FUNCTION__,
+ (unsigned) neigh->prefix_list,
+ neigh->prefix_list ? (int) listcount(neigh->prefix_list) : -1,
+ (unsigned) addr_list,
+ addr_list ? (int) listcount(addr_list) : -1);
+#endif
+
+ if (neigh->prefix_list == addr_list) {
+ if (addr_list) {
+ zlog_err("%s: internal error: trying to replace same prefix list=%u",
+ __PRETTY_FUNCTION__, (unsigned) addr_list);
+ }
+ }
+ else {
+ /* Delete existing secondary address list */
+ delete_prefix_list(neigh);
+ }
+
+ if (addr_list) {
+ delete_from_neigh_addr(neigh->interface, addr_list, neigh->source_addr);
+ }
+
+ /* Replace secondary address list */
+ neigh->prefix_list = addr_list;
+
+ update_dr_priority(neigh,
+ hello_options,
+ dr_priority);
+ /*
+ Copy flags
+ */
+ neigh->hello_options = hello_options;
+}
diff --git a/pimd/pim_neighbor.h b/pimd/pim_neighbor.h
new file mode 100644
index 00000000..8f19c750
--- /dev/null
+++ b/pimd/pim_neighbor.h
@@ -0,0 +1,74 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_NEIGHBOR_H
+#define PIM_NEIGHBOR_H
+
+#include <zebra.h>
+
+#include "if.h"
+#include "linklist.h"
+
+#include "pim_tlv.h"
+
+struct pim_neighbor {
+ int64_t creation; /* timestamp of creation */
+ struct in_addr source_addr;
+ pim_hello_options hello_options;
+ uint16_t holdtime;
+ uint16_t propagation_delay_msec;
+ uint16_t override_interval_msec;
+ uint32_t dr_priority;
+ uint32_t generation_id;
+ struct list *prefix_list; /* list of struct prefix */
+ struct thread *t_expire_timer;
+ struct interface *interface;
+};
+
+void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime);
+void pim_neighbor_free(struct pim_neighbor *neigh);
+struct pim_neighbor *pim_neighbor_find(struct interface *ifp,
+ struct in_addr source_addr);
+struct pim_neighbor *pim_neighbor_add(struct interface *ifp,
+ struct in_addr source_addr,
+ pim_hello_options hello_options,
+ uint16_t holdtime,
+ uint16_t propagation_delay,
+ uint16_t override_interval,
+ uint32_t dr_priority,
+ uint32_t generation_id,
+ struct list *addr_list);
+void pim_neighbor_delete(struct interface *ifp,
+ struct pim_neighbor *neigh,
+ const char *delete_message);
+void pim_neighbor_delete_all(struct interface *ifp,
+ const char *delete_message);
+void pim_neighbor_update(struct pim_neighbor *neigh,
+ pim_hello_options hello_options,
+ uint16_t holdtime,
+ uint32_t dr_priority,
+ struct list *addr_list);
+struct prefix *pim_neighbor_find_secondary(struct pim_neighbor *neigh,
+ struct in_addr addr);
+void pim_if_dr_election(struct interface *ifp);
+
+#endif /* PIM_NEIGHBOR_H */
diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c
new file mode 100644
index 00000000..2c8b056e
--- /dev/null
+++ b/pimd/pim_oil.c
@@ -0,0 +1,140 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "linklist.h"
+
+#include "pimd.h"
+#include "pim_oil.h"
+#include "pim_str.h"
+#include "pim_iface.h"
+
+void pim_channel_oil_free(struct channel_oil *c_oil)
+{
+ XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
+}
+
+static void pim_channel_oil_delete(struct channel_oil *c_oil)
+{
+ /*
+ notice that listnode_delete() can't be moved
+ into pim_channel_oil_free() because the later is
+ called by list_delete_all_node()
+ */
+ listnode_delete(qpim_channel_oil_list, c_oil);
+
+ pim_channel_oil_free(c_oil);
+}
+
+static struct channel_oil *channel_oil_new(struct in_addr group_addr,
+ struct in_addr source_addr,
+ int input_vif_index)
+{
+ struct channel_oil *c_oil;
+ struct interface *ifp_in;
+
+ ifp_in = pim_if_find_by_vif_index(input_vif_index);
+ if (!ifp_in) {
+ /* warning only */
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
+ __PRETTY_FUNCTION__,
+ source_str, group_str, input_vif_index);
+ }
+
+ c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
+ if (!c_oil) {
+ zlog_err("PIM XCALLOC(%d) failure", sizeof(*c_oil));
+ return 0;
+ }
+
+ c_oil->oil.mfcc_mcastgrp = group_addr;
+ c_oil->oil.mfcc_origin = source_addr;
+ c_oil->oil.mfcc_parent = input_vif_index;
+ c_oil->oil_ref_count = 1;
+
+ zassert(c_oil->oil_size == 0);
+
+ return c_oil;
+}
+
+static struct channel_oil *pim_add_channel_oil(struct in_addr group_addr,
+ struct in_addr source_addr,
+ int input_vif_index)
+{
+ struct channel_oil *c_oil;
+
+ c_oil = channel_oil_new(group_addr, source_addr, input_vif_index);
+ if (!c_oil) {
+ zlog_warn("PIM XCALLOC(%d) failure", sizeof(*c_oil));
+ return 0;
+ }
+
+ listnode_add(qpim_channel_oil_list, c_oil);
+
+ return c_oil;
+}
+
+static struct channel_oil *pim_find_channel_oil(struct in_addr group_addr,
+ struct in_addr source_addr)
+{
+ struct listnode *node;
+ struct channel_oil *c_oil;
+
+ for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
+ if ((group_addr.s_addr == c_oil->oil.mfcc_mcastgrp.s_addr) &&
+ (source_addr.s_addr == c_oil->oil.mfcc_origin.s_addr))
+ return c_oil;
+ }
+
+ return 0;
+}
+
+struct channel_oil *pim_channel_oil_add(struct in_addr group_addr,
+ struct in_addr source_addr,
+ int input_vif_index)
+{
+ struct channel_oil *c_oil;
+
+ c_oil = pim_find_channel_oil(group_addr, source_addr);
+ if (c_oil) {
+ ++c_oil->oil_ref_count;
+ return c_oil;
+ }
+
+ return pim_add_channel_oil(group_addr, source_addr, input_vif_index);
+}
+
+void pim_channel_oil_del(struct channel_oil *c_oil)
+{
+ --c_oil->oil_ref_count;
+
+ if (c_oil->oil_ref_count < 1) {
+ pim_channel_oil_delete(c_oil);
+ }
+}
diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h
new file mode 100644
index 00000000..1753545a
--- /dev/null
+++ b/pimd/pim_oil.h
@@ -0,0 +1,53 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_OIL_H
+#define PIM_OIL_H
+
+#include "pim_mroute.h"
+
+#define PIM_OIF_FLAG_PROTO_IGMP (1 << 0) /* bitmask 1 */
+#define PIM_OIF_FLAG_PROTO_PIM (1 << 1) /* bitmask 2 */
+#define PIM_OIF_FLAG_PROTO_ANY (3) /* bitmask (1 | 2) */
+
+/*
+ qpim_channel_oil_list holds a list of struct channel_oil.
+
+ Each channel_oil.oil is used to control an (S,G) entry in the Kernel
+ Multicast Forwarding Cache.
+*/
+
+struct channel_oil {
+ struct mfcctl oil;
+ int oil_size;
+ int oil_ref_count;
+ time_t oif_creation[MAXVIFS];
+ uint32_t oif_flags[MAXVIFS];
+};
+
+void pim_channel_oil_free(struct channel_oil *c_oil);
+struct channel_oil *pim_channel_oil_add(struct in_addr group_addr,
+ struct in_addr source_addr,
+ int input_vif_index);
+void pim_channel_oil_del(struct channel_oil *c_oil);
+
+#endif /* PIM_OIL_H */
diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c
new file mode 100644
index 00000000..9595d2d7
--- /dev/null
+++ b/pimd/pim_pim.c
@@ -0,0 +1,734 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "thread.h"
+#include "memory.h"
+
+#include "pimd.h"
+#include "pim_pim.h"
+#include "pim_time.h"
+#include "pim_iface.h"
+#include "pim_sock.h"
+#include "pim_str.h"
+#include "pim_util.h"
+#include "pim_tlv.h"
+#include "pim_neighbor.h"
+#include "pim_hello.h"
+#include "pim_join.h"
+#include "pim_assert.h"
+#include "pim_msg.h"
+#include "pim_rand.h"
+
+static int on_pim_hello_send(struct thread *t);
+static int pim_hello_send(struct interface *ifp,
+ uint16_t holdtime);
+
+static void sock_close(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp = ifp->info;
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ if (pim_ifp->t_pim_sock_read) {
+ zlog_debug("Cancelling READ event for PIM socket fd=%d on interface %s",
+ pim_ifp->pim_sock_fd,
+ ifp->name);
+ }
+ }
+ THREAD_OFF(pim_ifp->t_pim_sock_read);
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ if (pim_ifp->t_pim_hello_timer) {
+ zlog_debug("Cancelling PIM hello timer for interface %s",
+ ifp->name);
+ }
+ }
+ THREAD_OFF(pim_ifp->t_pim_hello_timer);
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ zlog_debug("Deleting PIM socket fd=%d on interface %s",
+ pim_ifp->pim_sock_fd, ifp->name);
+ }
+
+ if (close(pim_ifp->pim_sock_fd)) {
+ zlog_warn("Failure closing PIM socket fd=%d on interface %s: errno=%d: %s",
+ pim_ifp->pim_sock_fd, ifp->name,
+ errno, safe_strerror(errno));
+ }
+
+ pim_ifp->pim_sock_fd = -1;
+ pim_ifp->pim_sock_creation = 0;
+
+ zassert(pim_ifp->pim_sock_fd < 0);
+ zassert(!pim_ifp->t_pim_sock_read);
+ zassert(!pim_ifp->t_pim_hello_timer);
+ zassert(!pim_ifp->pim_sock_creation);
+}
+
+void pim_sock_delete(struct interface *ifp, const char *delete_message)
+{
+ zlog_info("PIM INTERFACE DOWN: on interface %s: %s",
+ ifp->name, delete_message);
+
+ if (!ifp->info) {
+ zlog_err("%s: %s: but PIM not enabled on interface %s (!)",
+ __PRETTY_FUNCTION__, delete_message, ifp->name);
+ return;
+ }
+
+ /*
+ RFC 4601: 4.3.1. Sending Hello Messages
+
+ Before an interface goes down or changes primary IP address, a Hello
+ message with a zero HoldTime should be sent immediately (with the
+ old IP address if the IP address changed).
+ */
+ pim_hello_send(ifp, 0 /* zero-sec holdtime */);
+
+ pim_neighbor_delete_all(ifp, delete_message);
+
+ sock_close(ifp);
+}
+
+int pim_pim_packet(struct interface *ifp, char *buf, size_t len)
+{
+ struct ip *ip_hdr;
+ size_t ip_hlen; /* ip header length in bytes */
+ char src_str[100];
+ char dst_str[100];
+ char *pim_msg;
+ int pim_msg_len;
+ uint8_t pim_version;
+ uint8_t pim_type;
+ uint16_t pim_checksum; /* received checksum */
+ uint16_t checksum; /* computed checksum */
+ struct pim_neighbor *neigh;
+
+ if (!ifp->info) {
+ zlog_warn("%s: PIM not enabled on interface %s",
+ __PRETTY_FUNCTION__, ifp->name);
+ return -1;
+ }
+
+ if (len < sizeof(*ip_hdr)) {
+ zlog_warn("PIM packet size=%d shorter than minimum=%d",
+ len, sizeof(*ip_hdr));
+ return -1;
+ }
+
+ ip_hdr = (struct ip *) buf;
+
+ pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
+ pim_inet4_dump("<dst?>", ip_hdr->ip_dst, dst_str, sizeof(dst_str));
+
+ ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
+
+ if (PIM_DEBUG_PIM_PACKETS) {
+ zlog_debug("Recv IP packet from %s to %s on %s: size=%d ip_header_size=%d ip_proto=%d",
+ src_str, dst_str, ifp->name, len, ip_hlen, ip_hdr->ip_p);
+ }
+
+ if (ip_hdr->ip_p != PIM_IP_PROTO_PIM) {
+ zlog_warn("IP packet protocol=%d is not PIM=%d",
+ ip_hdr->ip_p, PIM_IP_PROTO_PIM);
+ return -1;
+ }
+
+ if (ip_hlen < PIM_IP_HEADER_MIN_LEN) {
+ zlog_warn("IP packet header size=%d shorter than minimum=%d",
+ ip_hlen, PIM_IP_HEADER_MIN_LEN);
+ return -1;
+ }
+ if (ip_hlen > PIM_IP_HEADER_MAX_LEN) {
+ zlog_warn("IP packet header size=%d greater than maximum=%d",
+ ip_hlen, PIM_IP_HEADER_MAX_LEN);
+ return -1;
+ }
+
+ pim_msg = buf + ip_hlen;
+ pim_msg_len = len - ip_hlen;
+
+ if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
+ pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_len);
+ }
+
+ if (pim_msg_len < PIM_PIM_MIN_LEN) {
+ zlog_warn("PIM message size=%d shorter than minimum=%d",
+ pim_msg_len, PIM_PIM_MIN_LEN);
+ return -1;
+ }
+
+ pim_version = PIM_MSG_HDR_GET_VERSION(pim_msg);
+ pim_type = PIM_MSG_HDR_GET_TYPE(pim_msg);
+
+ if (pim_version != PIM_PROTO_VERSION) {
+ zlog_warn("Ignoring PIM pkt from %s with unsupported version: %d",
+ ifp->name, pim_version);
+ return -1;
+ }
+
+ /* save received checksum */
+ pim_checksum = PIM_MSG_HDR_GET_CHECKSUM(pim_msg);
+
+ /* for computing checksum */
+ *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = 0;
+
+ checksum = pim_inet_checksum(pim_msg, pim_msg_len);
+ if (checksum != pim_checksum) {
+ zlog_warn("Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
+ ifp->name, pim_checksum, checksum);
+ return -1;
+ }
+
+ if (PIM_DEBUG_PIM_PACKETS) {
+ zlog_debug("Recv PIM packet from %s to %s on %s: ttl=%d pim_version=%d pim_type=%d pim_msg_size=%d checksum=%x",
+ src_str, dst_str, ifp->name, ip_hdr->ip_ttl,
+ pim_version, pim_type, pim_msg_len, checksum);
+ }
+
+ if (pim_type == PIM_MSG_TYPE_HELLO) {
+ return pim_hello_recv(ifp,
+ ip_hdr->ip_src,
+ pim_msg + PIM_MSG_HEADER_LEN,
+ pim_msg_len - PIM_MSG_HEADER_LEN);
+ }
+
+ neigh = pim_neighbor_find(ifp, ip_hdr->ip_src);
+ if (!neigh) {
+ zlog_warn("%s %s: non-hello PIM message type=%d from non-neighbor %s on %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ pim_type, src_str, ifp->name);
+ return -1;
+ }
+
+ switch (pim_type) {
+ case PIM_MSG_TYPE_JOIN_PRUNE:
+ return pim_joinprune_recv(ifp, neigh,
+ ip_hdr->ip_src,
+ pim_msg + PIM_MSG_HEADER_LEN,
+ pim_msg_len - PIM_MSG_HEADER_LEN);
+ case PIM_MSG_TYPE_ASSERT:
+ return pim_assert_recv(ifp, neigh,
+ ip_hdr->ip_src,
+ pim_msg + PIM_MSG_HEADER_LEN,
+ pim_msg_len - PIM_MSG_HEADER_LEN);
+ default:
+ zlog_warn("%s %s: unsupported PIM message type=%d from %s on %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ pim_type, src_str, ifp->name);
+ }
+
+ return -1;
+}
+
+static void pim_sock_read_on(struct interface *ifp);
+
+static int pim_sock_read(struct thread *t)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ int fd;
+ struct sockaddr_in from;
+ struct sockaddr_in to;
+ socklen_t fromlen = sizeof(from);
+ socklen_t tolen = sizeof(to);
+ char buf[PIM_PIM_BUFSIZE_READ];
+ int len;
+ int ifindex = -1;
+ int result = -1; /* defaults to bad */
+
+ zassert(t);
+
+ ifp = THREAD_ARG(t);
+ zassert(ifp);
+
+ fd = THREAD_FD(t);
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ zassert(fd == pim_ifp->pim_sock_fd);
+
+ len = pim_socket_recvfromto(fd, buf, sizeof(buf),
+ &from, &fromlen,
+ &to, &tolen,
+ &ifindex);
+ if (len < 0) {
+ zlog_warn("Failure receiving IP PIM packet on fd=%d: errno=%d: %s",
+ fd, errno, safe_strerror(errno));
+ goto done;
+ }
+
+ if (PIM_DEBUG_PIM_PACKETS) {
+ char from_str[100];
+ char to_str[100];
+
+ if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
+ sprintf(from_str, "<from?>");
+ if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
+ sprintf(to_str, "<to?>");
+
+ zlog_debug("Recv IP PIM pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
+ len, from_str, to_str, fd, ifindex, ifp->ifindex);
+ }
+
+ if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
+ pim_pkt_dump(__PRETTY_FUNCTION__, buf, len);
+ }
+
+#ifdef PIM_CHECK_RECV_IFINDEX_SANITY
+ /* ifindex sanity check */
+ if (ifindex != (int) ifp->ifindex) {
+ char from_str[100];
+ char to_str[100];
+ struct interface *recv_ifp;
+
+ if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
+ sprintf(from_str, "<from?>");
+ if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
+ sprintf(to_str, "<to?>");
+
+ recv_ifp = if_lookup_by_index(ifindex);
+ if (recv_ifp) {
+ zassert(ifindex == (int) recv_ifp->ifindex);
+ }
+
+#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
+ zlog_warn("Interface mismatch: recv PIM pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
+ from_str, to_str, fd,
+ ifindex, recv_ifp ? recv_ifp->name : "<if-notfound>",
+ ifp->ifindex, ifp->name);
+#endif
+ goto done;
+ }
+#endif
+
+ if (pim_pim_packet(ifp, buf, len)) {
+ goto done;
+ }
+
+ result = 0; /* good */
+
+ done:
+ pim_sock_read_on(ifp);
+
+ if (result) {
+ ++pim_ifp->pim_ifstat_hello_recvfail;
+ }
+
+ return result;
+}
+
+static void pim_sock_read_on(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+
+ zassert(ifp);
+ zassert(ifp->info);
+
+ pim_ifp = ifp->info;
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ zlog_debug("Scheduling READ event on PIM socket fd=%d",
+ pim_ifp->pim_sock_fd);
+ }
+ pim_ifp->t_pim_sock_read = 0;
+ zassert(!pim_ifp->t_pim_sock_read);
+ THREAD_READ_ON(master, pim_ifp->t_pim_sock_read, pim_sock_read, ifp,
+ pim_ifp->pim_sock_fd);
+}
+
+static int pim_sock_open(struct in_addr ifaddr, int ifindex)
+{
+ int fd;
+
+ fd = pim_socket_mcast(IPPROTO_PIM, ifaddr, 0 /* loop=false */);
+ if (fd < 0)
+ return -1;
+
+ if (pim_socket_join(fd, qpim_all_pim_routers_addr, ifaddr, ifindex)) {
+ return -2;
+ }
+
+ return fd;
+}
+
+void pim_ifstat_reset(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+
+ zassert(ifp);
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp) {
+ return;
+ }
+
+ pim_ifp->pim_ifstat_start = pim_time_monotonic_sec();
+ pim_ifp->pim_ifstat_hello_sent = 0;
+ pim_ifp->pim_ifstat_hello_sendfail = 0;
+ pim_ifp->pim_ifstat_hello_recv = 0;
+ pim_ifp->pim_ifstat_hello_recvfail = 0;
+}
+
+void pim_sock_reset(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+
+ zassert(ifp);
+ zassert(ifp->info);
+
+ pim_ifp = ifp->info;
+
+ pim_ifp->primary_address = pim_find_primary_addr(ifp);
+
+ pim_ifp->pim_sock_fd = -1;
+ pim_ifp->pim_sock_creation = 0;
+ pim_ifp->t_pim_sock_read = 0;
+
+ pim_ifp->t_pim_hello_timer = 0;
+ pim_ifp->pim_hello_period = PIM_DEFAULT_HELLO_PERIOD;
+ pim_ifp->pim_default_holdtime = -1; /* unset: means 3.5 * pim_hello_period */
+ pim_ifp->pim_triggered_hello_delay = PIM_DEFAULT_TRIGGERED_HELLO_DELAY;
+ pim_ifp->pim_dr_priority = PIM_DEFAULT_DR_PRIORITY;
+ pim_ifp->pim_propagation_delay_msec = PIM_DEFAULT_PROPAGATION_DELAY_MSEC;
+ pim_ifp->pim_override_interval_msec = PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC;
+ if (PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION) {
+ PIM_IF_DO_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options);
+ }
+ else {
+ PIM_IF_DONT_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options);
+ }
+
+ /* neighbors without lan_delay */
+ pim_ifp->pim_number_of_nonlandelay_neighbors = 0;
+ pim_ifp->pim_neighbors_highest_propagation_delay_msec = 0;
+ pim_ifp->pim_neighbors_highest_override_interval_msec = 0;
+
+ /* DR Election */
+ pim_ifp->pim_dr_election_last = 0; /* timestamp */
+ pim_ifp->pim_dr_election_count = 0;
+ pim_ifp->pim_dr_num_nondrpri_neighbors = 0; /* neighbors without dr_pri */
+ pim_ifp->pim_dr_addr = pim_ifp->primary_address;
+
+ pim_ifstat_reset(ifp);
+}
+
+int pim_msg_send(int fd,
+ struct in_addr dst,
+ char *pim_msg,
+ int pim_msg_size,
+ const char *ifname)
+{
+ ssize_t sent;
+ struct sockaddr_in to;
+ socklen_t tolen;
+
+ if (PIM_DEBUG_PIM_PACKETS) {
+ char dst_str[100];
+ pim_inet4_dump("<dst?>", dst, dst_str, sizeof(dst_str));
+ zlog_debug("%s: to %s on %s: msg_size=%d checksum=%x",
+ __PRETTY_FUNCTION__,
+ dst_str, ifname, pim_msg_size,
+ *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg));
+ }
+
+#if 0
+ memset(&to, 0, sizeof(to));
+#endif
+ to.sin_family = AF_INET;
+ to.sin_addr = dst;
+#if 0
+ to.sin_port = htons(0);
+#endif
+ tolen = sizeof(to);
+
+ if (PIM_DEBUG_PIM_PACKETDUMP_SEND) {
+ pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_size);
+ }
+
+ sent = sendto(fd, pim_msg, pim_msg_size, MSG_DONTWAIT, &to, tolen);
+ if (sent != (ssize_t) pim_msg_size) {
+ int e = errno;
+ char dst_str[100];
+ pim_inet4_dump("<dst?>", dst, dst_str, sizeof(dst_str));
+ if (sent < 0) {
+ zlog_warn("%s: sendto() failure to %s on %s: fd=%d msg_size=%d: errno=%d: %s",
+ __PRETTY_FUNCTION__,
+ dst_str, ifname, fd, pim_msg_size,
+ e, safe_strerror(e));
+ }
+ else {
+ zlog_warn("%s: sendto() partial to %s on %s: fd=%d msg_size=%d: sent=%d",
+ __PRETTY_FUNCTION__,
+ dst_str, ifname, fd,
+ pim_msg_size, sent);
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+static int hello_send(struct interface *ifp,
+ uint16_t holdtime)
+{
+ char pim_msg[PIM_PIM_BUFSIZE_WRITE];
+ struct pim_interface *pim_ifp;
+ int pim_tlv_size;
+ int pim_msg_size;
+
+ pim_ifp = ifp->info;
+
+ if (PIM_DEBUG_PIM_PACKETS) {
+ char dst_str[100];
+ pim_inet4_dump("<dst?>", qpim_all_pim_routers_addr, dst_str, sizeof(dst_str));
+ zlog_debug("%s: to %s on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%d",
+ __PRETTY_FUNCTION__,
+ dst_str, ifp->name,
+ holdtime,
+ pim_ifp->pim_propagation_delay_msec, pim_ifp->pim_override_interval_msec,
+ PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options),
+ pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id,
+ listcount(ifp->connected));
+ }
+
+ pim_tlv_size = pim_hello_build_tlv(ifp->name,
+ pim_msg + PIM_PIM_MIN_LEN,
+ sizeof(pim_msg) - PIM_PIM_MIN_LEN,
+ holdtime,
+ pim_ifp->pim_dr_priority,
+ pim_ifp->pim_generation_id,
+ pim_ifp->pim_propagation_delay_msec,
+ pim_ifp->pim_override_interval_msec,
+ PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options),
+ ifp->connected);
+ if (pim_tlv_size < 0) {
+ return -1;
+ }
+
+ pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN;
+
+ zassert(pim_msg_size >= PIM_PIM_MIN_LEN);
+ zassert(pim_msg_size <= PIM_PIM_BUFSIZE_WRITE);
+
+ pim_msg_build_header(pim_msg, pim_msg_size,
+ PIM_MSG_TYPE_HELLO);
+
+ if (pim_msg_send(pim_ifp->pim_sock_fd,
+ qpim_all_pim_routers_addr,
+ pim_msg,
+ pim_msg_size,
+ ifp->name)) {
+ zlog_warn("%s: could not send PIM message on interface %s",
+ __PRETTY_FUNCTION__, ifp->name);
+ return -2;
+ }
+
+ return 0;
+}
+
+static int pim_hello_send(struct interface *ifp,
+ uint16_t holdtime)
+{
+ struct pim_interface *pim_ifp;
+
+ zassert(ifp);
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ if (hello_send(ifp, holdtime)) {
+ ++pim_ifp->pim_ifstat_hello_sendfail;
+
+ zlog_warn("Could not send PIM hello on interface %s",
+ ifp->name);
+ return -1;
+ }
+
+ ++pim_ifp->pim_ifstat_hello_sent;
+
+ return 0;
+}
+
+static void hello_resched(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+
+ zassert(ifp);
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ zlog_debug("Rescheduling %d sec hello on interface %s",
+ pim_ifp->pim_hello_period, ifp->name);
+ }
+ THREAD_OFF(pim_ifp->t_pim_hello_timer);
+ THREAD_TIMER_ON(master, pim_ifp->t_pim_hello_timer,
+ on_pim_hello_send,
+ ifp, pim_ifp->pim_hello_period);
+}
+
+/*
+ Periodic hello timer
+ */
+static int on_pim_hello_send(struct thread *t)
+{
+ struct pim_interface *pim_ifp;
+ struct interface *ifp;
+
+ zassert(t);
+ ifp = THREAD_ARG(t);
+ zassert(ifp);
+
+ pim_ifp = ifp->info;
+
+ /*
+ * Schedule next hello
+ */
+ pim_ifp->t_pim_hello_timer = 0;
+ hello_resched(ifp);
+
+ /*
+ * Send hello
+ */
+ return pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
+}
+
+/*
+ RFC 4601: 4.3.1. Sending Hello Messages
+
+ Thus, if a router needs to send a Join/Prune or Assert message on an
+ interface on which it has not yet sent a Hello message with the
+ currently configured IP address, then it MUST immediately send the
+ relevant Hello message without waiting for the Hello Timer to
+ expire, followed by the Join/Prune or Assert message.
+ */
+void pim_hello_restart_now(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+
+ zassert(ifp);
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ /*
+ * Reset next hello timer
+ */
+ hello_resched(ifp);
+
+ /*
+ * Immediately send hello
+ */
+ pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
+}
+
+/*
+ RFC 4601: 4.3.1. Sending Hello Messages
+
+ To allow new or rebooting routers to learn of PIM neighbors quickly,
+ when a Hello message is received from a new neighbor, or a Hello
+ message with a new GenID is received from an existing neighbor, a
+ new Hello message should be sent on this interface after a
+ randomized delay between 0 and Triggered_Hello_Delay.
+ */
+void pim_hello_restart_triggered(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ int triggered_hello_delay_msec;
+ int random_msec;
+
+ zassert(ifp);
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ triggered_hello_delay_msec = 1000 * pim_ifp->pim_triggered_hello_delay;
+
+ if (pim_ifp->t_pim_hello_timer) {
+ long remain_msec = pim_time_timer_remain_msec(pim_ifp->t_pim_hello_timer);
+ if (remain_msec <= triggered_hello_delay_msec) {
+ /* Rescheduling hello would increase the delay, then it's faster
+ to just wait for the scheduled periodic hello. */
+ return;
+ }
+
+ THREAD_OFF(pim_ifp->t_pim_hello_timer);
+ pim_ifp->t_pim_hello_timer = 0;
+ }
+ zassert(!pim_ifp->t_pim_hello_timer);
+
+ random_msec = pim_rand_next(0, triggered_hello_delay_msec);
+
+ if (PIM_DEBUG_PIM_EVENTS) {
+ zlog_debug("Scheduling %d msec triggered hello on interface %s",
+ random_msec, ifp->name);
+ }
+
+ THREAD_TIMER_MSEC_ON(master, pim_ifp->t_pim_hello_timer,
+ on_pim_hello_send,
+ ifp, random_msec);
+}
+
+int pim_sock_add(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ struct in_addr ifaddr;
+
+ pim_ifp = ifp->info;
+ zassert(pim_ifp);
+
+ if (pim_ifp->pim_sock_fd >= 0) {
+ zlog_warn("Can't recreate existing PIM socket fd=%d for interface %s",
+ pim_ifp->pim_sock_fd, ifp->name);
+ return -1;
+ }
+
+ ifaddr = pim_ifp->primary_address;
+
+ pim_ifp->pim_sock_fd = pim_sock_open(ifaddr, ifp->ifindex);
+ if (pim_ifp->pim_sock_fd < 0) {
+ zlog_warn("Could not open PIM socket on interface %s",
+ ifp->name);
+ return -2;
+ }
+
+ pim_ifp->t_pim_sock_read = 0;
+ pim_ifp->pim_sock_creation = pim_time_monotonic_sec();
+
+ pim_ifp->pim_generation_id = pim_rand() & (int64_t) 0xFFFFFFFF;
+
+ zlog_info("PIM INTERFACE UP: on interface %s",
+ ifp->name);
+
+ /*
+ * Start receiving PIM messages
+ */
+ pim_sock_read_on(ifp);
+
+ /*
+ * Start sending PIM hello's
+ */
+ pim_hello_restart_triggered(ifp);
+
+ return 0;
+}
diff --git a/pimd/pim_pim.h b/pimd/pim_pim.h
new file mode 100644
index 00000000..a2c8fa58
--- /dev/null
+++ b/pimd/pim_pim.h
@@ -0,0 +1,71 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_PIM_H
+#define PIM_PIM_H
+
+#include <zebra.h>
+
+#include "if.h"
+
+#define PIM_PIM_BUFSIZE_READ (20000)
+#define PIM_PIM_BUFSIZE_WRITE (20000)
+
+#define PIM_NEXTHOP_IFINDEX_TAB_SIZE (20)
+
+#define PIM_DEFAULT_HELLO_PERIOD (30) /* seconds, RFC 4601: 4.11 */
+#define PIM_DEFAULT_TRIGGERED_HELLO_DELAY (5) /* seconds, RFC 4601: 4.11 */
+#define PIM_DEFAULT_DR_PRIORITY (1) /* RFC 4601: 4.3.1 */
+#define PIM_DEFAULT_PROPAGATION_DELAY_MSEC (500) /* RFC 4601: 4.11. Timer Values */
+#define PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC (2500) /* RFC 4601: 4.11. Timer Values */
+#define PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION (0) /* boolean */
+#define PIM_DEFAULT_T_PERIODIC (60) /* RFC 4601: 4.11. Timer Values */
+
+#define PIM_MSG_TYPE_HELLO (0)
+#define PIM_MSG_TYPE_JOIN_PRUNE (3)
+#define PIM_MSG_TYPE_ASSERT (5)
+
+#define PIM_MSG_HDR_OFFSET_VERSION(pim_msg) (pim_msg)
+#define PIM_MSG_HDR_OFFSET_TYPE(pim_msg) (pim_msg)
+#define PIM_MSG_HDR_OFFSET_RESERVED(pim_msg) (((char *)(pim_msg)) + 1)
+#define PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) (((char *)(pim_msg)) + 2)
+
+#define PIM_MSG_HDR_GET_VERSION(pim_msg) ((*(uint8_t*) PIM_MSG_HDR_OFFSET_VERSION(pim_msg)) >> 4)
+#define PIM_MSG_HDR_GET_TYPE(pim_msg) ((*(uint8_t*) PIM_MSG_HDR_OFFSET_TYPE(pim_msg)) & 0xF)
+#define PIM_MSG_HDR_GET_CHECKSUM(pim_msg) (*(uint16_t*) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg))
+
+void pim_ifstat_reset(struct interface *ifp);
+void pim_sock_reset(struct interface *ifp);
+int pim_sock_add(struct interface *ifp);
+void pim_sock_delete(struct interface *ifp, const char *delete_message);
+void pim_hello_restart_now(struct interface *ifp);
+void pim_hello_restart_triggered(struct interface *ifp);
+
+int pim_pim_packet(struct interface *ifp, char *buf, size_t len);
+
+int pim_msg_send(int fd,
+ struct in_addr dst,
+ char *pim_msg,
+ int pim_msg_size,
+ const char *ifname);
+
+#endif /* PIM_PIM_H */
diff --git a/pimd/pim_rand.c b/pimd/pim_rand.c
new file mode 100644
index 00000000..df2a1111
--- /dev/null
+++ b/pimd/pim_rand.c
@@ -0,0 +1,60 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include "pim_rand.h"
+#include "pim_time.h"
+
+/* Quick and dirty random number generator from NUMERICAL RECIPES IN C:
+ THE ART OF SCIENTIFIC COMPUTING (ISBN 0-521-43108-5). */
+/* BEWARE: '_qseed_' is assigned! */
+#define QRANDOM(_qseed_) ((_qseed_) = (((_qseed_) * 1664525L) + 1013904223L))
+
+static long qpim_rand_seed;
+
+void pim_rand_init()
+{
+ qpim_rand_seed = pim_time_monotonic_sec() ^ getpid();
+}
+
+long pim_rand()
+{
+ return QRANDOM(qpim_rand_seed);
+}
+
+int pim_rand_next(int min, int max)
+{
+ long rand;
+
+ assert(min <= max);
+
+ /* FIXME better random generator ? */
+
+ rand = QRANDOM(qpim_rand_seed);
+ if (rand < 0)
+ rand = -rand;
+ rand = rand % (1 + max - min) + min;
+
+ assert(rand >= min);
+ assert(rand <= max);
+
+ return rand;
+}
diff --git a/pimd/pim_rand.h b/pimd/pim_rand.h
new file mode 100644
index 00000000..a1df5054
--- /dev/null
+++ b/pimd/pim_rand.h
@@ -0,0 +1,30 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_RAND_H
+#define PIM_RAND_H
+
+void pim_rand_init(void);
+long pim_rand(void);
+int pim_rand_next(int min, int max);
+
+#endif /* PIM_RAND_H */
diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c
new file mode 100644
index 00000000..7fdeecfd
--- /dev/null
+++ b/pimd/pim_rpf.c
@@ -0,0 +1,254 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+#include "memory.h"
+
+#include "pimd.h"
+#include "pim_rpf.h"
+#include "pim_pim.h"
+#include "pim_str.h"
+#include "pim_iface.h"
+#include "pim_zlookup.h"
+#include "pim_ifchannel.h"
+
+static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up);
+
+int pim_nexthop_lookup(struct pim_nexthop *nexthop,
+ struct in_addr addr)
+{
+ struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE];
+ int num_ifindex;
+ struct interface *ifp;
+ int first_ifindex;
+
+ num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab,
+ PIM_NEXTHOP_IFINDEX_TAB_SIZE,
+ addr, PIM_NEXTHOP_LOOKUP_MAX);
+ if (num_ifindex < 1) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_warn("%s %s: could not find nexthop ifindex for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ addr_str);
+ return -1;
+ }
+
+ first_ifindex = nexthop_tab[0].ifindex;
+
+ if (num_ifindex > 1) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)",
+ __FILE__, __PRETTY_FUNCTION__,
+ num_ifindex, addr_str, first_ifindex);
+ /* debug warning only, do not return */
+ }
+
+ ifp = if_lookup_by_index(first_ifindex);
+ if (!ifp) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_warn("%s %s: could not find interface for ifindex %d (address %s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ first_ifindex, addr_str);
+ return -2;
+ }
+
+ if (!ifp->info) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_warn("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)",
+ __PRETTY_FUNCTION__,
+ ifp->name, first_ifindex, addr_str);
+ /* debug warning only, do not return */
+ }
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char nexthop_str[100];
+ char addr_str[100];
+ pim_inet4_dump("<nexthop?>", nexthop_tab[0].nexthop_addr, nexthop_str, sizeof(nexthop_str));
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug("%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d",
+ __FILE__, __PRETTY_FUNCTION__,
+ nexthop_str, addr_str,
+ ifp->name, first_ifindex,
+ nexthop_tab[0].route_metric,
+ nexthop_tab[0].protocol_distance);
+ }
+
+ /* update nextop data */
+ nexthop->interface = ifp;
+ nexthop->mrib_nexthop_addr = nexthop_tab[0].nexthop_addr;
+ nexthop->mrib_metric_preference = nexthop_tab[0].protocol_distance;
+ nexthop->mrib_route_metric = nexthop_tab[0].route_metric;
+
+ return 0;
+}
+
+static int nexthop_mismatch(const struct pim_nexthop *nh1,
+ const struct pim_nexthop *nh2)
+{
+ return (nh1->interface != nh2->interface)
+ ||
+ (nh1->mrib_nexthop_addr.s_addr != nh2->mrib_nexthop_addr.s_addr)
+ ||
+ (nh1->mrib_metric_preference != nh2->mrib_metric_preference)
+ ||
+ (nh1->mrib_route_metric != nh2->mrib_route_metric);
+}
+
+enum pim_rpf_result pim_rpf_update(struct pim_upstream *up,
+ struct in_addr *old_rpf_addr)
+{
+ struct in_addr save_rpf_addr;
+ struct pim_nexthop save_nexthop;
+ struct pim_rpf *rpf = &up->rpf;
+
+ save_nexthop = rpf->source_nexthop; /* detect change in pim_nexthop */
+ save_rpf_addr = rpf->rpf_addr; /* detect change in RPF'(S,G) */
+
+ if (pim_nexthop_lookup(&rpf->source_nexthop,
+ up->source_addr)) {
+ return PIM_RPF_FAILURE;
+ }
+
+ rpf->rpf_addr = pim_rpf_find_rpf_addr(up);
+ if (PIM_INADDR_IS_ANY(rpf->rpf_addr)) {
+ /* RPF'(S,G) not found */
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ zlog_warn("%s %s: RPF'(%s,%s) not found: won't send join upstream",
+ __FILE__, __PRETTY_FUNCTION__,
+ src_str, grp_str);
+ /* warning only */
+ }
+
+ /* detect change in pim_nexthop */
+ if (nexthop_mismatch(&rpf->source_nexthop, &save_nexthop)) {
+ char src_str[100];
+ char grp_str[100];
+ char nhaddr_str[100];
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<addr?>", rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str));
+ zlog_warn("%s %s: (S,G)=(%s,%s) source nexthop now is: interface=%s address=%s pref=%d metric=%d",
+ __FILE__, __PRETTY_FUNCTION__,
+ src_str, grp_str,
+ rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>",
+ nhaddr_str,
+ rpf->source_nexthop.mrib_metric_preference,
+ rpf->source_nexthop.mrib_route_metric);
+ /* warning only */
+
+ pim_upstream_update_join_desired(up);
+ pim_upstream_update_could_assert(up);
+ pim_upstream_update_my_assert_metric(up);
+ }
+
+ /* detect change in RPF_interface(S) */
+ if (save_nexthop.interface != rpf->source_nexthop.interface) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ zlog_warn("%s %s: (S,G)=(%s,%s) RPF_interface(S) changed from %s to %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ src_str, grp_str,
+ save_nexthop.interface ? save_nexthop.interface->name : "<oldif?>",
+ rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<newif?>");
+ /* warning only */
+
+ pim_upstream_rpf_interface_changed(up, save_nexthop.interface);
+ }
+
+ /* detect change in RPF'(S,G) */
+ if (save_rpf_addr.s_addr != rpf->rpf_addr.s_addr) {
+
+ /* return old rpf to caller ? */
+ if (old_rpf_addr)
+ *old_rpf_addr = save_rpf_addr;
+
+ return PIM_RPF_CHANGED;
+ }
+
+ return PIM_RPF_OK;
+}
+
+/*
+ RFC 4601: 4.1.6. State Summarization Macros
+
+ neighbor RPF'(S,G) {
+ if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
+ return AssertWinner(S, G, RPF_interface(S) )
+ } else {
+ return NBR( RPF_interface(S), MRIB.next_hop( S ) )
+ }
+ }
+
+ RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
+ packets should be coming and to which joins should be sent on the RP
+ tree and SPT, respectively.
+*/
+static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up)
+{
+ struct pim_ifchannel *rpf_ch;
+ struct pim_neighbor *neigh;
+ struct in_addr rpf_addr;
+
+ if (!up->rpf.source_nexthop.interface) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ zlog_warn("%s: missing RPF interface for upstream (S,G)=(%s,%s)",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str);
+
+ rpf_addr.s_addr = PIM_NET_INADDR_ANY;
+ return rpf_addr;
+ }
+
+ rpf_ch = pim_ifchannel_find(up->rpf.source_nexthop.interface,
+ up->source_addr, up->group_addr);
+ if (rpf_ch) {
+ if (rpf_ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
+ return rpf_ch->ifassert_winner;
+ }
+ }
+
+ /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
+
+ neigh = pim_if_find_neighbor(up->rpf.source_nexthop.interface,
+ up->rpf.source_nexthop.mrib_nexthop_addr);
+ if (neigh)
+ rpf_addr = neigh->source_addr;
+ else
+ rpf_addr.s_addr = PIM_NET_INADDR_ANY;
+
+ return rpf_addr;
+}
diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h
new file mode 100644
index 00000000..078e89f3
--- /dev/null
+++ b/pimd/pim_rpf.h
@@ -0,0 +1,36 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_RPF_H
+#define PIM_RPF_H
+
+#include <zebra.h>
+
+#include "pim_upstream.h"
+#include "pim_neighbor.h"
+
+int pim_nexthop_lookup(struct pim_nexthop *nexthop,
+ struct in_addr addr);
+enum pim_rpf_result pim_rpf_update(struct pim_upstream *up,
+ struct in_addr *old_rpf_addr);
+
+#endif /* PIM_RPF_H */
diff --git a/pimd/pim_signals.c b/pimd/pim_signals.c
new file mode 100644
index 00000000..1b146f6f
--- /dev/null
+++ b/pimd/pim_signals.c
@@ -0,0 +1,85 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <signal.h>
+
+#include <zebra.h>
+#include "sigevent.h"
+#include "log.h"
+
+#include "pim_signals.h"
+#include "pimd.h"
+
+/*
+ * Signal handlers
+ */
+
+static void pim_sighup()
+{
+ zlog_debug ("SIGHUP received, ignoring");
+}
+
+static void pim_sigint()
+{
+ zlog_notice("Terminating on signal SIGINT");
+ pim_terminate();
+ exit(1);
+}
+
+static void pim_sigterm()
+{
+ zlog_notice("Terminating on signal SIGTERM");
+ pim_terminate();
+ exit(1);
+}
+
+static void pim_sigusr1()
+{
+ zlog_debug ("SIGUSR1 received");
+ zlog_rotate (NULL);
+}
+
+struct quagga_signal_t pimd_signals[] =
+{
+ {
+ .signal = SIGHUP,
+ .handler = &pim_sighup,
+ },
+ {
+ .signal = SIGUSR1,
+ .handler = &pim_sigusr1,
+ },
+ {
+ .signal = SIGINT,
+ .handler = &pim_sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &pim_sigterm,
+ },
+};
+
+void pim_signals_init()
+{
+ signal_init(master, Q_SIGC(pimd_signals), pimd_signals);
+}
+
diff --git a/pimd/pim_signals.h b/pimd/pim_signals.h
new file mode 100644
index 00000000..62523c03
--- /dev/null
+++ b/pimd/pim_signals.h
@@ -0,0 +1,28 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_SIGNALS_H
+#define PIM_SIGNALS_H
+
+void pim_signals_init(void);
+
+#endif /* PIM_SIGNALS_H */
diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c
new file mode 100644
index 00000000..2e786054
--- /dev/null
+++ b/pimd/pim_sock.c
@@ -0,0 +1,389 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include "pim_mroute.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/igmp.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <errno.h>
+
+#include <zebra.h>
+#include "log.h"
+#include "privs.h"
+
+#include "pimd.h"
+#include "pim_sock.h"
+#include "pim_str.h"
+#include "pim_igmp_join.h"
+
+/* GLOBAL VARS */
+extern struct zebra_privs_t pimd_privs;
+
+int pim_socket_raw(int protocol)
+{
+ int fd;
+
+ if ( pimd_privs.change (ZPRIVS_RAISE) )
+ zlog_err ("pim_sockek_raw: could not raise privs, %s",
+ safe_strerror (errno) );
+
+ fd = socket(AF_INET, SOCK_RAW, protocol);
+
+ if ( pimd_privs.change (ZPRIVS_LOWER) )
+ zlog_err ("pim_socket_raw: could not lower privs, %s",
+ safe_strerror (errno) );
+
+ if (fd < 0) {
+ zlog_warn("Could not create raw socket: errno=%d: %s",
+ errno, safe_strerror(errno));
+ return PIM_SOCK_ERR_SOCKET;
+ }
+
+ return fd;
+}
+
+int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop)
+{
+ int fd;
+
+ fd = pim_socket_raw(protocol);
+ if (fd < 0) {
+ zlog_warn("Could not create multicast socket: errno=%d: %s",
+ errno, safe_strerror(errno));
+ return PIM_SOCK_ERR_SOCKET;
+ }
+
+ /* Needed to obtain destination address from recvmsg() */
+ {
+#if defined(HAVE_IP_PKTINFO)
+ /* Linux IP_PKTINFO */
+ int opt = 1;
+ if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) {
+ zlog_warn("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
+ fd, errno, safe_strerror(errno));
+ }
+#elif defined(HAVE_IP_RECVDSTADDR)
+ /* BSD IP_RECVDSTADDR */
+ int opt = 1;
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
+ zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
+ fd, errno, safe_strerror(errno));
+ }
+#else
+ zlog_err("%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
+ __FILE__, __PRETTY_FUNCTION__);
+ close(fd);
+ return PIM_SOCK_ERR_DSTADDR;
+#endif
+ }
+
+
+ /* Set router alert (RFC 2113) for all IGMP messages (RFC 3376 4. Message Formats)*/
+ if (protocol == IPPROTO_IGMP) {
+ char ra[4];
+ ra[0] = 148;
+ ra[1] = 4;
+ ra[2] = 0;
+ ra[3] = 0;
+ if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) {
+ zlog_warn("Could not set Router Alert Option on socket fd=%d: errno=%d: %s",
+ fd, errno, safe_strerror(errno));
+ close(fd);
+ return PIM_SOCK_ERR_RA;
+ }
+ }
+
+ {
+ int reuse = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (void *) &reuse, sizeof(reuse))) {
+ zlog_warn("Could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
+ fd, errno, safe_strerror(errno));
+ close(fd);
+ return PIM_SOCK_ERR_REUSE;
+ }
+ }
+
+ {
+ const int MTTL = 1;
+ int ttl = MTTL;
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
+ (void *) &ttl, sizeof(ttl))) {
+ zlog_warn("Could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
+ MTTL, fd, errno, safe_strerror(errno));
+ close(fd);
+ return PIM_SOCK_ERR_TTL;
+ }
+ }
+
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (void *) &loop, sizeof(loop))) {
+ zlog_warn("Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
+ loop ? "enable" : "disable",
+ fd, errno, safe_strerror(errno));
+ close(fd);
+ return PIM_SOCK_ERR_LOOP;
+ }
+
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
+ (void *) &ifaddr, sizeof(ifaddr))) {
+ zlog_warn("Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
+ fd, errno, safe_strerror(errno));
+ close(fd);
+ return PIM_SOCK_ERR_IFACE;
+ }
+
+ {
+ long flags;
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0) {
+ zlog_warn("Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
+ fd, errno, safe_strerror(errno));
+ close(fd);
+ return PIM_SOCK_ERR_NONBLOCK_GETFL;
+ }
+
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
+ zlog_warn("Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
+ fd, errno, safe_strerror(errno));
+ close(fd);
+ return PIM_SOCK_ERR_NONBLOCK_SETFL;
+ }
+ }
+
+ return fd;
+}
+
+int pim_socket_join(int fd, struct in_addr group,
+ struct in_addr ifaddr, int ifindex)
+{
+ int ret;
+
+#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+ struct ip_mreqn opt;
+#else
+ struct ip_mreq opt;
+#endif
+
+ opt.imr_multiaddr = group;
+
+#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+ opt.imr_address = ifaddr;
+ opt.imr_ifindex = ifindex;
+#else
+ opt.imr_interface = ifaddr;
+#endif
+
+ ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));
+ if (ret) {
+ char group_str[100];
+ char ifaddr_str[100];
+ if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str)))
+ sprintf(group_str, "<group?>");
+ if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str)))
+ sprintf(ifaddr_str, "<ifaddr?>");
+
+ zlog_err("Failure socket joining fd=%d group %s on interface address %s: errno=%d: %s",
+ fd, group_str, ifaddr_str, errno, safe_strerror(errno));
+ return ret;
+ }
+
+ if (PIM_DEBUG_TRACE) {
+ char group_str[100];
+ char ifaddr_str[100];
+ if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str)))
+ sprintf(group_str, "<group?>");
+ if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str)))
+ sprintf(ifaddr_str, "<ifaddr?>");
+
+ zlog_debug("Socket fd=%d joined group %s on interface address %s",
+ fd, group_str, ifaddr_str);
+ }
+
+ return ret;
+}
+
+int pim_socket_join_source(int fd, int ifindex,
+ struct in_addr group_addr,
+ struct in_addr source_addr,
+ const char *ifname)
+{
+ if (pim_igmp_join_source(fd, ifindex, group_addr, source_addr)) {
+ int e = errno;
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s",
+ __PRETTY_FUNCTION__,
+ fd, group_str, source_str, ifindex, ifname,
+ e, safe_strerror(e));
+ return -1;
+ }
+
+ return 0;
+}
+
+int pim_socket_recvfromto(int fd, char *buf, size_t len,
+ struct sockaddr_in *from, socklen_t *fromlen,
+ struct sockaddr_in *to, socklen_t *tolen,
+ int *ifindex)
+{
+ struct msghdr msgh;
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ char cbuf[1000];
+ int err;
+
+ /*
+ * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
+ * Use getsockname() to get sin_port.
+ */
+ if (to) {
+ struct sockaddr_in si;
+ socklen_t si_len = sizeof(si);
+
+ ((struct sockaddr_in *) to)->sin_family = AF_INET;
+
+ if (pim_socket_getsockname(fd, (struct sockaddr *) &si, &si_len)) {
+ ((struct sockaddr_in *) to)->sin_port = ntohs(0);
+ ((struct sockaddr_in *) to)->sin_addr.s_addr = ntohl(0);
+ }
+ else {
+ ((struct sockaddr_in *) to)->sin_port = si.sin_port;
+ ((struct sockaddr_in *) to)->sin_addr = si.sin_addr;
+ }
+
+ if (tolen)
+ *tolen = sizeof(si);
+ }
+
+ memset(&msgh, 0, sizeof(struct msghdr));
+ iov.iov_base = buf;
+ iov.iov_len = len;
+ msgh.msg_control = cbuf;
+ msgh.msg_controllen = sizeof(cbuf);
+ msgh.msg_name = from;
+ msgh.msg_namelen = fromlen ? *fromlen : 0;
+ msgh.msg_iov = &iov;
+ msgh.msg_iovlen = 1;
+ msgh.msg_flags = 0;
+
+ err = recvmsg(fd, &msgh, 0);
+ if (err < 0)
+ return err;
+
+ if (fromlen)
+ *fromlen = msgh.msg_namelen;
+
+ for (cmsg = CMSG_FIRSTHDR(&msgh);
+ cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
+
+#ifdef HAVE_IP_PKTINFO
+ if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
+ struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
+ if (to)
+ ((struct sockaddr_in *) to)->sin_addr = i->ipi_addr;
+ if (tolen)
+ *tolen = sizeof(struct sockaddr_in);
+ if (ifindex)
+ *ifindex = i->ipi_ifindex;
+
+ if (to && PIM_DEBUG_PACKETS) {
+ char to_str[100];
+ pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str));
+ zlog_debug("%s: HAVE_IP_PKTINFO to=%s,%d",
+ __PRETTY_FUNCTION__,
+ to_str, ntohs(to->sin_port));
+ }
+
+ break;
+ }
+#endif
+
+#ifdef HAVE_IP_RECVDSTADDR
+ if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
+ struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
+ if (to)
+ ((struct sockaddr_in *) to)->sin_addr = *i;
+ if (tolen)
+ *tolen = sizeof(struct sockaddr_in);
+
+ if (to && PIM_DEBUG_PACKETS) {
+ char to_str[100];
+ pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str));
+ zlog_debug("%s: HAVE_IP_RECVDSTADDR to=%s,%d",
+ __PRETTY_FUNCTION__,
+ to_str, ntohs(to->sin_port));
+ }
+
+ break;
+ }
+#endif
+
+#if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
+ if (cmsg->cmsg_type == IP_RECVIF)
+ if (ifindex)
+ *ifindex = CMSG_IFINDEX(cmsg);
+#endif
+
+ } /* for (cmsg) */
+
+ return err; /* len */
+}
+
+int pim_socket_mcastloop_get(int fd)
+{
+ int loop;
+ socklen_t loop_len = sizeof(loop);
+
+ if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ &loop, &loop_len)) {
+ int e = errno;
+ zlog_warn("Could not get Multicast Loopback Option on socket fd=%d: errno=%d: %s",
+ fd, errno, safe_strerror(errno));
+ errno = e;
+ return PIM_SOCK_ERR_LOOP;
+ }
+
+ return loop;
+}
+
+int pim_socket_getsockname(int fd, struct sockaddr *name, int *namelen)
+{
+ if (getsockname(fd, name, namelen)) {
+ int e = errno;
+ zlog_warn("Could not get Socket Name for socket fd=%d: errno=%d: %s",
+ fd, errno, safe_strerror(errno));
+ errno = e;
+ return PIM_SOCK_ERR_NAME;
+ }
+
+ return PIM_SOCK_ERR_NONE;
+}
diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h
new file mode 100644
index 00000000..3f026dcd
--- /dev/null
+++ b/pimd/pim_sock.h
@@ -0,0 +1,57 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_SOCK_H
+#define PIM_SOCK_H
+
+#include <netinet/in.h>
+
+#define PIM_SOCK_ERR_NONE (0) /* No error */
+#define PIM_SOCK_ERR_SOCKET (-1) /* socket() */
+#define PIM_SOCK_ERR_RA (-2) /* Router Alert option */
+#define PIM_SOCK_ERR_REUSE (-3) /* Reuse option */
+#define PIM_SOCK_ERR_TTL (-4) /* TTL option */
+#define PIM_SOCK_ERR_LOOP (-5) /* Loopback option */
+#define PIM_SOCK_ERR_IFACE (-6) /* Outgoing interface option */
+#define PIM_SOCK_ERR_DSTADDR (-7) /* Outgoing interface option */
+#define PIM_SOCK_ERR_NONBLOCK_GETFL (-8) /* Get O_NONBLOCK */
+#define PIM_SOCK_ERR_NONBLOCK_SETFL (-9) /* Set O_NONBLOCK */
+#define PIM_SOCK_ERR_NAME (-10) /* Socket name (getsockname) */
+
+int pim_socket_raw(int protocol);
+int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop);
+int pim_socket_join(int fd, struct in_addr group,
+ struct in_addr ifaddr, int ifindex);
+int pim_socket_join_source(int fd, int ifindex,
+ struct in_addr group_addr,
+ struct in_addr source_addr,
+ const char *ifname);
+int pim_socket_recvfromto(int fd, char *buf, size_t len,
+ struct sockaddr_in *from, socklen_t *fromlen,
+ struct sockaddr_in *to, socklen_t *tolen,
+ int *ifindex);
+
+int pim_socket_mcastloop_get(int fd);
+
+int pim_socket_getsockname(int fd, struct sockaddr *name, int *namelen);
+
+#endif /* PIM_SOCK_H */
diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c
new file mode 100644
index 00000000..6422cf62
--- /dev/null
+++ b/pimd/pim_ssmpingd.c
@@ -0,0 +1,448 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "if.h"
+#include "log.h"
+#include "memory.h"
+
+#include "pim_ssmpingd.h"
+#include "pim_time.h"
+#include "pim_sock.h"
+#include "pim_str.h"
+#include "pimd.h"
+
+static const char * const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234";
+
+enum {
+ PIM_SSMPINGD_REQUEST = 'Q',
+ PIM_SSMPINGD_REPLY = 'A'
+};
+
+static void ssmpingd_read_on(struct ssmpingd_sock *ss);
+
+void pim_ssmpingd_init()
+{
+ int result;
+
+ zassert(!qpim_ssmpingd_list);
+
+ result = inet_pton(AF_INET, PIM_SSMPINGD_REPLY_GROUP, &qpim_ssmpingd_group_addr);
+
+ zassert(result > 0);
+}
+
+void pim_ssmpingd_destroy()
+{
+ if (qpim_ssmpingd_list) {
+ list_free(qpim_ssmpingd_list);
+ qpim_ssmpingd_list = 0;
+ }
+}
+
+static struct ssmpingd_sock *ssmpingd_find(struct in_addr source_addr)
+{
+ struct listnode *node;
+ struct ssmpingd_sock *ss;
+
+ if (!qpim_ssmpingd_list)
+ return 0;
+
+ for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss))
+ if (source_addr.s_addr == ss->source_addr.s_addr)
+ return ss;
+
+ return 0;
+}
+
+static void ssmpingd_free(struct ssmpingd_sock *ss)
+{
+ XFREE(MTYPE_PIM_SSMPINGD, ss);
+}
+
+static int ssmpingd_socket(struct in_addr addr, int port, int mttl)
+{
+ struct sockaddr_in sockaddr;
+ int fd;
+
+ fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (fd < 0) {
+ zlog_err("%s: could not create socket: errno=%d: %s",
+ __PRETTY_FUNCTION__, errno, safe_strerror(errno));
+ return -1;
+ }
+
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_addr = addr;
+ sockaddr.sin_port = htons(port);
+
+ if (bind(fd, &sockaddr, sizeof(sockaddr))) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%d) failure: errno=%d: %s",
+ __PRETTY_FUNCTION__,
+ fd, addr_str, port, sizeof(sockaddr),
+ errno, safe_strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ /* Needed to obtain destination address from recvmsg() */
+ {
+#if defined(HAVE_IP_PKTINFO)
+ /* Linux IP_PKTINFO */
+ int opt = 1;
+ if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) {
+ zlog_warn("%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
+ __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+ }
+#elif defined(HAVE_IP_RECVDSTADDR)
+ /* BSD IP_RECVDSTADDR */
+ int opt = 1;
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
+ zlog_warn("%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
+ __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+ }
+#else
+ zlog_err("%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
+ __FILE__, __PRETTY_FUNCTION__);
+ close(fd);
+ return -1;
+#endif
+ }
+
+ {
+ int reuse = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (void *) &reuse, sizeof(reuse))) {
+ zlog_warn("%s: could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
+ __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+ close(fd);
+ return -1;
+ }
+ }
+
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
+ (void *) &mttl, sizeof(mttl))) {
+ zlog_warn("%s: could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
+ __PRETTY_FUNCTION__, mttl, fd, errno, safe_strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ {
+ int loop = 0;
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (void *) &loop, sizeof(loop))) {
+ zlog_warn("%s: could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
+ __PRETTY_FUNCTION__,
+ loop ? "enable" : "disable",
+ fd, errno, safe_strerror(errno));
+ close(fd);
+ return PIM_SOCK_ERR_LOOP;
+ }
+ }
+
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
+ (void *) &addr, sizeof(addr))) {
+ zlog_warn("%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
+ __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ {
+ long flags;
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0) {
+ zlog_warn("%s: could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
+ __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
+ zlog_warn("%s: could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
+ __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+ close(fd);
+ return -1;
+ }
+ }
+
+ return fd;
+}
+
+static void ssmpingd_delete(struct ssmpingd_sock *ss)
+{
+ zassert(ss);
+ zassert(qpim_ssmpingd_list);
+
+ THREAD_OFF(ss->t_sock_read);
+
+ if (close(ss->sock_fd)) {
+ int e = errno;
+ char source_str[100];
+ pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s",
+ __PRETTY_FUNCTION__,
+ ss->sock_fd, source_str, e, safe_strerror(e));
+ /* warning only */
+ }
+
+ listnode_delete(qpim_ssmpingd_list, ss);
+ ssmpingd_free(ss);
+}
+
+static void ssmpingd_sendto(struct ssmpingd_sock *ss,
+ const char *buf,
+ int len,
+ struct sockaddr_in to)
+{
+ socklen_t tolen = sizeof(to);
+ int sent;
+
+ sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT, &to, tolen);
+ if (sent != len) {
+ int e = errno;
+ char to_str[100];
+ pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
+ if (sent < 0) {
+ zlog_warn("%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s",
+ __PRETTY_FUNCTION__,
+ to_str, ntohs(to.sin_port), ss->sock_fd, len,
+ e, safe_strerror(e));
+ }
+ else {
+ zlog_warn("%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d",
+ __PRETTY_FUNCTION__,
+ to_str, ntohs(to.sin_port), ss->sock_fd,
+ len, sent);
+ }
+ }
+}
+
+static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
+{
+ struct interface *ifp;
+ struct sockaddr_in from;
+ struct sockaddr_in to;
+ socklen_t fromlen = sizeof(from);
+ socklen_t tolen = sizeof(to);
+ int ifindex = -1;
+ char buf[1000];
+ int len;
+
+ ++ss->requests;
+
+ len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf),
+ &from, &fromlen,
+ &to, &tolen,
+ &ifindex);
+ if (len < 0) {
+ char source_str[100];
+ pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s",
+ __PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, safe_strerror(errno));
+ return -1;
+ }
+
+ ifp = if_lookup_by_index(ifindex);
+
+ if (buf[0] != PIM_SSMPINGD_REQUEST) {
+ char source_str[100];
+ char from_str[100];
+ char to_str[100];
+ pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
+ pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
+ pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
+ zlog_warn("%s: bad ssmping type=%d from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s",
+ __PRETTY_FUNCTION__,
+ buf[0],
+ from_str, ntohs(from.sin_port),
+ to_str, ntohs(to.sin_port),
+ ifp ? ifp->name : "<iface?>",
+ ifindex, ss->sock_fd,
+ source_str);
+ return 0;
+ }
+
+ if (PIM_DEBUG_SSMPINGD) {
+ char source_str[100];
+ char from_str[100];
+ char to_str[100];
+ pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
+ pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
+ pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
+ zlog_debug("%s: recv ssmping from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s",
+ __PRETTY_FUNCTION__,
+ from_str, ntohs(from.sin_port),
+ to_str, ntohs(to.sin_port),
+ ifp ? ifp->name : "<iface?>",
+ ifindex, ss->sock_fd,
+ source_str);
+ }
+
+ buf[0] = PIM_SSMPINGD_REPLY;
+
+ /* unicast reply */
+ ssmpingd_sendto(ss, buf, len, from);
+
+ /* multicast reply */
+ from.sin_addr = qpim_ssmpingd_group_addr;
+ ssmpingd_sendto(ss, buf, len, from);
+
+ return 0;
+}
+
+static int ssmpingd_sock_read(struct thread *t)
+{
+ struct ssmpingd_sock *ss;
+ int sock_fd;
+ int result;
+
+ zassert(t);
+
+ ss = THREAD_ARG(t);
+ zassert(ss);
+
+ sock_fd = THREAD_FD(t);
+ zassert(sock_fd == ss->sock_fd);
+
+ result = ssmpingd_read_msg(ss);
+
+ /* Keep reading */
+ ss->t_sock_read = 0;
+ ssmpingd_read_on(ss);
+
+ return result;
+}
+
+static void ssmpingd_read_on(struct ssmpingd_sock *ss)
+{
+ zassert(!ss->t_sock_read);
+ THREAD_READ_ON(master, ss->t_sock_read,
+ ssmpingd_sock_read, ss, ss->sock_fd);
+}
+
+static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr)
+{
+ struct ssmpingd_sock *ss;
+ int sock_fd;
+
+ if (!qpim_ssmpingd_list) {
+ qpim_ssmpingd_list = list_new();
+ if (!qpim_ssmpingd_list) {
+ zlog_err("%s %s: failure: qpim_ssmpingd_list=list_new()",
+ __FILE__, __PRETTY_FUNCTION__);
+ return 0;
+ }
+ qpim_ssmpingd_list->del = (void (*)(void *)) ssmpingd_free;
+ }
+
+ sock_fd = ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64);
+ if (sock_fd < 0) {
+ char source_str[100];
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: ssmpingd_socket() failure for source %s",
+ __PRETTY_FUNCTION__, source_str);
+ return 0;
+ }
+
+ ss = XMALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
+ if (!ss) {
+ char source_str[100];
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_err("%s: XMALLOC(%d) failure for ssmpingd source %s",
+ __PRETTY_FUNCTION__,
+ sizeof(*ss), source_str);
+ close(sock_fd);
+ return 0;
+ }
+
+ ss->sock_fd = sock_fd;
+ ss->t_sock_read = 0;
+ ss->source_addr = source_addr;
+ ss->creation = pim_time_monotonic_sec();
+ ss->requests = 0;
+
+ listnode_add(qpim_ssmpingd_list, ss);
+
+ ssmpingd_read_on(ss);
+
+ return ss;
+}
+
+int pim_ssmpingd_start(struct in_addr source_addr)
+{
+ struct ssmpingd_sock *ss;
+
+ ss = ssmpingd_find(source_addr);
+ if (ss) {
+ /* silently ignore request to recreate entry */
+ return 0;
+ }
+
+ {
+ char source_str[100];
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_info("%s: starting ssmpingd for source %s",
+ __PRETTY_FUNCTION__, source_str);
+ }
+
+ ss = ssmpingd_new(source_addr);
+ if (!ss) {
+ char source_str[100];
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: ssmpingd_new() failure for source %s",
+ __PRETTY_FUNCTION__, source_str);
+ return -1;
+ }
+
+ return 0;
+}
+
+int pim_ssmpingd_stop(struct in_addr source_addr)
+{
+ struct ssmpingd_sock *ss;
+
+ ss = ssmpingd_find(source_addr);
+ if (!ss) {
+ char source_str[100];
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: could not find ssmpingd for source %s",
+ __PRETTY_FUNCTION__, source_str);
+ return -1;
+ }
+
+ {
+ char source_str[100];
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_info("%s: stopping ssmpingd for source %s",
+ __PRETTY_FUNCTION__, source_str);
+ }
+
+ ssmpingd_delete(ss);
+
+ return 0;
+}
diff --git a/pimd/pim_ssmpingd.h b/pimd/pim_ssmpingd.h
new file mode 100644
index 00000000..4bef20b2
--- /dev/null
+++ b/pimd/pim_ssmpingd.h
@@ -0,0 +1,45 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_SSMPINGD_H
+#define PIM_SSMPINGD_H
+
+#include <zebra.h>
+
+#include "if.h"
+
+#include "pim_iface.h"
+
+struct ssmpingd_sock {
+ int sock_fd; /* socket */
+ struct thread *t_sock_read; /* thread for reading socket */
+ struct in_addr source_addr; /* source address */
+ int64_t creation; /* timestamp of socket creation */
+ int64_t requests; /* counter */
+};
+
+void pim_ssmpingd_init(void);
+void pim_ssmpingd_destroy(void);
+int pim_ssmpingd_start(struct in_addr source_addr);
+int pim_ssmpingd_stop(struct in_addr source_addr);
+
+#endif /* PIM_SSMPINGD_H */
diff --git a/pimd/pim_str.c b/pimd/pim_str.c
new file mode 100644
index 00000000..af5a184d
--- /dev/null
+++ b/pimd/pim_str.c
@@ -0,0 +1,46 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include <zebra.h>
+
+#include "log.h"
+
+#include "pim_str.h"
+
+void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size)
+{
+ int save_errno = errno;
+
+ if (!inet_ntop(AF_INET, &addr, buf, buf_size)) {
+ int e = errno;
+ zlog_warn("pim_inet4_dump: inet_ntop(AF_INET,buf_size=%d): errno=%d: %s",
+ buf_size, e, safe_strerror(e));
+ if (onfail)
+ snprintf(buf, buf_size, "%s", onfail);
+ }
+
+ errno = save_errno;
+}
diff --git a/pimd/pim_str.h b/pimd/pim_str.h
new file mode 100644
index 00000000..925f17f7
--- /dev/null
+++ b/pimd/pim_str.h
@@ -0,0 +1,32 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_STR_H
+#define PIM_STR_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size);
+
+#endif
diff --git a/pimd/pim_time.c b/pimd/pim_time.c
new file mode 100644
index 00000000..63861e5c
--- /dev/null
+++ b/pimd/pim_time.c
@@ -0,0 +1,151 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <zebra.h>
+#include "log.h"
+#include "thread.h"
+
+#include "pim_time.h"
+
+/*
+ see man clock_gettime
+ */
+static int pim_gettime(clockid_t clk_id, struct timeval *tv)
+{
+ return quagga_gettime(clk_id, tv);
+}
+
+/*
+ pim_time_monotonic_sec():
+ number of seconds since some unspecified starting point
+*/
+int64_t pim_time_monotonic_sec()
+{
+ struct timeval now_tv;
+
+ if (pim_gettime(CLOCK_MONOTONIC, &now_tv)) {
+ zlog_err("%s: gettime(CLOCK_MONOTONIC) failure: errno=%d: %s",
+ __PRETTY_FUNCTION__,
+ errno, safe_strerror(errno));
+ return -1;
+ }
+
+ return now_tv.tv_sec;
+}
+
+/*
+ pim_time_monotonic_dsec():
+ number of deciseconds since some unspecified starting point
+*/
+int64_t pim_time_monotonic_dsec()
+{
+ struct timeval now_tv;
+ int64_t now_dsec;
+
+ if (pim_gettime(CLOCK_MONOTONIC, &now_tv)) {
+ zlog_err("%s: gettime(CLOCK_MONOTONIC) failure: errno=%d: %s",
+ __PRETTY_FUNCTION__,
+ errno, safe_strerror(errno));
+ return -1;
+ }
+
+ now_dsec = ((int64_t) now_tv.tv_sec) * 10 + ((int64_t) now_tv.tv_usec) / 100000;
+
+ return now_dsec;
+}
+
+int pim_time_mmss(char *buf, int buf_size, long sec)
+{
+ long mm;
+ int wr;
+
+ zassert(buf_size >= 5);
+
+ mm = sec / 60;
+ sec %= 60;
+
+ wr = snprintf(buf, buf_size, "%02ld:%02ld", mm, sec);
+
+ return wr != 8;
+}
+
+static int pim_time_hhmmss(char *buf, int buf_size, long sec)
+{
+ long hh;
+ long mm;
+ int wr;
+
+ zassert(buf_size >= 8);
+
+ hh = sec / 3600;
+ sec %= 3600;
+ mm = sec / 60;
+ sec %= 60;
+
+ wr = snprintf(buf, buf_size, "%02ld:%02ld:%02ld", hh, mm, sec);
+
+ return wr != 8;
+}
+
+void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t_timer)
+{
+ if (t_timer) {
+ pim_time_mmss(buf, buf_size,
+ thread_timer_remain_second(t_timer));
+ }
+ else {
+ snprintf(buf, buf_size, "--:--");
+ }
+}
+
+void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t_timer)
+{
+ if (t_timer) {
+ pim_time_hhmmss(buf, buf_size,
+ thread_timer_remain_second(t_timer));
+ }
+ else {
+ snprintf(buf, buf_size, "--:--:--");
+ }
+}
+
+void pim_time_uptime(char *buf, int buf_size, int64_t uptime_sec)
+{
+ zassert(buf_size >= 8);
+
+ pim_time_hhmmss(buf, buf_size, uptime_sec);
+}
+
+long pim_time_timer_remain_msec(struct thread *t_timer)
+{
+ /* FIXME: Actually fetch msec resolution from thread */
+
+ /* no timer thread running means timer has expired: return 0 */
+
+ return t_timer ?
+ 1000 * thread_timer_remain_second(t_timer) :
+ 0;
+}
diff --git a/pimd/pim_time.h b/pimd/pim_time.h
new file mode 100644
index 00000000..379eb6cf
--- /dev/null
+++ b/pimd/pim_time.h
@@ -0,0 +1,39 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_TIME_H
+#define PIM_TIME_H
+
+#include <stdint.h>
+
+#include <zebra.h>
+#include "thread.h"
+
+int64_t pim_time_monotonic_sec(void);
+int64_t pim_time_monotonic_dsec(void);
+int pim_time_mmss(char *buf, int buf_size, long sec);
+void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t);
+void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t);
+void pim_time_uptime(char *buf, int buf_size, int64_t uptime_sec);
+long pim_time_timer_remain_msec(struct thread *t_timer);
+
+#endif /* PIM_TIME_H */
diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c
new file mode 100644
index 00000000..16b7e93e
--- /dev/null
+++ b/pimd/pim_tlv.c
@@ -0,0 +1,726 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+
+#include "pimd.h"
+#include "pim_int.h"
+#include "pim_tlv.h"
+#include "pim_str.h"
+#include "pim_msg.h"
+
+char *pim_tlv_append_uint16(uint8_t *buf,
+ const uint8_t *buf_pastend,
+ uint16_t option_type,
+ uint16_t option_value)
+{
+ uint16_t option_len = 2;
+
+ if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) {
+ zlog_warn("%s: buffer overflow: left=%d needed=%d",
+ __PRETTY_FUNCTION__,
+ buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len));
+ return 0;
+ }
+
+ *(uint16_t *) buf = htons(option_type);
+ buf += 2;
+ *(uint16_t *) buf = htons(option_len);
+ buf += 2;
+ *(uint16_t *) buf = htons(option_value);
+ buf += option_len;
+
+ return buf;
+}
+
+char *pim_tlv_append_2uint16(uint8_t *buf,
+ const uint8_t *buf_pastend,
+ uint16_t option_type,
+ uint16_t option_value1,
+ uint16_t option_value2)
+{
+ uint16_t option_len = 4;
+
+ if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) {
+ zlog_warn("%s: buffer overflow: left=%d needed=%d",
+ __PRETTY_FUNCTION__,
+ buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len));
+ return 0;
+ }
+
+ *(uint16_t *) buf = htons(option_type);
+ buf += 2;
+ *(uint16_t *) buf = htons(option_len);
+ buf += 2;
+ *(uint16_t *) buf = htons(option_value1);
+ buf += 2;
+ *(uint16_t *) buf = htons(option_value2);
+ buf += 2;
+
+ return buf;
+}
+
+char *pim_tlv_append_uint32(uint8_t *buf,
+ const uint8_t *buf_pastend,
+ uint16_t option_type,
+ uint32_t option_value)
+{
+ uint16_t option_len = 4;
+
+ if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) {
+ zlog_warn("%s: buffer overflow: left=%d needed=%d",
+ __PRETTY_FUNCTION__,
+ buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len));
+ return 0;
+ }
+
+ *(uint16_t *) buf = htons(option_type);
+ buf += 2;
+ *(uint16_t *) buf = htons(option_len);
+ buf += 2;
+ pim_write_uint32(buf, option_value);
+ buf += option_len;
+
+ return buf;
+}
+
+#define ucast_ipv4_encoding_len (2 + sizeof(struct in_addr))
+
+char *pim_tlv_append_addrlist_ucast(uint8_t *buf,
+ const uint8_t *buf_pastend,
+ struct list *ifconnected)
+{
+ struct listnode *node;
+ uint16_t option_len = 0;
+
+ uint8_t *curr;
+
+ node = listhead(ifconnected);
+
+ /* Empty address list ? */
+ if (!node) {
+ return buf;
+ }
+
+ /* Skip first address (primary) */
+ node = listnextnode(node);
+
+ /* Scan secondary address list */
+ curr = buf + 4; /* skip T and L */
+ for (; node; node = listnextnode(node)) {
+ struct connected *ifc = listgetdata(node);
+ struct prefix *p = ifc->address;
+
+ if (p->family != AF_INET)
+ continue;
+
+ if ((curr + ucast_ipv4_encoding_len) > buf_pastend) {
+ zlog_warn("%s: buffer overflow: left=%d needed=%d",
+ __PRETTY_FUNCTION__,
+ buf_pastend - curr, ucast_ipv4_encoding_len);
+ return 0;
+ }
+
+ /* Write encoded unicast IPv4 address */
+ *(uint8_t *) curr = PIM_MSG_ADDRESS_FAMILY_IPV4; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
+ ++curr;
+ *(uint8_t *) curr = 0; /* ucast IPv4 native encoding type (RFC 4601: 4.9.1) */
+ ++curr;
+ *(struct in_addr *) curr = p->u.prefix4;
+ curr += sizeof(struct in_addr);
+
+ option_len += ucast_ipv4_encoding_len;
+ }
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ zlog_warn("%s: number of encoded secondary unicast IPv4 addresses: %d",
+ __PRETTY_FUNCTION__,
+ option_len / ucast_ipv4_encoding_len);
+ }
+
+ if (option_len < 1) {
+ /* Empty secondary unicast IPv4 address list */
+ return buf;
+ }
+
+ /*
+ * Write T and L
+ */
+ *(uint16_t *) buf = htons(PIM_MSG_OPTION_TYPE_ADDRESS_LIST);
+ *(uint16_t *) (buf + 2) = htons(option_len);
+
+ return curr;
+}
+
+static int check_tlv_length(const char *label, const char *tlv_name,
+ const char *ifname, struct in_addr src_addr,
+ int correct_len, int option_len)
+{
+ if (option_len != correct_len) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: PIM hello %s TLV with incorrect value size=%d correct=%d from %s on interface %s",
+ label, tlv_name,
+ option_len, correct_len,
+ src_str, ifname);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void check_tlv_redefinition_uint16(const char *label, const char *tlv_name,
+ const char *ifname, struct in_addr src_addr,
+ pim_hello_options options,
+ pim_hello_options opt_mask,
+ uint16_t new, uint16_t old)
+{
+ if (PIM_OPTION_IS_SET(options, opt_mask)) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s",
+ label, tlv_name,
+ new, old,
+ src_str, ifname);
+ }
+}
+
+static void check_tlv_redefinition_uint32(const char *label, const char *tlv_name,
+ const char *ifname, struct in_addr src_addr,
+ pim_hello_options options,
+ pim_hello_options opt_mask,
+ uint32_t new, uint32_t old)
+{
+ if (PIM_OPTION_IS_SET(options, opt_mask)) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s",
+ label, tlv_name,
+ new, old,
+ src_str, ifname);
+ }
+}
+
+static void check_tlv_redefinition_uint32_hex(const char *label, const char *tlv_name,
+ const char *ifname, struct in_addr src_addr,
+ pim_hello_options options,
+ pim_hello_options opt_mask,
+ uint32_t new, uint32_t old)
+{
+ if (PIM_OPTION_IS_SET(options, opt_mask)) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: PIM hello TLV redefined %s=%08x old=%08x from %s on interface %s",
+ label, tlv_name,
+ new, old,
+ src_str, ifname);
+ }
+}
+
+int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr,
+ pim_hello_options *hello_options,
+ uint16_t *hello_option_holdtime,
+ uint16_t option_len,
+ const char *tlv_curr)
+{
+ const char *label = "holdtime";
+
+ if (check_tlv_length(__PRETTY_FUNCTION__, label,
+ ifname, src_addr,
+ sizeof(uint16_t), option_len)) {
+ return -1;
+ }
+
+ check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, label,
+ ifname, src_addr,
+ *hello_options, PIM_OPTION_MASK_HOLDTIME,
+ PIM_TLV_GET_HOLDTIME(tlv_curr),
+ *hello_option_holdtime);
+
+ PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_HOLDTIME);
+
+ *hello_option_holdtime = PIM_TLV_GET_HOLDTIME(tlv_curr);
+
+ return 0;
+}
+
+int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr,
+ pim_hello_options *hello_options,
+ uint16_t *hello_option_propagation_delay,
+ uint16_t *hello_option_override_interval,
+ uint16_t option_len,
+ const char *tlv_curr)
+{
+ if (check_tlv_length(__PRETTY_FUNCTION__, "lan_prune_delay",
+ ifname, src_addr,
+ sizeof(uint32_t), option_len)) {
+ return -1;
+ }
+
+ check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, "propagation_delay",
+ ifname, src_addr,
+ *hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY,
+ PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr),
+ *hello_option_propagation_delay);
+
+ PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY);
+
+ *hello_option_propagation_delay = PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr);
+ if (PIM_TLV_GET_CAN_DISABLE_JOIN_SUPPRESSION(tlv_curr)) {
+ PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION);
+ }
+ else {
+ PIM_OPTION_UNSET(*hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION);
+ }
+ ++tlv_curr;
+ ++tlv_curr;
+ *hello_option_override_interval = PIM_TLV_GET_OVERRIDE_INTERVAL(tlv_curr);
+
+ return 0;
+}
+
+int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr,
+ pim_hello_options *hello_options,
+ uint32_t *hello_option_dr_priority,
+ uint16_t option_len,
+ const char *tlv_curr)
+{
+ const char *label = "dr_priority";
+
+ if (check_tlv_length(__PRETTY_FUNCTION__, label,
+ ifname, src_addr,
+ sizeof(uint32_t), option_len)) {
+ return -1;
+ }
+
+ check_tlv_redefinition_uint32(__PRETTY_FUNCTION__, label,
+ ifname, src_addr,
+ *hello_options, PIM_OPTION_MASK_DR_PRIORITY,
+ PIM_TLV_GET_DR_PRIORITY(tlv_curr),
+ *hello_option_dr_priority);
+
+ PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_DR_PRIORITY);
+
+ *hello_option_dr_priority = PIM_TLV_GET_DR_PRIORITY(tlv_curr);
+
+ return 0;
+}
+
+int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr,
+ pim_hello_options *hello_options,
+ uint32_t *hello_option_generation_id,
+ uint16_t option_len,
+ const char *tlv_curr)
+{
+ const char *label = "generation_id";
+
+ if (check_tlv_length(__PRETTY_FUNCTION__, label,
+ ifname, src_addr,
+ sizeof(uint32_t), option_len)) {
+ return -1;
+ }
+
+ check_tlv_redefinition_uint32_hex(__PRETTY_FUNCTION__, label,
+ ifname, src_addr,
+ *hello_options, PIM_OPTION_MASK_GENERATION_ID,
+ PIM_TLV_GET_GENERATION_ID(tlv_curr),
+ *hello_option_generation_id);
+
+ PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_GENERATION_ID);
+
+ *hello_option_generation_id = PIM_TLV_GET_GENERATION_ID(tlv_curr);
+
+ return 0;
+}
+
+int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr,
+ struct prefix *p,
+ const char *buf,
+ int buf_size)
+{
+ const int ucast_encoding_min_len = 3; /* 1 family + 1 type + 1 addr */
+ const char *addr;
+ const char *pastend;
+ int family;
+ int type;
+
+ if (buf_size < ucast_encoding_min_len) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: unicast address encoding overflow: left=%d needed=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ buf_size, ucast_encoding_min_len,
+ src_str, ifname);
+ return -1;
+ }
+
+ addr = buf;
+ pastend = buf + buf_size;
+
+ family = *(const uint8_t *) addr;
+ ++addr;
+ type = *(const uint8_t *) addr;
+ ++addr;
+
+ switch (family) {
+ case PIM_MSG_ADDRESS_FAMILY_IPV4:
+ if (type) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: unknown unicast address encoding type=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ type, src_str, ifname);
+ return -2;
+ }
+
+ if ((addr + sizeof(struct in_addr)) > pastend) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: IPv4 unicast address overflow: left=%d needed=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ pastend - addr, sizeof(struct in_addr),
+ src_str, ifname);
+ return -3;
+ }
+
+ p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
+ p->u.prefix4 = *(const struct in_addr *) addr;
+ addr += sizeof(struct in_addr);
+
+ break;
+ default:
+ {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: unknown unicast address encoding family=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ family, src_str, ifname);
+ return -4;
+ }
+ }
+
+ return addr - buf;
+}
+
+int pim_parse_addr_group(const char *ifname, struct in_addr src_addr,
+ struct prefix *p,
+ const char *buf,
+ int buf_size)
+{
+ const int grp_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */
+ const char *addr;
+ const char *pastend;
+ int family;
+ int type;
+ int mask_len;
+
+ if (buf_size < grp_encoding_min_len) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: group address encoding overflow: left=%d needed=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ buf_size, grp_encoding_min_len,
+ src_str, ifname);
+ return -1;
+ }
+
+ addr = buf;
+ pastend = buf + buf_size;
+
+ family = *(const uint8_t *) addr;
+ ++addr;
+ type = *(const uint8_t *) addr;
+ ++addr;
+ ++addr; /* skip b_reserved_z fields */
+ mask_len = *(const uint8_t *) addr;
+ ++addr;
+
+ switch (family) {
+ case PIM_MSG_ADDRESS_FAMILY_IPV4:
+ if (type) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: unknown group address encoding type=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ type, src_str, ifname);
+ return -2;
+ }
+
+ if ((addr + sizeof(struct in_addr)) > pastend) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: IPv4 group address overflow: left=%d needed=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ pastend - addr, sizeof(struct in_addr),
+ src_str, ifname);
+ return -3;
+ }
+
+ p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
+ p->u.prefix4 = *(const struct in_addr *) addr;
+ p->prefixlen = mask_len;
+
+ addr += sizeof(struct in_addr);
+
+ break;
+ default:
+ {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: unknown group address encoding family=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ family, src_str, ifname);
+ return -4;
+ }
+ }
+
+ return addr - buf;
+}
+
+int pim_parse_addr_source(const char *ifname,
+ struct in_addr src_addr,
+ struct prefix *p,
+ uint8_t *flags,
+ const char *buf,
+ int buf_size)
+{
+ const int src_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */
+ const char *addr;
+ const char *pastend;
+ int family;
+ int type;
+ int mask_len;
+
+ if (buf_size < src_encoding_min_len) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: source address encoding overflow: left=%d needed=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ buf_size, src_encoding_min_len,
+ src_str, ifname);
+ return -1;
+ }
+
+ addr = buf;
+ pastend = buf + buf_size;
+
+ family = *(const uint8_t *) addr;
+ ++addr;
+ type = *(const uint8_t *) addr;
+ ++addr;
+ *flags = *(const uint8_t *) addr;
+ ++addr;
+ mask_len = *(const uint8_t *) addr;
+ ++addr;
+
+ switch (family) {
+ case PIM_MSG_ADDRESS_FAMILY_IPV4:
+ if (type) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: unknown source address encoding type=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ type, src_str, ifname);
+ return -2;
+ }
+
+ if ((addr + sizeof(struct in_addr)) > pastend) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: IPv4 source address overflow: left=%d needed=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ pastend - addr, sizeof(struct in_addr),
+ src_str, ifname);
+ return -3;
+ }
+
+ p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
+ p->u.prefix4 = *(const struct in_addr *) addr;
+ p->prefixlen = mask_len;
+
+ /*
+ RFC 4601: 4.9.1 Encoded Source and Group Address Formats
+
+ Encoded-Source Address
+
+ The mask length MUST be equal to the mask length in bits for
+ the given Address Family and Encoding Type (32 for IPv4 native
+ and 128 for IPv6 native). A router SHOULD ignore any messages
+ received with any other mask length.
+ */
+ if (p->prefixlen != 32) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", p->u.prefix4, src_str, sizeof(src_str));
+ zlog_warn("%s: IPv4 bad source address mask: %s/%d",
+ __PRETTY_FUNCTION__, src_str, p->prefixlen);
+ return -4;
+ }
+
+ addr += sizeof(struct in_addr);
+
+ break;
+ default:
+ {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: unknown source address encoding family=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ family, src_str, ifname);
+ return -5;
+ }
+ }
+
+ return addr - buf;
+}
+
+#define FREE_ADDR_LIST(hello_option_addr_list) \
+{ \
+ if (hello_option_addr_list) { \
+ list_delete(hello_option_addr_list); \
+ hello_option_addr_list = 0; \
+ } \
+}
+
+int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr,
+ pim_hello_options *hello_options,
+ struct list **hello_option_addr_list,
+ uint16_t option_len,
+ const char *tlv_curr)
+{
+ const char *addr;
+ const char *pastend;
+
+ zassert(hello_option_addr_list);
+
+ /*
+ Scan addr list
+ */
+ addr = tlv_curr;
+ pastend = tlv_curr + option_len;
+ while (addr < pastend) {
+ struct prefix tmp;
+ int addr_offset;
+
+ /*
+ Parse ucast addr
+ */
+ addr_offset = pim_parse_addr_ucast(ifname, src_addr, &tmp,
+ addr, pastend - addr);
+ if (addr_offset < 1) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
+ __PRETTY_FUNCTION__,
+ src_str, ifname);
+ FREE_ADDR_LIST(*hello_option_addr_list);
+ return -1;
+ }
+ addr += addr_offset;
+
+ /*
+ Debug
+ */
+ if (PIM_DEBUG_PIM_TRACE) {
+ switch (tmp.family) {
+ case AF_INET:
+ {
+ char addr_str[100];
+ char src_str[100];
+ pim_inet4_dump("<addr?>", tmp.u.prefix4, addr_str, sizeof(addr_str));
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_debug("%s: PIM hello TLV option: list_old_size=%d IPv4 address %s from %s on %s",
+ __PRETTY_FUNCTION__,
+ *hello_option_addr_list ?
+ ((int) listcount(*hello_option_addr_list)) : -1,
+ addr_str, src_str, ifname);
+ }
+ break;
+ default:
+ {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_debug("%s: PIM hello TLV option: list_old_size=%d UNKNOWN address family from %s on %s",
+ __PRETTY_FUNCTION__,
+ *hello_option_addr_list ?
+ ((int) listcount(*hello_option_addr_list)) : -1,
+ src_str, ifname);
+ }
+ }
+ }
+
+ /*
+ Exclude neighbor's primary address if incorrectly included in
+ the secondary address list
+ */
+ if (tmp.family == AF_INET) {
+ if (tmp.u.prefix4.s_addr == src_addr.s_addr) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: ignoring primary address in secondary list from %s on %s",
+ __PRETTY_FUNCTION__,
+ src_str, ifname);
+ continue;
+ }
+ }
+
+ /*
+ Allocate list if needed
+ */
+ if (!*hello_option_addr_list) {
+ *hello_option_addr_list = list_new();
+ if (!*hello_option_addr_list) {
+ zlog_err("%s %s: failure: hello_option_addr_list=list_new()",
+ __FILE__, __PRETTY_FUNCTION__);
+ return -2;
+ }
+ (*hello_option_addr_list)->del = (void (*)(void *)) prefix_free;
+ }
+
+ /*
+ Attach addr to list
+ */
+ {
+ struct prefix *p;
+ p = prefix_new();
+ if (!p) {
+ zlog_err("%s %s: failure: prefix_new()",
+ __FILE__, __PRETTY_FUNCTION__);
+ FREE_ADDR_LIST(*hello_option_addr_list);
+ return -3;
+ }
+ p->family = tmp.family;
+ p->u.prefix4 = tmp.u.prefix4;
+ listnode_add(*hello_option_addr_list, p);
+ }
+
+ } /* while (addr < pastend) */
+
+ /*
+ Mark hello option
+ */
+ PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_ADDRESS_LIST);
+
+ return 0;
+}
diff --git a/pimd/pim_tlv.h b/pimd/pim_tlv.h
new file mode 100644
index 00000000..5ec3dc43
--- /dev/null
+++ b/pimd/pim_tlv.h
@@ -0,0 +1,133 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_TLV_H
+#define PIM_TLV_H
+
+#include <zebra.h>
+
+#include "config.h"
+#include "if.h"
+#include "linklist.h"
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif /* HAVE_INTTYPES_H */
+
+#define PIM_MSG_OPTION_TYPE_HOLDTIME (1)
+#define PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY (2)
+#define PIM_MSG_OPTION_TYPE_DR_PRIORITY (19)
+#define PIM_MSG_OPTION_TYPE_GENERATION_ID (20)
+#define PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH (21)
+#define PIM_MSG_OPTION_TYPE_ADDRESS_LIST (24)
+
+typedef uint32_t pim_hello_options;
+#define PIM_OPTION_MASK_HOLDTIME (1 << 0) /* recv holdtime */
+#define PIM_OPTION_MASK_LAN_PRUNE_DELAY (1 << 1) /* recv lan_prune_delay */
+#define PIM_OPTION_MASK_DR_PRIORITY (1 << 2) /* recv dr_priority */
+#define PIM_OPTION_MASK_GENERATION_ID (1 << 3) /* recv generation_id */
+#define PIM_OPTION_MASK_ADDRESS_LIST (1 << 4) /* recv secondary address list */
+#define PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION (1 << 5) /* T bit value (valid if recv lan_prune_delay) */
+
+#define PIM_RPT_BIT_MASK (1 << 0)
+#define PIM_WILDCARD_BIT_MASK (1 << 1)
+
+#define PIM_OPTION_SET(options, option_mask) ((options) |= (option_mask))
+#define PIM_OPTION_UNSET(options, option_mask) ((options) &= ~(option_mask))
+#define PIM_OPTION_IS_SET(options, option_mask) ((options) & (option_mask))
+
+#define PIM_TLV_GET_UINT16(buf) ntohs(*(const uint16_t *)(buf))
+#define PIM_TLV_GET_UINT32(buf) ntohl(*(const uint32_t *)(buf))
+#define PIM_TLV_GET_TYPE(buf) PIM_TLV_GET_UINT16(buf)
+#define PIM_TLV_GET_LENGTH(buf) PIM_TLV_GET_UINT16(buf)
+#define PIM_TLV_GET_HOLDTIME(buf) PIM_TLV_GET_UINT16(buf)
+#define PIM_TLV_GET_PROPAGATION_DELAY(buf) (PIM_TLV_GET_UINT16(buf) & 0x7FFF)
+#define PIM_TLV_GET_OVERRIDE_INTERVAL(buf) PIM_TLV_GET_UINT16(buf)
+#define PIM_TLV_GET_CAN_DISABLE_JOIN_SUPPRESSION(buf) ((*(const uint8_t *)(buf)) & 0x80)
+#define PIM_TLV_GET_DR_PRIORITY(buf) PIM_TLV_GET_UINT32(buf)
+#define PIM_TLV_GET_GENERATION_ID(buf) PIM_TLV_GET_UINT32(buf)
+
+#define PIM_TLV_TYPE_SIZE (2)
+#define PIM_TLV_LENGTH_SIZE (2)
+#define PIM_TLV_MIN_SIZE (PIM_TLV_TYPE_SIZE + PIM_TLV_LENGTH_SIZE)
+#define PIM_TLV_OPTION_SIZE(option_len) (PIM_TLV_MIN_SIZE + (option_len))
+
+char *pim_tlv_append_uint16(uint8_t *buf,
+ const uint8_t *buf_pastend,
+ uint16_t option_type,
+ uint16_t option_value);
+char *pim_tlv_append_2uint16(uint8_t *buf,
+ const uint8_t *buf_pastend,
+ uint16_t option_type,
+ uint16_t option_value1,
+ uint16_t option_value2);
+char *pim_tlv_append_uint32(uint8_t *buf,
+ const uint8_t *buf_pastend,
+ uint16_t option_type,
+ uint32_t option_value);
+char *pim_tlv_append_addrlist_ucast(uint8_t *buf,
+ const uint8_t *buf_pastend,
+ struct list *ifconnected);
+
+int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr,
+ pim_hello_options *hello_options,
+ uint16_t *hello_option_holdtime,
+ uint16_t option_len,
+ const char *tlv_curr);
+int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr,
+ pim_hello_options *hello_options,
+ uint16_t *hello_option_propagation_delay,
+ uint16_t *hello_option_override_interval,
+ uint16_t option_len,
+ const char *tlv_curr);
+int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr,
+ pim_hello_options *hello_options,
+ uint32_t *hello_option_dr_priority,
+ uint16_t option_len,
+ const char *tlv_curr);
+int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr,
+ pim_hello_options *hello_options,
+ uint32_t *hello_option_generation_id,
+ uint16_t option_len,
+ const char *tlv_curr);
+int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr,
+ pim_hello_options *hello_options,
+ struct list **hello_option_addr_list,
+ uint16_t option_len,
+ const char *tlv_curr);
+
+int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr,
+ struct prefix *p,
+ const char *buf,
+ int buf_size);
+int pim_parse_addr_group(const char *ifname, struct in_addr src_addr,
+ struct prefix *p,
+ const char *buf,
+ int buf_size);
+int pim_parse_addr_source(const char *ifname,
+ struct in_addr src_addr,
+ struct prefix *p,
+ uint8_t *flags,
+ const char *buf,
+ int buf_size);
+
+#endif /* PIM_TLV_H */
diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c
new file mode 100644
index 00000000..b9cf1e52
--- /dev/null
+++ b/pimd/pim_upstream.c
@@ -0,0 +1,686 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "zebra/rib.h"
+
+#include "log.h"
+#include "zclient.h"
+#include "memory.h"
+#include "thread.h"
+#include "linklist.h"
+
+#include "pimd.h"
+#include "pim_pim.h"
+#include "pim_str.h"
+#include "pim_time.h"
+#include "pim_iface.h"
+#include "pim_join.h"
+#include "pim_zlookup.h"
+#include "pim_upstream.h"
+#include "pim_ifchannel.h"
+#include "pim_neighbor.h"
+#include "pim_rpf.h"
+#include "pim_zebra.h"
+#include "pim_oil.h"
+#include "pim_macro.h"
+
+static void join_timer_start(struct pim_upstream *up);
+static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
+
+void pim_upstream_free(struct pim_upstream *up)
+{
+ XFREE(MTYPE_PIM_UPSTREAM, up);
+}
+
+static void upstream_channel_oil_detach(struct pim_upstream *up)
+{
+ if (up->channel_oil) {
+ pim_channel_oil_del(up->channel_oil);
+ up->channel_oil = 0;
+ }
+}
+
+void pim_upstream_delete(struct pim_upstream *up)
+{
+ THREAD_OFF(up->t_join_timer);
+
+ upstream_channel_oil_detach(up);
+
+ /*
+ notice that listnode_delete() can't be moved
+ into pim_upstream_free() because the later is
+ called by list_delete_all_node()
+ */
+ listnode_delete(qpim_upstream_list, up);
+
+ pim_upstream_free(up);
+}
+
+static void send_join(struct pim_upstream *up)
+{
+ zassert(up->join_state == PIM_UPSTREAM_JOINED);
+
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
+ char src_str[100];
+ char grp_str[100];
+ char rpf_str[100];
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
+ zlog_warn("%s: can't send join upstream: RPF'(%s,%s)=%s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str, rpf_str);
+ /* warning only */
+ }
+ }
+
+ /* send Join(S,G) to the current upstream neighbor */
+ pim_joinprune_send(up->rpf.source_nexthop.interface,
+ up->rpf.rpf_addr,
+ up->source_addr,
+ up->group_addr,
+ 1 /* join */);
+}
+
+static int on_join_timer(struct thread *t)
+{
+ struct pim_upstream *up;
+
+ zassert(t);
+ up = THREAD_ARG(t);
+ zassert(up);
+
+ send_join(up);
+
+ up->t_join_timer = 0;
+ join_timer_start(up);
+
+ return 0;
+}
+
+static void join_timer_start(struct pim_upstream *up)
+{
+ if (PIM_DEBUG_PIM_EVENTS) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ zlog_debug("%s: starting %d sec timer for upstream (S,G)=(%s,%s)",
+ __PRETTY_FUNCTION__,
+ qpim_t_periodic,
+ src_str, grp_str);
+ }
+
+ zassert(!up->t_join_timer);
+
+ THREAD_TIMER_ON(master, up->t_join_timer,
+ on_join_timer,
+ up, qpim_t_periodic);
+}
+
+void pim_upstream_join_timer_restart(struct pim_upstream *up)
+{
+ THREAD_OFF(up->t_join_timer);
+ join_timer_start(up);
+}
+
+static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
+ int interval_msec)
+{
+ if (PIM_DEBUG_PIM_EVENTS) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ zlog_debug("%s: restarting %d msec timer for upstream (S,G)=(%s,%s)",
+ __PRETTY_FUNCTION__,
+ interval_msec,
+ src_str, grp_str);
+ }
+
+ THREAD_OFF(up->t_join_timer);
+ THREAD_TIMER_MSEC_ON(master, up->t_join_timer,
+ on_join_timer,
+ up, interval_msec);
+}
+
+void pim_upstream_join_suppress(struct pim_upstream *up,
+ struct in_addr rpf_addr,
+ int holdtime)
+{
+ long t_joinsuppress_msec;
+ long join_timer_remain_msec;
+
+ t_joinsuppress_msec = MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface),
+ 1000 * holdtime);
+
+ join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ char grp_str[100];
+ char rpf_str[100];
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
+ zlog_debug("%s %s: detected Join(%s,%s) to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
+ __FILE__, __PRETTY_FUNCTION__,
+ src_str, grp_str,
+ rpf_str,
+ join_timer_remain_msec, t_joinsuppress_msec);
+ }
+
+ if (join_timer_remain_msec < t_joinsuppress_msec) {
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ zlog_debug("%s %s: suppressing Join(S,G)=(%s,%s) for %ld msec",
+ __FILE__, __PRETTY_FUNCTION__,
+ src_str, grp_str, t_joinsuppress_msec);
+ }
+
+ pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
+ }
+}
+
+void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
+ struct pim_upstream *up,
+ struct in_addr rpf_addr)
+{
+ long join_timer_remain_msec;
+ int t_override_msec;
+
+ join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
+ t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface);
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ char grp_str[100];
+ char rpf_str[100];
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
+ zlog_debug("%s: to RPF'(%s,%s)=%s: join_timer=%ld msec t_override=%d msec",
+ debug_label,
+ src_str, grp_str, rpf_str,
+ join_timer_remain_msec, t_override_msec);
+ }
+
+ if (join_timer_remain_msec > t_override_msec) {
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ zlog_debug("%s: decreasing (S,G)=(%s,%s) join timer to t_override=%d msec",
+ debug_label,
+ src_str, grp_str,
+ t_override_msec);
+ }
+
+ pim_upstream_join_timer_restart_msec(up, t_override_msec);
+ }
+}
+
+static void forward_on(struct pim_upstream *up)
+{
+ struct listnode *ifnode;
+ struct listnode *ifnextnode;
+ struct listnode *chnode;
+ struct listnode *chnextnode;
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_ifchannel *ch;
+ struct in_addr ifaddr;
+
+ /* scan all interfaces */
+ for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ /* scan per-interface (S,G) state */
+ for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+
+ if (ch->upstream != up)
+ continue;
+
+ if (pim_macro_chisin_oiflist(ch))
+ pim_forward_start(ch);
+
+ } /* scan iface channel list */
+ } /* scan iflist */
+}
+
+static void forward_off(struct pim_upstream *up)
+{
+ struct listnode *ifnode;
+ struct listnode *ifnextnode;
+ struct listnode *chnode;
+ struct listnode *chnextnode;
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_ifchannel *ch;
+
+ /* scan all interfaces */
+ for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ continue;
+
+ /* scan per-interface (S,G) state */
+ for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+
+ if (ch->upstream != up)
+ continue;
+
+ pim_forward_stop(ch);
+
+ } /* scan iface channel list */
+ } /* scan iflist */
+}
+
+static void pim_upstream_switch(struct pim_upstream *up,
+ enum pim_upstream_state new_state)
+{
+ enum pim_upstream_state old_state = up->join_state;
+
+ zassert(old_state != new_state);
+
+ up->join_state = new_state;
+ up->state_transition = pim_time_monotonic_sec();
+
+ if (PIM_DEBUG_PIM_EVENTS) {
+ char src_str[100];
+ char grp_str[100];
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ zlog_debug("%s: PIM_UPSTREAM_%s: (S,G)=(%s,%s)",
+ __PRETTY_FUNCTION__,
+ ((new_state == PIM_UPSTREAM_JOINED) ? "JOINED" : "NOTJOINED"),
+ src_str, grp_str);
+ }
+
+ pim_upstream_update_assert_tracking_desired(up);
+
+ if (new_state == PIM_UPSTREAM_JOINED) {
+ forward_on(up);
+ send_join(up);
+ join_timer_start(up);
+ }
+ else {
+ forward_off(up);
+ pim_joinprune_send(up->rpf.source_nexthop.interface,
+ up->rpf.rpf_addr,
+ up->source_addr,
+ up->group_addr,
+ 0 /* prune */);
+ zassert(up->t_join_timer);
+ THREAD_OFF(up->t_join_timer);
+ }
+
+}
+
+static struct pim_upstream *pim_upstream_new(struct in_addr source_addr,
+ struct in_addr group_addr)
+{
+ struct pim_upstream *up;
+
+ up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
+ if (!up) {
+ zlog_err("%s: PIM XMALLOC(%d) failure",
+ __PRETTY_FUNCTION__, sizeof(*up));
+ return 0;
+ }
+
+ up->source_addr = source_addr;
+ up->group_addr = group_addr;
+ up->flags = 0;
+ up->ref_count = 1;
+ up->t_join_timer = 0;
+ up->join_state = 0;
+ up->state_transition = pim_time_monotonic_sec();
+ up->channel_oil = 0;
+
+ up->rpf.source_nexthop.interface = 0;
+ up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
+ up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference;
+ up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric;
+ up->rpf.rpf_addr.s_addr = PIM_NET_INADDR_ANY;
+
+ pim_rpf_update(up, 0);
+
+ listnode_add(qpim_upstream_list, up);
+
+ return up;
+}
+
+struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
+ struct in_addr group_addr)
+{
+ struct listnode *up_node;
+ struct pim_upstream *up;
+
+ for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
+ if (
+ (source_addr.s_addr == up->source_addr.s_addr) &&
+ (group_addr.s_addr == up->group_addr.s_addr)
+ ) {
+ return up;
+ }
+ }
+
+ return 0;
+}
+
+struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
+ struct in_addr group_addr)
+{
+ struct pim_upstream *up;
+
+ up = pim_upstream_find(source_addr, group_addr);
+ if (up) {
+ ++up->ref_count;
+ }
+ else {
+ up = pim_upstream_new(source_addr, group_addr);
+ }
+
+ return up;
+}
+
+void pim_upstream_del(struct pim_upstream *up)
+{
+ --up->ref_count;
+
+ if (up->ref_count < 1) {
+ pim_upstream_delete(up);
+ }
+}
+
+/*
+ Evaluate JoinDesired(S,G):
+
+ JoinDesired(S,G) is true if there is a downstream (S,G) interface I
+ in the set:
+
+ inherited_olist(S,G) =
+ joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
+
+ JoinDesired(S,G) may be affected by changes in the following:
+
+ pim_ifp->primary_address
+ pim_ifp->pim_dr_addr
+ ch->ifassert_winner_metric
+ ch->ifassert_winner
+ ch->local_ifmembership
+ ch->ifjoin_state
+ ch->upstream->rpf.source_nexthop.mrib_metric_preference
+ ch->upstream->rpf.source_nexthop.mrib_route_metric
+ ch->upstream->rpf.source_nexthop.interface
+
+ See also pim_upstream_update_join_desired() below.
+ */
+int pim_upstream_evaluate_join_desired(struct pim_upstream *up)
+{
+ struct listnode *ifnode;
+ struct listnode *ifnextnode;
+ struct listnode *chnode;
+ struct listnode *chnextnode;
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_ifchannel *ch;
+
+ /* scan all interfaces */
+ for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ continue;
+
+ /* scan per-interface (S,G) state */
+ for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+ if (ch->upstream != up)
+ continue;
+
+ if (pim_macro_ch_lost_assert(ch))
+ continue; /* keep searching */
+
+ if (pim_macro_chisin_joins_or_include(ch))
+ return 1; /* true */
+ } /* scan iface channel list */
+ } /* scan iflist */
+
+ return 0; /* false */
+}
+
+/*
+ See also pim_upstream_evaluate_join_desired() above.
+*/
+void pim_upstream_update_join_desired(struct pim_upstream *up)
+{
+ int was_join_desired; /* boolean */
+ int is_join_desired; /* boolean */
+
+ was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags);
+
+ is_join_desired = pim_upstream_evaluate_join_desired(up);
+ if (is_join_desired)
+ PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags);
+ else
+ PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
+
+ /* switched from false to true */
+ if (is_join_desired && !was_join_desired) {
+ zassert(up->join_state == PIM_UPSTREAM_NOTJOINED);
+ pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
+ return;
+ }
+
+ /* switched from true to false */
+ if (!is_join_desired && was_join_desired) {
+ zassert(up->join_state == PIM_UPSTREAM_JOINED);
+ pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED);
+ return;
+ }
+}
+
+/*
+ RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
+ Transitions from Joined State
+ RPF'(S,G) GenID changes
+
+ The upstream (S,G) state machine remains in Joined state. If the
+ Join Timer is set to expire in more than t_override seconds, reset
+ it so that it expires after t_override seconds.
+*/
+void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
+{
+ struct listnode *up_node;
+ struct listnode *up_nextnode;
+ struct pim_upstream *up;
+
+ /*
+ Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
+ */
+ for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char neigh_str[100];
+ char src_str[100];
+ char grp_str[100];
+ char rpf_addr_str[100];
+ pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
+ pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
+ zlog_debug("%s: matching neigh=%s against upstream (S,G)=(%s,%s) joined=%d rpf_addr=%s",
+ __PRETTY_FUNCTION__,
+ neigh_str, src_str, grp_str,
+ up->join_state == PIM_UPSTREAM_JOINED,
+ rpf_addr_str);
+ }
+
+ /* consider only (S,G) upstream in Joined state */
+ if (up->join_state != PIM_UPSTREAM_JOINED)
+ continue;
+
+ /* match RPF'(S,G)=neigh_addr */
+ if (up->rpf.rpf_addr.s_addr != neigh_addr.s_addr)
+ continue;
+
+ pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
+ up, neigh_addr);
+ }
+}
+
+
+void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
+ struct interface *old_rpf_ifp)
+{
+ struct listnode *ifnode;
+ struct listnode *ifnextnode;
+ struct interface *ifp;
+
+ /* scan all interfaces */
+ for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+ struct listnode *chnode;
+ struct listnode *chnextnode;
+ struct pim_ifchannel *ch;
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ continue;
+
+ /* search all ifchannels */
+ for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+ if (ch->upstream != up)
+ continue;
+
+ if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
+ if (
+ /* RPF_interface(S) was NOT I */
+ (old_rpf_ifp == ch->interface)
+ &&
+ /* RPF_interface(S) stopped being I */
+ (ch->upstream->rpf.source_nexthop.interface != ch->interface)
+ ) {
+ assert_action_a5(ch);
+ }
+ } /* PIM_IFASSERT_I_AM_LOSER */
+
+ pim_ifchannel_update_assert_tracking_desired(ch);
+ }
+ }
+}
+
+void pim_upstream_update_could_assert(struct pim_upstream *up)
+{
+ struct listnode *ifnode;
+ struct listnode *ifnextnode;
+ struct listnode *chnode;
+ struct listnode *chnextnode;
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_ifchannel *ch;
+
+ /* scan all interfaces */
+ for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ continue;
+
+ /* scan per-interface (S,G) state */
+ for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+
+ if (ch->upstream != up)
+ continue;
+
+ pim_ifchannel_update_could_assert(ch);
+
+ } /* scan iface channel list */
+ } /* scan iflist */
+}
+
+void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
+{
+ struct listnode *ifnode;
+ struct listnode *ifnextnode;
+ struct listnode *chnode;
+ struct listnode *chnextnode;
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_ifchannel *ch;
+
+ /* scan all interfaces */
+ for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ continue;
+
+ /* scan per-interface (S,G) state */
+ for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+
+ if (ch->upstream != up)
+ continue;
+
+ pim_ifchannel_update_my_assert_metric(ch);
+
+ } /* scan iface channel list */
+ } /* scan iflist */
+}
+
+static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
+{
+ struct listnode *ifnode;
+ struct listnode *ifnextnode;
+ struct listnode *chnode;
+ struct listnode *chnextnode;
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_ifchannel *ch;
+
+ /* scan all interfaces */
+ for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ continue;
+
+ /* scan per-interface (S,G) state */
+ for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+
+ if (ch->upstream != up)
+ continue;
+
+ pim_ifchannel_update_assert_tracking_desired(ch);
+
+ } /* scan iface channel list */
+ } /* scan iflist */
+}
diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h
new file mode 100644
index 00000000..5b5182dd
--- /dev/null
+++ b/pimd/pim_upstream.h
@@ -0,0 +1,122 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_UPSTREAM_H
+#define PIM_UPSTREAM_H
+
+#include <zebra.h>
+
+#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED (1 << 0)
+#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED (2 << 0)
+
+#define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
+#define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
+
+#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
+#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
+
+#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
+#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
+
+/*
+ RFC 4601:
+
+ Metric Preference
+ Preference value assigned to the unicast routing protocol that
+ provided the route to the multicast source or Rendezvous-Point.
+
+ Metric
+ The unicast routing table metric associated with the route used to
+ reach the multicast source or Rendezvous-Point. The metric is in
+ units applicable to the unicast routing protocol used.
+*/
+struct pim_nexthop {
+ struct interface *interface; /* RPF_interface(S) */
+ struct in_addr mrib_nexthop_addr; /* MRIB.next_hop(S) */
+ uint32_t mrib_metric_preference; /* MRIB.pref(S) */
+ uint32_t mrib_route_metric; /* MRIB.metric(S) */
+};
+
+struct pim_rpf {
+ struct pim_nexthop source_nexthop;
+ struct in_addr rpf_addr; /* RPF'(S,G) */
+};
+
+enum pim_rpf_result {
+ PIM_RPF_OK = 0,
+ PIM_RPF_CHANGED,
+ PIM_RPF_FAILURE
+};
+
+enum pim_upstream_state {
+ PIM_UPSTREAM_NOTJOINED,
+ PIM_UPSTREAM_JOINED
+};
+
+/*
+ Upstream (S,G) channel in Joined state
+
+ (S,G) in the "Not Joined" state is not represented
+
+ See RFC 4601: 4.5.7. Sending (S,G) Join/Prune Message
+*/
+struct pim_upstream {
+ struct in_addr source_addr; /* (S,G) source key */
+ struct in_addr group_addr; /* (S,G) group key */
+ uint32_t flags;
+ struct channel_oil *channel_oil;
+
+ enum pim_upstream_state join_state;
+ int ref_count;
+
+ struct pim_rpf rpf;
+
+ struct thread *t_join_timer;
+ int64_t state_transition; /* Record current state uptime */
+};
+
+void pim_upstream_free(struct pim_upstream *up);
+void pim_upstream_delete(struct pim_upstream *up);
+struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
+ struct in_addr group_addr);
+struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
+ struct in_addr group_addr);
+void pim_upstream_del(struct pim_upstream *up);
+
+int pim_upstream_evaluate_join_desired(struct pim_upstream *up);
+void pim_upstream_update_join_desired(struct pim_upstream *up);
+
+void pim_upstream_join_suppress(struct pim_upstream *up,
+ struct in_addr rpf_addr,
+ int holdtime);
+void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
+ struct pim_upstream *up,
+ struct in_addr rpf_addr);
+void pim_upstream_join_timer_restart(struct pim_upstream *up);
+void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr);
+void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
+ struct interface *old_rpf_ifp);
+
+void pim_upstream_update_could_assert(struct pim_upstream *up);
+void pim_upstream_update_my_assert_metric(struct pim_upstream *up);
+
+#endif /* PIM_UPSTREAM_H */
diff --git a/pimd/pim_util.c b/pimd/pim_util.c
new file mode 100644
index 00000000..d97b599c
--- /dev/null
+++ b/pimd/pim_util.c
@@ -0,0 +1,159 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+
+#include "pim_util.h"
+
+/*
+ RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
+
+ If QQIC < 128, QQI = QQIC
+ If QQIC >= 128, QQI = (mant | 0x10) << (exp + 3)
+
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-+-+-+-+
+ |1| exp | mant |
+ +-+-+-+-+-+-+-+-+
+
+ Since exp=0..7 then (exp+3)=3..10, then QQI has
+ one of the following bit patterns:
+
+ exp=0: QQI = 0000.0000.1MMM.M000
+ exp=1: QQI = 0000.0001.MMMM.0000
+ ...
+ exp=6: QQI = 001M.MMM0.0000.0000
+ exp=7: QQI = 01MM.MM00.0000.0000
+ --------- ---------
+ 0x4 0x0 0x0 0x0
+*/
+uint8_t igmp_msg_encode16to8(uint16_t value)
+{
+ uint8_t code;
+
+ if (value < 128) {
+ code = value;
+ }
+ else {
+ uint16_t mask = 0x4000;
+ uint8_t exp;
+ uint16_t mant;
+ for (exp = 7; exp > 0; --exp) {
+ if (mask & value)
+ break;
+ mask >>= 1;
+ }
+ mant = 0x000F & (value >> (exp + 3));
+ code = ((uint8_t) 1 << 7) | ((uint8_t) exp << 4) | (uint8_t) mant;
+ }
+
+ return code;
+}
+
+/*
+ RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
+
+ If QQIC < 128, QQI = QQIC
+ If QQIC >= 128, QQI = (mant | 0x10) << (exp + 3)
+
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-+-+-+-+
+ |1| exp | mant |
+ +-+-+-+-+-+-+-+-+
+*/
+uint16_t igmp_msg_decode8to16(uint8_t code)
+{
+ uint16_t value;
+
+ if (code < 128) {
+ value = code;
+ }
+ else {
+ uint16_t mant = (code & 0x0F);
+ uint8_t exp = (code & 0x70) >> 4;
+ value = (mant | 0x10) << (exp + 3);
+ }
+
+ return value;
+}
+
+#ifndef PIM_USE_QUAGGA_INET_CHECKSUM
+/*
+ RFC 3376: 4.1.2. Checksum
+
+ The Checksum is the 16-bit one's complement of the one's complement
+ sum of the whole IGMP message (the entire IP payload). For
+ computing the checksum, the Checksum field is set to zero. When
+ receiving packets, the checksum MUST be verified before processing a
+ packet. [RFC-1071]
+*/
+uint16_t pim_inet_checksum(const char *buf, int size)
+{
+ const uint16_t *ptr;
+ uint32_t sum;
+ uint16_t checksum;
+
+ ptr = (const uint16_t *) buf;
+ sum = 0;
+ while (size > 1) {
+ sum += *ptr;
+ ++ptr;
+ size -= 2;
+ }
+
+ /* Add left-over byte, if any */
+ if (size > 0)
+ sum += (uint16_t) *(const uint8_t *) ptr;
+
+ /* Fold 32-bit sum to 16 bits */
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ checksum = ~sum;
+
+ return checksum;
+}
+#endif /* PIM_USE_QUAGGA_INET_CHECKSUM */
+
+void pim_pkt_dump(const char *label, const uint8_t *buf, int size)
+{
+ char dump_buf[1000];
+ int i = 0;
+ int j = 0;
+
+ for (; i < size; ++i, j += 3) {
+ int left = sizeof(dump_buf) - j;
+ if (left < 4) {
+ if (left > 1) {
+ strcat(dump_buf + j, "!"); /* mark as truncated */
+ }
+ break;
+ }
+ snprintf(dump_buf + j, left, " %02x", buf[i]);
+ }
+
+ zlog_debug("%s: pkt dump size=%d:%s",
+ label,
+ size,
+ dump_buf);
+}
diff --git a/pimd/pim_util.h b/pimd/pim_util.h
new file mode 100644
index 00000000..6f5bf223
--- /dev/null
+++ b/pimd/pim_util.h
@@ -0,0 +1,43 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_UTIL_H
+#define PIM_UTIL_H
+
+#include <stdint.h>
+
+#include <zebra.h>
+
+#include "checksum.h"
+
+uint8_t igmp_msg_encode16to8(uint16_t value);
+uint16_t igmp_msg_decode8to16(uint8_t code);
+
+#ifdef PIM_USE_QUAGGA_INET_CHECKSUM
+#define pim_inet_checksum(buf,size) in_cksum(buf,size)
+#else
+uint16_t pim_inet_checksum(const char *buf, int size);
+#endif /* PIM_USE_QUAGGA_INET_CHECKSUM */
+
+void pim_pkt_dump(const char *label, const uint8_t *buf, int size);
+
+#endif /* PIM_UTIL_H */
diff --git a/pimd/pim_version.c b/pimd/pim_version.c
new file mode 100644
index 00000000..fe7e5634
--- /dev/null
+++ b/pimd/pim_version.c
@@ -0,0 +1,25 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include "pim_version.h"
+
+const char * const PIMD_VERSION = PIMD_VERSION_STR;
diff --git a/pimd/pim_version.h b/pimd/pim_version.h
new file mode 100644
index 00000000..546ace3b
--- /dev/null
+++ b/pimd/pim_version.h
@@ -0,0 +1,30 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_VERSION_H
+#define PIM_VERSION_H
+
+#define PIMD_VERSION_STR "0.159"
+
+const char * const PIMD_VERSION;
+
+#endif /* PIM_VERSION_H */
diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c
new file mode 100644
index 00000000..3a1abf13
--- /dev/null
+++ b/pimd/pim_vty.c
@@ -0,0 +1,173 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "if.h"
+#include "linklist.h"
+
+#include "pimd.h"
+#include "pim_vty.h"
+#include "pim_iface.h"
+#include "pim_cmd.h"
+#include "pim_str.h"
+#include "pim_ssmpingd.h"
+
+int pim_debug_config_write(struct vty *vty)
+{
+ int writes = 0;
+
+ if (PIM_DEBUG_IGMP_EVENTS) {
+ vty_out(vty, "debug igmp events%s", VTY_NEWLINE);
+ ++writes;
+ }
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ vty_out(vty, "debug igmp packets%s", VTY_NEWLINE);
+ ++writes;
+ }
+ if (PIM_DEBUG_IGMP_TRACE) {
+ vty_out(vty, "debug igmp trace%s", VTY_NEWLINE);
+ ++writes;
+ }
+
+ if (PIM_DEBUG_PIM_EVENTS) {
+ vty_out(vty, "debug pim events%s", VTY_NEWLINE);
+ ++writes;
+ }
+ if (PIM_DEBUG_PIM_PACKETS) {
+ vty_out(vty, "debug pim packets%s", VTY_NEWLINE);
+ ++writes;
+ }
+ if (PIM_DEBUG_PIM_PACKETDUMP_SEND) {
+ vty_out(vty, "debug pim packet-dump send%s", VTY_NEWLINE);
+ ++writes;
+ }
+ if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
+ vty_out(vty, "debug pim packet-dump receive%s", VTY_NEWLINE);
+ ++writes;
+ }
+ if (PIM_DEBUG_PIM_TRACE) {
+ vty_out(vty, "debug pim trace%s", VTY_NEWLINE);
+ ++writes;
+ }
+
+ if (PIM_DEBUG_ZEBRA) {
+ vty_out(vty, "debug pim zebra%s", VTY_NEWLINE);
+ ++writes;
+ }
+
+ if (PIM_DEBUG_SSMPINGD) {
+ vty_out(vty, "debug ssmpingd%s", VTY_NEWLINE);
+ ++writes;
+ }
+
+ return writes;
+}
+
+int pim_global_config_write(struct vty *vty)
+{
+ int writes = 0;
+
+ if (PIM_MROUTE_IS_ENABLED) {
+ vty_out(vty, "%s%s", PIM_CMD_IP_MULTICAST_ROUTING, VTY_NEWLINE);
+ ++writes;
+ }
+
+ if (qpim_ssmpingd_list) {
+ struct listnode *node;
+ struct ssmpingd_sock *ss;
+ vty_out(vty, "!%s", VTY_NEWLINE);
+ ++writes;
+ for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) {
+ char source_str[100];
+ pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
+ vty_out(vty, "ip ssmpingd %s%s", source_str, VTY_NEWLINE);
+ ++writes;
+ }
+ }
+
+ return writes;
+}
+
+int pim_interface_config_write(struct vty *vty)
+{
+ int writes = 0;
+ struct listnode *node;
+ struct interface *ifp;
+
+ for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+
+ /* IF name */
+ vty_out(vty, "interface %s%s", ifp->name, VTY_NEWLINE);
+ ++writes;
+
+ if (ifp->info) {
+ struct pim_interface *pim_ifp = ifp->info;
+
+ /* IF ip pim ssm */
+ if (PIM_IF_TEST_PIM(pim_ifp->options)) {
+ vty_out(vty, " ip pim ssm%s", VTY_NEWLINE);
+ ++writes;
+ }
+
+ /* IF ip igmp */
+ if (PIM_IF_TEST_IGMP(pim_ifp->options)) {
+ vty_out(vty, " ip igmp%s", VTY_NEWLINE);
+ ++writes;
+ }
+
+ /* IF ip igmp query-interval */
+ vty_out(vty, " %s %d%s",
+ PIM_CMD_IP_IGMP_QUERY_INTERVAL,
+ pim_ifp->igmp_default_query_interval,
+ VTY_NEWLINE);
+ ++writes;
+
+ /* IF ip igmp query-max-response-time */
+ vty_out(vty, " %s %d%s",
+ PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC,
+ pim_ifp->igmp_query_max_response_time_dsec,
+ VTY_NEWLINE);
+ ++writes;
+
+ /* IF ip igmp join */
+ if (pim_ifp->igmp_join_list) {
+ struct listnode *node;
+ struct igmp_join *ij;
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_join_list, node, ij)) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<grp?>", ij->group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<src?>", ij->source_addr, source_str, sizeof(source_str));
+ vty_out(vty, " ip igmp join %s %s%s",
+ group_str, source_str,
+ VTY_NEWLINE);
+ ++writes;
+ }
+ }
+ }
+ vty_out(vty, "!%s", VTY_NEWLINE);
+ ++writes;
+ }
+
+ return writes;
+}
diff --git a/pimd/pim_vty.h b/pimd/pim_vty.h
new file mode 100644
index 00000000..904ee553
--- /dev/null
+++ b/pimd/pim_vty.h
@@ -0,0 +1,32 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_VTY_H
+#define PIM_VTY_H
+
+#include "vty.h"
+
+int pim_debug_config_write(struct vty *vty);
+int pim_global_config_write(struct vty *vty);
+int pim_interface_config_write(struct vty *vty);
+
+#endif /* PIM_VTY_H */
diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c
new file mode 100644
index 00000000..9e3a0ec3
--- /dev/null
+++ b/pimd/pim_zebra.c
@@ -0,0 +1,1188 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "zebra/rib.h"
+
+#include "log.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "stream.h"
+#include "network.h"
+#include "paths.h"
+
+#include "pimd.h"
+#include "pim_pim.h"
+#include "pim_zebra.h"
+#include "pim_iface.h"
+#include "pim_str.h"
+#include "pim_oil.h"
+#include "pim_rpf.h"
+#include "pim_time.h"
+#include "pim_join.h"
+#include "pim_zlookup.h"
+#include "pim_ifchannel.h"
+
+#undef PIM_DEBUG_IFADDR_DUMP
+#define PIM_DEBUG_IFADDR_DUMP
+
+static int fib_lookup_if_vif_index(struct in_addr addr);
+static int del_oif(struct channel_oil *channel_oil,
+ struct interface *oif,
+ uint32_t proto_mask);
+
+/* Router-id update message from zebra. */
+static int pim_router_id_update_zebra(int command, struct zclient *zclient,
+ zebra_size_t length)
+{
+ struct prefix router_id;
+
+ /* FIXME: actually use router_id for anything ? */
+ zebra_router_id_update_read(zclient->ibuf, &router_id);
+
+ return 0;
+}
+
+static int pim_zebra_if_add(int command, struct zclient *zclient,
+ zebra_size_t length)
+{
+ struct interface *ifp;
+
+ /*
+ zebra api adds/dels interfaces using the same call
+ interface_add_read below, see comments in lib/zclient.c
+ */
+ ifp = zebra_interface_add_read(zclient->ibuf);
+ if (!ifp)
+ return 0;
+
+ if (PIM_DEBUG_ZEBRA) {
+ zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d",
+ __PRETTY_FUNCTION__,
+ ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric,
+ ifp->mtu, if_is_operative(ifp));
+ }
+
+ if (if_is_operative(ifp))
+ pim_if_addr_add_all(ifp);
+
+ return 0;
+}
+
+static int pim_zebra_if_del(int command, struct zclient *zclient,
+ zebra_size_t length)
+{
+ struct interface *ifp;
+
+ /*
+ zebra api adds/dels interfaces using the same call
+ interface_add_read below, see comments in lib/zclient.c
+ */
+ ifp = zebra_interface_add_read(zclient->ibuf);
+ if (!ifp)
+ return 0;
+
+ if (PIM_DEBUG_ZEBRA) {
+ zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d",
+ __PRETTY_FUNCTION__,
+ ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric,
+ ifp->mtu, if_is_operative(ifp));
+ }
+
+ if (!if_is_operative(ifp))
+ pim_if_addr_del_all(ifp);
+
+ return 0;
+}
+
+static int pim_zebra_if_state_up(int command, struct zclient *zclient,
+ zebra_size_t length)
+{
+ struct interface *ifp;
+
+ /*
+ zebra api notifies interface up/down events by using the same call
+ interface_add_read below, see comments in lib/zclient.c
+ */
+ ifp = zebra_interface_state_read(zclient->ibuf);
+ if (!ifp)
+ return 0;
+
+ zlog_info("INTERFACE UP: %s", ifp->name);
+
+ if (PIM_DEBUG_ZEBRA) {
+ zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d",
+ __PRETTY_FUNCTION__,
+ ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric,
+ ifp->mtu, if_is_operative(ifp));
+ }
+
+ if (if_is_operative(ifp)) {
+ /*
+ pim_if_addr_add_all() suffices for bringing up both IGMP and PIM
+ */
+ pim_if_addr_add_all(ifp);
+ }
+
+ return 0;
+}
+
+static int pim_zebra_if_state_down(int command, struct zclient *zclient,
+ zebra_size_t length)
+{
+ struct interface *ifp;
+
+ /*
+ zebra api notifies interface up/down events by using the same call
+ interface_add_read below, see comments in lib/zclient.c
+ */
+ ifp = zebra_interface_state_read(zclient->ibuf);
+ if (!ifp)
+ return 0;
+
+ zlog_info("INTERFACE DOWN: %s", ifp->name);
+
+ if (PIM_DEBUG_ZEBRA) {
+ zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d",
+ __PRETTY_FUNCTION__,
+ ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric,
+ ifp->mtu, if_is_operative(ifp));
+ }
+
+ if (!if_is_operative(ifp)) {
+ /*
+ pim_if_addr_del_all() suffices for shutting down IGMP,
+ but not for shutting down PIM
+ */
+ pim_if_addr_del_all(ifp);
+
+ /*
+ pim_sock_delete() closes the socket, stops read and timer threads,
+ and kills all neighbors.
+ */
+ if (ifp->info) {
+ pim_sock_delete(ifp, "link down");
+ }
+ }
+
+ return 0;
+}
+
+#ifdef PIM_DEBUG_IFADDR_DUMP
+static void dump_if_address(struct interface *ifp)
+{
+ struct connected *ifc;
+ struct listnode *node;
+
+ zlog_debug("%s %s: interface %s addresses:",
+ __FILE__, __PRETTY_FUNCTION__,
+ ifp->name);
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
+ struct prefix *p = ifc->address;
+
+ if (p->family != AF_INET)
+ continue;
+
+ zlog_debug("%s %s: interface %s address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ ifp->name,
+ inet_ntoa(p->u.prefix4));
+ }
+}
+#endif
+
+static int pim_zebra_if_address_add(int command, struct zclient *zclient,
+ zebra_size_t length)
+{
+ struct connected *c;
+ struct prefix *p;
+
+ zassert(command == ZEBRA_INTERFACE_ADDRESS_ADD);
+
+ /*
+ zebra api notifies address adds/dels events by using the same call
+ interface_add_read below, see comments in lib/zclient.c
+
+ zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, ...)
+ will add address to interface list by calling
+ connected_add_by_prefix()
+ */
+ c = zebra_interface_address_read(command, zclient->ibuf);
+ if (!c)
+ return 0;
+
+ p = c->address;
+ if (p->family != AF_INET)
+ return 0;
+
+ if (PIM_DEBUG_ZEBRA) {
+ char buf[BUFSIZ];
+ prefix2str(p, buf, BUFSIZ);
+ zlog_debug("%s: %s connected IP address %s flags %u",
+ __PRETTY_FUNCTION__,
+ c->ifp->name, buf, c->flags);
+
+#ifdef PIM_DEBUG_IFADDR_DUMP
+ dump_if_address(c->ifp);
+#endif
+ }
+
+ pim_if_addr_add(c);
+
+ return 0;
+}
+
+static int pim_zebra_if_address_del(int command, struct zclient *client,
+ zebra_size_t length)
+{
+ struct connected *c;
+ struct prefix *p;
+
+ zassert(command == ZEBRA_INTERFACE_ADDRESS_DELETE);
+
+ /*
+ zebra api notifies address adds/dels events by using the same call
+ interface_add_read below, see comments in lib/zclient.c
+
+ zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, ...)
+ will remove address from interface list by calling
+ connected_delete_by_prefix()
+ */
+ c = zebra_interface_address_read(command, client->ibuf);
+ if (!c)
+ return 0;
+
+ p = c->address;
+ if (p->family != AF_INET)
+ return 0;
+
+ if (PIM_DEBUG_ZEBRA) {
+ char buf[BUFSIZ];
+ prefix2str(p, buf, BUFSIZ);
+ zlog_debug("%s: %s disconnected IP address %s flags %u",
+ __PRETTY_FUNCTION__,
+ c->ifp->name, buf, c->flags);
+
+#ifdef PIM_DEBUG_IFADDR_DUMP
+ dump_if_address(c->ifp);
+#endif
+ }
+
+ pim_if_addr_del(c);
+
+ return 0;
+}
+
+static void scan_upstream_rpf_cache()
+{
+ struct listnode *up_node;
+ struct listnode *up_nextnode;
+ struct pim_upstream *up;
+
+ for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
+ struct in_addr old_rpf_addr;
+ enum pim_rpf_result rpf_result;
+
+ rpf_result = pim_rpf_update(up, &old_rpf_addr);
+ if (rpf_result == PIM_RPF_FAILURE)
+ continue;
+
+ if (rpf_result == PIM_RPF_CHANGED) {
+
+ if (up->join_state == PIM_UPSTREAM_JOINED) {
+
+ /*
+ RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
+
+ Transitions from Joined State
+
+ RPF'(S,G) changes not due to an Assert
+
+ The upstream (S,G) state machine remains in Joined
+ state. Send Join(S,G) to the new upstream neighbor, which is
+ the new value of RPF'(S,G). Send Prune(S,G) to the old
+ upstream neighbor, which is the old value of RPF'(S,G). Set
+ the Join Timer (JT) to expire after t_periodic seconds.
+ */
+
+
+ /* send Prune(S,G) to the old upstream neighbor */
+ pim_joinprune_send(up->rpf.source_nexthop.interface,
+ old_rpf_addr,
+ up->source_addr,
+ up->group_addr,
+ 0 /* prune */);
+
+ /* send Join(S,G) to the current upstream neighbor */
+ pim_joinprune_send(up->rpf.source_nexthop.interface,
+ up->rpf.rpf_addr,
+ up->source_addr,
+ up->group_addr,
+ 1 /* join */);
+
+ pim_upstream_join_timer_restart(up);
+ } /* up->join_state == PIM_UPSTREAM_JOINED */
+
+ /* FIXME can join_desired actually be changed by pim_rpf_update()
+ returning PIM_RPF_CHANGED ? */
+ pim_upstream_update_join_desired(up);
+
+ } /* PIM_RPF_CHANGED */
+
+ } /* for (qpim_upstream_list) */
+
+}
+
+static void scan_oil()
+{
+ struct listnode *node;
+ struct listnode *nextnode;
+ struct channel_oil *c_oil;
+
+ for (ALL_LIST_ELEMENTS(qpim_channel_oil_list, node, nextnode, c_oil)) {
+ int old_vif_index;
+ int input_iface_vif_index = fib_lookup_if_vif_index(c_oil->oil.mfcc_origin);
+ if (input_iface_vif_index < 1) {
+ char source_str[100];
+ char group_str[100];
+ pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ zlog_warn("%s %s: could not find input interface for (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str, group_str);
+ continue;
+ }
+
+ if (input_iface_vif_index == c_oil->oil.mfcc_parent) {
+ /* RPF unchanged */
+ continue;
+ }
+
+ if (PIM_DEBUG_ZEBRA) {
+ struct interface *old_iif = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent);
+ struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
+ char source_str[100];
+ char group_str[100];
+ pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ zlog_debug("%s %s: (S,G)=(%s,%s) input interface changed from %s vif_index=%d to %s vif_index=%d",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str, group_str,
+ old_iif ? old_iif->name : "<old_iif?>", c_oil->oil.mfcc_parent,
+ new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
+ }
+
+ /* new iif loops to existing oif ? */
+ if (c_oil->oil.mfcc_ttls[input_iface_vif_index]) {
+ struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
+
+ if (PIM_DEBUG_ZEBRA) {
+ char source_str[100];
+ char group_str[100];
+ pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ zlog_debug("%s %s: (S,G)=(%s,%s) new iif loops to existing oif: %s vif_index=%d",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str, group_str,
+ new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
+ }
+
+ del_oif(c_oil, new_iif, PIM_OIF_FLAG_PROTO_ANY);
+ }
+
+ /* update iif vif_index */
+ old_vif_index = c_oil->oil.mfcc_parent;
+ c_oil->oil.mfcc_parent = input_iface_vif_index;
+
+ /* update kernel multicast forwarding cache (MFC) */
+ if (pim_mroute_add(&c_oil->oil)) {
+ /* just log warning */
+ struct interface *old_iif = pim_if_find_by_vif_index(old_vif_index);
+ struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
+ char source_str[100];
+ char group_str[100];
+ pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ zlog_warn("%s %s: (S,G)=(%s,%s) failure updating input interface from %s vif_index=%d to %s vif_index=%d",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str, group_str,
+ old_iif ? old_iif->name : "<old_iif?>", c_oil->oil.mfcc_parent,
+ new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
+ continue;
+ }
+
+ } /* for (qpim_channel_oil_list) */
+}
+
+static int on_rpf_cache_refresh(struct thread *t)
+{
+ zassert(t);
+ zassert(qpim_rpf_cache_refresher);
+
+ qpim_rpf_cache_refresher = 0;
+
+ /* update PIM protocol state */
+ scan_upstream_rpf_cache();
+
+ /* update kernel multicast forwarding cache (MFC) */
+ scan_oil();
+
+ qpim_rpf_cache_refresh_last = pim_time_monotonic_sec();
+ ++qpim_rpf_cache_refresh_events;
+
+ return 0;
+}
+
+static void sched_rpf_cache_refresh()
+{
+ ++qpim_rpf_cache_refresh_requests;
+
+ if (qpim_rpf_cache_refresher) {
+ /* Refresh timer is already running */
+ return;
+ }
+
+ /* Start refresh timer */
+
+ if (PIM_DEBUG_ZEBRA) {
+ zlog_debug("%s: triggering %ld msec timer",
+ __PRETTY_FUNCTION__,
+ qpim_rpf_cache_refresh_delay_msec);
+ }
+
+ THREAD_TIMER_MSEC_ON(master, qpim_rpf_cache_refresher,
+ on_rpf_cache_refresh,
+ 0, qpim_rpf_cache_refresh_delay_msec);
+}
+
+static int redist_read_ipv4_route(int command, struct zclient *zclient,
+ zebra_size_t length)
+{
+ struct stream *s;
+ struct zapi_ipv4 api;
+ unsigned long ifindex;
+ struct in_addr nexthop;
+ struct prefix_ipv4 p;
+ int min_len = 4;
+
+ if (length < min_len) {
+ zlog_warn("%s %s: short buffer: length=%d min=%d",
+ __FILE__, __PRETTY_FUNCTION__,
+ length, min_len);
+ return -1;
+ }
+
+ s = zclient->ibuf;
+ ifindex = 0;
+ nexthop.s_addr = 0;
+
+ /* Type, flags, message. */
+ api.type = stream_getc(s);
+ api.flags = stream_getc(s);
+ api.message = stream_getc(s);
+
+ /* IPv4 prefix length. */
+ memset(&p, 0, sizeof(struct prefix_ipv4));
+ p.family = AF_INET;
+ p.prefixlen = stream_getc(s);
+
+ min_len +=
+ PSIZE(p.prefixlen) +
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? 5 : 0 +
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? 5 : 0 +
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? 1 : 0 +
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? 4 : 0;
+
+ if (PIM_DEBUG_ZEBRA) {
+ zlog_debug("%s %s: length=%d min_len=%d flags=%s%s%s%s",
+ __FILE__, __PRETTY_FUNCTION__,
+ length, min_len,
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? "nh" : "",
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? " ifi" : "",
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? " dist" : "",
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : "");
+ }
+
+ if (length < min_len) {
+ zlog_warn("%s %s: short buffer: length=%d min_len=%d flags=%s%s%s%s",
+ __FILE__, __PRETTY_FUNCTION__,
+ length, min_len,
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? "nh" : "",
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? " ifi" : "",
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? " dist" : "",
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : "");
+ return -1;
+ }
+
+ /* IPv4 prefix. */
+ stream_get(&p.prefix, s, PSIZE(p.prefixlen));
+
+ /* Nexthop, ifindex, distance, metric. */
+ if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) {
+ api.nexthop_num = stream_getc(s);
+ nexthop.s_addr = stream_get_ipv4(s);
+ }
+ if (CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX)) {
+ api.ifindex_num = stream_getc(s);
+ ifindex = stream_getl(s);
+ }
+
+ api.distance = CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ?
+ api.distance = stream_getc(s) :
+ 0;
+
+ api.metric = CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ?
+ stream_getl(s) :
+ 0;
+
+ switch (command) {
+ case ZEBRA_IPV4_ROUTE_ADD:
+ if (PIM_DEBUG_ZEBRA) {
+ char buf[2][INET_ADDRSTRLEN];
+ zlog_debug("%s: add %s %s/%d "
+ "nexthop %s ifindex %ld metric%s %u distance%s %u",
+ __PRETTY_FUNCTION__,
+ zebra_route_string(api.type),
+ inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])),
+ p.prefixlen,
+ inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])),
+ ifindex,
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? "-recv" : "-miss",
+ api.metric,
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? "-recv" : "-miss",
+ api.distance);
+ }
+ break;
+ case ZEBRA_IPV4_ROUTE_DELETE:
+ if (PIM_DEBUG_ZEBRA) {
+ char buf[2][INET_ADDRSTRLEN];
+ zlog_debug("%s: delete %s %s/%d "
+ "nexthop %s ifindex %ld metric%s %u distance%s %u",
+ __PRETTY_FUNCTION__,
+ zebra_route_string(api.type),
+ inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])),
+ p.prefixlen,
+ inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])),
+ ifindex,
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? "-recv" : "-miss",
+ api.metric,
+ CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? "-recv" : "-miss",
+ api.distance);
+ }
+ break;
+ default:
+ zlog_warn("%s: unknown command=%d", __PRETTY_FUNCTION__, command);
+ return -1;
+ }
+
+ sched_rpf_cache_refresh();
+
+ return 0;
+}
+
+void pim_zebra_init()
+{
+ struct zclient *zclient;
+ int i;
+
+#ifdef HAVE_TCP_ZEBRA
+ zlog_notice("zclient update contacting ZEBRA daemon at socket TCP %s,%d", "127.0.0.1", ZEBRA_PORT);
+#else
+ zlog_notice("zclient update contacting ZEBRA daemon at socket UNIX %s", path_state (ZEBRA_SERV_NAME));
+#endif
+
+ /* Socket for receiving updates from Zebra daemon */
+ zclient = zclient_new();
+
+ zclient->router_id_update = pim_router_id_update_zebra;
+ zclient->interface_add = pim_zebra_if_add;
+ zclient->interface_delete = pim_zebra_if_del;
+ zclient->interface_up = pim_zebra_if_state_up;
+ zclient->interface_down = pim_zebra_if_state_down;
+ zclient->interface_address_add = pim_zebra_if_address_add;
+ zclient->interface_address_delete = pim_zebra_if_address_del;
+ zclient->ipv4_route_add = redist_read_ipv4_route;
+ zclient->ipv4_route_delete = redist_read_ipv4_route;
+
+ zclient_init(zclient, ZEBRA_ROUTE_PIM);
+ zlog_info("zclient_init cleared redistribution request");
+
+ zassert(zclient->redist_default == ZEBRA_ROUTE_PIM);
+
+ /* Request all redistribution */
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ if (i == zclient->redist_default)
+ continue;
+ zclient->redist[i] = 1;
+ zlog_info("%s: requesting redistribution for %s (%i)",
+ __PRETTY_FUNCTION__, zebra_route_string(i), i);
+ }
+
+ /* Request default information */
+ zclient->default_information = 1;
+ zlog_info("%s: requesting default information redistribution",
+ __PRETTY_FUNCTION__);
+
+ zlog_notice("%s: zclient update socket initialized",
+ __PRETTY_FUNCTION__);
+
+ zassert(!qpim_zclient_lookup);
+ qpim_zclient_lookup = zclient_lookup_new();
+ zassert(qpim_zclient_lookup);
+}
+
+void igmp_anysource_forward_start(struct igmp_group *group)
+{
+ /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
+ zassert(group->group_filtermode_isexcl);
+ zassert(listcount(group->group_source_list) < 1);
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ zlog_debug("%s %s: UNIMPLEMENTED",
+ __FILE__, __PRETTY_FUNCTION__);
+ }
+}
+
+void igmp_anysource_forward_stop(struct igmp_group *group)
+{
+ /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
+ zassert((!group->group_filtermode_isexcl) || (listcount(group->group_source_list) > 0));
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ zlog_debug("%s %s: UNIMPLEMENTED",
+ __FILE__, __PRETTY_FUNCTION__);
+ }
+}
+
+static int fib_lookup_if_vif_index(struct in_addr addr)
+{
+ struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE];
+ int num_ifindex;
+ int vif_index;
+ int first_ifindex;
+
+ num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab,
+ PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr,
+ PIM_NEXTHOP_LOOKUP_MAX);
+ if (num_ifindex < 1) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_warn("%s %s: could not find nexthop ifindex for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ addr_str);
+ return -1;
+ }
+
+ first_ifindex = nexthop_tab[0].ifindex;
+
+ if (num_ifindex > 1) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)",
+ __FILE__, __PRETTY_FUNCTION__,
+ num_ifindex, addr_str, first_ifindex);
+ /* debug warning only, do not return */
+ }
+
+ if (PIM_DEBUG_ZEBRA) {
+ char addr_str[100];
+ pim_inet4_dump("<ifaddr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug("%s %s: found nexthop ifindex=%d (interface %s) for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ first_ifindex, ifindex2ifname(first_ifindex), addr_str);
+ }
+
+ vif_index = pim_if_find_vifindex_by_ifindex(first_ifindex);
+
+ if (vif_index < 1) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_warn("%s %s: low vif_index=%d < 1 nexthop for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ vif_index, addr_str);
+ return -2;
+ }
+
+ zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
+
+ if (vif_index > qpim_mroute_oif_highest_vif_index) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_warn("%s %s: high vif_index=%d > highest_vif_index=%d nexthop for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ vif_index, qpim_mroute_oif_highest_vif_index, addr_str);
+
+ zlog_warn("%s %s: pim disabled on interface %s vif_index=%d ?",
+ __FILE__, __PRETTY_FUNCTION__,
+ ifindex2ifname(vif_index),
+ vif_index);
+
+ return -3;
+ }
+
+ return vif_index;
+}
+
+static int add_oif(struct channel_oil *channel_oil,
+ struct interface *oif,
+ uint32_t proto_mask)
+{
+ struct pim_interface *pim_ifp;
+ int old_ttl;
+
+ zassert(channel_oil);
+
+ pim_ifp = oif->info;
+
+ if (pim_ifp->mroute_vif_index < 1) {
+ zlog_warn("%s %s: interface %s vif_index=%d < 1",
+ __FILE__, __PRETTY_FUNCTION__,
+ oif->name, pim_ifp->mroute_vif_index);
+ return -1;
+ }
+
+#ifdef PIM_ENFORCE_LOOPFREE_MFC
+ /*
+ Prevent creating MFC entry with OIF=IIF.
+
+ This is a protection against implementation mistakes.
+
+ PIM protocol implicitely ensures loopfree multicast topology.
+
+ IGMP must be protected against adding looped MFC entries created
+ by both source and receiver attached to the same interface. See
+ TODO T22.
+ */
+ if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_warn("%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ proto_mask, oif->name, pim_ifp->mroute_vif_index,
+ source_str, group_str);
+ return -2;
+ }
+#endif
+
+ zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
+ zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index);
+
+ /* Prevent single protocol from subscribing same interface to
+ channel (S,G) multiple times */
+ if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_warn("%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ proto_mask, oif->name, pim_ifp->mroute_vif_index,
+ channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+ source_str, group_str);
+ return -3;
+ }
+
+ /* Allow other protocol to request subscription of same interface to
+ channel (S,G) multiple times, by silently ignoring further
+ requests */
+ if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & PIM_OIF_FLAG_PROTO_ANY) {
+
+ /* Check the OIF really exists before returning, and only log
+ warning otherwise */
+ if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_warn("%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ proto_mask, oif->name, pim_ifp->mroute_vif_index,
+ channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+ source_str, group_str);
+ }
+
+ return 0;
+ }
+
+ old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
+
+ if (old_ttl > 0) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_warn("%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ oif->name, pim_ifp->mroute_vif_index,
+ source_str, group_str);
+ return -4;
+ }
+
+ channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL;
+
+ if (pim_mroute_add(&channel_oil->oil)) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_warn("%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ oif->name, pim_ifp->mroute_vif_index,
+ source_str, group_str);
+
+ channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
+ return -5;
+ }
+
+ channel_oil->oif_creation[pim_ifp->mroute_vif_index] = pim_time_monotonic_sec();
+ ++channel_oil->oil_size;
+ channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
+
+ return 0;
+}
+
+static int del_oif(struct channel_oil *channel_oil,
+ struct interface *oif,
+ uint32_t proto_mask)
+{
+ struct pim_interface *pim_ifp;
+ int old_ttl;
+
+ zassert(channel_oil);
+
+ pim_ifp = oif->info;
+
+ zassert(pim_ifp->mroute_vif_index >= 1);
+ zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
+ zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index);
+
+ /* Prevent single protocol from unsubscribing same interface from
+ channel (S,G) multiple times */
+ if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_warn("%s %s: nonexistent protocol mask %u removed OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ proto_mask, oif->name, pim_ifp->mroute_vif_index,
+ channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+ source_str, group_str);
+ return -2;
+ }
+
+ /* Mark that protocol is no longer interested in this OIF */
+ channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
+
+ /* Allow multiple protocols to unsubscribe same interface from
+ channel (S,G) multiple times, by silently ignoring requests while
+ there is at least one protocol interested in the channel */
+ if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & PIM_OIF_FLAG_PROTO_ANY) {
+
+ /* Check the OIF keeps existing before returning, and only log
+ warning otherwise */
+ if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_warn("%s %s: protocol mask %u removing nonexistent OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ proto_mask, oif->name, pim_ifp->mroute_vif_index,
+ channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+ source_str, group_str);
+ }
+
+ return 0;
+ }
+
+ old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
+
+ if (old_ttl < 1) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_warn("%s %s: interface %s (vif_index=%d) is not output for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ oif->name, pim_ifp->mroute_vif_index,
+ source_str, group_str);
+ return -3;
+ }
+
+ channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
+
+ if (pim_mroute_add(&channel_oil->oil)) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_warn("%s %s: could not remove output interface %s (vif_index=%d) from channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ oif->name, pim_ifp->mroute_vif_index,
+ source_str, group_str);
+
+ channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
+ return -4;
+ }
+
+ --channel_oil->oil_size;
+
+ if (channel_oil->oil_size < 1) {
+ if (pim_mroute_del(&channel_oil->oil)) {
+ /* just log a warning in case of failure */
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+ zlog_warn("%s %s: failure removing OIL for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str, group_str);
+ }
+ }
+
+ return 0;
+}
+
+void igmp_source_forward_start(struct igmp_source *source)
+{
+ struct igmp_group *group;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char source_str[100];
+ char group_str[100];
+ pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
+ zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d",
+ __PRETTY_FUNCTION__,
+ source_str, group_str,
+ source->source_group->group_igmp_sock->fd,
+ source->source_group->group_igmp_sock->interface->name,
+ IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
+ }
+
+ /* Prevent IGMP interface from installing multicast route multiple
+ times */
+ if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
+ return;
+ }
+
+ group = source->source_group;
+
+ if (!source->source_channel_oil) {
+ struct pim_interface *pim_oif;
+ int input_iface_vif_index = fib_lookup_if_vif_index(source->source_addr);
+ if (input_iface_vif_index < 1) {
+ char source_str[100];
+ pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s %s: could not find input interface for source %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str);
+ return;
+ }
+
+ /*
+ Protect IGMP against adding looped MFC entries created by both
+ source and receiver attached to the same interface. See TODO
+ T22.
+ */
+ pim_oif = source->source_group->group_igmp_sock->interface->info;
+ if (!pim_oif) {
+ zlog_warn("%s: multicast not enabled on oif=%s ?",
+ __PRETTY_FUNCTION__,
+ source->source_group->group_igmp_sock->interface->name);
+ return;
+ }
+ if (pim_oif->mroute_vif_index < 1) {
+ zlog_warn("%s %s: oif=%s vif_index=%d < 1",
+ __FILE__, __PRETTY_FUNCTION__,
+ source->source_group->group_igmp_sock->interface->name,
+ pim_oif->mroute_vif_index);
+ return;
+ }
+ if (input_iface_vif_index == pim_oif->mroute_vif_index) {
+ /* ignore request for looped MFC entry */
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char source_str[100];
+ char group_str[100];
+ pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
+ zlog_debug("%s: ignoring request for looped MFC entry (S,G)=(%s,%s): igmp_sock=%d oif=%s vif_index=%d",
+ __PRETTY_FUNCTION__,
+ source_str, group_str,
+ source->source_group->group_igmp_sock->fd,
+ source->source_group->group_igmp_sock->interface->name,
+ input_iface_vif_index);
+ }
+ return;
+ }
+
+ source->source_channel_oil = pim_channel_oil_add(group->group_addr,
+ source->source_addr,
+ input_iface_vif_index);
+ if (!source->source_channel_oil) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str, group_str);
+ return;
+ }
+ }
+
+ if (add_oif(source->source_channel_oil,
+ group->group_igmp_sock->interface,
+ PIM_OIF_FLAG_PROTO_IGMP)) {
+ return;
+ }
+
+ /*
+ Feed IGMPv3-gathered local membership information into PIM
+ per-interface (S,G) state.
+ */
+ pim_ifchannel_local_membership_add(group->group_igmp_sock->interface,
+ source->source_addr, group->group_addr);
+
+ IGMP_SOURCE_DO_FORWARDING(source->source_flags);
+}
+
+void igmp_source_forward_stop(struct igmp_source *source)
+{
+ struct igmp_group *group;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char source_str[100];
+ char group_str[100];
+ pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
+ zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d",
+ __PRETTY_FUNCTION__,
+ source_str, group_str,
+ source->source_group->group_igmp_sock->fd,
+ source->source_group->group_igmp_sock->interface->name,
+ IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
+ }
+
+ /* Prevent IGMP interface from removing multicast route multiple
+ times */
+ if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
+ return;
+ }
+
+ group = source->source_group;
+
+ if (del_oif(source->source_channel_oil,
+ group->group_igmp_sock->interface,
+ PIM_OIF_FLAG_PROTO_IGMP)) {
+ return;
+ }
+
+ /*
+ Feed IGMPv3-gathered local membership information into PIM
+ per-interface (S,G) state.
+ */
+ pim_ifchannel_local_membership_del(group->group_igmp_sock->interface,
+ source->source_addr, group->group_addr);
+
+ IGMP_SOURCE_DONT_FORWARDING(source->source_flags);
+}
+
+void pim_forward_start(struct pim_ifchannel *ch)
+{
+ struct pim_upstream *up = ch->upstream;
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char source_str[100];
+ char group_str[100];
+ pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
+ zlog_debug("%s: (S,G)=(%s,%s) oif=%s",
+ __PRETTY_FUNCTION__,
+ source_str, group_str, ch->interface->name);
+ }
+
+ if (!up->channel_oil) {
+ int input_iface_vif_index = fib_lookup_if_vif_index(up->source_addr);
+ if (input_iface_vif_index < 1) {
+ char source_str[100];
+ pim_inet4_dump("<source?>", up->source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s %s: could not find input interface for source %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str);
+ return;
+ }
+
+ up->channel_oil = pim_channel_oil_add(up->group_addr, up->source_addr,
+ input_iface_vif_index);
+ if (!up->channel_oil) {
+ char group_str[100];
+ char source_str[100];
+ pim_inet4_dump("<group?>", up->group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>", up->source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str, group_str);
+ return;
+ }
+ }
+
+ add_oif(up->channel_oil,
+ ch->interface,
+ PIM_OIF_FLAG_PROTO_PIM);
+}
+
+void pim_forward_stop(struct pim_ifchannel *ch)
+{
+ struct pim_upstream *up = ch->upstream;
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char source_str[100];
+ char group_str[100];
+ pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
+ zlog_debug("%s: (S,G)=(%s,%s) oif=%s",
+ __PRETTY_FUNCTION__,
+ source_str, group_str, ch->interface->name);
+ }
+
+ if (!up->channel_oil) {
+ char source_str[100];
+ char group_str[100];
+ pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
+ pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
+ zlog_warn("%s: (S,G)=(%s,%s) oif=%s missing channel OIL",
+ __PRETTY_FUNCTION__,
+ source_str, group_str, ch->interface->name);
+
+ return;
+ }
+
+ del_oif(up->channel_oil,
+ ch->interface,
+ PIM_OIF_FLAG_PROTO_PIM);
+}
diff --git a/pimd/pim_zebra.h b/pimd/pim_zebra.h
new file mode 100644
index 00000000..e0c9bdcb
--- /dev/null
+++ b/pimd/pim_zebra.h
@@ -0,0 +1,40 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_ZEBRA_H
+#define PIM_ZEBRA_H
+
+#include "pim_igmp.h"
+#include "pim_ifchannel.h"
+
+void pim_zebra_init(void);
+
+void igmp_anysource_forward_start(struct igmp_group *group);
+void igmp_anysource_forward_stop(struct igmp_group *group);
+
+void igmp_source_forward_start(struct igmp_source *source);
+void igmp_source_forward_stop(struct igmp_source *source);
+
+void pim_forward_start(struct pim_ifchannel *ch);
+void pim_forward_stop(struct pim_ifchannel *ch);
+
+#endif /* PIM_ZEBRA_H */
diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c
new file mode 100644
index 00000000..5f3eeaba
--- /dev/null
+++ b/pimd/pim_zlookup.c
@@ -0,0 +1,456 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+#include "zebra/rib.h"
+
+#include "log.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "stream.h"
+#include "network.h"
+#include "thread.h"
+#include "paths.h"
+
+#include "pimd.h"
+#include "pim_pim.h"
+#include "pim_str.h"
+#include "pim_zlookup.h"
+
+extern int zclient_debug;
+
+static void zclient_lookup_sched(struct zclient *zlookup, int delay);
+
+/* Connect to zebra for nexthop lookup. */
+static int zclient_lookup_connect(struct thread *t)
+{
+ struct zclient *zlookup;
+
+ zlookup = THREAD_ARG(t);
+ zlookup->t_connect = NULL;
+
+ if (zlookup->sock >= 0) {
+ return 0;
+ }
+
+#ifdef HAVE_TCP_ZEBRA
+ zlog_debug("%s: FIXME blocking connect: zclient_socket()",
+ __PRETTY_FUNCTION__);
+ zlookup->sock = zclient_socket();
+ if (zlookup->sock < 0) {
+ zlog_warn("%s: failure connecting TCP socket %s,%d",
+ __PRETTY_FUNCTION__, "127.0.0.1", ZEBRA_PORT);
+ }
+ else if (zclient_debug) {
+ zlog_notice("%s: connected TCP socket %s,%d",
+ __PRETTY_FUNCTION__, "127.0.0.1", ZEBRA_PORT);
+ }
+#else
+ zlog_debug("%s: FIXME blocking connect: zclient_socket_un()",
+ __PRETTY_FUNCTION__);
+ zlookup->sock = zclient_socket_un(path_state (ZEBRA_SERV_NAME));
+ if (zlookup->sock < 0) {
+ zlog_warn("%s: failure connecting UNIX socket %s",
+ __PRETTY_FUNCTION__, path_state (ZEBRA_SERV_NAME));
+ }
+ else if (zclient_debug) {
+ zlog_notice("%s: connected UNIX socket %s",
+ __PRETTY_FUNCTION__, path_state (ZEBRA_SERV_NAME));
+ }
+#endif /* HAVE_TCP_ZEBRA */
+
+ zassert(!zlookup->t_connect);
+ if (zlookup->sock < 0) {
+ /* Since last connect failed, retry within 10 secs */
+ zclient_lookup_sched(zlookup, 10);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Schedule connection with delay. */
+static void zclient_lookup_sched(struct zclient *zlookup, int delay)
+{
+ zassert(!zlookup->t_connect);
+
+ THREAD_TIMER_ON(master, zlookup->t_connect,
+ zclient_lookup_connect,
+ zlookup, delay);
+
+ zlog_notice("%s: zclient lookup connection scheduled for %d seconds",
+ __PRETTY_FUNCTION__, delay);
+}
+
+/* Schedule connection for now. */
+static void zclient_lookup_sched_now(struct zclient *zlookup)
+{
+ zassert(!zlookup->t_connect);
+
+ zlookup->t_connect = thread_add_event(master, zclient_lookup_connect,
+ zlookup, 0);
+
+ zlog_notice("%s: zclient lookup immediate connection scheduled",
+ __PRETTY_FUNCTION__);
+}
+
+/* Schedule reconnection, if needed. */
+static void zclient_lookup_reconnect(struct zclient *zlookup)
+{
+ if (zlookup->t_connect) {
+ return;
+ }
+
+ zclient_lookup_sched_now(zlookup);
+}
+
+struct zclient *zclient_lookup_new()
+{
+ struct zclient *zlookup;
+
+ zlookup = zclient_new();
+ if (!zlookup) {
+ zlog_err("%s: zclient_new() failure",
+ __PRETTY_FUNCTION__);
+ return 0;
+ }
+
+ zlookup->sock = -1;
+ zlookup->ibuf = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ zlookup->obuf = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ zlookup->t_connect = 0;
+
+ zclient_lookup_sched_now(zlookup);
+
+ zlog_notice("%s: zclient lookup socket initialized",
+ __PRETTY_FUNCTION__);
+
+ return zlookup;
+}
+
+static int zclient_read_nexthop(struct zclient *zlookup,
+ struct pim_zlookup_nexthop nexthop_tab[],
+ const int tab_size,
+ struct in_addr addr)
+{
+ int num_ifindex = 0;
+ struct stream *s;
+ const uint16_t MIN_LEN = 14; /* getc=1 getc=1 getw=2 getipv4=4 getc=1 getl=4 getc=1 */
+ uint16_t length, len;
+ u_char marker;
+ u_char version;
+ uint16_t command;
+ int nbytes;
+ struct in_addr raddr;
+ uint8_t distance;
+ uint32_t metric;
+ int nexthop_num;
+ int i;
+
+ if (PIM_DEBUG_ZEBRA) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug("%s: addr=%s",
+ __PRETTY_FUNCTION__,
+ addr_str);
+ }
+
+ s = zlookup->ibuf;
+ stream_reset(s);
+
+ nbytes = stream_read(s, zlookup->sock, 2);
+ if (nbytes < 2) {
+ zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d",
+ __FILE__, __PRETTY_FUNCTION__, nbytes);
+ close(zlookup->sock);
+ zlookup->sock = -1;
+ zclient_lookup_reconnect(zlookup);
+ return -1;
+ }
+ length = stream_getw(s);
+
+ len = length - 2;
+
+ if (len < MIN_LEN) {
+ zlog_err("%s %s: failure reading zclient lookup socket: len=%d < MIN_LEN=%d",
+ __FILE__, __PRETTY_FUNCTION__, len, MIN_LEN);
+ close(zlookup->sock);
+ zlookup->sock = -1;
+ zclient_lookup_reconnect(zlookup);
+ return -2;
+ }
+
+ nbytes = stream_read(s, zlookup->sock, len);
+ if (nbytes < (length - 2)) {
+ zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d < len=%d",
+ __FILE__, __PRETTY_FUNCTION__, nbytes, len);
+ close(zlookup->sock);
+ zlookup->sock = -1;
+ zclient_lookup_reconnect(zlookup);
+ return -3;
+ }
+ marker = stream_getc(s);
+ version = stream_getc(s);
+
+ if (version != ZSERV_VERSION || marker != ZEBRA_HEADER_MARKER) {
+ zlog_err("%s: socket %d version mismatch, marker %d, version %d",
+ __func__, zlookup->sock, marker, version);
+ return -4;
+ }
+
+ command = stream_getw(s);
+ if (command != ZEBRA_IPV4_NEXTHOP_LOOKUP_V2) {
+ zlog_err("%s: socket %d command mismatch: %d",
+ __func__, zlookup->sock, command);
+ return -5;
+ }
+
+ raddr.s_addr = stream_get_ipv4(s);
+
+ if (raddr.s_addr != addr.s_addr) {
+ char addr_str[100];
+ char raddr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ pim_inet4_dump("<raddr?>", raddr, raddr_str, sizeof(raddr_str));
+ zlog_warn("%s: address mismatch: addr=%s raddr=%s",
+ __PRETTY_FUNCTION__,
+ addr_str, raddr_str);
+ /* warning only */
+ }
+
+ distance = stream_getc(s);
+ metric = stream_getl(s);
+ nexthop_num = stream_getc(s);
+
+ if (nexthop_num < 1) {
+ zlog_err("%s: socket %d bad nexthop_num=%d",
+ __func__, zlookup->sock, nexthop_num);
+ return -6;
+ }
+
+ len -= MIN_LEN;
+
+ for (i = 0; i < nexthop_num; ++i) {
+ enum nexthop_types_t nexthop_type;
+
+ if (len < 1) {
+ zlog_err("%s: socket %d empty input expecting nexthop_type: len=%d",
+ __func__, zlookup->sock, len);
+ return -7;
+ }
+
+ nexthop_type = stream_getc(s);
+ --len;
+
+ switch (nexthop_type) {
+ case ZEBRA_NEXTHOP_IFINDEX:
+ case ZEBRA_NEXTHOP_IFNAME:
+ case ZEBRA_NEXTHOP_IPV4_IFINDEX:
+ if (num_ifindex >= tab_size) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ (num_ifindex + 1), tab_size, addr_str);
+ return num_ifindex;
+ }
+ if (nexthop_type == ZEBRA_NEXTHOP_IPV4_IFINDEX) {
+ if (len < 4) {
+ zlog_err("%s: socket %d short input expecting nexthop IPv4-addr: len=%d",
+ __func__, zlookup->sock, len);
+ return -8;
+ }
+ nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
+ len -= 4;
+ }
+ else {
+ nexthop_tab[num_ifindex].nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
+ }
+ nexthop_tab[num_ifindex].ifindex = stream_getl(s);
+ nexthop_tab[num_ifindex].protocol_distance = distance;
+ nexthop_tab[num_ifindex].route_metric = metric;
+ ++num_ifindex;
+ break;
+ case ZEBRA_NEXTHOP_IPV4:
+ if (num_ifindex >= tab_size) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ (num_ifindex + 1), tab_size, addr_str);
+ return num_ifindex;
+ }
+ nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
+ len -= 4;
+ nexthop_tab[num_ifindex].ifindex = 0;
+ nexthop_tab[num_ifindex].protocol_distance = distance;
+ nexthop_tab[num_ifindex].route_metric = metric;
+ {
+ char addr_str[100];
+ char nexthop_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ pim_inet4_dump("<nexthop?>", nexthop_tab[num_ifindex].nexthop_addr, nexthop_str, sizeof(nexthop_str));
+ zlog_warn("%s %s: zebra returned recursive nexthop %s for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ nexthop_str, addr_str);
+ }
+ ++num_ifindex;
+ break;
+ default:
+ /* do nothing */
+ {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_warn("%s %s: found non-ifindex nexthop type=%d for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ nexthop_type, addr_str);
+ }
+ break;
+ }
+ }
+
+ return num_ifindex;
+}
+
+static int zclient_lookup_nexthop_once(struct zclient *zlookup,
+ struct pim_zlookup_nexthop nexthop_tab[],
+ const int tab_size,
+ struct in_addr addr)
+{
+ struct stream *s;
+ int ret;
+
+ if (PIM_DEBUG_ZEBRA) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_debug("%s: addr=%s",
+ __PRETTY_FUNCTION__,
+ addr_str);
+ }
+
+ /* Check socket. */
+ if (zlookup->sock < 0) {
+ zlog_err("%s %s: zclient lookup socket is not connected",
+ __FILE__, __PRETTY_FUNCTION__);
+ zclient_lookup_reconnect(zlookup);
+ return -1;
+ }
+
+ s = zlookup->obuf;
+ stream_reset(s);
+ zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_V2);
+ stream_put_in_addr(s, &addr);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ ret = writen(zlookup->sock, s->data, stream_get_endp(s));
+ if (ret < 0) {
+ zlog_err("%s %s: writen() failure writing to zclient lookup socket",
+ __FILE__, __PRETTY_FUNCTION__);
+ close(zlookup->sock);
+ zlookup->sock = -1;
+ zclient_lookup_reconnect(zlookup);
+ return -2;
+ }
+ if (ret == 0) {
+ zlog_err("%s %s: connection closed on zclient lookup socket",
+ __FILE__, __PRETTY_FUNCTION__);
+ close(zlookup->sock);
+ zlookup->sock = -1;
+ zclient_lookup_reconnect(zlookup);
+ return -3;
+ }
+
+ return zclient_read_nexthop(zlookup, nexthop_tab,
+ tab_size, addr);
+}
+
+int zclient_lookup_nexthop(struct zclient *zlookup,
+ struct pim_zlookup_nexthop nexthop_tab[],
+ const int tab_size,
+ struct in_addr addr,
+ int max_lookup)
+{
+ int lookup;
+
+ for (lookup = 0; lookup < max_lookup; ++lookup) {
+ int num_ifindex;
+ int first_ifindex;
+ struct in_addr nexthop_addr;
+
+ num_ifindex = zclient_lookup_nexthop_once(qpim_zclient_lookup, nexthop_tab,
+ PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr);
+ if (num_ifindex < 1) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_warn("%s %s: lookup=%d/%d: could not find nexthop ifindex for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ lookup, max_lookup, addr_str);
+ return -1;
+ }
+
+ /*
+ FIXME: Non-recursive nexthop ensured only for first ifindex.
+ However, recursive route lookup should really be fixed in zebra daemon.
+ See also TODO T24.
+ */
+ first_ifindex = nexthop_tab[0].ifindex;
+ nexthop_addr = nexthop_tab[0].nexthop_addr;
+ if (first_ifindex > 0) {
+ /* found: first ifindex is non-recursive nexthop */
+
+ if (lookup > 0) {
+ /* Report non-recursive success after first lookup */
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_info("%s %s: lookup=%d/%d: found non-recursive ifindex=%d for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ lookup, max_lookup, first_ifindex, addr_str);
+
+ /* use last address as nexthop address */
+ nexthop_tab[0].nexthop_addr = addr;
+ }
+
+ return num_ifindex;
+ }
+
+ {
+ char addr_str[100];
+ char nexthop_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ pim_inet4_dump("<nexthop?>", nexthop_addr, nexthop_str, sizeof(nexthop_str));
+ zlog_warn("%s %s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ lookup, max_lookup, nexthop_str, addr_str);
+ }
+
+ addr = nexthop_addr; /* use nexthop addr for recursive lookup */
+
+ } /* for (max_lookup) */
+
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_warn("%s %s: lookup=%d/%d: failure searching recursive nexthop ifindex for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ lookup, max_lookup, addr_str);
+
+ return -2;
+}
diff --git a/pimd/pim_zlookup.h b/pimd/pim_zlookup.h
new file mode 100644
index 00000000..1f184942
--- /dev/null
+++ b/pimd/pim_zlookup.h
@@ -0,0 +1,47 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_ZLOOKUP_H
+#define PIM_ZLOOKUP_H
+
+#include <zebra.h>
+
+#include "zclient.h"
+
+#define PIM_NEXTHOP_LOOKUP_MAX (3) /* max. recursive route lookup */
+
+struct pim_zlookup_nexthop {
+ struct in_addr nexthop_addr;
+ int ifindex;
+ uint32_t route_metric;
+ uint8_t protocol_distance;
+};
+
+struct zclient *zclient_lookup_new(void);
+
+int zclient_lookup_nexthop(struct zclient *zlookup,
+ struct pim_zlookup_nexthop nexthop_tab[],
+ const int tab_size,
+ struct in_addr addr,
+ int max_lookup);
+
+#endif /* PIM_ZLOOKUP_H */
diff --git a/pimd/pimd.c b/pimd/pimd.c
new file mode 100644
index 00000000..71b74e97
--- /dev/null
+++ b/pimd/pimd.c
@@ -0,0 +1,134 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+
+#include "pimd.h"
+#include "pim_cmd.h"
+#include "pim_iface.h"
+#include "pim_zebra.h"
+#include "pim_str.h"
+#include "pim_oil.h"
+#include "pim_pim.h"
+#include "pim_upstream.h"
+#include "pim_rand.h"
+#include "pim_rpf.h"
+#include "pim_ssmpingd.h"
+
+const char *const PIM_ALL_SYSTEMS = MCAST_ALL_SYSTEMS;
+const char *const PIM_ALL_ROUTERS = MCAST_ALL_ROUTERS;
+const char *const PIM_ALL_PIM_ROUTERS = MCAST_ALL_PIM_ROUTERS;
+const char *const PIM_ALL_IGMP_ROUTERS = MCAST_ALL_IGMP_ROUTERS;
+
+struct thread_master *master = 0;
+uint32_t qpim_debugs = 0;
+int qpim_mroute_socket_fd = -1;
+int64_t qpim_mroute_socket_creation = 0; /* timestamp of creation */
+struct thread *qpim_mroute_socket_reader = 0;
+int qpim_mroute_oif_highest_vif_index = -1;
+struct list *qpim_channel_oil_list = 0;
+int qpim_t_periodic = PIM_DEFAULT_T_PERIODIC; /* Period between Join/Prune Messages */
+struct list *qpim_upstream_list = 0;
+struct zclient *qpim_zclient_lookup = 0;
+struct pim_assert_metric qpim_infinite_assert_metric;
+long qpim_rpf_cache_refresh_delay_msec = 10000;
+struct thread *qpim_rpf_cache_refresher = 0;
+int64_t qpim_rpf_cache_refresh_requests = 0;
+int64_t qpim_rpf_cache_refresh_events = 0;
+int64_t qpim_rpf_cache_refresh_last = 0;
+struct in_addr qpim_inaddr_any;
+struct list *qpim_ssmpingd_list = 0;
+struct in_addr qpim_ssmpingd_group_addr;
+
+static void pim_free()
+{
+ pim_ssmpingd_destroy();
+
+ if (qpim_channel_oil_list)
+ list_free(qpim_channel_oil_list);
+
+ if (qpim_upstream_list)
+ list_free(qpim_upstream_list);
+}
+
+void pim_init()
+{
+ pim_rand_init();
+
+ if (!inet_aton(PIM_ALL_PIM_ROUTERS, &qpim_all_pim_routers_addr)) {
+ zlog_err("%s %s: could not solve %s to group address: errno=%d: %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ PIM_ALL_PIM_ROUTERS, errno, safe_strerror(errno));
+ zassert(0);
+ return;
+ }
+
+ qpim_channel_oil_list = list_new();
+ if (!qpim_channel_oil_list) {
+ zlog_err("%s %s: failure: channel_oil_list=list_new()",
+ __FILE__, __PRETTY_FUNCTION__);
+ return;
+ }
+ qpim_channel_oil_list->del = (void (*)(void *)) pim_channel_oil_free;
+
+ qpim_upstream_list = list_new();
+ if (!qpim_upstream_list) {
+ zlog_err("%s %s: failure: upstream_list=list_new()",
+ __FILE__, __PRETTY_FUNCTION__);
+ pim_free();
+ return;
+ }
+ qpim_upstream_list->del = (void (*)(void *)) pim_upstream_free;
+
+ qpim_mroute_socket_fd = -1; /* mark mroute as disabled */
+ qpim_mroute_oif_highest_vif_index = -1;
+
+ zassert(!qpim_debugs);
+ zassert(!PIM_MROUTE_IS_ENABLED);
+
+ qpim_inaddr_any.s_addr = PIM_NET_INADDR_ANY;
+
+ /*
+ RFC 4601: 4.6.3. Assert Metrics
+
+ assert_metric
+ infinite_assert_metric() {
+ return {1,infinity,infinity,0}
+ }
+ */
+ qpim_infinite_assert_metric.rpt_bit_flag = 1;
+ qpim_infinite_assert_metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX;
+ qpim_infinite_assert_metric.route_metric = PIM_ASSERT_ROUTE_METRIC_MAX;
+ qpim_infinite_assert_metric.ip_address = qpim_inaddr_any;
+
+ pim_if_init();
+ pim_cmd_init();
+ pim_ssmpingd_init();
+}
+
+void pim_terminate()
+{
+ pim_free();
+}
diff --git a/pimd/pimd.conf.sample b/pimd/pimd.conf.sample
new file mode 100644
index 00000000..67530856
--- /dev/null
+++ b/pimd/pimd.conf.sample
@@ -0,0 +1,41 @@
+!
+! pimd sample configuration file
+! $QuaggaId: $Format:%an, %ai, %h$ $
+!
+hostname quagga-pimd-router
+password zebra
+!enable password zebra
+!
+!log file pimd.log
+log stdout
+!
+line vty
+ exec-timeout 60
+!
+!debug igmp
+!debug pim
+!debug pim zebra
+!
+ip multicast-routing
+!
+! ! You may want to enable ssmpingd for troubleshooting
+! ! See http://www.venaas.no/multicast/ssmping/
+! !
+! ip ssmpingd 1.1.1.1
+! ip ssmpingd 2.2.2.2
+!
+! ! HINTS:
+! ! - Enable "ip pim ssm" on the interface directly attached to the
+! ! multicast source host (if this is the first-hop router)
+! ! - Enable "ip pim ssm" on pim-routers-facing interfaces
+! ! - Enable "ip igmp" on IGMPv3-hosts-facing interfaces
+! ! - In order to inject IGMPv3 local membership information in the
+! ! PIM protocol state, enable both "ip pim ssm" and "ip igmp" on
+! ! the same interface; otherwise PIM won't advertise
+! ! IGMPv3-learned membership to other PIM routers
+!
+interface eth0
+ ip pim ssm
+ ip igmp
+
+! -x-
diff --git a/pimd/pimd.h b/pimd/pimd.h
new file mode 100644
index 00000000..1dcd8654
--- /dev/null
+++ b/pimd/pimd.h
@@ -0,0 +1,138 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIMD_H
+#define PIMD_H
+
+#include <stdint.h>
+
+#include "pim_mroute.h"
+#include "pim_assert.h"
+
+#define PIMD_PROGNAME "pimd"
+#define PIMD_DEFAULT_CONFIG "pimd.conf"
+#define PIMD_VTY_PORT 2611
+#define PIMD_BUG_ADDRESS "http://savannah.nongnu.org/projects/qpimd"
+
+#define PIM_IP_HEADER_MIN_LEN (20)
+#define PIM_IP_HEADER_MAX_LEN (60)
+#define PIM_IP_PROTO_IGMP (2)
+#define PIM_IP_PROTO_PIM (103)
+#define PIM_IGMP_MIN_LEN (8)
+#define PIM_MSG_HEADER_LEN (4)
+#define PIM_PIM_MIN_LEN PIM_MSG_HEADER_LEN
+#define PIM_PROTO_VERSION (2)
+
+#define MCAST_ALL_SYSTEMS "224.0.0.1"
+#define MCAST_ALL_ROUTERS "224.0.0.2"
+#define MCAST_ALL_PIM_ROUTERS "224.0.0.13"
+#define MCAST_ALL_IGMP_ROUTERS "224.0.0.22"
+
+#define PIM_FORCE_BOOLEAN(expr) ((expr) != 0)
+
+#define PIM_NET_INADDR_ANY (htonl(INADDR_ANY))
+#define PIM_INADDR_IS_ANY(addr) ((addr).s_addr == PIM_NET_INADDR_ANY) /* struct in_addr addr */
+#define PIM_INADDR_ISNOT_ANY(addr) ((addr).s_addr != PIM_NET_INADDR_ANY) /* struct in_addr addr */
+
+#define PIM_MASK_PIM_EVENTS (1 << 0)
+#define PIM_MASK_PIM_PACKETS (1 << 1)
+#define PIM_MASK_PIM_PACKETDUMP_SEND (1 << 2)
+#define PIM_MASK_PIM_PACKETDUMP_RECV (1 << 3)
+#define PIM_MASK_PIM_TRACE (1 << 4)
+#define PIM_MASK_IGMP_EVENTS (1 << 5)
+#define PIM_MASK_IGMP_PACKETS (1 << 6)
+#define PIM_MASK_IGMP_TRACE (1 << 7)
+#define PIM_MASK_ZEBRA (1 << 8)
+#define PIM_MASK_SSMPINGD (1 << 9)
+
+const char *const PIM_ALL_SYSTEMS;
+const char *const PIM_ALL_ROUTERS;
+const char *const PIM_ALL_PIM_ROUTERS;
+const char *const PIM_ALL_IGMP_ROUTERS;
+
+struct thread_master *master;
+uint32_t qpim_debugs;
+int qpim_mroute_socket_fd;
+int64_t qpim_mroute_socket_creation; /* timestamp of creation */
+struct thread *qpim_mroute_socket_reader;
+int qpim_mroute_oif_highest_vif_index;
+struct list *qpim_channel_oil_list; /* list of struct channel_oil */
+struct in_addr qpim_all_pim_routers_addr;
+int qpim_t_periodic; /* Period between Join/Prune Messages */
+struct list *qpim_upstream_list; /* list of struct pim_upstream */
+struct zclient *qpim_zclient_lookup;
+struct pim_assert_metric qpim_infinite_assert_metric;
+long qpim_rpf_cache_refresh_delay_msec;
+struct thread *qpim_rpf_cache_refresher;
+int64_t qpim_rpf_cache_refresh_requests;
+int64_t qpim_rpf_cache_refresh_events;
+int64_t qpim_rpf_cache_refresh_last;
+struct in_addr qpim_inaddr_any;
+struct list *qpim_ssmpingd_list; /* list of struct ssmpingd_sock */
+struct in_addr qpim_ssmpingd_group_addr;
+
+#define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2)
+
+#define PIM_MROUTE_IS_ENABLED (qpim_mroute_socket_fd >= 0)
+#define PIM_MROUTE_IS_DISABLED (qpim_mroute_socket_fd < 0)
+
+#define PIM_DEBUG_PIM_EVENTS (qpim_debugs & PIM_MASK_PIM_EVENTS)
+#define PIM_DEBUG_PIM_PACKETS (qpim_debugs & PIM_MASK_PIM_PACKETS)
+#define PIM_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs & PIM_MASK_PIM_PACKETDUMP_SEND)
+#define PIM_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs & PIM_MASK_PIM_PACKETDUMP_RECV)
+#define PIM_DEBUG_PIM_TRACE (qpim_debugs & PIM_MASK_PIM_TRACE)
+#define PIM_DEBUG_IGMP_EVENTS (qpim_debugs & PIM_MASK_IGMP_EVENTS)
+#define PIM_DEBUG_IGMP_PACKETS (qpim_debugs & PIM_MASK_IGMP_PACKETS)
+#define PIM_DEBUG_IGMP_TRACE (qpim_debugs & PIM_MASK_IGMP_TRACE)
+#define PIM_DEBUG_ZEBRA (qpim_debugs & PIM_MASK_ZEBRA)
+#define PIM_DEBUG_SSMPINGD (qpim_debugs & PIM_MASK_SSMPINGD)
+
+#define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS))
+#define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS))
+#define PIM_DEBUG_TRACE (qpim_debugs & (PIM_MASK_PIM_TRACE | PIM_MASK_IGMP_TRACE))
+
+#define PIM_DO_DEBUG_PIM_EVENTS (qpim_debugs |= PIM_MASK_PIM_EVENTS)
+#define PIM_DO_DEBUG_PIM_PACKETS (qpim_debugs |= PIM_MASK_PIM_PACKETS)
+#define PIM_DO_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs |= PIM_MASK_PIM_PACKETDUMP_SEND)
+#define PIM_DO_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs |= PIM_MASK_PIM_PACKETDUMP_RECV)
+#define PIM_DO_DEBUG_PIM_TRACE (qpim_debugs |= PIM_MASK_PIM_TRACE)
+#define PIM_DO_DEBUG_IGMP_EVENTS (qpim_debugs |= PIM_MASK_IGMP_EVENTS)
+#define PIM_DO_DEBUG_IGMP_PACKETS (qpim_debugs |= PIM_MASK_IGMP_PACKETS)
+#define PIM_DO_DEBUG_IGMP_TRACE (qpim_debugs |= PIM_MASK_IGMP_TRACE)
+#define PIM_DO_DEBUG_ZEBRA (qpim_debugs |= PIM_MASK_ZEBRA)
+#define PIM_DO_DEBUG_SSMPINGD (qpim_debugs |= PIM_MASK_SSMPINGD)
+
+#define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS)
+#define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS)
+#define PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs &= ~PIM_MASK_PIM_PACKETDUMP_SEND)
+#define PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs &= ~PIM_MASK_PIM_PACKETDUMP_RECV)
+#define PIM_DONT_DEBUG_PIM_TRACE (qpim_debugs &= ~PIM_MASK_PIM_TRACE)
+#define PIM_DONT_DEBUG_IGMP_EVENTS (qpim_debugs &= ~PIM_MASK_IGMP_EVENTS)
+#define PIM_DONT_DEBUG_IGMP_PACKETS (qpim_debugs &= ~PIM_MASK_IGMP_PACKETS)
+#define PIM_DONT_DEBUG_IGMP_TRACE (qpim_debugs &= ~PIM_MASK_IGMP_TRACE)
+#define PIM_DONT_DEBUG_ZEBRA (qpim_debugs &= ~PIM_MASK_ZEBRA)
+#define PIM_DONT_DEBUG_SSMPINGD (qpim_debugs &= ~PIM_MASK_SSMPINGD)
+
+void pim_init(void);
+void pim_terminate(void);
+
+#endif /* PIMD_H */
diff --git a/pimd/quagga-bootstrap.sh b/pimd/quagga-bootstrap.sh
new file mode 100755
index 00000000..1ad9a121
--- /dev/null
+++ b/pimd/quagga-bootstrap.sh
@@ -0,0 +1,21 @@
+#! /bin/bash
+#
+# Bootstrap Quagga autotools for pimd.
+#
+# Run from quagga's top dir as:
+# ./pimd/quagga-bootstrap.sh
+#
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+me=`basename $0`
+msg () {
+ echo >&2 $me: $*
+}
+
+if [ -f ./bootstrap.sh ]; then
+ msg found ./bootstrap.sh from quagga
+ ./bootstrap.sh
+else
+ msg missing ./bootstrap.sh from quagga
+ autoreconf -i --force
+fi
diff --git a/pimd/quagga-build.sh b/pimd/quagga-build.sh
new file mode 100755
index 00000000..2ad476d0
--- /dev/null
+++ b/pimd/quagga-build.sh
@@ -0,0 +1,10 @@
+#! /bin/bash
+#
+# Build minimum Quagga needed for pimd.
+#
+# Run from quagga's top dir as:
+# ./pimd/quagga-build.sh
+#
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+./pimd/quagga-memtypes.sh && ./pimd/quagga-bootstrap.sh && ./pimd/quagga-configure.sh && make
diff --git a/pimd/quagga-configure.sh b/pimd/quagga-configure.sh
new file mode 100755
index 00000000..45d7be08
--- /dev/null
+++ b/pimd/quagga-configure.sh
@@ -0,0 +1,10 @@
+#! /bin/bash
+#
+# Configure for minimum Quagga build needed for pimd.
+#
+# Run from quagga's top dir as:
+# . pimd/quagga-configure.sh
+#
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+./configure --disable-bgpd --disable-ripd --disable-ripngd --disable-ospfd --disable-ospf6d --disable-watchquagga --disable-bgp-announce --disable-ospfapi --disable-ospfclient --disable-rtadv --disable-irdp --enable-pimd --enable-tcp-zebra --enable-ipv6 --enable-vtysh
diff --git a/pimd/quagga-git-add.sh b/pimd/quagga-git-add.sh
new file mode 100755
index 00000000..3824e984
--- /dev/null
+++ b/pimd/quagga-git-add.sh
@@ -0,0 +1,12 @@
+#! /bin/bash
+#
+# Add to git new files created by qpimd patch
+#
+# Run from quagga's top dir as:
+# ./pimd/quagga-git-add.sh
+#
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+chmod a+rx pimd/*.sh
+git add doc/pimd.8
+git add pimd
diff --git a/pimd/quagga-memtypes.sh b/pimd/quagga-memtypes.sh
new file mode 100755
index 00000000..e86f414b
--- /dev/null
+++ b/pimd/quagga-memtypes.sh
@@ -0,0 +1,22 @@
+#! /bin/bash
+#
+# Check lib/memtypes.h from Quagga
+#
+# Run from quagga's top dir as:
+# ./pimd/quagga-memtypes.sh
+#
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+me=`basename $0`
+msg () {
+ echo >&2 $me: $*
+}
+
+memtypes_h=lib/memtypes.h
+if [ -e $memtypes_h ]; then
+ memtypes_h_size=`ls -s $memtypes_h | cut -d' ' -f1`
+ if [ "$memtypes_h_size" -lt 1 ]; then
+ msg WARNING: removing empty file: $memtypes_h -- awk failed?
+ rm $memtypes_h
+ fi
+fi
diff --git a/pimd/savannah-git-clone.sh b/pimd/savannah-git-clone.sh
new file mode 100755
index 00000000..1aad51bb
--- /dev/null
+++ b/pimd/savannah-git-clone.sh
@@ -0,0 +1,27 @@
+#! /bin/bash
+#
+# Savannah Developer Git Checkout
+#
+# Delete remote branch qpimd: git push origin :qpimd
+# (git push origin :refs/heads/branch_to_delete)
+# Delete remote tag v0.139: git push origin :v0.139
+# (git push origin :refs/tags/tag_to_delete)
+# Create remote-tracking branch: git checkout -b pim0.142 origin/pim0.142
+# Rename branch qpimd to pim: git branch -m qpimd pim
+# Commit changes: git commit -a
+# Send changes: git push --all
+#
+# Recipe to re-sync with Quagga repository:
+# git clone ssh://evertonm@git.sv.gnu.org/srv/git/qpimd.git quagga
+# cd quagga
+# git checkout master
+# git pull git://code.quagga.net/quagga.git master
+# git checkout -b pim origin/pim
+# git rebase master pim
+# # Test, then push back into Savannah repository:
+# git push origin :pim ;# delete remote branch pim
+# git push --all
+#
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+git clone ssh://evertonm@git.sv.gnu.org/srv/git/qpimd.git quagga
diff --git a/pimd/test_igmpv3_join.c b/pimd/test_igmpv3_join.c
new file mode 100644
index 00000000..179ac035
--- /dev/null
+++ b/pimd/test_igmpv3_join.c
@@ -0,0 +1,149 @@
+/*
+ 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 <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include "pim_igmp_join.h"
+
+const char *prog_name = 0;
+
+static int iface_solve_index(const char *ifname)
+{
+ struct if_nameindex *ini;
+ int ifindex = -1;
+ int i;
+
+ if (!ifname)
+ return -1;
+
+ ini = if_nameindex();
+ if (!ini) {
+ int err = errno;
+ fprintf(stderr,
+ "%s: interface=%s: failure solving index: errno=%d: %s\n",
+ prog_name, ifname, err, strerror(err));
+ errno = err;
+ return -1;
+ }
+
+ for (i = 0; ini[i].if_index; ++i) {
+#if 0
+ fprintf(stderr,
+ "%s: interface=%s matching against local ifname=%s ifindex=%d\n",
+ prog_name, ifname, ini[i].if_name, ini[i].if_index);
+#endif
+ if (!strcmp(ini[i].if_name, ifname)) {
+ ifindex = ini[i].if_index;
+ break;
+ }
+ }
+
+ if_freenameindex(ini);
+
+ return ifindex;
+}
+
+int main(int argc, const char *argv[])
+{
+ struct in_addr group_addr;
+ struct in_addr source_addr;
+ const char *ifname;
+ const char *group;
+ const char *source;
+ int ifindex;
+ int result;
+ int fd;
+
+ prog_name = argv[0];
+
+ fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (fd < 0) {
+ fprintf(stderr,
+ "%s: could not create socket: socket(): errno=%d: %s\n",
+ prog_name, errno, strerror(errno));
+ exit(1);
+ }
+
+ if (argc != 4) {
+ fprintf(stderr,
+ "usage: %s interface group source\n"
+ "example: %s eth0 232.1.1.1 1.1.1.1\n",
+ prog_name, prog_name);
+ exit(1);
+ }
+
+ ifname = argv[1];
+ group = argv[2];
+ source = argv[3];
+
+ ifindex = iface_solve_index(ifname);
+ if (ifindex < 0) {
+ fprintf(stderr, "%s: could not find interface: %s\n",
+ prog_name, ifname);
+ exit(1);
+ }
+
+ result = inet_pton(AF_INET, group, &group_addr);
+ if (result <= 0) {
+ fprintf(stderr, "%s: bad group address: %s\n",
+ prog_name, group);
+ exit(1);
+ }
+
+ result = inet_pton(AF_INET, source, &source_addr);
+ if (result <= 0) {
+ fprintf(stderr, "%s: bad source address: %s\n",
+ prog_name, source);
+ exit(1);
+ }
+
+ result = pim_igmp_join_source(fd, ifindex, group_addr, source_addr);
+ if (result) {
+ fprintf(stderr,
+ "%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s\n",
+ prog_name, fd, group, source, ifindex, ifname,
+ errno, strerror(errno));
+ exit(1);
+ }
+
+ printf("%s: joined channel (S,G)=(%s,%s) on interface %s\n",
+ prog_name, source, group, ifname);
+
+ printf("%s: waiting...\n", prog_name);
+
+ getchar();
+
+ close(fd);
+
+ printf("%s: left channel (S,G)=(%s,%s) on interface %s\n",
+ prog_name, source, group, ifname);
+
+ exit(0);
+}
diff --git a/ports/Makefile b/ports/Makefile
index d085d06e..86f77bd0 100644
--- a/ports/Makefile
+++ b/ports/Makefile
@@ -54,5 +54,6 @@ post-install:
@echo " ripngd 2603/tcp # RIPngd vty";
@echo " ospfd 2604/tcp # OSPFd vty";
@echo " bgpd 2605/tcp # BGPd vty";
+ @echo " pimd 2611/tcp # PIMd vty";
.include <bsd.port.mk>
diff --git a/ports/pkg/DESCR b/ports/pkg/DESCR
index 0c8d01b7..aeb1950e 100644
--- a/ports/pkg/DESCR
+++ b/ports/pkg/DESCR
@@ -47,6 +47,7 @@ ripd 2602/tcp # RIPd vty
ripngd 2603/tcp # RIPngd vty
ospfd 2604/tcp # OSPFd vty
bgpd 2605/tcp # BGPd vty
+pimd 2611/tcp # PIMd vty
I recommend you to add upper list to /etc/services.
diff --git a/redhat/quagga.spec.in b/redhat/quagga.spec.in
index a8c7fbf3..2d1f2c06 100644
--- a/redhat/quagga.spec.in
+++ b/redhat/quagga.spec.in
@@ -48,16 +48,16 @@
%define quagga_buildreqs %{quagga_buildreqs} patch libcap-devel
# FC4 and 5 split texi2html out of tetex package.
-%if "%dist" != "fc2" || "%dist" != "fc3"
+%if "%dist" == "fc4" || "%dist" == "fc5"
%define quagga_buildreqs %{quagga_buildreqs} texi2html
%endif
# pam_stack is deprecated in FC5
# default to pam_stack, default should be changed later.
-%if "%dist" == "fc4" || "%dist" == "fc3"
-%define quagga_pam_source quagga.pam.stack
-%else
+%if "%dist" == "fc5"
%define quagga_pam_source quagga.pam
+%else
+%define quagga_pam_source quagga.pam.stack
%endif
############################################################################
@@ -303,6 +303,9 @@ zebra_spec_add_service ospfapi 2607/tcp "OSPF-API"
%if %{with_isisd}
zebra_spec_add_service isisd 2608/tcp "ISISd vty"
%endif
+%if %{with_pimd}
+zebra_spec_add_service pimd 2611/tcp "PIMd vty"
+%endif
for daemon in %daemon_list ; do
/sbin/chkconfig --add ${daemon}
diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am
index 59265060..5597251f 100644
--- a/vtysh/Makefile.am
+++ b/vtysh/Makefile.am
@@ -31,7 +31,7 @@ vtysh_cmd_FILES = $(top_srcdir)/bgpd/*.c $(top_srcdir)/isisd/*.c \
$(top_srcdir)/zebra/irdp_interface.c \
$(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \
$(top_srcdir)/zebra/zserv.c $(top_srcdir)/zebra/router-id.c \
- $(top_srcdir)/zebra/zebra_routemap.c
+ $(top_srcdir)/zebra/zebra_routemap.c $(top_srcdir)/pimd/pim_cmd.c
vtysh_cmd.c: $(vtysh_cmd_FILES)
./$(EXTRA_DIST) $(vtysh_cmd_FILES) > vtysh_cmd.c
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 8df8dc17..fbd1b16c 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -58,6 +58,7 @@ struct vtysh_client
{ .fd = -1, .name = "ospf6d", .flag = VTYSH_OSPF6D},
{ .fd = -1, .name = "bgpd", .flag = VTYSH_BGPD},
{ .fd = -1, .name = "isisd", .flag = VTYSH_ISISD},
+ { .fd = -1, .name = "pimd", .flag = VTYSH_PIMD},
};
#define VTYSH_INDEX_MAX (sizeof(vtysh_client)/sizeof(vtysh_client[0]))
diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
index e711d593..620d35a3 100644
--- a/vtysh/vtysh.h
+++ b/vtysh/vtysh.h
@@ -29,9 +29,10 @@
#define VTYSH_OSPF6D 0x10
#define VTYSH_BGPD 0x20
#define VTYSH_ISISD 0x40
-#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD
+#define VTYSH_PIMD 0x80
+#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD
#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD
-#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD
+#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD
/* vtysh local configuration file. */
#define VTYSH_DEFAULT_CONFIG "vtysh.conf"
diff --git a/zebra/interface.c b/zebra/interface.c
index f385f8e6..23554620 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -83,6 +83,10 @@ if_zebra_new_hook (struct interface *ifp)
rtadv->DefaultPreference = RTADV_PREF_MEDIUM;
rtadv->AdvPrefixList = list_new ();
+
+ rtadv->AdvRDNSSFlag = 0;
+ rtadv->AdvRDNSSLifetime = RTADV_RDNSS_DEFAULT_LIFETIME;
+ rtadv->AdvRDNSSList = list_new ();
}
#endif /* RTADV */
@@ -655,6 +659,9 @@ nd_dump_vty (struct vty *vty, struct interface *ifp)
vty_out (vty, " ND router advertisements with "
"Home Agent flag bit set.%s",
VTY_NEWLINE);
+ if (rtadv->AdvRDNSSFlag)
+ vty_out (vty, " ND router advertisements with "
+ "RDNSS information.%s", VTY_NEWLINE);
if (rtadv->AdvIntervalOption)
vty_out (vty, " ND router advertisements with Adv. Interval option.%s",
VTY_NEWLINE);
@@ -1016,6 +1023,53 @@ DEFUN (no_multicast,
return CMD_SUCCESS;
}
+DEFUN (unnumbered,
+ unnumbered_cmd,
+ "unnumbered",
+ "Set interface to IP Unnumbered mode\n")
+{
+ int ret;
+ struct interface *ifp;
+ struct zebra_if *if_data;
+
+ ifp = (struct interface *) vty->index;
+
+ zlog_debug("VTY: interface %s, Setting ifp->status |= ZEBRA_INTERFACE_UNNUMBERED",
+ ifp->name);
+
+ SET_FLAG(ifp->status, ZEBRA_INTERFACE_UNNUMBERED);
+
+ /* force protocols to recalculate routes due to IP change */
+ if (if_is_operative (ifp))
+ zebra_interface_up_update (ifp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_unnumbered,
+ no_unnumbered_cmd,
+ "no unnumbered",
+ NO_STR
+ "Set interface to IP Numbered mode\n")
+{
+ int ret;
+ struct interface *ifp;
+ struct zebra_if *if_data;
+
+ ifp = (struct interface *) vty->index;
+
+ zlog_debug("VTY: interface %s, Setting ifp->status &= ~ZEBRA_INTERFACE_UNNUMBERED;",
+ ifp->name);
+
+ UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_UNNUMBERED);
+
+ /* force protocols to recalculate routes due to IP change */
+ if (if_is_operative (ifp))
+ zebra_interface_up_update (ifp);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (linkdetect,
linkdetect_cmd,
"link-detect",
@@ -1711,6 +1765,9 @@ if_config_write (struct vty *vty)
if (ifp->bandwidth != 0)
vty_out(vty, " bandwidth %u%s", ifp->bandwidth, VTY_NEWLINE);
+ if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_UNNUMBERED))
+ vty_out (vty, " unnumbered%s", VTY_NEWLINE);
+
if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION))
vty_out(vty, " link-detect%s", VTY_NEWLINE);
@@ -1793,6 +1850,8 @@ zebra_if_init (void)
install_element (INTERFACE_NODE, &no_interface_desc_cmd);
install_element (INTERFACE_NODE, &multicast_cmd);
install_element (INTERFACE_NODE, &no_multicast_cmd);
+ install_element (INTERFACE_NODE, &unnumbered_cmd);
+ install_element (INTERFACE_NODE, &no_unnumbered_cmd);
install_element (INTERFACE_NODE, &linkdetect_cmd);
install_element (INTERFACE_NODE, &no_linkdetect_cmd);
install_element (INTERFACE_NODE, &shutdown_if_cmd);
diff --git a/zebra/interface.h b/zebra/interface.h
index 0cf66403..305abaac 100644
--- a/zebra/interface.h
+++ b/zebra/interface.h
@@ -178,6 +178,20 @@ struct rtadvconf
Default: 0 (medium) */
int DefaultPreference;
#define RTADV_PREF_MEDIUM 0x0 /* Per RFC4191. */
+
+ /* A list of Recursive DNS server addresses specified in
+ RFC 5006 */
+ int AdvRDNSSFlag;
+ struct list *AdvRDNSSList;
+
+ /* the maximum lifetime in seconds over which the RDNSS entry
+ * may be used. After this time a host may send a router solicitation
+ * message to refresh the RDNSS information.
+ *
+ * Default is infinity lifetime (0xffffffff) */
+ uint32_t AdvRDNSSLifetime;
+#define RTADV_RDNSS_INFINITY_LIFETIME (0xffffffff)
+#define RTADV_RDNSS_DEFAULT_LIFETIME RTADV_RDNSS_INFINITY_LIFETIME
};
#endif /* RTADV */
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index ea630c6a..d0908115 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -338,6 +338,7 @@ int
ifm_read (struct if_msghdr *ifm)
{
struct interface *ifp = NULL;
+ struct sockaddr_dl *sdl;
char ifname[IFNAMSIZ];
short ifnlen = 0;
caddr_t *cp;
@@ -375,6 +376,7 @@ ifm_read (struct if_msghdr *ifm)
RTA_ADDR_GET (NULL, RTA_GATEWAY, ifm->ifm_addrs, cp);
RTA_ATTR_GET (NULL, RTA_NETMASK, ifm->ifm_addrs, cp);
RTA_ADDR_GET (NULL, RTA_GENMASK, ifm->ifm_addrs, cp);
+ sdl = (struct sockaddr_dl *)cp;
RTA_NAME_GET (ifname, RTA_IFP, ifm->ifm_addrs, cp, ifnlen);
RTA_ADDR_GET (NULL, RTA_IFA, ifm->ifm_addrs, cp);
RTA_ADDR_GET (NULL, RTA_AUTHOR, ifm->ifm_addrs, cp);
@@ -473,6 +475,16 @@ ifm_read (struct if_msghdr *ifm)
#endif /* __bsdi__ */
if_get_metric (ifp);
+ /*
+ * XXX sockaddr_dl contents can be larger than the structure
+ * definition, so the user of the stored structure must be
+ * careful not to read off the end.
+ *
+ * a nonzero ifnlen from RTA_NAME_GET() means sdl is valid
+ */
+ if (ifnlen)
+ memcpy (&ifp->sdl, sdl, sizeof (struct sockaddr_dl));
+
if_add_update (ifp);
}
else
diff --git a/zebra/rib.h b/zebra/rib.h
index 72d1de0c..2abfed19 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -264,6 +264,7 @@ extern struct rib *rib_match_ipv4 (struct in_addr);
extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *);
extern void rib_update (void);
+extern void rib_update_background (void);
extern void rib_weed_tables (void);
extern void rib_sweep_route (void);
extern void rib_close (void);
diff --git a/zebra/rtadv.c b/zebra/rtadv.c
index d66d4e42..9528a31a 100644
--- a/zebra/rtadv.c
+++ b/zebra/rtadv.c
@@ -233,6 +233,32 @@ rtadv_send_packet (int sock, struct interface *ifp)
len += sizeof(struct nd_opt_homeagent_info);
}
+ if (zif->rtadv.AdvRDNSSFlag)
+ {
+ char *addr_ptr;
+ struct nd_opt_rdnss *ndopt_rdnss;
+ struct prefix *rdnss_prefix;
+ unsigned int rdnss_entries = 1;
+
+ ndopt_rdnss = (struct nd_opt_rdnss *) (buf + len);
+ ndopt_rdnss->nd_opt_rdnss_type = ND_OPT_RDNSS;
+ ndopt_rdnss->nd_opt_rdnss_reserved = 0;
+ ndopt_rdnss->nd_opt_rdnss_lifetime = htonl(zif->rtadv.AdvRDNSSLifetime);
+
+ len += sizeof(struct nd_opt_rdnss);
+
+ /* Fill in all RDNS server entries */
+ for (ALL_LIST_ELEMENTS_RO (zif->rtadv.AdvRDNSSList, node, rdnss_prefix))
+ {
+ addr_ptr = (char *)(buf + len);
+ memcpy(addr_ptr, &rdnss_prefix->u.prefix6, sizeof (struct in6_addr));
+ len += sizeof (struct in6_addr);
+ rdnss_entries += 2;
+ }
+
+ ndopt_rdnss->nd_opt_rdnss_len = rdnss_entries;
+ }
+
if (zif->rtadv.AdvIntervalOption)
{
struct nd_opt_adv_interval *ndopt_adv =
@@ -1430,6 +1456,115 @@ DEFUN (no_ipv6_nd_router_preference,
return CMD_SUCCESS;
}
+static struct prefix *
+rtadv_rdnss_lookup (struct list *list, struct prefix *p)
+{
+ struct listnode *node;
+ struct prefix *prefix;
+
+ for (ALL_LIST_ELEMENTS_RO (list, node, prefix))
+ if (prefix_same (prefix, p))
+ return prefix;
+ return NULL;
+}
+
+static void
+rtadv_rdnss_set (struct zebra_if *zif, struct prefix *p)
+{
+ struct prefix *prefix;
+ struct list *rdnsslist = zif->rtadv.AdvRDNSSList;
+
+ prefix = rtadv_rdnss_lookup (rdnsslist, p);
+ if (prefix)
+ return;
+
+ prefix = prefix_new ();
+ memcpy (prefix, p, sizeof (struct prefix));
+ listnode_add (rdnsslist, prefix);
+
+ return;
+}
+
+static int
+rtadv_rdnss_reset (struct zebra_if *zif, struct prefix *rp)
+{
+ struct prefix *prefix;
+
+ prefix = rtadv_rdnss_lookup(zif->rtadv.AdvRDNSSList, rp);
+ if (prefix != NULL)
+ {
+ listnode_delete (zif->rtadv.AdvRDNSSList, (void *) prefix);
+ prefix_free (prefix);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+DEFUN (ipv6_nd_rdnss,
+ ipv6_nd_rdnss_cmd,
+ "ipv6 nd rdnss X:X::X:X (<0-4294967295>|infinite)",
+ "Interface IPv6 config commands\n"
+ "Neighbor discovery\n"
+ "RDNSS Option\n"
+ "IPv6 address of recursive DNS server\n")
+{
+ int ret;
+ char *pnt;
+ struct interface *ifp;
+ struct zebra_if *zif;
+ struct prefix rp;
+
+ ifp = (struct interface *) vty->index;
+ zif = ifp->info;
+
+ /* make sure no slash exists in the argument */
+ pnt = strchr (argv[0], '/');
+ if (pnt != NULL)
+ {
+ vty_out (vty, "Malformed IPv6 RDNS address - no prefix notation allowed%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* now we can abuse str2prefix_ipv6 for a sanity check
+ * because IPv6 addresses with missing prefix
+ * slashes '/' are treated as host routes */
+ ret = str2prefix_ipv6 (argv[0], (struct prefix_ipv6 *) &rp);
+ if (!ret)
+ {
+ vty_out (vty, "Malformed IPv6 RDNS address%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ rtadv_rdnss_set(zif, &rp);
+ zif->rtadv.AdvRDNSSFlag = 1;
+
+ if (argc > 1)
+ {
+ if ( strncmp (argv[1], "i", 1) == 0)
+ {
+ zif->rtadv.AdvRDNSSLifetime = RTADV_RDNSS_INFINITY_LIFETIME;
+ }
+ else
+ {
+ zif->rtadv.AdvRDNSSLifetime =
+ (u_int32_t) strtoll (argv[1], (char **)NULL, 10);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+ALIAS (ipv6_nd_rdnss,
+ ipv6_nd_rdnss_nolife_cmd,
+ "ipv6 nd rdnss X:X::X:X",
+ "Interface IPv6 config commands\n"
+ "Neighbor discovery\n"
+ "RDNSS Option\n"
+ "IPv6 address of recursive DNS server\n")
+
+
/* Write configuration about router advertisement. */
void
rtadv_config_write (struct vty *vty, struct interface *ifp)
@@ -1610,6 +1745,8 @@ rtadv_init (void)
install_element (INTERFACE_NODE, &no_ipv6_nd_prefix_cmd);
install_element (INTERFACE_NODE, &ipv6_nd_router_preference_cmd);
install_element (INTERFACE_NODE, &no_ipv6_nd_router_preference_cmd);
+ install_element (INTERFACE_NODE, &ipv6_nd_rdnss_cmd);
+ install_element (INTERFACE_NODE, &ipv6_nd_rdnss_nolife_cmd);
}
static int
diff --git a/zebra/rtadv.h b/zebra/rtadv.h
index d8d263d0..6bd342e3 100644
--- a/zebra/rtadv.h
+++ b/zebra/rtadv.h
@@ -66,6 +66,9 @@ extern void rtadv_init (void);
#ifndef ND_OPT_HA_INFORMATION
#define ND_OPT_HA_INFORMATION 8 /* HA Information Option */
#endif
+#ifndef ND_OPT_RDNSS
+#define ND_OPT_RDNSS 25 /* RDNSS option (RFC 5006) */
+#endif
#ifndef HAVE_STRUCT_ND_OPT_ADV_INTERVAL
struct nd_opt_adv_interval { /* Advertisement interval option */
@@ -94,6 +97,17 @@ struct nd_opt_homeagent_info { /* Home Agent info */
} __attribute__((__packed__));
#endif
+#ifndef HAVE_STRUCT_ND_OPT_RDNSS
+/* see RFC 5006, section 5.1 */
+struct nd_opt_rdnss {
+ uint8_t nd_opt_rdnss_type;
+ uint8_t nd_opt_rdnss_len;
+ uint16_t nd_opt_rdnss_reserved;
+ uint32_t nd_opt_rdnss_lifetime;
+ /* followed by n (16 byte) entries */
+} __attribute__((__packed__));
+#endif
+
extern const char *rtadv_pref_strs[];
#endif /* _ZEBRA_RTADV_H */
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 50a19477..a10ca84b 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -352,6 +352,17 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set,
break;
}
+ if (IS_ZEBRA_DEBUG_RIB) {
+ char buf[INET6_ADDRSTRLEN];
+ inet_ntop(rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
+ zlog_debug("%s: %s/%d: nexthop match %p: %s type=%d sel=%d int=%d",
+ __func__, buf, rn->p.prefixlen, match,
+ match ? zebra_route_string(match->type) : "<?>",
+ match ? match->type : -1,
+ match ? CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED) : -1,
+ match ? CHECK_FLAG(match->flags, ZEBRA_FLAG_INTERNAL) : -1);
+ }
+
/* If there is no selected route or matched route is EGP, go up
tree. */
if (! match
@@ -375,7 +386,8 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set,
return 1;
}
- else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
+ else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL) ||
+ match->type == ZEBRA_ROUTE_KERNEL)
{
for (newhop = match->nexthop; newhop; newhop = newhop->next)
if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)
@@ -756,6 +768,43 @@ rib_match_ipv6 (struct in6_addr *addr)
}
#endif /* HAVE_IPV6 */
+static void nexthop_dump(struct nexthop *nexthop,
+ char *type_str_buf,
+ int type_str_buf_size,
+ char *addr_str_buf,
+ int addr_str_buf_size,
+ char *via_str_buf,
+ int via_str_buf_size)
+{
+ switch (nexthop->type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ snprintf(type_str_buf, type_str_buf_size, "ipv4");
+ snprintf(addr_str_buf, addr_str_buf_size, "%s", inet_ntoa(nexthop->gate.ipv4));
+ snprintf(via_str_buf, via_str_buf_size, "%s", nexthop->ifindex ? ifindex2ifname(nexthop->ifindex) : "<?>");
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ snprintf(type_str_buf, type_str_buf_size, "connected");
+ snprintf(addr_str_buf, addr_str_buf_size, "<connected>");
+ snprintf(via_str_buf, via_str_buf_size, "%s", ifindex2ifname(nexthop->ifindex));
+ break;
+ case NEXTHOP_TYPE_IFNAME:
+ snprintf(type_str_buf, type_str_buf_size, "connected");
+ snprintf(addr_str_buf, addr_str_buf_size, "<connected>");
+ snprintf(via_str_buf, via_str_buf_size, "%s", nexthop->ifname);
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ snprintf(type_str_buf, type_str_buf_size, "blackhole");
+ snprintf(addr_str_buf, addr_str_buf_size, "<blackhole>");
+ snprintf(via_str_buf, via_str_buf_size, "Null0");
+ break;
+ default:
+ snprintf(type_str_buf, type_str_buf_size, "unknown");
+ snprintf(addr_str_buf, addr_str_buf_size, "<unknown>");
+ snprintf(via_str_buf, via_str_buf_size, "<?>");
+ }
+}
+
#define RIB_SYSTEM_ROUTE(R) \
((R)->type == ZEBRA_ROUTE_KERNEL || (R)->type == ZEBRA_ROUTE_CONNECT)
@@ -809,10 +858,27 @@ nexthop_active_check (struct route_node *rn, struct rib *rib,
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
family = AFI_IP;
- if (nexthop_active_ipv4 (rib, nexthop, set, rn))
- SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- else
- UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ {
+ int nh_active = nexthop_active_ipv4 (rib, nexthop, set, rn);
+ if (IS_ZEBRA_DEBUG_RIB) {
+ char type_str_buf[100];
+ char addr_str_buf[100];
+ char via_str_buf[100];
+ nexthop_dump(nexthop,
+ type_str_buf, sizeof(type_str_buf),
+ addr_str_buf, sizeof(addr_str_buf),
+ via_str_buf, sizeof(via_str_buf));
+ zlog_debug("%s: rib %p nexthop %p type=%d %s %s via %s ifindex=%d nexthop_active_ipv4=%d",
+ __func__, rib, nexthop,
+ nexthop->type, type_str_buf,
+ addr_str_buf, via_str_buf, nexthop->ifindex,
+ nh_active);
+ }
+ if (nh_active)
+ SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else
+ UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ }
break;
#ifdef HAVE_IPV6
case NEXTHOP_TYPE_IPV6:
@@ -892,8 +958,23 @@ nexthop_active_update (struct route_node *rn, struct rib *rib, int set)
{
prev_active = CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
prev_index = nexthop->ifindex;
- if ((new_active = nexthop_active_check (rn, rib, nexthop, set)))
+ new_active = nexthop_active_check (rn, rib, nexthop, set);
+ if (new_active)
rib->nexthop_active_num++;
+ if (IS_ZEBRA_DEBUG_RIB) {
+ char type_str_buf[100];
+ char addr_str_buf[100];
+ char via_str_buf[100];
+ nexthop_dump(nexthop,
+ type_str_buf, sizeof(type_str_buf),
+ addr_str_buf, sizeof(addr_str_buf),
+ via_str_buf, sizeof(via_str_buf));
+ zlog_debug("%s: rib %p nexthop %p type=%d %s %s via %s ifindex=%d act=%d total_act=%d",
+ __func__, rib, nexthop,
+ nexthop->type, type_str_buf,
+ addr_str_buf, via_str_buf, nexthop->ifindex,
+ new_active, rib->nexthop_active_num);
+ }
if (prev_active != new_active ||
prev_index != nexthop->ifindex)
SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED);
@@ -993,6 +1074,15 @@ rib_process (struct route_node *rn)
* may be passed to rib_unlink() in the middle of iteration.
*/
next = rib->next;
+
+ if (IS_ZEBRA_DEBUG_RIB) {
+ zlog_debug("%s: %s/%d: scan rib %p: type=%d sel=%d rem=%d nh_act=%d dist=%d",
+ __func__, buf, rn->p.prefixlen, rib, rib->type,
+ CHECK_FLAG(rib->flags, ZEBRA_FLAG_SELECTED),
+ CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED),
+ nexthop_active_update(rn, rib, 0),
+ rib->distance);
+ }
/* Currently installed rib. */
if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
@@ -1333,7 +1423,6 @@ rib_queue_init (struct zebra_t *zebra)
zebra->ribq->spec.errorfunc = NULL;
/* XXX: TODO: These should be runtime configurable via vty */
zebra->ribq->spec.max_retries = 3;
- zebra->ribq->spec.hold = rib_process_hold_time;
if (!(zebra->mq = meta_queue_new ()))
zlog_err ("%s: could not initialise meta queue!", __func__);
@@ -2856,23 +2945,45 @@ static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
#endif /* HAVE_IPV6 */
/* RIB update function. */
-void
-rib_update (void)
+static void
+rib_update_table (struct table *table)
{
struct route_node *rn;
- struct route_table *table;
-
- table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
if (table)
for (rn = route_top (table); rn; rn = route_next (rn))
if (rn->info)
rib_queue_add (&zebrad, rn);
+}
- table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
- if (table)
- for (rn = route_top (table); rn; rn = route_next (rn))
- if (rn->info)
- rib_queue_add (&zebrad, rn);
+void
+rib_update (void)
+{
+ if (zebrad.update)
+ {
+ thread_cancel (zebrad.update);
+ zebrad.update = NULL;
+ }
+
+ rib_update_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
+
+#ifdef HAVE_IPV6
+ rib_update_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
+#endif
+}
+
+static int
+rib_update_thread (struct thread *self)
+{
+ rib_update ();
+ return 0;
+}
+
+void
+rib_update_background (void)
+{
+ if (!zebrad.update)
+ zebrad.update = thread_add_background (zebrad.master, rib_update_thread,
+ NULL, rib_process_hold_time);
}
diff --git a/zebra/zserv.c b/zebra/zserv.c
index e3e15a01..ced8fa5a 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -1,5 +1,6 @@
/* Zebra daemon server routine.
* Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+ * Portions Copyright (c) 2008 Everton da Silva Marques <everton.marques@gmail.com>
*
* This file is part of GNU Zebra.
*
@@ -596,6 +597,77 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr)
return zebra_server_send_message(client);
}
+/*
+ Modified version of zsend_ipv4_nexthop_lookup():
+ 1) Returns both nexthop address and nexthop ifindex
+ (with ZEBRA_NEXTHOP_IPV4_IFINDEX).
+ 2) Returns both route metric and protocol distance.
+*/
+static int
+zsend_ipv4_nexthop_lookup_v2 (struct zserv *client, struct in_addr addr)
+{
+ struct stream *s;
+ struct rib *rib;
+ unsigned long nump;
+ u_char num;
+ struct nexthop *nexthop;
+
+ /* Lookup nexthop. */
+ rib = rib_match_ipv4 (addr);
+
+ /* Get output stream. */
+ s = client->obuf;
+ stream_reset (s);
+
+ /* Fill in result. */
+ zserv_create_header (s, ZEBRA_IPV4_NEXTHOP_LOOKUP_V2);
+ stream_put_in_addr (s, &addr);
+
+ if (rib)
+ {
+ stream_putc (s, rib->distance);
+ stream_putl (s, rib->metric);
+ num = 0;
+ nump = stream_get_endp(s); /* remember position for nexthop_num */
+ stream_putc (s, 0); /* reserve room for nexthop_num */
+ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+ if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
+ {
+ stream_putc (s, nexthop->type);
+ switch (nexthop->type)
+ {
+ case ZEBRA_NEXTHOP_IPV4:
+ stream_put_in_addr (s, &nexthop->gate.ipv4);
+ break;
+ case ZEBRA_NEXTHOP_IFINDEX:
+ case ZEBRA_NEXTHOP_IFNAME:
+ stream_putl (s, nexthop->ifindex);
+ break;
+ case ZEBRA_NEXTHOP_IPV4_IFINDEX:
+ stream_put_in_addr (s, &nexthop->gate.ipv4);
+ stream_putl (s, nexthop->ifindex);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ num++;
+ }
+
+ stream_putc_at (s, nump, num); /* store nexthop_num */
+ }
+ else
+ {
+ stream_putc (s, 0); /* distance */
+ stream_putl (s, 0); /* metric */
+ stream_putc (s, 0); /* nexthop_num */
+ }
+
+ stream_putw_at (s, 0, stream_get_endp (s));
+
+ return zebra_server_send_message(client);
+}
+
static int
zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p)
{
@@ -805,6 +877,8 @@ zread_ipv4_add (struct zserv *client, u_short length)
/* Table */
rib->table=zebrad.rtm_table_default;
rib_add_ipv4_multipath (&p, rib);
+
+ rib_update_background ();
return 0;
}
@@ -879,6 +953,8 @@ zread_ipv4_delete (struct zserv *client, u_short length)
rib_delete_ipv4 (api.type, api.flags, &p, &nexthop, ifindex,
client->rtm_table);
+
+ rib_update_background ();
return 0;
}
@@ -892,6 +968,16 @@ zread_ipv4_nexthop_lookup (struct zserv *client, u_short length)
return zsend_ipv4_nexthop_lookup (client, addr);
}
+/* Nexthop lookup v2 for IPv4. */
+static int
+zread_ipv4_nexthop_lookup_v2 (struct zserv *client, u_short length)
+{
+ struct in_addr addr;
+
+ addr.s_addr = stream_get_ipv4 (client->ibuf);
+ return zsend_ipv4_nexthop_lookup_v2 (client, addr);
+}
+
/* Nexthop lookup for IPv4. */
static int
zread_ipv4_import_lookup (struct zserv *client, u_short length)
@@ -970,6 +1056,8 @@ zread_ipv6_add (struct zserv *client, u_short length)
else
rib_add_ipv6 (api.type, api.flags, &p, &nexthop, ifindex, zebrad.rtm_table_default, api.metric,
api.distance);
+
+ rib_update_background ();
return 0;
}
@@ -1034,6 +1122,8 @@ zread_ipv6_delete (struct zserv *client, u_short length)
rib_delete_ipv6 (api.type, api.flags, &p, NULL, ifindex, client->rtm_table);
else
rib_delete_ipv6 (api.type, api.flags, &p, &nexthop, ifindex, client->rtm_table);
+
+ rib_update_background ();
return 0;
}
@@ -1276,6 +1366,9 @@ zebra_client_read (struct thread *thread)
case ZEBRA_IPV4_NEXTHOP_LOOKUP:
zread_ipv4_nexthop_lookup (client, length);
break;
+ case ZEBRA_IPV4_NEXTHOP_LOOKUP_V2:
+ zread_ipv4_nexthop_lookup_v2 (client, length);
+ break;
#ifdef HAVE_IPV6
case ZEBRA_IPV6_NEXTHOP_LOOKUP:
zread_ipv6_nexthop_lookup (client, length);
diff --git a/zebra/zserv.h b/zebra/zserv.h
index cccd9be0..340e7f5d 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -83,6 +83,9 @@ struct zebra_t
/* rib work queue */
struct work_queue *ribq;
struct meta_queue *mq;
+
+ /* rib update thread */
+ struct thread *update;
};
/* Count prefix size from mask length */