]>
Commit | Line | Data |
---|---|---|
2a333e0f | 1 | /* |
2 | * IP MSDP packet helper | |
3 | * Copyright (C) 2016 Cumulus Networks, Inc. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; see the file COPYING; if not, write to the | |
17 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | |
18 | * MA 02110-1301 USA | |
19 | */ | |
20 | #include <zebra.h> | |
21 | ||
22 | #include <lib/log.h> | |
23 | #include <lib/network.h> | |
24 | #include <lib/stream.h> | |
25 | #include <lib/thread.h> | |
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 | ||
34 | static char * | |
35 | pim_msdp_pkt_type_dump(enum pim_msdp_tlv type, char *buf, int buf_size) | |
36 | { | |
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; | |
63 | } | |
64 | ||
65 | static void | |
66 | pim_msdp_pkt_dump(struct pim_msdp_peer *mp, int type, int len, bool rx) | |
67 | { | |
68 | char key_str[PIM_MSDP_PEER_KEY_STRLEN]; | |
69 | char type_str[PIM_MSDP_PKT_TYPE_STRLEN]; | |
70 | ||
71 | pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), false); | |
72 | pim_msdp_pkt_type_dump(type, type_str, sizeof(type_str)); | |
73 | ||
74 | zlog_debug("%s pkt %s type %s len %d", | |
75 | key_str, rx?"rx":"tx", type_str, len); | |
76 | /* XXX: dump actual data */ | |
77 | } | |
78 | ||
79 | /* Check file descriptor whether connect is established. */ | |
80 | static void | |
81 | pim_msdp_connect_check(struct pim_msdp_peer *mp) | |
82 | { | |
83 | int status; | |
84 | socklen_t slen; | |
85 | int ret; | |
86 | ||
87 | if (mp->state != PIM_MSDP_CONNECTING) { | |
88 | /* if we are here it means we are not in a connecting or established state | |
89 | * for now treat this as a fatal error */ | |
90 | /* XXX:revisit; reset TCP connection */ | |
91 | pim_msdp_peer_reset_tcp_conn(mp, "invalid-state"); | |
92 | return; | |
93 | } | |
94 | ||
95 | PIM_MSDP_PEER_READ_OFF(mp); | |
96 | PIM_MSDP_PEER_WRITE_OFF(mp); | |
97 | ||
98 | /* Check file descriptor. */ | |
99 | slen = sizeof(status); | |
100 | ret = getsockopt(mp->fd, SOL_SOCKET, SO_ERROR, (void *)&status, &slen); | |
101 | ||
102 | /* If getsockopt is fail, this is fatal error. */ | |
103 | if (ret < 0) { | |
104 | zlog_err("can't get sockopt for nonblocking connect"); | |
105 | /* XXX:revisit; reset TCP connection */ | |
106 | pim_msdp_peer_reset_tcp_conn(mp, "connect-failed"); | |
107 | return; | |
108 | } | |
109 | ||
110 | /* When status is 0 then TCP connection is established. */ | |
111 | if (PIM_DEBUG_MSDP_INTERNAL) { | |
112 | char key_str[PIM_MSDP_PEER_KEY_STRLEN]; | |
113 | ||
114 | pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), false); | |
115 | zlog_debug("%s pim_connect_check %s", key_str, status?"fail":"success"); | |
116 | } | |
117 | if (status == 0) { | |
118 | pim_msdp_peer_established(mp); | |
119 | } else { | |
120 | /* XXX:revisit; reset TCP connection */ | |
121 | pim_msdp_peer_reset_tcp_conn(mp, "connect-failed"); | |
122 | } | |
123 | } | |
124 | ||
125 | static void | |
126 | pim_msdp_pkt_delete(struct pim_msdp_peer *mp) | |
127 | { | |
128 | stream_free(stream_fifo_pop(mp->obuf)); | |
129 | } | |
130 | ||
131 | static void | |
132 | pim_msdp_write_proceed_actions(struct pim_msdp_peer *mp) | |
133 | { | |
134 | if (stream_fifo_head(mp->obuf)) { | |
135 | PIM_MSDP_PEER_WRITE_ON(mp); | |
136 | } | |
137 | } | |
138 | ||
139 | int | |
140 | pim_msdp_write(struct thread *thread) | |
141 | { | |
142 | struct pim_msdp_peer *mp; | |
143 | struct stream *s; | |
144 | int num; | |
145 | enum pim_msdp_tlv type; | |
146 | ||
147 | mp = THREAD_ARG(thread); | |
148 | mp->t_write = NULL; | |
149 | ||
150 | if (PIM_DEBUG_MSDP_INTERNAL) { | |
151 | char key_str[PIM_MSDP_PEER_KEY_STRLEN]; | |
152 | ||
153 | pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), false); | |
154 | zlog_debug("%s pim_msdp_write", key_str); | |
155 | } | |
156 | if (mp->fd < 0) { | |
157 | return -1; | |
158 | } | |
159 | ||
160 | /* check if TCP connection is established */ | |
161 | if (mp->state != PIM_MSDP_ESTABLISHED) { | |
162 | pim_msdp_connect_check(mp); | |
163 | return 0; | |
164 | } | |
165 | ||
166 | s = stream_fifo_head(mp->obuf); | |
167 | if (!s) { | |
168 | pim_msdp_write_proceed_actions(mp); | |
169 | return 0; | |
170 | } | |
171 | ||
172 | sockopt_cork (mp->fd, 1); | |
173 | ||
174 | /* Nonblocking write until TCP output buffer is full */ | |
175 | do | |
176 | { | |
177 | int writenum; | |
178 | ||
179 | /* Number of bytes to be sent */ | |
180 | writenum = stream_get_endp(s) - stream_get_getp(s); | |
181 | ||
182 | /* Call write() system call */ | |
183 | num = write(mp->fd, STREAM_PNT(s), writenum); | |
184 | if (num < 0) { | |
185 | /* write failed either retry needed or error */ | |
186 | if (ERRNO_IO_RETRY(errno)) | |
187 | break; | |
188 | ||
189 | /* XXX:revisit; reset TCP connection */ | |
190 | pim_msdp_peer_reset_tcp_conn(mp, "pkt-tx-failed"); | |
191 | return 0; | |
192 | } | |
193 | ||
194 | if (num != writenum) { | |
195 | /* Partial write */ | |
196 | stream_forward_getp(s, num); | |
197 | break; | |
198 | } | |
199 | ||
200 | /* Retrieve msdp packet type. */ | |
201 | type = stream_getc(s); | |
202 | switch (type) | |
203 | { | |
204 | case PIM_MSDP_KEEPALIVE: | |
205 | mp->ka_tx_cnt++; | |
206 | break; | |
207 | case PIM_MSDP_V4_SOURCE_ACTIVE: | |
208 | mp->sa_tx_cnt++; | |
209 | break; | |
210 | default:; | |
211 | } | |
212 | if (PIM_DEBUG_MSDP_PACKETS) { | |
213 | pim_msdp_pkt_dump(mp, type, writenum, false /*rx*/); | |
214 | } | |
215 | ||
216 | /* packet sent delete it. */ | |
217 | pim_msdp_pkt_delete(mp); | |
218 | ||
219 | /* XXX - may need to pause if we have done too much work in this | |
220 | * loop */ | |
221 | } while ((s = stream_fifo_head(mp->obuf)) != NULL); | |
222 | pim_msdp_write_proceed_actions(mp); | |
223 | ||
224 | sockopt_cork (mp->fd, 0); | |
225 | ||
226 | return 0; | |
227 | } | |
228 | ||
229 | static void | |
230 | pim_msdp_pkt_send(struct pim_msdp_peer *mp, struct stream *s) | |
231 | { | |
232 | /* Add packet to the end of list. */ | |
233 | stream_fifo_push(mp->obuf, s); | |
234 | ||
235 | PIM_MSDP_PEER_WRITE_ON(mp); | |
236 | } | |
237 | ||
238 | /* Make keepalive packet and send it to the peer | |
239 | 0 1 2 3 | |
240 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | |
241 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
242 | | 4 | 3 | | |
243 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
244 | */ | |
245 | void | |
246 | pim_msdp_pkt_ka_tx(struct pim_msdp_peer *mp) | |
247 | { | |
248 | struct stream *s; | |
249 | ||
250 | s = stream_new(PIM_MSDP_KA_TLV_MAX_SIZE); | |
251 | stream_putc(s, PIM_MSDP_KEEPALIVE); | |
252 | stream_putw(s, PIM_MSDP_KA_TLV_MAX_SIZE); | |
253 | ||
254 | pim_msdp_pkt_send(mp, s); | |
255 | } | |
256 | ||
257 | static void | |
258 | pim_msdp_pkt_rxed_with_fatal_error(struct pim_msdp_peer *mp) | |
259 | { | |
260 | /* XXX:revisit; reset TCP connection */ | |
261 | pim_msdp_peer_reset_tcp_conn(mp, "invalid-pkt-rx"); | |
262 | } | |
263 | ||
264 | static void | |
265 | pim_msdp_pkt_ka_rx(struct pim_msdp_peer *mp, int len) | |
266 | { | |
267 | mp->ka_rx_cnt++; | |
268 | if (len != PIM_MSDP_KA_TLV_MAX_SIZE) { | |
269 | pim_msdp_pkt_rxed_with_fatal_error(mp); | |
270 | return; | |
271 | } | |
272 | pim_msdp_peer_pkt_rxed(mp); | |
273 | } | |
274 | ||
275 | static void | |
276 | pim_msdp_pkt_sa_rx(struct pim_msdp_peer *mp, int len) | |
277 | { | |
278 | mp->sa_rx_cnt++; | |
279 | /* XXX: proc SA ... */ | |
280 | pim_msdp_peer_pkt_rxed(mp); | |
281 | } | |
282 | ||
283 | /* Theoretically you could have different tlv types in the same message. | |
284 | * For the time being I am assuming one; will revisit before 3.2 - XXX */ | |
285 | static void | |
286 | pim_msdp_pkt_rx(struct pim_msdp_peer *mp, int nbytes) | |
287 | { | |
288 | enum pim_msdp_tlv type; | |
289 | int len; | |
290 | ||
291 | type = stream_getc(mp->ibuf); | |
292 | len = stream_getw(mp->ibuf); | |
293 | if (len < PIM_MSDP_HEADER_SIZE) { | |
294 | pim_msdp_pkt_rxed_with_fatal_error(mp); | |
295 | return; | |
296 | } | |
297 | ||
298 | if (len > PIM_MSDP_SA_TLV_MAX_SIZE) { | |
299 | /* if tlv size if greater than max just ignore the tlv */ | |
300 | return; | |
301 | } | |
302 | ||
303 | if (len > nbytes) { | |
304 | /* we got a partial read or the packet is malformed */ | |
305 | pim_msdp_pkt_rxed_with_fatal_error(mp); | |
306 | return; | |
307 | } | |
308 | ||
309 | if (PIM_DEBUG_MSDP_PACKETS) { | |
310 | pim_msdp_pkt_dump(mp, type, len, true /*rx*/); | |
311 | } | |
312 | ||
313 | switch(type) { | |
314 | case PIM_MSDP_KEEPALIVE: | |
315 | pim_msdp_pkt_ka_rx(mp, len); | |
316 | break; | |
317 | case PIM_MSDP_V4_SOURCE_ACTIVE: | |
318 | mp->sa_rx_cnt++; | |
319 | pim_msdp_pkt_sa_rx(mp, len); | |
320 | break; | |
321 | default: | |
322 | mp->unk_rx_cnt++; | |
323 | } | |
324 | /* XXX: process next tlv*/ | |
325 | } | |
326 | ||
327 | /* pim msdp read utility function. */ | |
328 | static int | |
329 | pim_msdp_read_packet(struct pim_msdp_peer *mp) | |
330 | { | |
331 | int nbytes; | |
332 | /* Read packet from fd. */ | |
333 | nbytes = stream_read_try(mp->ibuf, mp->fd, PIM_MSDP_MAX_PACKET_SIZE); | |
334 | if (nbytes < PIM_MSDP_HEADER_SIZE) { | |
335 | if (nbytes == -2) { | |
336 | /* transient error retry */ | |
337 | return -1; | |
338 | } | |
339 | pim_msdp_pkt_rxed_with_fatal_error(mp); | |
340 | return -1; | |
341 | } | |
342 | return nbytes; | |
343 | } | |
344 | ||
345 | int | |
346 | pim_msdp_read(struct thread *thread) | |
347 | { | |
348 | struct pim_msdp_peer *mp; | |
349 | int rc; | |
350 | ||
351 | mp = THREAD_ARG(thread); | |
352 | mp->t_read = NULL; | |
353 | ||
354 | if (PIM_DEBUG_MSDP_INTERNAL) { | |
355 | char key_str[PIM_MSDP_PEER_KEY_STRLEN]; | |
356 | ||
357 | pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), false); | |
358 | zlog_debug("%s pim_msdp_read", key_str); | |
359 | } | |
360 | ||
361 | if (mp->fd < 0) { | |
362 | return -1; | |
363 | } | |
364 | ||
365 | /* check if TCP connection is established */ | |
366 | if (mp->state != PIM_MSDP_ESTABLISHED) { | |
367 | pim_msdp_connect_check(mp); | |
368 | return 0; | |
369 | } | |
370 | ||
371 | THREAD_READ_ON(msdp->master, mp->t_read, pim_msdp_read, mp, mp->fd); | |
372 | ||
373 | rc = pim_msdp_read_packet(mp); | |
374 | if (rc > 0) { | |
375 | pim_msdp_pkt_rx(mp, rc); | |
376 | } | |
377 | ||
378 | stream_reset(mp->ibuf); | |
379 | return 0; | |
380 | } |