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