2 * IP MSDP packet helper
3 * Copyright (C) 2016 Cumulus Networks, Inc.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; see the file COPYING; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
23 #include <lib/network.h>
24 #include <lib/stream.h>
25 #include <lib/thread.h>
32 #include "pim_msdp_packet.h"
33 #include "pim_msdp_socket.h"
36 pim_msdp_pkt_type_dump(enum pim_msdp_tlv type
, char *buf
, int buf_size
)
39 case PIM_MSDP_V4_SOURCE_ACTIVE
:
40 snprintf(buf
, buf_size
, "%s", "SA");
42 case PIM_MSDP_V4_SOURCE_ACTIVE_REQUEST
:
43 snprintf(buf
, buf_size
, "%s", "SA_REQ");
45 case PIM_MSDP_V4_SOURCE_ACTIVE_RESPONSE
:
46 snprintf(buf
, buf_size
, "%s", "SA_RESP");
48 case PIM_MSDP_KEEPALIVE
:
49 snprintf(buf
, buf_size
, "%s", "KA");
51 case PIM_MSDP_RESERVED
:
52 snprintf(buf
, buf_size
, "%s", "RSVD");
54 case PIM_MSDP_TRACEROUTE_PROGRESS
:
55 snprintf(buf
, buf_size
, "%s", "TRACE_PROG");
57 case PIM_MSDP_TRACEROUTE_REPLY
:
58 snprintf(buf
, buf_size
, "%s", "TRACE_REPLY");
61 snprintf(buf
, buf_size
, "UNK-%d", type
);
67 pim_msdp_pkt_sa_dump_one(struct stream
*s
)
71 /* just throw away the three reserved bytes */
73 /* throw away the prefix length also */
76 memset(&sg
, 0, sizeof (struct prefix_sg
));
77 sg
.grp
.s_addr
= stream_get_ipv4(s
);
78 sg
.src
.s_addr
= stream_get_ipv4(s
);
80 zlog_debug(" sg %s", pim_str_sg_dump(&sg
));
84 pim_msdp_pkt_sa_dump(struct stream
*s
)
88 struct in_addr rp
; /* Last RP address associated with this SA */
90 entry_cnt
= stream_getc(s
);
91 rp
.s_addr
= stream_get_ipv4(s
);
93 if (PIM_DEBUG_MSDP_PACKETS
) {
94 char rp_str
[INET_ADDRSTRLEN
];
95 pim_inet4_dump("<rp?>", rp
, rp_str
, sizeof(rp_str
));
96 zlog_debug(" entry_cnt %d rp %s", entry_cnt
, rp_str
);
100 for (i
= 0; i
< entry_cnt
; ++i
) {
101 pim_msdp_pkt_sa_dump_one(s
);
106 pim_msdp_pkt_dump(struct pim_msdp_peer
*mp
, int type
, int len
, bool rx
,
109 char type_str
[PIM_MSDP_PKT_TYPE_STRLEN
];
111 pim_msdp_pkt_type_dump(type
, type_str
, sizeof(type_str
));
113 zlog_debug("MSDP peer %s pkt %s type %s len %d",
114 mp
->key_str
, rx
?"rx":"tx", type_str
, len
);
121 case PIM_MSDP_V4_SOURCE_ACTIVE
:
122 pim_msdp_pkt_sa_dump(s
);
128 /* Check file descriptor whether connect is established. */
130 pim_msdp_connect_check(struct pim_msdp_peer
*mp
)
136 if (mp
->state
!= PIM_MSDP_CONNECTING
) {
137 /* if we are here it means we are not in a connecting or established state
138 * for now treat this as a fatal error */
139 pim_msdp_peer_reset_tcp_conn(mp
, "invalid-state");
143 PIM_MSDP_PEER_READ_OFF(mp
);
144 PIM_MSDP_PEER_WRITE_OFF(mp
);
146 /* Check file descriptor. */
147 slen
= sizeof(status
);
148 ret
= getsockopt(mp
->fd
, SOL_SOCKET
, SO_ERROR
, (void *)&status
, &slen
);
150 /* If getsockopt is fail, this is fatal error. */
152 zlog_err("can't get sockopt for nonblocking connect");
153 pim_msdp_peer_reset_tcp_conn(mp
, "connect-failed");
157 /* When status is 0 then TCP connection is established. */
158 if (PIM_DEBUG_MSDP_INTERNAL
) {
159 zlog_debug("MSDP peer %s pim_connect_check %s", mp
->key_str
, status
?"fail":"success");
162 pim_msdp_peer_established(mp
);
164 pim_msdp_peer_reset_tcp_conn(mp
, "connect-failed");
169 pim_msdp_pkt_delete(struct pim_msdp_peer
*mp
)
171 stream_free(stream_fifo_pop(mp
->obuf
));
175 pim_msdp_pkt_add(struct pim_msdp_peer
*mp
, struct stream
*s
)
177 stream_fifo_push(mp
->obuf
, s
);
181 pim_msdp_write_proceed_actions(struct pim_msdp_peer
*mp
)
183 if (stream_fifo_head(mp
->obuf
)) {
184 PIM_MSDP_PEER_WRITE_ON(mp
);
189 pim_msdp_write(struct thread
*thread
)
191 struct pim_msdp_peer
*mp
;
194 enum pim_msdp_tlv type
;
197 int work_max_cnt
= 100;
199 mp
= THREAD_ARG(thread
);
202 if (PIM_DEBUG_MSDP_INTERNAL
) {
203 zlog_debug("MSDP peer %s pim_msdp_write", mp
->key_str
);
209 /* check if TCP connection is established */
210 if (mp
->state
!= PIM_MSDP_ESTABLISHED
) {
211 pim_msdp_connect_check(mp
);
215 s
= stream_fifo_head(mp
->obuf
);
217 pim_msdp_write_proceed_actions(mp
);
221 sockopt_cork(mp
->fd
, 1);
223 /* Nonblocking write until TCP output buffer is full */
228 /* Number of bytes to be sent */
229 writenum
= stream_get_endp(s
) - stream_get_getp(s
);
231 /* Call write() system call */
232 num
= write(mp
->fd
, STREAM_PNT(s
), writenum
);
234 /* write failed either retry needed or error */
235 if (ERRNO_IO_RETRY(errno
)) {
236 if (PIM_DEBUG_MSDP_INTERNAL
) {
237 zlog_debug("MSDP peer %s pim_msdp_write io retry", mp
->key_str
);
242 pim_msdp_peer_reset_tcp_conn(mp
, "pkt-tx-failed");
246 if (num
!= writenum
) {
248 stream_forward_getp(s
, num
);
249 if (PIM_DEBUG_MSDP_INTERNAL
) {
250 zlog_debug("MSDP peer %s pim_msdp_partial_write", mp
->key_str
);
255 /* Retrieve msdp packet type. */
256 stream_set_getp(s
,0);
257 type
= stream_getc(s
);
258 len
= stream_getw(s
);
261 case PIM_MSDP_KEEPALIVE
:
264 case PIM_MSDP_V4_SOURCE_ACTIVE
:
269 if (PIM_DEBUG_MSDP_PACKETS
) {
270 pim_msdp_pkt_dump(mp
, type
, len
, false /*rx*/, s
);
273 /* packet sent delete it. */
274 pim_msdp_pkt_delete(mp
);
277 /* may need to pause if we have done too much work in this
279 if (work_cnt
>= work_max_cnt
) {
282 } while ((s
= stream_fifo_head(mp
->obuf
)) != NULL
);
283 pim_msdp_write_proceed_actions(mp
);
285 sockopt_cork(mp
->fd
, 0);
287 if (PIM_DEBUG_MSDP_INTERNAL
) {
288 zlog_debug("MSDP peer %s pim_msdp_write wrote %d packets", mp
->key_str
, work_cnt
);
295 pim_msdp_pkt_send(struct pim_msdp_peer
*mp
, struct stream
*s
)
297 /* Add packet to the end of list. */
298 pim_msdp_pkt_add(mp
, s
);
300 PIM_MSDP_PEER_WRITE_ON(mp
);
304 pim_msdp_pkt_ka_tx(struct pim_msdp_peer
*mp
)
308 if (mp
->state
!= PIM_MSDP_ESTABLISHED
) {
309 /* don't tx anything unless a session is established */
312 s
= stream_new(PIM_MSDP_KA_TLV_MAX_SIZE
);
313 stream_putc(s
, PIM_MSDP_KEEPALIVE
);
314 stream_putw(s
, PIM_MSDP_KA_TLV_MAX_SIZE
);
316 pim_msdp_pkt_send(mp
, s
);
320 pim_msdp_pkt_sa_push_to_one_peer(struct pim_msdp_peer
*mp
)
324 if (mp
->state
!= PIM_MSDP_ESTABLISHED
) {
325 /* don't tx anything unless a session is established */
328 s
= stream_dup(msdp
->work_obuf
);
330 pim_msdp_pkt_send(mp
, s
);
331 mp
->flags
|= PIM_MSDP_PEERF_SA_JUST_SENT
;
335 /* push the stream into the obuf fifo of all the peers */
337 pim_msdp_pkt_sa_push(struct pim_msdp_peer
*mp
)
339 struct listnode
*mpnode
;
342 pim_msdp_pkt_sa_push_to_one_peer(mp
);
344 for (ALL_LIST_ELEMENTS_RO(msdp
->peer_list
, mpnode
, mp
)) {
345 if (PIM_DEBUG_MSDP_INTERNAL
) {
346 zlog_debug("MSDP peer %s pim_msdp_pkt_sa_push", mp
->key_str
);
348 pim_msdp_pkt_sa_push_to_one_peer(mp
);
354 pim_msdp_pkt_sa_fill_hdr(int local_cnt
)
358 stream_reset(msdp
->work_obuf
);
359 curr_tlv_ecnt
= local_cnt
>PIM_MSDP_SA_MAX_ENTRY_CNT
?PIM_MSDP_SA_MAX_ENTRY_CNT
:local_cnt
;
360 local_cnt
-= curr_tlv_ecnt
;
361 stream_putc(msdp
->work_obuf
, PIM_MSDP_V4_SOURCE_ACTIVE
);
362 stream_putw(msdp
->work_obuf
, PIM_MSDP_SA_ENTRY_CNT2SIZE(curr_tlv_ecnt
));
363 stream_putc(msdp
->work_obuf
, curr_tlv_ecnt
);
364 stream_put_ipv4(msdp
->work_obuf
, msdp
->originator_id
.s_addr
);
370 pim_msdp_pkt_sa_fill_one(struct pim_msdp_sa
*sa
)
372 stream_put3(msdp
->work_obuf
, 0 /* reserved */);
373 stream_putc(msdp
->work_obuf
, 32 /* sprefix len */);
374 stream_put_ipv4(msdp
->work_obuf
, sa
->sg
.grp
.s_addr
);
375 stream_put_ipv4(msdp
->work_obuf
, sa
->sg
.src
.s_addr
);
379 pim_msdp_pkt_sa_gen(struct pim_msdp_peer
*mp
)
381 struct listnode
*sanode
;
382 struct pim_msdp_sa
*sa
;
384 int local_cnt
= msdp
->local_cnt
;
387 if (PIM_DEBUG_MSDP_INTERNAL
) {
388 zlog_debug(" sa gen %d", local_cnt
);
391 local_cnt
= pim_msdp_pkt_sa_fill_hdr(local_cnt
);
393 for (ALL_LIST_ELEMENTS_RO(msdp
->sa_list
, sanode
, sa
)) {
394 if (!(sa
->flags
& PIM_MSDP_SAF_LOCAL
)) {
395 /* current implementation of MSDP is for anycast i.e. full mesh. so
396 * no re-forwarding of SAs that we learnt from other peers */
399 /* add sa into scratch pad */
400 pim_msdp_pkt_sa_fill_one(sa
);
402 if (sa_count
>= PIM_MSDP_SA_MAX_ENTRY_CNT
) {
403 pim_msdp_pkt_sa_push(mp
);
406 if (PIM_DEBUG_MSDP_INTERNAL
) {
407 zlog_debug(" sa gen for remainder %d", local_cnt
);
409 local_cnt
= pim_msdp_pkt_sa_fill_hdr(local_cnt
);
414 pim_msdp_pkt_sa_push(mp
);
420 pim_msdp_pkt_sa_tx_done(void)
422 struct listnode
*mpnode
;
423 struct pim_msdp_peer
*mp
;
425 /* if SA were sent to the peers we restart ka timer and avoid
426 * unnecessary ka noise */
427 for (ALL_LIST_ELEMENTS_RO(msdp
->peer_list
, mpnode
, mp
)) {
428 if (mp
->flags
& PIM_MSDP_PEERF_SA_JUST_SENT
) {
429 mp
->flags
&= ~PIM_MSDP_PEERF_SA_JUST_SENT
;
430 pim_msdp_peer_pkt_txed(mp
);
436 pim_msdp_pkt_sa_tx(void)
438 pim_msdp_pkt_sa_gen(NULL
/* mp */);
439 pim_msdp_pkt_sa_tx_done();
443 pim_msdp_pkt_sa_tx_one(struct pim_msdp_sa
*sa
)
445 pim_msdp_pkt_sa_fill_hdr(1 /* cnt */);
446 pim_msdp_pkt_sa_fill_one(sa
);
447 pim_msdp_pkt_sa_push(NULL
);
448 pim_msdp_pkt_sa_tx_done();
451 /* when a connection is first established we push all SAs immediately */
453 pim_msdp_pkt_sa_tx_to_one_peer(struct pim_msdp_peer
*mp
)
455 pim_msdp_pkt_sa_gen(mp
);
456 pim_msdp_pkt_sa_tx_done();
460 pim_msdp_pkt_rxed_with_fatal_error(struct pim_msdp_peer
*mp
)
462 pim_msdp_peer_reset_tcp_conn(mp
, "invalid-pkt-rx");
466 pim_msdp_pkt_ka_rx(struct pim_msdp_peer
*mp
, int len
)
469 if (len
!= PIM_MSDP_KA_TLV_MAX_SIZE
) {
470 pim_msdp_pkt_rxed_with_fatal_error(mp
);
473 pim_msdp_peer_pkt_rxed(mp
);
477 pim_msdp_pkt_sa_rx_one(struct pim_msdp_peer
*mp
, struct in_addr rp
)
482 /* just throw away the three reserved bytes */
483 stream_get3(mp
->ibuf
);
484 prefix_len
= stream_getc(mp
->ibuf
);
486 memset(&sg
, 0, sizeof (struct prefix_sg
));
487 sg
.grp
.s_addr
= stream_get_ipv4(mp
->ibuf
);
488 sg
.src
.s_addr
= stream_get_ipv4(mp
->ibuf
);
490 if (prefix_len
!= 32) {
491 /* ignore SA update if the prefix length is not 32 */
492 zlog_err("rxed sa update with invalid prefix length %d", prefix_len
);
495 if (PIM_DEBUG_MSDP_PACKETS
) {
496 zlog_debug(" sg %s", pim_str_sg_dump(&sg
));
498 pim_msdp_sa_ref(mp
, &sg
, rp
);
502 pim_msdp_pkt_sa_rx(struct pim_msdp_peer
*mp
, int len
)
506 struct in_addr rp
; /* Last RP address associated with this SA */
510 if (len
< PIM_MSDP_SA_TLV_MIN_SIZE
) {
511 pim_msdp_pkt_rxed_with_fatal_error(mp
);
515 entry_cnt
= stream_getc(mp
->ibuf
);
516 /* some vendors include the actual multicast data in the tlv (at the end).
517 * we will ignore such data. in the future we may consider pushing it down
519 if (len
< PIM_MSDP_SA_ENTRY_CNT2SIZE(entry_cnt
)) {
520 pim_msdp_pkt_rxed_with_fatal_error(mp
);
523 rp
.s_addr
= stream_get_ipv4(mp
->ibuf
);
525 if (PIM_DEBUG_MSDP_PACKETS
) {
526 char rp_str
[INET_ADDRSTRLEN
];
527 pim_inet4_dump("<rp?>", rp
, rp_str
, sizeof(rp_str
));
528 zlog_debug(" entry_cnt %d rp %s", entry_cnt
, rp_str
);
531 if (!pim_msdp_peer_rpf_check(mp
, rp
)) {
532 /* if peer-RPF check fails don't process the packet any further */
533 if (PIM_DEBUG_MSDP_PACKETS
) {
534 zlog_debug(" peer RPF check failed");
539 pim_msdp_peer_pkt_rxed(mp
);
541 /* update SA cache */
542 for (i
= 0; i
< entry_cnt
; ++i
) {
543 pim_msdp_pkt_sa_rx_one(mp
, rp
);
548 pim_msdp_pkt_rx(struct pim_msdp_peer
*mp
)
550 enum pim_msdp_tlv type
;
553 /* re-read type and len */
554 type
= stream_getc_from(mp
->ibuf
, 0);
555 len
= stream_getw_from(mp
->ibuf
, 1);
556 if (len
< PIM_MSDP_HEADER_SIZE
) {
557 pim_msdp_pkt_rxed_with_fatal_error(mp
);
561 if (len
> PIM_MSDP_SA_TLV_MAX_SIZE
) {
562 /* if tlv size if greater than max just ignore the tlv */
566 if (PIM_DEBUG_MSDP_PACKETS
) {
567 pim_msdp_pkt_dump(mp
, type
, len
, true /*rx*/, NULL
/*s*/);
571 case PIM_MSDP_KEEPALIVE
:
572 pim_msdp_pkt_ka_rx(mp
, len
);
574 case PIM_MSDP_V4_SOURCE_ACTIVE
:
576 pim_msdp_pkt_sa_rx(mp
, len
);
583 /* pim msdp read utility function. */
585 pim_msdp_read_packet(struct pim_msdp_peer
*mp
)
592 old_endp
= stream_get_endp(mp
->ibuf
);
593 readsize
= mp
->packet_size
- old_endp
;
598 /* Read packet from fd */
599 nbytes
= stream_read_try(mp
->ibuf
, mp
->fd
, readsize
);
600 new_endp
= stream_get_endp(mp
->ibuf
);
602 if (PIM_DEBUG_MSDP_INTERNAL
) {
603 zlog_debug("MSDP peer %s read failed %d", mp
->key_str
, nbytes
);
606 if (PIM_DEBUG_MSDP_INTERNAL
) {
607 zlog_debug("MSDP peer %s pim_msdp_read io retry old_end: %d new_end: %d", mp
->key_str
, old_endp
, new_endp
);
609 /* transient error retry */
612 pim_msdp_pkt_rxed_with_fatal_error(mp
);
617 if (PIM_DEBUG_MSDP_INTERNAL
) {
618 zlog_debug("MSDP peer %s read failed %d", mp
->key_str
, nbytes
);
620 pim_msdp_peer_reset_tcp_conn(mp
, "peer-down");
624 /* We read partial packet. */
625 if (stream_get_endp(mp
->ibuf
) != mp
->packet_size
) {
626 if (PIM_DEBUG_MSDP_INTERNAL
) {
627 zlog_debug("MSDP peer %s read partial len %d old_endp %d new_endp %d", mp
->key_str
, mp
->packet_size
, old_endp
, new_endp
);
636 pim_msdp_read(struct thread
*thread
)
638 struct pim_msdp_peer
*mp
;
642 mp
= THREAD_ARG(thread
);
645 if (PIM_DEBUG_MSDP_INTERNAL
) {
646 zlog_debug("MSDP peer %s pim_msdp_read", mp
->key_str
);
653 /* check if TCP connection is established */
654 if (mp
->state
!= PIM_MSDP_ESTABLISHED
) {
655 pim_msdp_connect_check(mp
);
659 PIM_MSDP_PEER_READ_ON(mp
);
661 if (!mp
->packet_size
) {
662 mp
->packet_size
= PIM_MSDP_HEADER_SIZE
;
665 if (stream_get_endp(mp
->ibuf
) < PIM_MSDP_HEADER_SIZE
) {
666 /* start by reading the TLV header */
667 rc
= pim_msdp_read_packet(mp
);
669 goto pim_msdp_read_end
;
672 /* Find TLV type and len */
673 stream_getc(mp
->ibuf
);
674 len
= stream_getw(mp
->ibuf
);
675 if (len
< PIM_MSDP_HEADER_SIZE
) {
676 pim_msdp_pkt_rxed_with_fatal_error(mp
);
677 goto pim_msdp_read_end
;
679 /* read complete TLV */
680 mp
->packet_size
= len
;
683 rc
= pim_msdp_read_packet(mp
);
685 goto pim_msdp_read_end
;
690 /* reset input buffers and get ready for the next packet */
692 stream_reset(mp
->ibuf
);