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