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