]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_msdp_packet.c
pim-msdp: part-1 - initial protocol infra.
[mirror_frr.git] / pimd / pim_msdp_packet.c
CommitLineData
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
34static char *
35pim_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
65static void
66pim_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. */
80static void
81pim_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
125static void
126pim_msdp_pkt_delete(struct pim_msdp_peer *mp)
127{
128 stream_free(stream_fifo_pop(mp->obuf));
129}
130
131static void
132pim_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
139int
140pim_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
229static void
230pim_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*/
245void
246pim_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
257static void
258pim_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
264static void
265pim_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
275static void
276pim_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 */
285static void
286pim_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. */
328static int
329pim_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
345int
346pim_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}