summaryrefslogtreecommitdiffstats
path: root/pimd/pim_msdp_packet.c
diff options
context:
space:
mode:
authoranuradhak <anuradhak@cumulusnetworks.com>2016-10-25 19:59:48 +0200
committerDonald Sharp <sharpd@cumulusnetworks.com>2016-12-22 02:26:12 +0100
commit2a333e0f220fb727406459d60815a1cc9000183a (patch)
treec82e9f2fb5be1253e62215703b5ea02ca92b6b9d /pimd/pim_msdp_packet.c
parentpimd: Fix gcc compile issue. (diff)
downloadfrr-2a333e0f220fb727406459d60815a1cc9000183a.tar.xz
frr-2a333e0f220fb727406459d60815a1cc9000183a.zip
pim-msdp: part-1 - initial protocol infra.
This commit includes the following changes - 1. Support for MSDP peer DB (hash and sorted list). 2. Support for the following timers - keepalive, connect-retry, hold. 3. TCP session management (lower-ip is active, higher-ip is passive). 4. MSDP KA packet rx/tx. 5. Limited temporary config (will be replaced with the more automation friendly RP-set). Testing done - Peer bringup/deletion (including interop with another vendor) Sample out - root@dell-s6000-04:~# sudo vtysh -c "show ip msdp peer" Peer Local Mesh-group State Uptime 100.1.1.1 100.1.2.1 default established 00:07:27 100.1.3.1 100.1.2.1 default established 00:31:50 root@dell-s6000-04:~# Coming soon - 1. part-2: SA cache management. 2. part-3: SPT setup using source in SA cache. 3. part-4: CLI cleanup. Ticket: CM-13306 Signed-off-by: Anuradha Karuppiah <anuradhak@cumulusnetworks.com> Acked-by: Donald Sharp <sharpd@cumulusnetworks.com>
Diffstat (limited to 'pimd/pim_msdp_packet.c')
-rw-r--r--pimd/pim_msdp_packet.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/pimd/pim_msdp_packet.c b/pimd/pim_msdp_packet.c
new file mode 100644
index 000000000..458d5e462
--- /dev/null
+++ b/pimd/pim_msdp_packet.c
@@ -0,0 +1,380 @@
+/*
+ * IP MSDP packet helper
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 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
+ */
+#include <zebra.h>
+
+#include <lib/log.h>
+#include <lib/network.h>
+#include <lib/stream.h>
+#include <lib/thread.h>
+
+#include "pimd.h"
+#include "pim_str.h"
+
+#include "pim_msdp.h"
+#include "pim_msdp_packet.h"
+#include "pim_msdp_socket.h"
+
+static char *
+pim_msdp_pkt_type_dump(enum pim_msdp_tlv type, char *buf, int buf_size)
+{
+ switch (type) {
+ case PIM_MSDP_V4_SOURCE_ACTIVE:
+ snprintf(buf, buf_size, "%s", "SA");
+ break;
+ case PIM_MSDP_V4_SOURCE_ACTIVE_REQUEST:
+ snprintf(buf, buf_size, "%s", "SA_REQ");
+ break;
+ case PIM_MSDP_V4_SOURCE_ACTIVE_RESPONSE:
+ snprintf(buf, buf_size, "%s", "SA_RESP");
+ break;
+ case PIM_MSDP_KEEPALIVE:
+ snprintf(buf, buf_size, "%s", "KA");
+ break;
+ case PIM_MSDP_RESERVED:
+ snprintf(buf, buf_size, "%s", "RSVD");
+ break;
+ case PIM_MSDP_TRACEROUTE_PROGRESS:
+ snprintf(buf, buf_size, "%s", "TRACE_PROG");
+ break;
+ case PIM_MSDP_TRACEROUTE_REPLY:
+ snprintf(buf, buf_size, "%s", "TRACE_REPLY");
+ break;
+ default:
+ snprintf(buf, buf_size, "UNK-%d", type);
+ }
+ return buf;
+}
+
+static void
+pim_msdp_pkt_dump(struct pim_msdp_peer *mp, int type, int len, bool rx)
+{
+ char key_str[PIM_MSDP_PEER_KEY_STRLEN];
+ char type_str[PIM_MSDP_PKT_TYPE_STRLEN];
+
+ pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), false);
+ pim_msdp_pkt_type_dump(type, type_str, sizeof(type_str));
+
+ zlog_debug("%s pkt %s type %s len %d",
+ key_str, rx?"rx":"tx", type_str, len);
+ /* XXX: dump actual data */
+}
+
+/* Check file descriptor whether connect is established. */
+static void
+pim_msdp_connect_check(struct pim_msdp_peer *mp)
+{
+ int status;
+ socklen_t slen;
+ int ret;
+
+ if (mp->state != PIM_MSDP_CONNECTING) {
+ /* if we are here it means we are not in a connecting or established state
+ * for now treat this as a fatal error */
+ /* XXX:revisit; reset TCP connection */
+ pim_msdp_peer_reset_tcp_conn(mp, "invalid-state");
+ return;
+ }
+
+ PIM_MSDP_PEER_READ_OFF(mp);
+ PIM_MSDP_PEER_WRITE_OFF(mp);
+
+ /* Check file descriptor. */
+ slen = sizeof(status);
+ ret = getsockopt(mp->fd, SOL_SOCKET, SO_ERROR, (void *)&status, &slen);
+
+ /* If getsockopt is fail, this is fatal error. */
+ if (ret < 0) {
+ zlog_err("can't get sockopt for nonblocking connect");
+ /* XXX:revisit; reset TCP connection */
+ pim_msdp_peer_reset_tcp_conn(mp, "connect-failed");
+ return;
+ }
+
+ /* When status is 0 then TCP connection is established. */
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ char key_str[PIM_MSDP_PEER_KEY_STRLEN];
+
+ pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), false);
+ zlog_debug("%s pim_connect_check %s", key_str, status?"fail":"success");
+ }
+ if (status == 0) {
+ pim_msdp_peer_established(mp);
+ } else {
+ /* XXX:revisit; reset TCP connection */
+ pim_msdp_peer_reset_tcp_conn(mp, "connect-failed");
+ }
+}
+
+static void
+pim_msdp_pkt_delete(struct pim_msdp_peer *mp)
+{
+ stream_free(stream_fifo_pop(mp->obuf));
+}
+
+static void
+pim_msdp_write_proceed_actions(struct pim_msdp_peer *mp)
+{
+ if (stream_fifo_head(mp->obuf)) {
+ PIM_MSDP_PEER_WRITE_ON(mp);
+ }
+}
+
+int
+pim_msdp_write(struct thread *thread)
+{
+ struct pim_msdp_peer *mp;
+ struct stream *s;
+ int num;
+ enum pim_msdp_tlv type;
+
+ mp = THREAD_ARG(thread);
+ mp->t_write = NULL;
+
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ char key_str[PIM_MSDP_PEER_KEY_STRLEN];
+
+ pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), false);
+ zlog_debug("%s pim_msdp_write", key_str);
+ }
+ if (mp->fd < 0) {
+ return -1;
+ }
+
+ /* check if TCP connection is established */
+ if (mp->state != PIM_MSDP_ESTABLISHED) {
+ pim_msdp_connect_check(mp);
+ return 0;
+ }
+
+ s = stream_fifo_head(mp->obuf);
+ if (!s) {
+ pim_msdp_write_proceed_actions(mp);
+ return 0;
+ }
+
+ sockopt_cork (mp->fd, 1);
+
+ /* Nonblocking write until TCP output buffer is full */
+ do
+ {
+ int writenum;
+
+ /* Number of bytes to be sent */
+ writenum = stream_get_endp(s) - stream_get_getp(s);
+
+ /* Call write() system call */
+ num = write(mp->fd, STREAM_PNT(s), writenum);
+ if (num < 0) {
+ /* write failed either retry needed or error */
+ if (ERRNO_IO_RETRY(errno))
+ break;
+
+ /* XXX:revisit; reset TCP connection */
+ pim_msdp_peer_reset_tcp_conn(mp, "pkt-tx-failed");
+ return 0;
+ }
+
+ if (num != writenum) {
+ /* Partial write */
+ stream_forward_getp(s, num);
+ break;
+ }
+
+ /* Retrieve msdp packet type. */
+ type = stream_getc(s);
+ switch (type)
+ {
+ case PIM_MSDP_KEEPALIVE:
+ mp->ka_tx_cnt++;
+ break;
+ case PIM_MSDP_V4_SOURCE_ACTIVE:
+ mp->sa_tx_cnt++;
+ break;
+ default:;
+ }
+ if (PIM_DEBUG_MSDP_PACKETS) {
+ pim_msdp_pkt_dump(mp, type, writenum, false /*rx*/);
+ }
+
+ /* packet sent delete it. */
+ pim_msdp_pkt_delete(mp);
+
+ /* XXX - may need to pause if we have done too much work in this
+ * loop */
+ } while ((s = stream_fifo_head(mp->obuf)) != NULL);
+ pim_msdp_write_proceed_actions(mp);
+
+ sockopt_cork (mp->fd, 0);
+
+ return 0;
+}
+
+static void
+pim_msdp_pkt_send(struct pim_msdp_peer *mp, struct stream *s)
+{
+ /* Add packet to the end of list. */
+ stream_fifo_push(mp->obuf, s);
+
+ PIM_MSDP_PEER_WRITE_ON(mp);
+}
+
+/* Make keepalive packet and send it to the peer
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| 4 | 3 |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+void
+pim_msdp_pkt_ka_tx(struct pim_msdp_peer *mp)
+{
+ struct stream *s;
+
+ s = stream_new(PIM_MSDP_KA_TLV_MAX_SIZE);
+ stream_putc(s, PIM_MSDP_KEEPALIVE);
+ stream_putw(s, PIM_MSDP_KA_TLV_MAX_SIZE);
+
+ pim_msdp_pkt_send(mp, s);
+}
+
+static void
+pim_msdp_pkt_rxed_with_fatal_error(struct pim_msdp_peer *mp)
+{
+ /* XXX:revisit; reset TCP connection */
+ pim_msdp_peer_reset_tcp_conn(mp, "invalid-pkt-rx");
+}
+
+static void
+pim_msdp_pkt_ka_rx(struct pim_msdp_peer *mp, int len)
+{
+ mp->ka_rx_cnt++;
+ if (len != PIM_MSDP_KA_TLV_MAX_SIZE) {
+ pim_msdp_pkt_rxed_with_fatal_error(mp);
+ return;
+ }
+ pim_msdp_peer_pkt_rxed(mp);
+}
+
+static void
+pim_msdp_pkt_sa_rx(struct pim_msdp_peer *mp, int len)
+{
+ mp->sa_rx_cnt++;
+ /* XXX: proc SA ... */
+ pim_msdp_peer_pkt_rxed(mp);
+}
+
+/* Theoretically you could have different tlv types in the same message.
+ * For the time being I am assuming one; will revisit before 3.2 - XXX */
+static void
+pim_msdp_pkt_rx(struct pim_msdp_peer *mp, int nbytes)
+{
+ enum pim_msdp_tlv type;
+ int len;
+
+ type = stream_getc(mp->ibuf);
+ len = stream_getw(mp->ibuf);
+ if (len < PIM_MSDP_HEADER_SIZE) {
+ pim_msdp_pkt_rxed_with_fatal_error(mp);
+ return;
+ }
+
+ if (len > PIM_MSDP_SA_TLV_MAX_SIZE) {
+ /* if tlv size if greater than max just ignore the tlv */
+ return;
+ }
+
+ if (len > nbytes) {
+ /* we got a partial read or the packet is malformed */
+ pim_msdp_pkt_rxed_with_fatal_error(mp);
+ return;
+ }
+
+ if (PIM_DEBUG_MSDP_PACKETS) {
+ pim_msdp_pkt_dump(mp, type, len, true /*rx*/);
+ }
+
+ switch(type) {
+ case PIM_MSDP_KEEPALIVE:
+ pim_msdp_pkt_ka_rx(mp, len);
+ break;
+ case PIM_MSDP_V4_SOURCE_ACTIVE:
+ mp->sa_rx_cnt++;
+ pim_msdp_pkt_sa_rx(mp, len);
+ break;
+ default:
+ mp->unk_rx_cnt++;
+ }
+ /* XXX: process next tlv*/
+}
+
+/* pim msdp read utility function. */
+static int
+pim_msdp_read_packet(struct pim_msdp_peer *mp)
+{
+ int nbytes;
+ /* Read packet from fd. */
+ nbytes = stream_read_try(mp->ibuf, mp->fd, PIM_MSDP_MAX_PACKET_SIZE);
+ if (nbytes < PIM_MSDP_HEADER_SIZE) {
+ if (nbytes == -2) {
+ /* transient error retry */
+ return -1;
+ }
+ pim_msdp_pkt_rxed_with_fatal_error(mp);
+ return -1;
+ }
+ return nbytes;
+}
+
+int
+pim_msdp_read(struct thread *thread)
+{
+ struct pim_msdp_peer *mp;
+ int rc;
+
+ mp = THREAD_ARG(thread);
+ mp->t_read = NULL;
+
+ if (PIM_DEBUG_MSDP_INTERNAL) {
+ char key_str[PIM_MSDP_PEER_KEY_STRLEN];
+
+ pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), false);
+ zlog_debug("%s pim_msdp_read", key_str);
+ }
+
+ if (mp->fd < 0) {
+ return -1;
+ }
+
+ /* check if TCP connection is established */
+ if (mp->state != PIM_MSDP_ESTABLISHED) {
+ pim_msdp_connect_check(mp);
+ return 0;
+ }
+
+ THREAD_READ_ON(msdp->master, mp->t_read, pim_msdp_read, mp, mp->fd);
+
+ rc = pim_msdp_read_packet(mp);
+ if (rc > 0) {
+ pim_msdp_pkt_rx(mp, rc);
+ }
+
+ stream_reset(mp->ibuf);
+ return 0;
+}