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 along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include <lib/network.h>
23 #include <lib/stream.h>
24 #include <lib/thread.h>
26 #include <lib/lib_errors.h>
30 #include "pim_errors.h"
33 #include "pim_msdp_packet.h"
34 #include "pim_msdp_socket.h"
36 static char *pim_msdp_pkt_type_dump(enum pim_msdp_tlv type
, char *buf
,
40 case PIM_MSDP_V4_SOURCE_ACTIVE
:
41 snprintf(buf
, buf_size
, "%s", "SA");
43 case PIM_MSDP_V4_SOURCE_ACTIVE_REQUEST
:
44 snprintf(buf
, buf_size
, "%s", "SA_REQ");
46 case PIM_MSDP_V4_SOURCE_ACTIVE_RESPONSE
:
47 snprintf(buf
, buf_size
, "%s", "SA_RESP");
49 case PIM_MSDP_KEEPALIVE
:
50 snprintf(buf
, buf_size
, "%s", "KA");
52 case PIM_MSDP_RESERVED
:
53 snprintf(buf
, buf_size
, "%s", "RSVD");
55 case PIM_MSDP_TRACEROUTE_PROGRESS
:
56 snprintf(buf
, buf_size
, "%s", "TRACE_PROG");
58 case PIM_MSDP_TRACEROUTE_REPLY
:
59 snprintf(buf
, buf_size
, "%s", "TRACE_REPLY");
62 snprintf(buf
, buf_size
, "UNK-%d", type
);
67 static void 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
));
83 static void pim_msdp_pkt_sa_dump(struct stream
*s
)
87 struct in_addr rp
; /* Last RP address associated with this SA */
89 entry_cnt
= stream_getc(s
);
90 rp
.s_addr
= stream_get_ipv4(s
);
92 if (PIM_DEBUG_MSDP_PACKETS
) {
93 char rp_str
[INET_ADDRSTRLEN
];
94 pim_inet4_dump("<rp?>", rp
, rp_str
, sizeof(rp_str
));
95 zlog_debug(" entry_cnt %d rp %s", entry_cnt
, rp_str
);
99 for (i
= 0; i
< entry_cnt
; ++i
) {
100 pim_msdp_pkt_sa_dump_one(s
);
104 static void pim_msdp_pkt_dump(struct pim_msdp_peer
*mp
, int type
, int len
,
105 bool rx
, struct stream
*s
)
107 char type_str
[PIM_MSDP_PKT_TYPE_STRLEN
];
109 pim_msdp_pkt_type_dump(type
, type_str
, sizeof(type_str
));
111 zlog_debug("MSDP peer %s pkt %s type %s len %d", mp
->key_str
,
112 rx
? "rx" : "tx", type_str
, len
);
119 case PIM_MSDP_V4_SOURCE_ACTIVE
:
120 pim_msdp_pkt_sa_dump(s
);
126 /* Check file descriptor whether connect is established. */
127 static void pim_msdp_connect_check(struct pim_msdp_peer
*mp
)
133 if (mp
->state
!= PIM_MSDP_CONNECTING
) {
134 /* if we are here it means we are not in a connecting or
136 * for now treat this as a fatal error */
137 pim_msdp_peer_reset_tcp_conn(mp
, "invalid-state");
141 PIM_MSDP_PEER_READ_OFF(mp
);
142 PIM_MSDP_PEER_WRITE_OFF(mp
);
144 /* Check file descriptor. */
145 slen
= sizeof(status
);
146 ret
= getsockopt(mp
->fd
, SOL_SOCKET
, SO_ERROR
, (void *)&status
, &slen
);
148 /* If getsockopt is fail, this is fatal error. */
150 flog_err_sys(EC_LIB_SOCKET
,
151 "can't get sockopt for nonblocking connect");
152 pim_msdp_peer_reset_tcp_conn(mp
, "connect-failed");
156 /* When status is 0 then TCP connection is established. */
157 if (PIM_DEBUG_MSDP_INTERNAL
) {
158 zlog_debug("MSDP peer %s pim_connect_check %s", mp
->key_str
,
159 status
? "fail" : "success");
162 pim_msdp_peer_established(mp
);
164 pim_msdp_peer_reset_tcp_conn(mp
, "connect-failed");
168 static void pim_msdp_pkt_delete(struct pim_msdp_peer
*mp
)
170 stream_free(stream_fifo_pop(mp
->obuf
));
173 static void pim_msdp_pkt_add(struct pim_msdp_peer
*mp
, struct stream
*s
)
175 stream_fifo_push(mp
->obuf
, s
);
178 static void pim_msdp_write_proceed_actions(struct pim_msdp_peer
*mp
)
180 if (stream_fifo_head(mp
->obuf
)) {
181 PIM_MSDP_PEER_WRITE_ON(mp
);
185 int pim_msdp_write(struct thread
*thread
)
187 struct pim_msdp_peer
*mp
;
190 enum pim_msdp_tlv type
;
193 int work_max_cnt
= 100;
195 mp
= THREAD_ARG(thread
);
198 if (PIM_DEBUG_MSDP_INTERNAL
) {
199 zlog_debug("MSDP peer %s pim_msdp_write", mp
->key_str
);
205 /* check if TCP connection is established */
206 if (mp
->state
!= PIM_MSDP_ESTABLISHED
) {
207 pim_msdp_connect_check(mp
);
211 s
= stream_fifo_head(mp
->obuf
);
213 pim_msdp_write_proceed_actions(mp
);
217 sockopt_cork(mp
->fd
, 1);
219 /* Nonblocking write until TCP output buffer is full */
223 /* Number of bytes to be sent */
224 writenum
= stream_get_endp(s
) - stream_get_getp(s
);
226 /* Call write() system call */
227 num
= write(mp
->fd
, stream_pnt(s
), writenum
);
229 /* write failed either retry needed or error */
230 if (ERRNO_IO_RETRY(errno
)) {
231 if (PIM_DEBUG_MSDP_INTERNAL
) {
233 "MSDP peer %s pim_msdp_write io retry",
239 pim_msdp_peer_reset_tcp_conn(mp
, "pkt-tx-failed");
243 if (num
!= writenum
) {
245 stream_forward_getp(s
, num
);
246 if (PIM_DEBUG_MSDP_INTERNAL
) {
248 "MSDP peer %s pim_msdp_partial_write",
254 /* Retrieve msdp packet type. */
255 stream_set_getp(s
, 0);
256 type
= stream_getc(s
);
257 len
= stream_getw(s
);
259 case PIM_MSDP_KEEPALIVE
:
262 case PIM_MSDP_V4_SOURCE_ACTIVE
:
267 if (PIM_DEBUG_MSDP_PACKETS
) {
268 pim_msdp_pkt_dump(mp
, type
, len
, false /*rx*/, s
);
271 /* packet sent delete it. */
272 pim_msdp_pkt_delete(mp
);
275 /* may need to pause if we have done too much work in this
277 if (work_cnt
>= work_max_cnt
) {
280 } while ((s
= stream_fifo_head(mp
->obuf
)) != NULL
);
281 pim_msdp_write_proceed_actions(mp
);
283 sockopt_cork(mp
->fd
, 0);
285 if (PIM_DEBUG_MSDP_INTERNAL
) {
286 zlog_debug("MSDP peer %s pim_msdp_write wrote %d packets",
287 mp
->key_str
, work_cnt
);
293 static void pim_msdp_pkt_send(struct pim_msdp_peer
*mp
, struct stream
*s
)
295 /* Add packet to the end of list. */
296 pim_msdp_pkt_add(mp
, s
);
298 PIM_MSDP_PEER_WRITE_ON(mp
);
301 void pim_msdp_pkt_ka_tx(struct pim_msdp_peer
*mp
)
305 if (mp
->state
!= PIM_MSDP_ESTABLISHED
) {
306 /* don't tx anything unless a session is established */
309 s
= stream_new(PIM_MSDP_KA_TLV_MAX_SIZE
);
310 stream_putc(s
, PIM_MSDP_KEEPALIVE
);
311 stream_putw(s
, PIM_MSDP_KA_TLV_MAX_SIZE
);
313 pim_msdp_pkt_send(mp
, s
);
316 static void pim_msdp_pkt_sa_push_to_one_peer(struct pim_instance
*pim
,
317 struct pim_msdp_peer
*mp
)
321 if (mp
->state
!= PIM_MSDP_ESTABLISHED
) {
322 /* don't tx anything unless a session is established */
325 s
= stream_dup(pim
->msdp
.work_obuf
);
327 pim_msdp_pkt_send(mp
, s
);
328 mp
->flags
|= PIM_MSDP_PEERF_SA_JUST_SENT
;
332 /* push the stream into the obuf fifo of all the peers */
333 static void pim_msdp_pkt_sa_push(struct pim_instance
*pim
,
334 struct pim_msdp_peer
*mp
)
336 struct listnode
*mpnode
;
339 pim_msdp_pkt_sa_push_to_one_peer(pim
, mp
);
341 for (ALL_LIST_ELEMENTS_RO(pim
->msdp
.peer_list
, mpnode
, mp
)) {
342 if (PIM_DEBUG_MSDP_INTERNAL
) {
343 zlog_debug("MSDP peer %s pim_msdp_pkt_sa_push",
346 pim_msdp_pkt_sa_push_to_one_peer(pim
, mp
);
351 static int pim_msdp_pkt_sa_fill_hdr(struct pim_instance
*pim
, int local_cnt
)
355 stream_reset(pim
->msdp
.work_obuf
);
356 curr_tlv_ecnt
= local_cnt
> PIM_MSDP_SA_MAX_ENTRY_CNT
357 ? PIM_MSDP_SA_MAX_ENTRY_CNT
359 local_cnt
-= curr_tlv_ecnt
;
360 stream_putc(pim
->msdp
.work_obuf
, PIM_MSDP_V4_SOURCE_ACTIVE
);
361 stream_putw(pim
->msdp
.work_obuf
,
362 PIM_MSDP_SA_ENTRY_CNT2SIZE(curr_tlv_ecnt
));
363 stream_putc(pim
->msdp
.work_obuf
, curr_tlv_ecnt
);
364 stream_put_ipv4(pim
->msdp
.work_obuf
, pim
->msdp
.originator_id
.s_addr
);
369 static void pim_msdp_pkt_sa_fill_one(struct pim_msdp_sa
*sa
)
371 stream_put3(sa
->pim
->msdp
.work_obuf
, 0 /* reserved */);
372 stream_putc(sa
->pim
->msdp
.work_obuf
, 32 /* sprefix len */);
373 stream_put_ipv4(sa
->pim
->msdp
.work_obuf
, sa
->sg
.grp
.s_addr
);
374 stream_put_ipv4(sa
->pim
->msdp
.work_obuf
, sa
->sg
.src
.s_addr
);
377 static void pim_msdp_pkt_sa_gen(struct pim_instance
*pim
,
378 struct pim_msdp_peer
*mp
)
380 struct listnode
*sanode
;
381 struct pim_msdp_sa
*sa
;
383 int local_cnt
= pim
->msdp
.local_cnt
;
386 if (PIM_DEBUG_MSDP_INTERNAL
) {
387 zlog_debug(" sa gen %d", local_cnt
);
390 local_cnt
= pim_msdp_pkt_sa_fill_hdr(pim
, local_cnt
);
392 for (ALL_LIST_ELEMENTS_RO(pim
->msdp
.sa_list
, sanode
, sa
)) {
393 if (!(sa
->flags
& PIM_MSDP_SAF_LOCAL
)) {
394 /* current implementation of MSDP is for anycast i.e.
396 * no re-forwarding of SAs that we learnt from other
400 /* add sa into scratch pad */
401 pim_msdp_pkt_sa_fill_one(sa
);
403 if (sa_count
>= PIM_MSDP_SA_MAX_ENTRY_CNT
) {
404 pim_msdp_pkt_sa_push(pim
, mp
);
407 if (PIM_DEBUG_MSDP_INTERNAL
) {
408 zlog_debug(" sa gen for remainder %d",
411 local_cnt
= pim_msdp_pkt_sa_fill_hdr(pim
, local_cnt
);
416 pim_msdp_pkt_sa_push(pim
, mp
);
421 static void pim_msdp_pkt_sa_tx_done(struct pim_instance
*pim
)
423 struct listnode
*mpnode
;
424 struct pim_msdp_peer
*mp
;
426 /* if SA were sent to the peers we restart ka timer and avoid
427 * unnecessary ka noise */
428 for (ALL_LIST_ELEMENTS_RO(pim
->msdp
.peer_list
, mpnode
, mp
)) {
429 if (mp
->flags
& PIM_MSDP_PEERF_SA_JUST_SENT
) {
430 mp
->flags
&= ~PIM_MSDP_PEERF_SA_JUST_SENT
;
431 pim_msdp_peer_pkt_txed(mp
);
436 void pim_msdp_pkt_sa_tx(struct pim_instance
*pim
)
438 pim_msdp_pkt_sa_gen(pim
, NULL
/* mp */);
439 pim_msdp_pkt_sa_tx_done(pim
);
442 void pim_msdp_pkt_sa_tx_one(struct pim_msdp_sa
*sa
)
444 pim_msdp_pkt_sa_fill_hdr(sa
->pim
, 1 /* cnt */);
445 pim_msdp_pkt_sa_fill_one(sa
);
446 pim_msdp_pkt_sa_push(sa
->pim
, NULL
);
447 pim_msdp_pkt_sa_tx_done(sa
->pim
);
450 /* when a connection is first established we push all SAs immediately */
451 void pim_msdp_pkt_sa_tx_to_one_peer(struct pim_msdp_peer
*mp
)
453 pim_msdp_pkt_sa_gen(mp
->pim
, mp
);
454 pim_msdp_pkt_sa_tx_done(mp
->pim
);
457 static void pim_msdp_pkt_rxed_with_fatal_error(struct pim_msdp_peer
*mp
)
459 pim_msdp_peer_reset_tcp_conn(mp
, "invalid-pkt-rx");
462 static void pim_msdp_pkt_ka_rx(struct pim_msdp_peer
*mp
, int len
)
465 if (len
!= PIM_MSDP_KA_TLV_MAX_SIZE
) {
466 pim_msdp_pkt_rxed_with_fatal_error(mp
);
469 pim_msdp_peer_pkt_rxed(mp
);
472 static void pim_msdp_pkt_sa_rx_one(struct pim_msdp_peer
*mp
, struct in_addr rp
)
477 /* just throw away the three reserved bytes */
478 stream_get3(mp
->ibuf
);
479 prefix_len
= stream_getc(mp
->ibuf
);
481 memset(&sg
, 0, sizeof(struct prefix_sg
));
482 sg
.grp
.s_addr
= stream_get_ipv4(mp
->ibuf
);
483 sg
.src
.s_addr
= stream_get_ipv4(mp
->ibuf
);
485 if (prefix_len
!= 32) {
486 /* ignore SA update if the prefix length is not 32 */
487 flog_err(EC_PIM_MSDP_PACKET
,
488 "rxed sa update with invalid prefix length %d",
492 if (PIM_DEBUG_MSDP_PACKETS
) {
493 zlog_debug(" sg %s", pim_str_sg_dump(&sg
));
495 pim_msdp_sa_ref(mp
->pim
, mp
, &sg
, rp
);
498 static void pim_msdp_pkt_sa_rx(struct pim_msdp_peer
*mp
, int len
)
502 struct in_addr rp
; /* Last RP address associated with this SA */
506 if (len
< PIM_MSDP_SA_TLV_MIN_SIZE
) {
507 pim_msdp_pkt_rxed_with_fatal_error(mp
);
511 entry_cnt
= stream_getc(mp
->ibuf
);
512 /* some vendors include the actual multicast data in the tlv (at the
514 * we will ignore such data. in the future we may consider pushing it
517 if (len
< PIM_MSDP_SA_ENTRY_CNT2SIZE(entry_cnt
)) {
518 pim_msdp_pkt_rxed_with_fatal_error(mp
);
521 rp
.s_addr
= stream_get_ipv4(mp
->ibuf
);
523 if (PIM_DEBUG_MSDP_PACKETS
) {
524 char rp_str
[INET_ADDRSTRLEN
];
525 pim_inet4_dump("<rp?>", rp
, rp_str
, sizeof(rp_str
));
526 zlog_debug(" entry_cnt %d rp %s", entry_cnt
, rp_str
);
529 if (!pim_msdp_peer_rpf_check(mp
, rp
)) {
530 /* if peer-RPF check fails don't process the packet any further
532 if (PIM_DEBUG_MSDP_PACKETS
) {
533 zlog_debug(" peer RPF check failed");
538 pim_msdp_peer_pkt_rxed(mp
);
540 /* update SA cache */
541 for (i
= 0; i
< entry_cnt
; ++i
) {
542 pim_msdp_pkt_sa_rx_one(mp
, rp
);
546 static void pim_msdp_pkt_rx(struct pim_msdp_peer
*mp
)
548 enum pim_msdp_tlv type
;
551 /* re-read type and len */
552 type
= stream_getc_from(mp
->ibuf
, 0);
553 len
= stream_getw_from(mp
->ibuf
, 1);
554 if (len
< PIM_MSDP_HEADER_SIZE
) {
555 pim_msdp_pkt_rxed_with_fatal_error(mp
);
559 if (len
> PIM_MSDP_SA_TLV_MAX_SIZE
) {
560 /* if tlv size if greater than max just ignore the tlv */
564 if (PIM_DEBUG_MSDP_PACKETS
) {
565 pim_msdp_pkt_dump(mp
, type
, len
, true /*rx*/, NULL
/*s*/);
569 case PIM_MSDP_KEEPALIVE
:
570 pim_msdp_pkt_ka_rx(mp
, len
);
572 case PIM_MSDP_V4_SOURCE_ACTIVE
:
574 pim_msdp_pkt_sa_rx(mp
, len
);
581 /* pim msdp read utility function. */
582 static int pim_msdp_read_packet(struct pim_msdp_peer
*mp
)
589 old_endp
= stream_get_endp(mp
->ibuf
);
590 readsize
= mp
->packet_size
- old_endp
;
595 /* Read packet from fd */
596 nbytes
= stream_read_try(mp
->ibuf
, mp
->fd
, readsize
);
597 new_endp
= stream_get_endp(mp
->ibuf
);
599 if (PIM_DEBUG_MSDP_INTERNAL
) {
600 zlog_debug("MSDP peer %s read failed %d", mp
->key_str
,
604 if (PIM_DEBUG_MSDP_INTERNAL
) {
606 "MSDP peer %s pim_msdp_read io retry old_end: %d new_end: %d",
607 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
,
621 pim_msdp_peer_reset_tcp_conn(mp
, "peer-down");
625 /* We read partial packet. */
626 if (stream_get_endp(mp
->ibuf
) != mp
->packet_size
) {
627 if (PIM_DEBUG_MSDP_INTERNAL
) {
629 "MSDP peer %s read partial len %d old_endp %d new_endp %d",
630 mp
->key_str
, mp
->packet_size
, old_endp
,
639 int pim_msdp_read(struct thread
*thread
)
641 struct pim_msdp_peer
*mp
;
645 mp
= THREAD_ARG(thread
);
648 if (PIM_DEBUG_MSDP_INTERNAL
) {
649 zlog_debug("MSDP peer %s pim_msdp_read", mp
->key_str
);
656 /* check if TCP connection is established */
657 if (mp
->state
!= PIM_MSDP_ESTABLISHED
) {
658 pim_msdp_connect_check(mp
);
662 PIM_MSDP_PEER_READ_ON(mp
);
664 if (!mp
->packet_size
) {
665 mp
->packet_size
= PIM_MSDP_HEADER_SIZE
;
668 if (stream_get_endp(mp
->ibuf
) < PIM_MSDP_HEADER_SIZE
) {
669 /* start by reading the TLV header */
670 rc
= pim_msdp_read_packet(mp
);
672 goto pim_msdp_read_end
;
675 /* Find TLV type and len */
676 stream_getc(mp
->ibuf
);
677 len
= stream_getw(mp
->ibuf
);
678 if (len
< PIM_MSDP_HEADER_SIZE
) {
679 pim_msdp_pkt_rxed_with_fatal_error(mp
);
680 goto pim_msdp_read_end
;
682 /* read complete TLV */
683 mp
->packet_size
= len
;
686 rc
= pim_msdp_read_packet(mp
);
688 goto pim_msdp_read_end
;
693 /* reset input buffers and get ready for the next packet */
695 stream_reset(mp
->ibuf
);