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