Line data Source code
1 : /*
2 : * PIM for Quagga
3 : * Copyright (C) 2008 Everton da Silva Marques
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 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
18 : */
19 :
20 : #include <zebra.h>
21 :
22 : #include "log.h"
23 : #include "thread.h"
24 : #include "memory.h"
25 : #include "if.h"
26 : #include "network.h"
27 :
28 : #include "pimd.h"
29 : #include "pim_instance.h"
30 : #include "pim_pim.h"
31 : #include "pim_time.h"
32 : #include "pim_iface.h"
33 : #include "pim_sock.h"
34 : #include "pim_str.h"
35 : #include "pim_util.h"
36 : #include "pim_tlv.h"
37 : #include "pim_neighbor.h"
38 : #include "pim_hello.h"
39 : #include "pim_join.h"
40 : #include "pim_assert.h"
41 : #include "pim_msg.h"
42 : #include "pim_register.h"
43 : #include "pim_errors.h"
44 : #include "pim_bsm.h"
45 : #include <lib/lib_errors.h>
46 :
47 : static void on_pim_hello_send(struct thread *t);
48 :
49 0 : static const char *pim_pim_msgtype2str(enum pim_msg_type type)
50 : {
51 0 : switch (type) {
52 : case PIM_MSG_TYPE_HELLO:
53 : return "HELLO";
54 0 : case PIM_MSG_TYPE_REGISTER:
55 0 : return "REGISTER";
56 0 : case PIM_MSG_TYPE_REG_STOP:
57 0 : return "REGSTOP";
58 0 : case PIM_MSG_TYPE_JOIN_PRUNE:
59 0 : return "JOINPRUNE";
60 0 : case PIM_MSG_TYPE_BOOTSTRAP:
61 0 : return "BOOT";
62 0 : case PIM_MSG_TYPE_ASSERT:
63 0 : return "ASSERT";
64 0 : case PIM_MSG_TYPE_GRAFT:
65 0 : return "GRAFT";
66 0 : case PIM_MSG_TYPE_GRAFT_ACK:
67 0 : return "GACK";
68 0 : case PIM_MSG_TYPE_CANDIDATE:
69 0 : return "CANDIDATE";
70 : }
71 :
72 0 : return "UNKNOWN";
73 : }
74 :
75 10 : static void sock_close(struct interface *ifp)
76 : {
77 10 : struct pim_interface *pim_ifp = ifp->info;
78 :
79 10 : if (PIM_DEBUG_PIM_TRACE) {
80 0 : if (pim_ifp->t_pim_sock_read) {
81 0 : zlog_debug(
82 : "Cancelling READ event for PIM socket fd=%d on interface %s",
83 : pim_ifp->pim_sock_fd, ifp->name);
84 : }
85 : }
86 10 : THREAD_OFF(pim_ifp->t_pim_sock_read);
87 :
88 10 : if (PIM_DEBUG_PIM_TRACE) {
89 0 : if (pim_ifp->t_pim_hello_timer) {
90 0 : zlog_debug(
91 : "Cancelling PIM hello timer for interface %s",
92 : ifp->name);
93 : }
94 : }
95 10 : THREAD_OFF(pim_ifp->t_pim_hello_timer);
96 :
97 10 : if (PIM_DEBUG_PIM_TRACE) {
98 0 : zlog_debug("Deleting PIM socket fd=%d on interface %s",
99 : pim_ifp->pim_sock_fd, ifp->name);
100 : }
101 :
102 : /*
103 : * If the fd is already deleted no need to do anything here
104 : */
105 10 : if (pim_ifp->pim_sock_fd > 0 && close(pim_ifp->pim_sock_fd)) {
106 0 : zlog_warn(
107 : "Failure closing PIM socket fd=%d on interface %s: errno=%d: %s",
108 : pim_ifp->pim_sock_fd, ifp->name, errno,
109 : safe_strerror(errno));
110 : }
111 :
112 10 : pim_ifp->pim_sock_fd = -1;
113 10 : pim_ifp->pim_sock_creation = 0;
114 10 : }
115 :
116 10 : void pim_sock_delete(struct interface *ifp, const char *delete_message)
117 : {
118 10 : zlog_info("PIM INTERFACE DOWN: on interface %s: %s", ifp->name,
119 : delete_message);
120 :
121 10 : if (!ifp->info) {
122 0 : flog_err(EC_PIM_CONFIG,
123 : "%s: %s: but PIM not enabled on interface %s (!)",
124 : __func__, delete_message, ifp->name);
125 0 : return;
126 : }
127 :
128 : /*
129 : RFC 4601: 4.3.1. Sending Hello Messages
130 :
131 : Before an interface goes down or changes primary IP address, a Hello
132 : message with a zero HoldTime should be sent immediately (with the
133 : old IP address if the IP address changed).
134 : */
135 10 : pim_hello_send(ifp, 0 /* zero-sec holdtime */);
136 :
137 10 : pim_neighbor_delete_all(ifp, delete_message);
138 :
139 10 : sock_close(ifp);
140 : }
141 :
142 : /* For now check dst address for hello, assrt and join/prune is all pim rtr */
143 116 : static bool pim_pkt_dst_addr_ok(enum pim_msg_type type, pim_addr addr)
144 : {
145 116 : if ((type == PIM_MSG_TYPE_HELLO) || (type == PIM_MSG_TYPE_ASSERT)
146 : || (type == PIM_MSG_TYPE_JOIN_PRUNE)) {
147 104 : if (pim_addr_cmp(addr, qpim_all_pim_routers_addr))
148 : return false;
149 : }
150 :
151 : return true;
152 : }
153 :
154 116 : int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len,
155 : pim_sgaddr sg)
156 : {
157 116 : struct iovec iov[2], *iovp = iov;
158 : #if PIM_IPV == 4
159 116 : struct ip *ip_hdr = (struct ip *)buf;
160 116 : size_t ip_hlen; /* ip header length in bytes */
161 : #endif
162 116 : uint8_t *pim_msg;
163 116 : uint32_t pim_msg_len = 0;
164 116 : uint16_t pim_checksum; /* received checksum */
165 116 : uint16_t checksum; /* computed checksum */
166 116 : struct pim_neighbor *neigh;
167 116 : struct pim_msg_header *header;
168 116 : bool no_fwd;
169 :
170 : #if PIM_IPV == 4
171 116 : if (len < sizeof(*ip_hdr)) {
172 0 : if (PIM_DEBUG_PIM_PACKETS)
173 0 : zlog_debug(
174 : "PIM packet size=%zu shorter than minimum=%zu",
175 : len, sizeof(*ip_hdr));
176 0 : return -1;
177 : }
178 :
179 116 : ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
180 116 : sg = pim_sgaddr_from_iphdr(ip_hdr);
181 :
182 116 : pim_msg = buf + ip_hlen;
183 116 : pim_msg_len = len - ip_hlen;
184 : #else
185 : struct ipv6_ph phdr = {
186 : .src = sg.src,
187 : .dst = sg.grp,
188 : .ulpl = htonl(len),
189 : .next_hdr = IPPROTO_PIM,
190 : };
191 :
192 : iovp->iov_base = &phdr;
193 : iovp->iov_len = sizeof(phdr);
194 : iovp++;
195 :
196 : /* NB: header is not included in IPv6 RX */
197 : pim_msg = buf;
198 : pim_msg_len = len;
199 : #endif
200 :
201 116 : iovp->iov_base = pim_msg;
202 116 : iovp->iov_len = pim_msg_len;
203 116 : iovp++;
204 :
205 116 : header = (struct pim_msg_header *)pim_msg;
206 116 : if (pim_msg_len < PIM_PIM_MIN_LEN) {
207 0 : if (PIM_DEBUG_PIM_PACKETS)
208 0 : zlog_debug(
209 : "PIM message size=%d shorter than minimum=%d",
210 : pim_msg_len, PIM_PIM_MIN_LEN);
211 0 : return -1;
212 : }
213 :
214 116 : if (header->ver != PIM_PROTO_VERSION) {
215 0 : if (PIM_DEBUG_PIM_PACKETS)
216 0 : zlog_debug(
217 : "Ignoring PIM pkt from %s with unsupported version: %d",
218 : ifp->name, header->ver);
219 0 : return -1;
220 : }
221 :
222 : /* save received checksum */
223 116 : pim_checksum = header->checksum;
224 :
225 : /* for computing checksum */
226 116 : header->checksum = 0;
227 116 : no_fwd = header->Nbit;
228 :
229 116 : if (header->type == PIM_MSG_TYPE_REGISTER) {
230 0 : if (pim_msg_len < PIM_MSG_REGISTER_LEN) {
231 0 : if (PIM_DEBUG_PIM_PACKETS)
232 0 : zlog_debug("PIM Register Message size=%d shorther than min length %d",
233 : pim_msg_len, PIM_MSG_REGISTER_LEN);
234 0 : return -1;
235 : }
236 :
237 : #if PIM_IPV == 6
238 : phdr.ulpl = htonl(PIM_MSG_REGISTER_LEN);
239 : #endif
240 : /* First 8 byte header checksum */
241 0 : iovp[-1].iov_len = PIM_MSG_REGISTER_LEN;
242 0 : checksum = in_cksumv(iov, iovp - iov);
243 :
244 0 : if (checksum != pim_checksum) {
245 : #if PIM_IPV == 6
246 : phdr.ulpl = htonl(pim_msg_len);
247 : #endif
248 0 : iovp[-1].iov_len = pim_msg_len;
249 :
250 0 : checksum = in_cksumv(iov, iovp - iov);
251 0 : if (checksum != pim_checksum) {
252 0 : if (PIM_DEBUG_PIM_PACKETS)
253 0 : zlog_debug(
254 : "Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
255 : ifp->name, pim_checksum,
256 : checksum);
257 :
258 0 : return -1;
259 : }
260 : }
261 : } else {
262 116 : checksum = in_cksumv(iov, iovp - iov);
263 116 : if (checksum != pim_checksum) {
264 0 : if (PIM_DEBUG_PIM_PACKETS)
265 0 : zlog_debug(
266 : "Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
267 : ifp->name, pim_checksum, checksum);
268 :
269 0 : return -1;
270 : }
271 : }
272 :
273 116 : if (PIM_DEBUG_PIM_PACKETS) {
274 0 : zlog_debug(
275 : "Recv PIM %s packet from %pPA to %pPA on %s: pim_version=%d pim_msg_size=%d checksum=%x",
276 : pim_pim_msgtype2str(header->type), &sg.src, &sg.grp,
277 : ifp->name, header->ver, pim_msg_len, checksum);
278 0 : if (PIM_DEBUG_PIM_PACKETDUMP_RECV)
279 0 : pim_pkt_dump(__func__, pim_msg, pim_msg_len);
280 : }
281 :
282 116 : if (!pim_pkt_dst_addr_ok(header->type, sg.grp)) {
283 0 : zlog_warn(
284 : "%s: Ignoring Pkt. Unexpected IP destination %pPA for %s (Expected: all_pim_routers_addr) from %pPA",
285 : __func__, &sg.grp, pim_pim_msgtype2str(header->type),
286 : &sg.src);
287 0 : return -1;
288 : }
289 :
290 116 : switch (header->type) {
291 104 : case PIM_MSG_TYPE_HELLO:
292 104 : return pim_hello_recv(ifp, sg.src, pim_msg + PIM_MSG_HEADER_LEN,
293 104 : pim_msg_len - PIM_MSG_HEADER_LEN);
294 0 : break;
295 0 : case PIM_MSG_TYPE_REGISTER:
296 0 : return pim_register_recv(ifp, sg.grp, sg.src,
297 : pim_msg + PIM_MSG_HEADER_LEN,
298 0 : pim_msg_len - PIM_MSG_HEADER_LEN);
299 0 : break;
300 0 : case PIM_MSG_TYPE_REG_STOP:
301 0 : return pim_register_stop_recv(ifp, pim_msg + PIM_MSG_HEADER_LEN,
302 0 : pim_msg_len - PIM_MSG_HEADER_LEN);
303 0 : break;
304 0 : case PIM_MSG_TYPE_JOIN_PRUNE:
305 0 : neigh = pim_neighbor_find(ifp, sg.src);
306 0 : if (!neigh) {
307 0 : if (PIM_DEBUG_PIM_PACKETS)
308 0 : zlog_debug(
309 : "%s %s: non-hello PIM message type=%d from non-neighbor %pPA on %s",
310 : __FILE__, __func__, header->type,
311 : &sg.src, ifp->name);
312 0 : return -1;
313 : }
314 0 : pim_neighbor_timer_reset(neigh, neigh->holdtime);
315 0 : return pim_joinprune_recv(ifp, neigh, sg.src,
316 : pim_msg + PIM_MSG_HEADER_LEN,
317 0 : pim_msg_len - PIM_MSG_HEADER_LEN);
318 0 : break;
319 0 : case PIM_MSG_TYPE_ASSERT:
320 0 : neigh = pim_neighbor_find(ifp, sg.src);
321 0 : if (!neigh) {
322 0 : if (PIM_DEBUG_PIM_PACKETS)
323 0 : zlog_debug(
324 : "%s %s: non-hello PIM message type=%d from non-neighbor %pPA on %s",
325 : __FILE__, __func__, header->type,
326 : &sg.src, ifp->name);
327 0 : return -1;
328 : }
329 0 : pim_neighbor_timer_reset(neigh, neigh->holdtime);
330 0 : return pim_assert_recv(ifp, neigh, sg.src,
331 : pim_msg + PIM_MSG_HEADER_LEN,
332 0 : pim_msg_len - PIM_MSG_HEADER_LEN);
333 12 : break;
334 12 : case PIM_MSG_TYPE_BOOTSTRAP:
335 12 : return pim_bsm_process(ifp, &sg, pim_msg, pim_msg_len, no_fwd);
336 0 : break;
337 :
338 0 : default:
339 0 : if (PIM_DEBUG_PIM_PACKETS) {
340 0 : zlog_debug(
341 : "Recv PIM packet type %d which is not currently understood",
342 : header->type);
343 : }
344 : return -1;
345 : }
346 : }
347 :
348 : static void pim_sock_read_on(struct interface *ifp);
349 :
350 96 : static void pim_sock_read(struct thread *t)
351 : {
352 96 : struct interface *ifp, *orig_ifp;
353 96 : struct pim_interface *pim_ifp;
354 96 : int fd;
355 96 : struct sockaddr_storage from;
356 96 : struct sockaddr_storage to;
357 96 : socklen_t fromlen = sizeof(from);
358 96 : socklen_t tolen = sizeof(to);
359 96 : uint8_t buf[PIM_PIM_BUFSIZE_READ];
360 96 : int len;
361 96 : ifindex_t ifindex = -1;
362 96 : int result = -1; /* defaults to bad */
363 96 : static long long count = 0;
364 96 : int cont = 1;
365 :
366 96 : orig_ifp = ifp = THREAD_ARG(t);
367 96 : fd = THREAD_FD(t);
368 :
369 96 : pim_ifp = ifp->info;
370 :
371 96 : while (cont) {
372 169 : pim_sgaddr sg;
373 :
374 169 : len = pim_socket_recvfromto(fd, buf, sizeof(buf), &from,
375 : &fromlen, &to, &tolen, &ifindex);
376 169 : if (len < 0) {
377 53 : if (errno == EINTR)
378 0 : continue;
379 53 : if (errno == EWOULDBLOCK || errno == EAGAIN)
380 : break;
381 :
382 0 : if (PIM_DEBUG_PIM_PACKETS)
383 0 : zlog_debug("Received errno: %d %s", errno,
384 : safe_strerror(errno));
385 8 : goto done;
386 : }
387 :
388 : /*
389 : * What? So with vrf's the incoming packet is received
390 : * on the vrf interface but recvfromto above returns
391 : * the right ifindex, so just use it. We know
392 : * it's the right interface because we bind to it
393 : */
394 116 : ifp = if_lookup_by_index(ifindex, pim_ifp->pim->vrf->vrf_id);
395 116 : if (!ifp || !ifp->info) {
396 0 : if (PIM_DEBUG_PIM_PACKETS)
397 0 : zlog_debug(
398 : "%s: Received incoming pim packet on interface(%s:%d) not yet configured for pim",
399 : __func__, ifp ? ifp->name : "Unknown",
400 : ifindex);
401 0 : goto done;
402 : }
403 : #if PIM_IPV == 4
404 116 : sg.src = ((struct sockaddr_in *)&from)->sin_addr;
405 116 : sg.grp = ((struct sockaddr_in *)&to)->sin_addr;
406 : #else
407 : sg.src = ((struct sockaddr_in6 *)&from)->sin6_addr;
408 : sg.grp = ((struct sockaddr_in6 *)&to)->sin6_addr;
409 : #endif
410 :
411 116 : int fail = pim_pim_packet(ifp, buf, len, sg);
412 116 : if (fail) {
413 8 : if (PIM_DEBUG_PIM_PACKETS)
414 0 : zlog_debug("%s: pim_pim_packet() return=%d",
415 : __func__, fail);
416 8 : goto done;
417 : }
418 :
419 108 : count++;
420 108 : if (count % router->packet_process == 0)
421 35 : cont = 0;
422 : }
423 :
424 : result = 0; /* good */
425 :
426 96 : done:
427 96 : pim_sock_read_on(orig_ifp);
428 :
429 96 : if (result) {
430 8 : ++pim_ifp->pim_ifstat_hello_recvfail;
431 : }
432 96 : }
433 :
434 104 : static void pim_sock_read_on(struct interface *ifp)
435 : {
436 104 : struct pim_interface *pim_ifp;
437 :
438 104 : assert(ifp);
439 104 : assert(ifp->info);
440 :
441 104 : pim_ifp = ifp->info;
442 :
443 104 : if (PIM_DEBUG_PIM_TRACE_DETAIL) {
444 0 : zlog_debug("Scheduling READ event on PIM socket fd=%d",
445 : pim_ifp->pim_sock_fd);
446 : }
447 104 : thread_add_read(router->master, pim_sock_read, ifp,
448 : pim_ifp->pim_sock_fd, &pim_ifp->t_pim_sock_read);
449 104 : }
450 :
451 8 : static int pim_sock_open(struct interface *ifp)
452 : {
453 8 : int fd;
454 8 : struct pim_interface *pim_ifp = ifp->info;
455 :
456 8 : fd = pim_socket_mcast(IPPROTO_PIM, pim_ifp->primary_address, ifp,
457 : 0 /* loop=false */);
458 8 : if (fd < 0)
459 : return -1;
460 :
461 8 : if (pim_socket_join(fd, qpim_all_pim_routers_addr,
462 : pim_ifp->primary_address, ifp->ifindex, pim_ifp)) {
463 0 : close(fd);
464 0 : return -2;
465 : }
466 :
467 : return fd;
468 : }
469 :
470 11 : void pim_ifstat_reset(struct interface *ifp)
471 : {
472 11 : struct pim_interface *pim_ifp;
473 :
474 11 : assert(ifp);
475 :
476 11 : pim_ifp = ifp->info;
477 11 : if (!pim_ifp) {
478 : return;
479 : }
480 :
481 11 : pim_ifp->pim_ifstat_start = pim_time_monotonic_sec();
482 11 : pim_ifp->pim_ifstat_hello_sent = 0;
483 11 : pim_ifp->pim_ifstat_hello_sendfail = 0;
484 11 : pim_ifp->pim_ifstat_hello_recv = 0;
485 11 : pim_ifp->pim_ifstat_hello_recvfail = 0;
486 11 : pim_ifp->pim_ifstat_bsm_rx = 0;
487 11 : pim_ifp->pim_ifstat_bsm_tx = 0;
488 11 : pim_ifp->pim_ifstat_join_recv = 0;
489 11 : pim_ifp->pim_ifstat_join_send = 0;
490 11 : pim_ifp->pim_ifstat_prune_recv = 0;
491 11 : pim_ifp->pim_ifstat_prune_send = 0;
492 11 : pim_ifp->pim_ifstat_reg_recv = 0;
493 11 : pim_ifp->pim_ifstat_reg_send = 0;
494 11 : pim_ifp->pim_ifstat_reg_stop_recv = 0;
495 11 : pim_ifp->pim_ifstat_reg_stop_send = 0;
496 11 : pim_ifp->pim_ifstat_assert_recv = 0;
497 11 : pim_ifp->pim_ifstat_assert_send = 0;
498 11 : pim_ifp->pim_ifstat_bsm_cfg_miss = 0;
499 11 : pim_ifp->pim_ifstat_ucast_bsm_cfg_miss = 0;
500 11 : pim_ifp->pim_ifstat_bsm_invalid_sz = 0;
501 11 : pim_ifp->igmp_ifstat_joins_sent = 0;
502 11 : pim_ifp->igmp_ifstat_joins_failed = 0;
503 11 : pim_ifp->igmp_peak_group_count = 0;
504 : }
505 :
506 9 : void pim_sock_reset(struct interface *ifp)
507 : {
508 9 : struct pim_interface *pim_ifp;
509 :
510 9 : assert(ifp);
511 9 : assert(ifp->info);
512 :
513 9 : pim_ifp = ifp->info;
514 :
515 9 : pim_ifp->primary_address = pim_find_primary_addr(ifp);
516 :
517 9 : pim_ifp->pim_sock_fd = -1;
518 9 : pim_ifp->pim_sock_creation = 0;
519 9 : pim_ifp->t_pim_sock_read = NULL;
520 :
521 9 : pim_ifp->t_pim_hello_timer = NULL;
522 9 : pim_ifp->pim_hello_period = PIM_DEFAULT_HELLO_PERIOD;
523 9 : pim_ifp->pim_default_holdtime =
524 : -1; /* unset: means 3.5 * pim_hello_period */
525 9 : pim_ifp->pim_triggered_hello_delay = PIM_DEFAULT_TRIGGERED_HELLO_DELAY;
526 9 : pim_ifp->pim_dr_priority = PIM_DEFAULT_DR_PRIORITY;
527 9 : pim_ifp->pim_propagation_delay_msec =
528 : PIM_DEFAULT_PROPAGATION_DELAY_MSEC;
529 9 : pim_ifp->pim_override_interval_msec =
530 : PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC;
531 9 : pim_ifp->pim_can_disable_join_suppression =
532 : PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION;
533 :
534 : /* neighbors without lan_delay */
535 9 : pim_ifp->pim_number_of_nonlandelay_neighbors = 0;
536 9 : pim_ifp->pim_neighbors_highest_propagation_delay_msec = 0;
537 9 : pim_ifp->pim_neighbors_highest_override_interval_msec = 0;
538 :
539 : /* DR Election */
540 9 : pim_ifp->pim_dr_election_last = 0; /* timestamp */
541 9 : pim_ifp->pim_dr_election_count = 0;
542 9 : pim_ifp->pim_dr_election_changes = 0;
543 9 : pim_ifp->pim_dr_num_nondrpri_neighbors =
544 : 0; /* neighbors without dr_pri */
545 9 : pim_ifp->pim_dr_addr = pim_ifp->primary_address;
546 9 : pim_ifp->am_i_dr = true;
547 :
548 9 : pim_ifstat_reset(ifp);
549 9 : }
550 :
551 : #if PIM_IPV == 4
552 : static uint16_t ip_id = 0;
553 : #endif
554 :
555 : #if PIM_IPV == 4
556 78 : static int pim_msg_send_frame(int fd, char *buf, size_t len,
557 : struct sockaddr *dst, size_t salen,
558 : const char *ifname)
559 : {
560 78 : if (sendto(fd, buf, len, MSG_DONTWAIT, dst, salen) >= 0)
561 : return 0;
562 :
563 9 : if (errno == EMSGSIZE) {
564 0 : struct ip *ip = (struct ip *)buf;
565 0 : size_t hdrsize = sizeof(struct ip);
566 0 : size_t newlen1 = ((len - hdrsize) / 2) & 0xFFF8;
567 0 : size_t sendlen = newlen1 + hdrsize;
568 0 : size_t offset = ntohs(ip->ip_off);
569 0 : int ret;
570 :
571 0 : ip->ip_len = htons(sendlen);
572 0 : ip->ip_off = htons(offset | IP_MF);
573 :
574 0 : ret = pim_msg_send_frame(fd, buf, sendlen, dst, salen, ifname);
575 0 : if (ret)
576 : return ret;
577 :
578 0 : struct ip *ip2 = (struct ip *)(buf + newlen1);
579 0 : size_t newlen2 = len - sendlen;
580 :
581 0 : sendlen = newlen2 + hdrsize;
582 :
583 0 : memcpy(ip2, ip, hdrsize);
584 0 : ip2->ip_len = htons(sendlen);
585 0 : ip2->ip_off = htons(offset + (newlen1 >> 3));
586 0 : return pim_msg_send_frame(fd, (char *)ip2, sendlen, dst, salen,
587 : ifname);
588 : }
589 :
590 9 : zlog_warn(
591 : "%s: sendto() failure to %pSU: iface=%s fd=%d msg_size=%zd: %m",
592 : __func__, dst, ifname, fd, len);
593 9 : return -1;
594 : }
595 :
596 : #else
597 : static int pim_msg_send_frame(pim_addr src, pim_addr dst, ifindex_t ifindex,
598 : struct iovec *message, int fd)
599 : {
600 : int retval;
601 : struct msghdr smsghdr = {};
602 : struct cmsghdr *scmsgp;
603 : union cmsgbuf {
604 : struct cmsghdr hdr;
605 : uint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
606 : };
607 : struct in6_pktinfo *pktinfo;
608 : struct sockaddr_in6 dst_sin6 = {};
609 :
610 : union cmsgbuf cmsg_buf = {};
611 :
612 : /* destination address */
613 : dst_sin6.sin6_family = AF_INET6;
614 : #ifdef SIN6_LEN
615 : dst_sin6.sin6_len = sizeof(struct sockaddr_in6);
616 : #endif /*SIN6_LEN*/
617 : dst_sin6.sin6_addr = dst;
618 : dst_sin6.sin6_scope_id = ifindex;
619 :
620 : /* send msg hdr */
621 : smsghdr.msg_iov = message;
622 : smsghdr.msg_iovlen = 1;
623 : smsghdr.msg_name = (caddr_t)&dst_sin6;
624 : smsghdr.msg_namelen = sizeof(dst_sin6);
625 : smsghdr.msg_control = (caddr_t)&cmsg_buf.buf;
626 : smsghdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
627 : smsghdr.msg_flags = 0;
628 :
629 : scmsgp = CMSG_FIRSTHDR(&smsghdr);
630 : scmsgp->cmsg_level = IPPROTO_IPV6;
631 : scmsgp->cmsg_type = IPV6_PKTINFO;
632 : scmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
633 :
634 : pktinfo = (struct in6_pktinfo *)(CMSG_DATA(scmsgp));
635 : pktinfo->ipi6_ifindex = ifindex;
636 : pktinfo->ipi6_addr = src;
637 :
638 : retval = sendmsg(fd, &smsghdr, 0);
639 : if (retval < 0)
640 : flog_err(
641 : EC_LIB_SOCKET,
642 : "sendmsg failed: source: %pI6 Dest: %pI6 ifindex: %d: %s (%d)",
643 : &src, &dst, ifindex, safe_strerror(errno), errno);
644 :
645 : return retval;
646 : }
647 : #endif
648 :
649 78 : int pim_msg_send(int fd, pim_addr src, pim_addr dst, uint8_t *pim_msg,
650 : int pim_msg_size, struct interface *ifp)
651 : {
652 78 : struct pim_interface *pim_ifp;
653 :
654 :
655 78 : pim_ifp = ifp->info;
656 :
657 78 : if (pim_ifp->pim_passive_enable) {
658 0 : if (PIM_DEBUG_PIM_PACKETS)
659 0 : zlog_debug(
660 : "skip sending PIM message on passive interface %s",
661 : ifp->name);
662 0 : return 0;
663 : }
664 :
665 : #if PIM_IPV == 4
666 78 : uint8_t ttl;
667 78 : struct pim_msg_header *header;
668 78 : unsigned char buffer[10000];
669 :
670 78 : memset(buffer, 0, 10000);
671 :
672 78 : header = (struct pim_msg_header *)pim_msg;
673 :
674 : /*
675 : * Omnios apparently doesn't have a #define for IP default
676 : * ttl that is the same as all other platforms.
677 : */
678 : #ifndef IPDEFTTL
679 : #define IPDEFTTL 64
680 : #endif
681 : /* TTL for packets destine to ALL-PIM-ROUTERS is 1 */
682 78 : switch (header->type) {
683 : case PIM_MSG_TYPE_HELLO:
684 : case PIM_MSG_TYPE_JOIN_PRUNE:
685 : case PIM_MSG_TYPE_BOOTSTRAP:
686 : case PIM_MSG_TYPE_ASSERT:
687 : ttl = 1;
688 : break;
689 0 : case PIM_MSG_TYPE_REGISTER:
690 : case PIM_MSG_TYPE_REG_STOP:
691 : case PIM_MSG_TYPE_GRAFT:
692 : case PIM_MSG_TYPE_GRAFT_ACK:
693 : case PIM_MSG_TYPE_CANDIDATE:
694 0 : ttl = IPDEFTTL;
695 0 : break;
696 0 : default:
697 0 : ttl = MAXTTL;
698 0 : break;
699 : }
700 :
701 78 : struct ip *ip = (struct ip *)buffer;
702 78 : struct sockaddr_in to = {};
703 78 : int sendlen = sizeof(*ip) + pim_msg_size;
704 78 : socklen_t tolen;
705 78 : unsigned char *msg_start;
706 :
707 78 : ip->ip_id = htons(++ip_id);
708 78 : ip->ip_hl = 5;
709 78 : ip->ip_v = 4;
710 78 : ip->ip_tos = IPTOS_PREC_INTERNETCONTROL;
711 78 : ip->ip_p = PIM_IP_PROTO_PIM;
712 78 : ip->ip_src = src;
713 78 : ip->ip_dst = dst;
714 78 : ip->ip_ttl = ttl;
715 78 : ip->ip_len = htons(sendlen);
716 :
717 78 : to.sin_family = AF_INET;
718 78 : to.sin_addr = dst;
719 78 : tolen = sizeof(to);
720 :
721 78 : msg_start = buffer + sizeof(*ip);
722 78 : memcpy(msg_start, pim_msg, pim_msg_size);
723 :
724 78 : if (PIM_DEBUG_PIM_PACKETS)
725 0 : zlog_debug("%s: to %pPA on %s: msg_size=%d checksum=%x",
726 : __func__, &dst, ifp->name, pim_msg_size,
727 : header->checksum);
728 :
729 78 : if (PIM_DEBUG_PIM_PACKETDUMP_SEND) {
730 0 : pim_pkt_dump(__func__, pim_msg, pim_msg_size);
731 : }
732 :
733 78 : pim_msg_send_frame(fd, (char *)buffer, sendlen, (struct sockaddr *)&to,
734 78 : tolen, ifp->name);
735 78 : return 0;
736 :
737 : #else
738 : struct iovec iovector[2];
739 :
740 : iovector[0].iov_base = pim_msg;
741 : iovector[0].iov_len = pim_msg_size;
742 :
743 : pim_msg_send_frame(src, dst, ifp->ifindex, &iovector[0], fd);
744 :
745 : return 0;
746 : #endif
747 : }
748 :
749 74 : static int hello_send(struct interface *ifp, uint16_t holdtime)
750 : {
751 74 : uint8_t pim_msg[PIM_PIM_BUFSIZE_WRITE];
752 74 : struct pim_interface *pim_ifp;
753 74 : int pim_tlv_size;
754 74 : int pim_msg_size;
755 :
756 74 : pim_ifp = ifp->info;
757 :
758 74 : if (PIM_DEBUG_PIM_HELLO)
759 0 : zlog_debug(
760 : "%s: to %pPA on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%d",
761 : __func__, &qpim_all_pim_routers_addr, ifp->name,
762 : holdtime, pim_ifp->pim_propagation_delay_msec,
763 : pim_ifp->pim_override_interval_msec,
764 : pim_ifp->pim_can_disable_join_suppression,
765 : pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id,
766 : listcount(ifp->connected));
767 :
768 148 : pim_tlv_size = pim_hello_build_tlv(
769 : ifp, pim_msg + PIM_PIM_MIN_LEN,
770 : sizeof(pim_msg) - PIM_PIM_MIN_LEN, holdtime,
771 : pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id,
772 74 : pim_ifp->pim_propagation_delay_msec,
773 74 : pim_ifp->pim_override_interval_msec,
774 74 : pim_ifp->pim_can_disable_join_suppression);
775 74 : if (pim_tlv_size < 0) {
776 : return -1;
777 : }
778 :
779 74 : pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN;
780 :
781 74 : assert(pim_msg_size >= PIM_PIM_MIN_LEN);
782 74 : assert(pim_msg_size <= PIM_PIM_BUFSIZE_WRITE);
783 :
784 74 : pim_msg_build_header(pim_ifp->primary_address,
785 : qpim_all_pim_routers_addr, pim_msg, pim_msg_size,
786 : PIM_MSG_TYPE_HELLO, false);
787 :
788 74 : if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address,
789 : qpim_all_pim_routers_addr, pim_msg, pim_msg_size,
790 : ifp)) {
791 0 : if (PIM_DEBUG_PIM_HELLO) {
792 0 : zlog_debug(
793 : "%s: could not send PIM message on interface %s",
794 : __func__, ifp->name);
795 : }
796 0 : return -2;
797 : }
798 :
799 : return 0;
800 : }
801 :
802 80 : int pim_hello_send(struct interface *ifp, uint16_t holdtime)
803 : {
804 80 : struct pim_interface *pim_ifp = ifp->info;
805 :
806 80 : if (if_is_loopback(ifp))
807 : return 0;
808 :
809 74 : if (hello_send(ifp, holdtime)) {
810 0 : ++pim_ifp->pim_ifstat_hello_sendfail;
811 :
812 0 : if (PIM_DEBUG_PIM_HELLO) {
813 0 : zlog_warn("Could not send PIM hello on interface %s",
814 : ifp->name);
815 : }
816 0 : return -1;
817 : }
818 :
819 74 : if (!pim_ifp->pim_passive_enable) {
820 74 : ++pim_ifp->pim_ifstat_hello_sent;
821 74 : PIM_IF_FLAG_SET_HELLO_SENT(pim_ifp->flags);
822 : }
823 :
824 : return 0;
825 : }
826 :
827 58 : static void hello_resched(struct interface *ifp)
828 : {
829 58 : struct pim_interface *pim_ifp;
830 :
831 58 : pim_ifp = ifp->info;
832 :
833 58 : if (PIM_DEBUG_PIM_HELLO) {
834 0 : zlog_debug("Rescheduling %d sec hello on interface %s",
835 : pim_ifp->pim_hello_period, ifp->name);
836 : }
837 58 : THREAD_OFF(pim_ifp->t_pim_hello_timer);
838 58 : thread_add_timer(router->master, on_pim_hello_send, ifp,
839 : pim_ifp->pim_hello_period,
840 : &pim_ifp->t_pim_hello_timer);
841 58 : }
842 :
843 : /*
844 : Periodic hello timer
845 : */
846 56 : static void on_pim_hello_send(struct thread *t)
847 : {
848 56 : struct pim_interface *pim_ifp;
849 56 : struct interface *ifp;
850 :
851 56 : ifp = THREAD_ARG(t);
852 56 : pim_ifp = ifp->info;
853 :
854 : /*
855 : * Schedule next hello
856 : */
857 56 : hello_resched(ifp);
858 :
859 : /*
860 : * Send hello
861 : */
862 112 : pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
863 56 : }
864 :
865 : /*
866 : RFC 4601: 4.3.1. Sending Hello Messages
867 :
868 : Thus, if a router needs to send a Join/Prune or Assert message on an
869 : interface on which it has not yet sent a Hello message with the
870 : currently configured IP address, then it MUST immediately send the
871 : relevant Hello message without waiting for the Hello Timer to
872 : expire, followed by the Join/Prune or Assert message.
873 : */
874 2 : void pim_hello_restart_now(struct interface *ifp)
875 : {
876 2 : struct pim_interface *pim_ifp;
877 :
878 2 : pim_ifp = ifp->info;
879 :
880 : /*
881 : * Reset next hello timer
882 : */
883 2 : hello_resched(ifp);
884 :
885 : /*
886 : * Immediately send hello
887 : */
888 4 : pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
889 2 : }
890 :
891 : /*
892 : RFC 4601: 4.3.1. Sending Hello Messages
893 :
894 : To allow new or rebooting routers to learn of PIM neighbors quickly,
895 : when a Hello message is received from a new neighbor, or a Hello
896 : message with a new GenID is received from an existing neighbor, a
897 : new Hello message should be sent on this interface after a
898 : randomized delay between 0 and Triggered_Hello_Delay.
899 : */
900 14 : void pim_hello_restart_triggered(struct interface *ifp)
901 : {
902 14 : struct pim_interface *pim_ifp;
903 14 : int triggered_hello_delay_msec;
904 14 : int random_msec;
905 :
906 14 : pim_ifp = ifp->info;
907 :
908 : /*
909 : * No need to ever start loopback or vrf device hello's
910 : */
911 14 : if (if_is_loopback(ifp))
912 : return;
913 :
914 : /*
915 : * There exists situations where we have the a RPF out this
916 : * interface, but we haven't formed a neighbor yet. This
917 : * happens especially during interface flaps. While
918 : * we would like to handle this more gracefully in other
919 : * parts of the code. In order to get us up and running
920 : * let's just send the hello immediate'ish
921 : * This should be revisited when we get nexthop tracking
922 : * in and when we have a better handle on safely
923 : * handling the rpf information for upstreams that
924 : * we cannot legally reach yet.
925 : */
926 11 : triggered_hello_delay_msec = 1;
927 : // triggered_hello_delay_msec = 1000 *
928 : // pim_ifp->pim_triggered_hello_delay;
929 :
930 11 : if (pim_ifp->t_pim_hello_timer) {
931 6 : long remain_msec =
932 6 : pim_time_timer_remain_msec(pim_ifp->t_pim_hello_timer);
933 6 : if (remain_msec <= triggered_hello_delay_msec) {
934 : /* Rescheduling hello would increase the delay, then
935 : it's faster
936 : to just wait for the scheduled periodic hello. */
937 : return;
938 : }
939 :
940 5 : THREAD_OFF(pim_ifp->t_pim_hello_timer);
941 : }
942 :
943 10 : random_msec = triggered_hello_delay_msec;
944 : // random_msec = random() % (triggered_hello_delay_msec + 1);
945 :
946 10 : if (PIM_DEBUG_PIM_HELLO) {
947 0 : zlog_debug("Scheduling %d msec triggered hello on interface %s",
948 : random_msec, ifp->name);
949 : }
950 :
951 10 : thread_add_timer_msec(router->master, on_pim_hello_send, ifp,
952 : random_msec, &pim_ifp->t_pim_hello_timer);
953 : }
954 :
955 8 : int pim_sock_add(struct interface *ifp)
956 : {
957 8 : struct pim_interface *pim_ifp;
958 8 : uint32_t old_genid;
959 :
960 8 : pim_ifp = ifp->info;
961 8 : assert(pim_ifp);
962 :
963 8 : if (pim_ifp->pim_sock_fd >= 0) {
964 0 : if (PIM_DEBUG_PIM_PACKETS)
965 0 : zlog_debug(
966 : "Can't recreate existing PIM socket fd=%d for interface %s",
967 : pim_ifp->pim_sock_fd, ifp->name);
968 0 : return -1;
969 : }
970 :
971 8 : pim_ifp->pim_sock_fd = pim_sock_open(ifp);
972 8 : if (pim_ifp->pim_sock_fd < 0) {
973 0 : if (PIM_DEBUG_PIM_PACKETS)
974 0 : zlog_debug("Could not open PIM socket on interface %s",
975 : ifp->name);
976 0 : return -2;
977 : }
978 :
979 8 : pim_socket_ip_hdr(pim_ifp->pim_sock_fd);
980 :
981 8 : pim_ifp->t_pim_sock_read = NULL;
982 8 : pim_ifp->pim_sock_creation = pim_time_monotonic_sec();
983 :
984 : /*
985 : * Just ensure that the new generation id
986 : * actually chooses something different.
987 : * Actually ran across a case where this
988 : * happened, pre-switch to random().
989 : * While this is unlikely to happen now
990 : * let's make sure it doesn't.
991 : */
992 8 : old_genid = pim_ifp->pim_generation_id;
993 :
994 16 : while (old_genid == pim_ifp->pim_generation_id)
995 8 : pim_ifp->pim_generation_id = frr_weak_random();
996 :
997 8 : zlog_info("PIM INTERFACE UP: on interface %s ifindex=%d", ifp->name,
998 : ifp->ifindex);
999 :
1000 : /*
1001 : * Start receiving PIM messages
1002 : */
1003 8 : pim_sock_read_on(ifp);
1004 :
1005 : /*
1006 : * Start sending PIM hello's
1007 : */
1008 8 : pim_hello_restart_triggered(ifp);
1009 :
1010 8 : return 0;
1011 : }
|