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