]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_msdp_packet.c
Merge remote-tracking branch 'origin/stable/3.0'
[mirror_frr.git] / pimd / pim_msdp_packet.c
1 /*
2 * IP MSDP packet helper
3 * Copyright (C) 2016 Cumulus Networks, Inc.
4 *
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.
9 *
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.
14 *
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
18 */
19 #include <zebra.h>
20
21 #include <lib/log.h>
22 #include <lib/network.h>
23 #include <lib/stream.h>
24 #include <lib/thread.h>
25 #include <lib/vty.h>
26
27 #include "pimd.h"
28 #include "pim_str.h"
29
30 #include "pim_msdp.h"
31 #include "pim_msdp_packet.h"
32 #include "pim_msdp_socket.h"
33
34 static char *
35 pim_msdp_pkt_type_dump(enum pim_msdp_tlv type, char *buf, int buf_size)
36 {
37 switch (type) {
38 case PIM_MSDP_V4_SOURCE_ACTIVE:
39 snprintf(buf, buf_size, "%s", "SA");
40 break;
41 case PIM_MSDP_V4_SOURCE_ACTIVE_REQUEST:
42 snprintf(buf, buf_size, "%s", "SA_REQ");
43 break;
44 case PIM_MSDP_V4_SOURCE_ACTIVE_RESPONSE:
45 snprintf(buf, buf_size, "%s", "SA_RESP");
46 break;
47 case PIM_MSDP_KEEPALIVE:
48 snprintf(buf, buf_size, "%s", "KA");
49 break;
50 case PIM_MSDP_RESERVED:
51 snprintf(buf, buf_size, "%s", "RSVD");
52 break;
53 case PIM_MSDP_TRACEROUTE_PROGRESS:
54 snprintf(buf, buf_size, "%s", "TRACE_PROG");
55 break;
56 case PIM_MSDP_TRACEROUTE_REPLY:
57 snprintf(buf, buf_size, "%s", "TRACE_REPLY");
58 break;
59 default:
60 snprintf(buf, buf_size, "UNK-%d", type);
61 }
62 return buf;
63 }
64
65 static void
66 pim_msdp_pkt_sa_dump_one(struct stream *s)
67 {
68 struct prefix_sg sg;
69
70 /* just throw away the three reserved bytes */
71 stream_get3(s);
72 /* throw away the prefix length also */
73 stream_getc(s);
74
75 memset(&sg, 0, sizeof (struct prefix_sg));
76 sg.grp.s_addr = stream_get_ipv4(s);
77 sg.src.s_addr = stream_get_ipv4(s);
78
79 zlog_debug(" sg %s", pim_str_sg_dump(&sg));
80 }
81
82 static void
83 pim_msdp_pkt_sa_dump(struct stream *s)
84 {
85 int entry_cnt;
86 int i;
87 struct in_addr rp; /* Last RP address associated with this SA */
88
89 entry_cnt = stream_getc(s);
90 rp.s_addr = stream_get_ipv4(s);
91
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);
96 }
97
98 /* dump SAs */
99 for (i = 0; i < entry_cnt; ++i) {
100 pim_msdp_pkt_sa_dump_one(s);
101 }
102 }
103
104 static void
105 pim_msdp_pkt_dump(struct pim_msdp_peer *mp, int type, int len, bool rx,
106 struct stream *s)
107 {
108 char type_str[PIM_MSDP_PKT_TYPE_STRLEN];
109
110 pim_msdp_pkt_type_dump(type, type_str, sizeof(type_str));
111
112 zlog_debug("MSDP peer %s pkt %s type %s len %d",
113 mp->key_str, rx?"rx":"tx", type_str, len);
114
115 if (!s) {
116 return;
117 }
118
119 switch(type) {
120 case PIM_MSDP_V4_SOURCE_ACTIVE:
121 pim_msdp_pkt_sa_dump(s);
122 break;
123 default:;
124 }
125 }
126
127 /* Check file descriptor whether connect is established. */
128 static void
129 pim_msdp_connect_check(struct pim_msdp_peer *mp)
130 {
131 int status;
132 socklen_t slen;
133 int ret;
134
135 if (mp->state != PIM_MSDP_CONNECTING) {
136 /* if we are here it means we are not in a connecting or established state
137 * for now treat this as a fatal error */
138 pim_msdp_peer_reset_tcp_conn(mp, "invalid-state");
139 return;
140 }
141
142 PIM_MSDP_PEER_READ_OFF(mp);
143 PIM_MSDP_PEER_WRITE_OFF(mp);
144
145 /* Check file descriptor. */
146 slen = sizeof(status);
147 ret = getsockopt(mp->fd, SOL_SOCKET, SO_ERROR, (void *)&status, &slen);
148
149 /* If getsockopt is fail, this is fatal error. */
150 if (ret < 0) {
151 zlog_err("can't get sockopt for nonblocking connect");
152 pim_msdp_peer_reset_tcp_conn(mp, "connect-failed");
153 return;
154 }
155
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, status?"fail":"success");
159 }
160 if (status == 0) {
161 pim_msdp_peer_established(mp);
162 } else {
163 pim_msdp_peer_reset_tcp_conn(mp, "connect-failed");
164 }
165 }
166
167 static void
168 pim_msdp_pkt_delete(struct pim_msdp_peer *mp)
169 {
170 stream_free(stream_fifo_pop(mp->obuf));
171 }
172
173 static void
174 pim_msdp_pkt_add(struct pim_msdp_peer *mp, struct stream *s)
175 {
176 stream_fifo_push(mp->obuf, s);
177 }
178
179 static void
180 pim_msdp_write_proceed_actions(struct pim_msdp_peer *mp)
181 {
182 if (stream_fifo_head(mp->obuf)) {
183 PIM_MSDP_PEER_WRITE_ON(mp);
184 }
185 }
186
187 int
188 pim_msdp_write(struct thread *thread)
189 {
190 struct pim_msdp_peer *mp;
191 struct stream *s;
192 int num;
193 enum pim_msdp_tlv type;
194 int len;
195 int work_cnt = 0;
196 int work_max_cnt = 100;
197
198 mp = THREAD_ARG(thread);
199 mp->t_write = NULL;
200
201 if (PIM_DEBUG_MSDP_INTERNAL) {
202 zlog_debug("MSDP peer %s pim_msdp_write", mp->key_str);
203 }
204 if (mp->fd < 0) {
205 return -1;
206 }
207
208 /* check if TCP connection is established */
209 if (mp->state != PIM_MSDP_ESTABLISHED) {
210 pim_msdp_connect_check(mp);
211 return 0;
212 }
213
214 s = stream_fifo_head(mp->obuf);
215 if (!s) {
216 pim_msdp_write_proceed_actions(mp);
217 return 0;
218 }
219
220 sockopt_cork(mp->fd, 1);
221
222 /* Nonblocking write until TCP output buffer is full */
223 do
224 {
225 int writenum;
226
227 /* Number of bytes to be sent */
228 writenum = stream_get_endp(s) - stream_get_getp(s);
229
230 /* Call write() system call */
231 num = write(mp->fd, STREAM_PNT(s), writenum);
232 if (num < 0) {
233 /* write failed either retry needed or error */
234 if (ERRNO_IO_RETRY(errno)) {
235 if (PIM_DEBUG_MSDP_INTERNAL) {
236 zlog_debug("MSDP peer %s pim_msdp_write io retry", mp->key_str);
237 }
238 break;
239 }
240
241 pim_msdp_peer_reset_tcp_conn(mp, "pkt-tx-failed");
242 return 0;
243 }
244
245 if (num != writenum) {
246 /* Partial write */
247 stream_forward_getp(s, num);
248 if (PIM_DEBUG_MSDP_INTERNAL) {
249 zlog_debug("MSDP peer %s pim_msdp_partial_write", mp->key_str);
250 }
251 break;
252 }
253
254 /* Retrieve msdp packet type. */
255 stream_set_getp(s,0);
256 type = stream_getc(s);
257 len = stream_getw(s);
258 switch (type)
259 {
260 case PIM_MSDP_KEEPALIVE:
261 mp->ka_tx_cnt++;
262 break;
263 case PIM_MSDP_V4_SOURCE_ACTIVE:
264 mp->sa_tx_cnt++;
265 break;
266 default:;
267 }
268 if (PIM_DEBUG_MSDP_PACKETS) {
269 pim_msdp_pkt_dump(mp, type, len, false /*rx*/, s);
270 }
271
272 /* packet sent delete it. */
273 pim_msdp_pkt_delete(mp);
274
275 ++work_cnt;
276 /* may need to pause if we have done too much work in this
277 * loop */
278 if (work_cnt >= work_max_cnt) {
279 break;
280 }
281 } while ((s = stream_fifo_head(mp->obuf)) != NULL);
282 pim_msdp_write_proceed_actions(mp);
283
284 sockopt_cork(mp->fd, 0);
285
286 if (PIM_DEBUG_MSDP_INTERNAL) {
287 zlog_debug("MSDP peer %s pim_msdp_write wrote %d packets", mp->key_str, work_cnt);
288 }
289
290 return 0;
291 }
292
293 static void
294 pim_msdp_pkt_send(struct pim_msdp_peer *mp, struct stream *s)
295 {
296 /* Add packet to the end of list. */
297 pim_msdp_pkt_add(mp, s);
298
299 PIM_MSDP_PEER_WRITE_ON(mp);
300 }
301
302 void
303 pim_msdp_pkt_ka_tx(struct pim_msdp_peer *mp)
304 {
305 struct stream *s;
306
307 if (mp->state != PIM_MSDP_ESTABLISHED) {
308 /* don't tx anything unless a session is established */
309 return;
310 }
311 s = stream_new(PIM_MSDP_KA_TLV_MAX_SIZE);
312 stream_putc(s, PIM_MSDP_KEEPALIVE);
313 stream_putw(s, PIM_MSDP_KA_TLV_MAX_SIZE);
314
315 pim_msdp_pkt_send(mp, s);
316 }
317
318 static void
319 pim_msdp_pkt_sa_push_to_one_peer(struct pim_msdp_peer *mp)
320 {
321 struct stream *s;
322
323 if (mp->state != PIM_MSDP_ESTABLISHED) {
324 /* don't tx anything unless a session is established */
325 return;
326 }
327 s = stream_dup(msdp->work_obuf);
328 if (s) {
329 pim_msdp_pkt_send(mp, s);
330 mp->flags |= PIM_MSDP_PEERF_SA_JUST_SENT;
331 }
332 }
333
334 /* push the stream into the obuf fifo of all the peers */
335 static void
336 pim_msdp_pkt_sa_push(struct pim_msdp_peer *mp)
337 {
338 struct listnode *mpnode;
339
340 if (mp) {
341 pim_msdp_pkt_sa_push_to_one_peer(mp);
342 } else {
343 for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) {
344 if (PIM_DEBUG_MSDP_INTERNAL) {
345 zlog_debug("MSDP peer %s pim_msdp_pkt_sa_push", mp->key_str);
346 }
347 pim_msdp_pkt_sa_push_to_one_peer(mp);
348 }
349 }
350 }
351
352 static int
353 pim_msdp_pkt_sa_fill_hdr(int local_cnt)
354 {
355 int curr_tlv_ecnt;
356
357 stream_reset(msdp->work_obuf);
358 curr_tlv_ecnt = local_cnt>PIM_MSDP_SA_MAX_ENTRY_CNT?PIM_MSDP_SA_MAX_ENTRY_CNT:local_cnt;
359 local_cnt -= curr_tlv_ecnt;
360 stream_putc(msdp->work_obuf, PIM_MSDP_V4_SOURCE_ACTIVE);
361 stream_putw(msdp->work_obuf, PIM_MSDP_SA_ENTRY_CNT2SIZE(curr_tlv_ecnt));
362 stream_putc(msdp->work_obuf, curr_tlv_ecnt);
363 stream_put_ipv4(msdp->work_obuf, msdp->originator_id.s_addr);
364
365 return local_cnt;
366 }
367
368 static void
369 pim_msdp_pkt_sa_fill_one(struct pim_msdp_sa *sa)
370 {
371 stream_put3(msdp->work_obuf, 0 /* reserved */);
372 stream_putc(msdp->work_obuf, 32 /* sprefix len */);
373 stream_put_ipv4(msdp->work_obuf, sa->sg.grp.s_addr);
374 stream_put_ipv4(msdp->work_obuf, sa->sg.src.s_addr);
375 }
376
377 static void
378 pim_msdp_pkt_sa_gen(struct pim_msdp_peer *mp)
379 {
380 struct listnode *sanode;
381 struct pim_msdp_sa *sa;
382 int sa_count;
383 int local_cnt = msdp->local_cnt;
384
385 sa_count = 0;
386 if (PIM_DEBUG_MSDP_INTERNAL) {
387 zlog_debug(" sa gen %d", local_cnt);
388 }
389
390 local_cnt = pim_msdp_pkt_sa_fill_hdr(local_cnt);
391
392 for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
393 if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) {
394 /* current implementation of MSDP is for anycast i.e. full mesh. so
395 * no re-forwarding of SAs that we learnt from other peers */
396 continue;
397 }
398 /* add sa into scratch pad */
399 pim_msdp_pkt_sa_fill_one(sa);
400 ++sa_count;
401 if (sa_count >= PIM_MSDP_SA_MAX_ENTRY_CNT) {
402 pim_msdp_pkt_sa_push(mp);
403 /* reset headers */
404 sa_count = 0;
405 if (PIM_DEBUG_MSDP_INTERNAL) {
406 zlog_debug(" sa gen for remainder %d", local_cnt);
407 }
408 local_cnt = pim_msdp_pkt_sa_fill_hdr(local_cnt);
409 }
410 }
411
412 if (sa_count) {
413 pim_msdp_pkt_sa_push(mp);
414 }
415 return;
416 }
417
418 static void
419 pim_msdp_pkt_sa_tx_done(void)
420 {
421 struct listnode *mpnode;
422 struct pim_msdp_peer *mp;
423
424 /* if SA were sent to the peers we restart ka timer and avoid
425 * unnecessary ka noise */
426 for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) {
427 if (mp->flags & PIM_MSDP_PEERF_SA_JUST_SENT) {
428 mp->flags &= ~PIM_MSDP_PEERF_SA_JUST_SENT;
429 pim_msdp_peer_pkt_txed(mp);
430 }
431 }
432 }
433
434 void
435 pim_msdp_pkt_sa_tx(void)
436 {
437 pim_msdp_pkt_sa_gen(NULL /* mp */);
438 pim_msdp_pkt_sa_tx_done();
439 }
440
441 void
442 pim_msdp_pkt_sa_tx_one(struct pim_msdp_sa *sa)
443 {
444 pim_msdp_pkt_sa_fill_hdr(1 /* cnt */);
445 pim_msdp_pkt_sa_fill_one(sa);
446 pim_msdp_pkt_sa_push(NULL);
447 pim_msdp_pkt_sa_tx_done();
448 }
449
450 /* when a connection is first established we push all SAs immediately */
451 void
452 pim_msdp_pkt_sa_tx_to_one_peer(struct pim_msdp_peer *mp)
453 {
454 pim_msdp_pkt_sa_gen(mp);
455 pim_msdp_pkt_sa_tx_done();
456 }
457
458 static void
459 pim_msdp_pkt_rxed_with_fatal_error(struct pim_msdp_peer *mp)
460 {
461 pim_msdp_peer_reset_tcp_conn(mp, "invalid-pkt-rx");
462 }
463
464 static void
465 pim_msdp_pkt_ka_rx(struct pim_msdp_peer *mp, int len)
466 {
467 mp->ka_rx_cnt++;
468 if (len != PIM_MSDP_KA_TLV_MAX_SIZE) {
469 pim_msdp_pkt_rxed_with_fatal_error(mp);
470 return;
471 }
472 pim_msdp_peer_pkt_rxed(mp);
473 }
474
475 static void
476 pim_msdp_pkt_sa_rx_one(struct pim_msdp_peer *mp, struct in_addr rp)
477 {
478 int prefix_len;
479 struct prefix_sg sg;
480
481 /* just throw away the three reserved bytes */
482 stream_get3(mp->ibuf);
483 prefix_len = stream_getc(mp->ibuf);
484
485 memset(&sg, 0, sizeof (struct prefix_sg));
486 sg.grp.s_addr = stream_get_ipv4(mp->ibuf);
487 sg.src.s_addr = stream_get_ipv4(mp->ibuf);
488
489 if (prefix_len != 32) {
490 /* ignore SA update if the prefix length is not 32 */
491 zlog_err("rxed sa update with invalid prefix length %d", prefix_len);
492 return;
493 }
494 if (PIM_DEBUG_MSDP_PACKETS) {
495 zlog_debug(" sg %s", pim_str_sg_dump(&sg));
496 }
497 pim_msdp_sa_ref(mp, &sg, rp);
498 }
499
500 static void
501 pim_msdp_pkt_sa_rx(struct pim_msdp_peer *mp, int len)
502 {
503 int entry_cnt;
504 int i;
505 struct in_addr rp; /* Last RP address associated with this SA */
506
507 mp->sa_rx_cnt++;
508
509 if (len < PIM_MSDP_SA_TLV_MIN_SIZE) {
510 pim_msdp_pkt_rxed_with_fatal_error(mp);
511 return;
512 }
513
514 entry_cnt = stream_getc(mp->ibuf);
515 /* some vendors include the actual multicast data in the tlv (at the end).
516 * we will ignore such data. in the future we may consider pushing it down
517 * the RPT */
518 if (len < PIM_MSDP_SA_ENTRY_CNT2SIZE(entry_cnt)) {
519 pim_msdp_pkt_rxed_with_fatal_error(mp);
520 return;
521 }
522 rp.s_addr = stream_get_ipv4(mp->ibuf);
523
524 if (PIM_DEBUG_MSDP_PACKETS) {
525 char rp_str[INET_ADDRSTRLEN];
526 pim_inet4_dump("<rp?>", rp, rp_str, sizeof(rp_str));
527 zlog_debug(" entry_cnt %d rp %s", entry_cnt, rp_str);
528 }
529
530 if (!pim_msdp_peer_rpf_check(mp, rp)) {
531 /* 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");
534 }
535 return;
536 }
537
538 pim_msdp_peer_pkt_rxed(mp);
539
540 /* update SA cache */
541 for (i = 0; i < entry_cnt; ++i) {
542 pim_msdp_pkt_sa_rx_one(mp, rp);
543 }
544 }
545
546 static void
547 pim_msdp_pkt_rx(struct pim_msdp_peer *mp)
548 {
549 enum pim_msdp_tlv type;
550 int len;
551
552 /* re-read type and len */
553 type = stream_getc_from(mp->ibuf, 0);
554 len = stream_getw_from(mp->ibuf, 1);
555 if (len < PIM_MSDP_HEADER_SIZE) {
556 pim_msdp_pkt_rxed_with_fatal_error(mp);
557 return;
558 }
559
560 if (len > PIM_MSDP_SA_TLV_MAX_SIZE) {
561 /* if tlv size if greater than max just ignore the tlv */
562 return;
563 }
564
565 if (PIM_DEBUG_MSDP_PACKETS) {
566 pim_msdp_pkt_dump(mp, type, len, true /*rx*/, NULL /*s*/);
567 }
568
569 switch(type) {
570 case PIM_MSDP_KEEPALIVE:
571 pim_msdp_pkt_ka_rx(mp, len);
572 break;
573 case PIM_MSDP_V4_SOURCE_ACTIVE:
574 mp->sa_rx_cnt++;
575 pim_msdp_pkt_sa_rx(mp, len);
576 break;
577 default:
578 mp->unk_rx_cnt++;
579 }
580 }
581
582 /* pim msdp read utility function. */
583 static int
584 pim_msdp_read_packet(struct pim_msdp_peer *mp)
585 {
586 int nbytes;
587 int readsize;
588 int old_endp;
589 int new_endp;
590
591 old_endp = stream_get_endp(mp->ibuf);
592 readsize = mp->packet_size - old_endp;
593 if (!readsize) {
594 return 0;
595 }
596
597 /* Read packet from fd */
598 nbytes = stream_read_try(mp->ibuf, mp->fd, readsize);
599 new_endp = stream_get_endp(mp->ibuf);
600 if (nbytes < 0) {
601 if (PIM_DEBUG_MSDP_INTERNAL) {
602 zlog_debug("MSDP peer %s read failed %d", mp->key_str, nbytes);
603 }
604 if (nbytes == -2) {
605 if (PIM_DEBUG_MSDP_INTERNAL) {
606 zlog_debug("MSDP peer %s pim_msdp_read io retry old_end: %d new_end: %d", mp->key_str, old_endp, new_endp);
607 }
608 /* transient error retry */
609 return -1;
610 }
611 pim_msdp_pkt_rxed_with_fatal_error(mp);
612 return -1;
613 }
614
615 if (!nbytes) {
616 if (PIM_DEBUG_MSDP_INTERNAL) {
617 zlog_debug("MSDP peer %s read failed %d", mp->key_str, nbytes);
618 }
619 pim_msdp_peer_reset_tcp_conn(mp, "peer-down");
620 return -1;
621 }
622
623 /* We read partial packet. */
624 if (stream_get_endp(mp->ibuf) != mp->packet_size) {
625 if (PIM_DEBUG_MSDP_INTERNAL) {
626 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);
627 }
628 return -1;
629 }
630
631 return 0;
632 }
633
634 int
635 pim_msdp_read(struct thread *thread)
636 {
637 struct pim_msdp_peer *mp;
638 int rc;
639 uint32_t len;
640
641 mp = THREAD_ARG(thread);
642 mp->t_read = NULL;
643
644 if (PIM_DEBUG_MSDP_INTERNAL) {
645 zlog_debug("MSDP peer %s pim_msdp_read", mp->key_str);
646 }
647
648 if (mp->fd < 0) {
649 return -1;
650 }
651
652 /* check if TCP connection is established */
653 if (mp->state != PIM_MSDP_ESTABLISHED) {
654 pim_msdp_connect_check(mp);
655 return 0;
656 }
657
658 PIM_MSDP_PEER_READ_ON(mp);
659
660 if (!mp->packet_size) {
661 mp->packet_size = PIM_MSDP_HEADER_SIZE;
662 }
663
664 if (stream_get_endp(mp->ibuf) < PIM_MSDP_HEADER_SIZE) {
665 /* start by reading the TLV header */
666 rc = pim_msdp_read_packet(mp);
667 if (rc < 0) {
668 goto pim_msdp_read_end;
669 }
670
671 /* Find TLV type and len */
672 stream_getc(mp->ibuf);
673 len = stream_getw(mp->ibuf);
674 if (len < PIM_MSDP_HEADER_SIZE) {
675 pim_msdp_pkt_rxed_with_fatal_error(mp);
676 goto pim_msdp_read_end;
677 }
678 /* read complete TLV */
679 mp->packet_size = len;
680 }
681
682 rc = pim_msdp_read_packet(mp);
683 if (rc < 0) {
684 goto pim_msdp_read_end;
685 }
686
687 pim_msdp_pkt_rx(mp);
688
689 /* reset input buffers and get ready for the next packet */
690 mp->packet_size = 0;
691 stream_reset(mp->ibuf);
692
693 pim_msdp_read_end:
694 return 0;
695 }