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 "prefix.h"
24 : #include "zclient.h"
25 : #include "stream.h"
26 : #include "network.h"
27 : #include "thread.h"
28 : #include "prefix.h"
29 : #include "vty.h"
30 : #include "lib_errors.h"
31 :
32 : #include "pimd.h"
33 : #include "pim_instance.h"
34 : #include "pim_iface.h"
35 : #include "pim_neighbor.h"
36 : #include "pim_pim.h"
37 : #include "pim_str.h"
38 : #include "pim_oil.h"
39 : #include "pim_zlookup.h"
40 : #include "pim_addr.h"
41 :
42 : static struct zclient *zlookup = NULL;
43 : struct thread *zlookup_read;
44 :
45 : static void zclient_lookup_sched(struct zclient *zlookup, int delay);
46 : static void zclient_lookup_read_pipe(struct thread *thread);
47 :
48 : /* Connect to zebra for nexthop lookup. */
49 4 : static void zclient_lookup_connect(struct thread *t)
50 : {
51 4 : struct zclient *zlookup;
52 :
53 4 : zlookup = THREAD_ARG(t);
54 :
55 4 : if (zlookup->sock >= 0) {
56 : return;
57 : }
58 :
59 4 : if (zclient_socket_connect(zlookup) < 0) {
60 0 : ++zlookup->fail;
61 0 : zlog_warn("%s: failure connecting zclient socket: failures=%d",
62 : __func__, zlookup->fail);
63 : } else {
64 4 : zlookup->fail = 0; /* reset counter on connection */
65 : }
66 :
67 4 : if (zclient_send_hello(zlookup) == ZCLIENT_SEND_FAILURE) {
68 0 : if (close(zlookup->sock)) {
69 0 : zlog_warn("%s: closing fd=%d: errno=%d %s", __func__,
70 : zlookup->sock, errno, safe_strerror(errno));
71 : }
72 0 : zlookup->sock = -1;
73 : }
74 :
75 4 : if (zlookup->sock < 0) {
76 : /* Since last connect failed, retry within 10 secs */
77 0 : zclient_lookup_sched(zlookup, 10);
78 0 : return;
79 : }
80 :
81 4 : thread_add_timer(router->master, zclient_lookup_read_pipe, zlookup, 60,
82 : &zlookup_read);
83 : }
84 :
85 : /* Schedule connection with delay. */
86 0 : static void zclient_lookup_sched(struct zclient *zlookup, int delay)
87 : {
88 0 : thread_add_timer(router->master, zclient_lookup_connect, zlookup, delay,
89 : &zlookup->t_connect);
90 :
91 0 : zlog_notice("%s: zclient lookup connection scheduled for %d seconds",
92 : __func__, delay);
93 0 : }
94 :
95 : /* Schedule connection for now. */
96 4 : static void zclient_lookup_sched_now(struct zclient *zlookup)
97 : {
98 4 : thread_add_event(router->master, zclient_lookup_connect, zlookup, 0,
99 : &zlookup->t_connect);
100 :
101 4 : zlog_notice("%s: zclient lookup immediate connection scheduled",
102 : __func__);
103 4 : }
104 :
105 : /* Schedule reconnection, if needed. */
106 0 : static void zclient_lookup_reconnect(struct zclient *zlookup)
107 : {
108 0 : if (zlookup->t_connect) {
109 : return;
110 : }
111 :
112 0 : zclient_lookup_sched_now(zlookup);
113 : }
114 :
115 0 : static void zclient_lookup_failed(struct zclient *zlookup)
116 : {
117 0 : if (zlookup->sock >= 0) {
118 0 : if (close(zlookup->sock)) {
119 0 : zlog_warn("%s: closing fd=%d: errno=%d %s", __func__,
120 : zlookup->sock, errno, safe_strerror(errno));
121 : }
122 0 : zlookup->sock = -1;
123 : }
124 :
125 0 : zclient_lookup_reconnect(zlookup);
126 0 : }
127 :
128 4 : void zclient_lookup_free(void)
129 : {
130 4 : THREAD_OFF(zlookup_read);
131 4 : zclient_stop(zlookup);
132 4 : zclient_free(zlookup);
133 4 : zlookup = NULL;
134 4 : }
135 :
136 4 : void zclient_lookup_new(void)
137 : {
138 4 : struct zclient_options options = zclient_options_default;
139 4 : options.synchronous = true;
140 :
141 4 : zlookup = zclient_new(router->master, &options, NULL, 0);
142 4 : if (!zlookup) {
143 0 : flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_new() failure",
144 : __func__);
145 0 : return;
146 : }
147 :
148 4 : zlookup->sock = -1;
149 4 : zlookup->t_connect = NULL;
150 4 : zlookup->privs = &pimd_privs;
151 :
152 4 : zclient_lookup_sched_now(zlookup);
153 :
154 4 : zlog_notice("%s: zclient lookup socket initialized", __func__);
155 : }
156 :
157 0 : static int zclient_read_nexthop(struct pim_instance *pim,
158 : struct zclient *zlookup,
159 : struct pim_zlookup_nexthop nexthop_tab[],
160 : const int tab_size, pim_addr addr)
161 : {
162 0 : int num_ifindex = 0;
163 0 : struct stream *s;
164 0 : uint16_t length;
165 0 : uint8_t marker;
166 0 : uint8_t version;
167 0 : vrf_id_t vrf_id;
168 0 : uint16_t command = 0;
169 0 : struct ipaddr raddr;
170 0 : uint8_t distance;
171 0 : uint32_t metric;
172 0 : int nexthop_num;
173 0 : int i, err;
174 :
175 0 : if (PIM_DEBUG_PIM_NHT_DETAIL)
176 0 : zlog_debug("%s: addr=%pPAs(%s)", __func__, &addr,
177 : pim->vrf->name);
178 :
179 0 : s = zlookup->ibuf;
180 :
181 0 : while (command != ZEBRA_NEXTHOP_LOOKUP_MRIB) {
182 0 : stream_reset(s);
183 0 : err = zclient_read_header(s, zlookup->sock, &length, &marker,
184 : &version, &vrf_id, &command);
185 0 : if (err < 0) {
186 0 : flog_err(EC_LIB_ZAPI_MISSMATCH,
187 : "%s: zclient_read_header() failed", __func__);
188 0 : zclient_lookup_failed(zlookup);
189 0 : return -1;
190 : }
191 :
192 0 : if (command == ZEBRA_ERROR) {
193 0 : enum zebra_error_types error;
194 :
195 0 : zapi_error_decode(s, &error);
196 : /* Do nothing with it for now */
197 0 : return -1;
198 : }
199 : }
200 :
201 0 : stream_get_ipaddr(s, &raddr);
202 :
203 0 : if (raddr.ipa_type != PIM_IPADDR ||
204 0 : pim_addr_cmp(raddr.ipaddr_pim, addr)) {
205 0 : zlog_warn("%s: address mismatch: addr=%pPA(%s) raddr=%pIA",
206 : __func__, &addr, pim->vrf->name, &raddr);
207 : /* warning only */
208 : }
209 :
210 0 : distance = stream_getc(s);
211 0 : metric = stream_getl(s);
212 0 : nexthop_num = stream_getc(s);
213 :
214 0 : if (nexthop_num < 1 || nexthop_num > router->multipath) {
215 0 : if (PIM_DEBUG_PIM_NHT_DETAIL)
216 0 : zlog_debug("%s: socket %d bad nexthop_num=%d", __func__,
217 : zlookup->sock, nexthop_num);
218 0 : return -6;
219 : }
220 :
221 0 : for (i = 0; i < nexthop_num; ++i) {
222 0 : vrf_id_t nexthop_vrf_id;
223 0 : enum nexthop_types_t nexthop_type;
224 0 : struct in_addr nh_ip4;
225 0 : struct in6_addr nh_ip6;
226 0 : ifindex_t nh_ifi;
227 :
228 0 : nexthop_vrf_id = stream_getl(s);
229 0 : nexthop_type = stream_getc(s);
230 0 : if (num_ifindex >= tab_size) {
231 0 : zlog_warn(
232 : "%s: found too many nexthop ifindexes (%d > %d) for address %pPAs(%s)",
233 : __func__, (num_ifindex + 1), tab_size, &addr,
234 : pim->vrf->name);
235 0 : return num_ifindex;
236 : }
237 0 : nexthop_tab[num_ifindex].protocol_distance = distance;
238 0 : nexthop_tab[num_ifindex].route_metric = metric;
239 0 : nexthop_tab[num_ifindex].vrf_id = nexthop_vrf_id;
240 0 : switch (nexthop_type) {
241 0 : case NEXTHOP_TYPE_IFINDEX:
242 0 : nexthop_tab[num_ifindex].ifindex = stream_getl(s);
243 : /*
244 : * Connected route (i.e. no nexthop), use
245 : * address passed in as PIM nexthop. This will
246 : * allow us to work in cases where we are
247 : * trying to find a route for this box.
248 : */
249 0 : nexthop_tab[num_ifindex].nexthop_addr = addr;
250 0 : ++num_ifindex;
251 0 : break;
252 0 : case NEXTHOP_TYPE_IPV4_IFINDEX:
253 : case NEXTHOP_TYPE_IPV4:
254 0 : nh_ip4.s_addr = stream_get_ipv4(s);
255 0 : nh_ifi = stream_getl(s);
256 : #if PIM_IPV == 4
257 0 : nexthop_tab[num_ifindex].nexthop_addr = nh_ip4;
258 0 : nexthop_tab[num_ifindex].ifindex = nh_ifi;
259 0 : ++num_ifindex;
260 : #else
261 : zlog_warn(
262 : "cannot use IPv4 nexthop %pI4(%d) for IPv6 %pPA",
263 : &nh_ip4, nh_ifi, &addr);
264 : #endif
265 0 : break;
266 0 : case NEXTHOP_TYPE_IPV6:
267 : case NEXTHOP_TYPE_IPV6_IFINDEX:
268 0 : stream_get(&nh_ip6, s, sizeof(nh_ip6));
269 0 : nh_ifi = stream_getl(s);
270 :
271 : #if PIM_IPV == 6
272 : nexthop_tab[num_ifindex].nexthop_addr = nh_ip6;
273 : nexthop_tab[num_ifindex].ifindex = nh_ifi;
274 : ++num_ifindex;
275 : #else
276 : /* RFC 5549 v4-over-v6 nexthop handling */
277 :
278 : /*
279 : * If we are sending v6 secondary assume we receive v6
280 : * secondary
281 : */
282 0 : struct interface *ifp = if_lookup_by_index(
283 : nh_ifi,
284 : nexthop_vrf_id);
285 :
286 0 : if (!ifp)
287 : break;
288 :
289 0 : struct pim_neighbor *nbr;
290 :
291 0 : if (pim->send_v6_secondary) {
292 0 : struct prefix p;
293 :
294 0 : p.family = AF_INET6;
295 0 : p.prefixlen = IPV6_MAX_BITLEN;
296 0 : p.u.prefix6 = nh_ip6;
297 :
298 0 : nbr = pim_neighbor_find_by_secondary(ifp, &p);
299 : } else
300 0 : nbr = pim_neighbor_find_if(ifp);
301 :
302 0 : if (!nbr)
303 : break;
304 :
305 0 : nexthop_tab[num_ifindex].nexthop_addr =
306 : nbr->source_addr;
307 0 : nexthop_tab[num_ifindex].ifindex = nh_ifi;
308 0 : ++num_ifindex;
309 : #endif
310 0 : break;
311 0 : case NEXTHOP_TYPE_BLACKHOLE:
312 : /* do nothing */
313 0 : zlog_warn(
314 : "%s: found non-ifindex nexthop type=%d for address %pPAs(%s)",
315 : __func__, nexthop_type, &addr, pim->vrf->name);
316 0 : break;
317 : }
318 : }
319 :
320 : return num_ifindex;
321 : }
322 :
323 0 : static int zclient_lookup_nexthop_once(struct pim_instance *pim,
324 : struct pim_zlookup_nexthop nexthop_tab[],
325 : const int tab_size, pim_addr addr)
326 : {
327 0 : struct stream *s;
328 0 : int ret;
329 0 : struct ipaddr ipaddr;
330 :
331 0 : if (PIM_DEBUG_PIM_NHT_DETAIL)
332 0 : zlog_debug("%s: addr=%pPAs(%s)", __func__, &addr,
333 : pim->vrf->name);
334 :
335 : /* Check socket. */
336 0 : if (zlookup->sock < 0) {
337 0 : flog_err(EC_LIB_ZAPI_SOCKET,
338 : "%s: zclient lookup socket is not connected",
339 : __func__);
340 0 : zclient_lookup_failed(zlookup);
341 0 : return -1;
342 : }
343 :
344 0 : if (pim->vrf->vrf_id == VRF_UNKNOWN) {
345 0 : zlog_notice(
346 : "%s: VRF: %s does not fully exist yet, delaying lookup",
347 : __func__, pim->vrf->name);
348 0 : return -1;
349 : }
350 :
351 0 : ipaddr.ipa_type = PIM_IPADDR;
352 0 : ipaddr.ipaddr_pim = addr;
353 :
354 0 : s = zlookup->obuf;
355 0 : stream_reset(s);
356 0 : zclient_create_header(s, ZEBRA_NEXTHOP_LOOKUP_MRIB, pim->vrf->vrf_id);
357 0 : stream_put_ipaddr(s, &ipaddr);
358 0 : stream_putw_at(s, 0, stream_get_endp(s));
359 :
360 0 : ret = writen(zlookup->sock, s->data, stream_get_endp(s));
361 0 : if (ret < 0) {
362 0 : flog_err(
363 : EC_LIB_SOCKET,
364 : "%s: writen() failure: %d writing to zclient lookup socket",
365 : __func__, errno);
366 0 : zclient_lookup_failed(zlookup);
367 0 : return -2;
368 : }
369 0 : if (ret == 0) {
370 0 : flog_err_sys(EC_LIB_SOCKET,
371 : "%s: connection closed on zclient lookup socket",
372 : __func__);
373 0 : zclient_lookup_failed(zlookup);
374 0 : return -3;
375 : }
376 :
377 0 : return zclient_read_nexthop(pim, zlookup, nexthop_tab, tab_size, addr);
378 : }
379 :
380 0 : void zclient_lookup_read_pipe(struct thread *thread)
381 : {
382 0 : struct zclient *zlookup = THREAD_ARG(thread);
383 0 : struct pim_instance *pim = pim_get_pim_instance(VRF_DEFAULT);
384 0 : struct pim_zlookup_nexthop nexthop_tab[10];
385 0 : pim_addr l = PIMADDR_ANY;
386 :
387 0 : if (!pim) {
388 0 : if (PIM_DEBUG_PIM_NHT_DETAIL)
389 0 : zlog_debug("%s: Unable to find pim instance", __func__);
390 0 : return;
391 : }
392 :
393 0 : zclient_lookup_nexthop_once(pim, nexthop_tab, 10, l);
394 0 : thread_add_timer(router->master, zclient_lookup_read_pipe, zlookup, 60,
395 : &zlookup_read);
396 : }
397 :
398 0 : int zclient_lookup_nexthop(struct pim_instance *pim,
399 : struct pim_zlookup_nexthop nexthop_tab[],
400 : const int tab_size, pim_addr addr,
401 : int max_lookup)
402 : {
403 0 : int lookup;
404 0 : uint32_t route_metric = 0xFFFFFFFF;
405 0 : uint8_t protocol_distance = 0xFF;
406 :
407 0 : pim->nexthop_lookups++;
408 :
409 0 : for (lookup = 0; lookup < max_lookup; ++lookup) {
410 0 : int num_ifindex;
411 0 : int first_ifindex;
412 0 : pim_addr nexthop_addr;
413 :
414 0 : num_ifindex = zclient_lookup_nexthop_once(pim, nexthop_tab,
415 : tab_size, addr);
416 0 : if (num_ifindex < 1) {
417 0 : if (PIM_DEBUG_PIM_NHT_DETAIL)
418 0 : zlog_debug(
419 : "%s: lookup=%d/%d: could not find nexthop ifindex for address %pPA(%s)",
420 : __func__, lookup, max_lookup, &addr,
421 : pim->vrf->name);
422 0 : return -1;
423 : }
424 :
425 0 : if (lookup < 1) {
426 : /* this is the non-recursive lookup - save original
427 : * metric/distance */
428 0 : route_metric = nexthop_tab[0].route_metric;
429 0 : protocol_distance = nexthop_tab[0].protocol_distance;
430 : }
431 :
432 : /*
433 : * FIXME: Non-recursive nexthop ensured only for first ifindex.
434 : * However, recursive route lookup should really be fixed in
435 : * zebra daemon.
436 : * See also TODO T24.
437 : *
438 : * So Zebra for NEXTHOP_TYPE_IPV4 returns the ifindex now since
439 : * it was being stored. This Doesn't solve all cases of
440 : * recursive lookup but for the most common types it does.
441 : */
442 0 : first_ifindex = nexthop_tab[0].ifindex;
443 0 : nexthop_addr = nexthop_tab[0].nexthop_addr;
444 0 : if (first_ifindex > 0) {
445 : /* found: first ifindex is non-recursive nexthop */
446 :
447 0 : if (lookup > 0) {
448 : /* Report non-recursive success after first
449 : * lookup */
450 0 : if (PIM_DEBUG_PIM_NHT)
451 0 : zlog_debug(
452 : "%s: lookup=%d/%d: found non-recursive ifindex=%d for address %pPA(%s) dist=%d met=%d",
453 : __func__, lookup, max_lookup,
454 : first_ifindex, &addr,
455 : pim->vrf->name,
456 : nexthop_tab[0]
457 : .protocol_distance,
458 : nexthop_tab[0].route_metric);
459 :
460 : /* use last address as nexthop address */
461 0 : nexthop_tab[0].nexthop_addr = addr;
462 :
463 : /* report original route metric/distance */
464 0 : nexthop_tab[0].route_metric = route_metric;
465 0 : nexthop_tab[0].protocol_distance =
466 : protocol_distance;
467 : }
468 :
469 0 : return num_ifindex;
470 : }
471 :
472 0 : if (PIM_DEBUG_PIM_NHT)
473 0 : zlog_debug(
474 : "%s: lookup=%d/%d: zebra returned recursive nexthop %pPAs for address %pPA(%s) dist=%d met=%d",
475 : __func__, lookup, max_lookup, &nexthop_addr,
476 : &addr, pim->vrf->name,
477 : nexthop_tab[0].protocol_distance,
478 : nexthop_tab[0].route_metric);
479 :
480 0 : addr = nexthop_addr; /* use nexthop
481 : addr for recursive lookup */
482 :
483 : } /* for (max_lookup) */
484 :
485 0 : if (PIM_DEBUG_PIM_NHT)
486 0 : zlog_warn(
487 : "%s: lookup=%d/%d: failure searching recursive nexthop ifindex for address %pPA(%s)",
488 : __func__, lookup, max_lookup, &addr, pim->vrf->name);
489 :
490 : return -2;
491 : }
492 :
493 0 : void pim_zlookup_show_ip_multicast(struct vty *vty)
494 : {
495 0 : vty_out(vty, "Zclient lookup socket: ");
496 0 : if (zlookup) {
497 0 : vty_out(vty, "%d failures=%d\n", zlookup->sock, zlookup->fail);
498 : } else {
499 0 : vty_out(vty, "<null zclient>\n");
500 : }
501 0 : }
502 :
503 0 : int pim_zlookup_sg_statistics(struct channel_oil *c_oil)
504 : {
505 0 : struct stream *s = zlookup->obuf;
506 0 : uint16_t command = 0;
507 0 : unsigned long long lastused;
508 0 : pim_sgaddr sg;
509 0 : int count = 0;
510 0 : int ret;
511 0 : pim_sgaddr more = {};
512 0 : struct interface *ifp =
513 0 : pim_if_find_by_vif_index(c_oil->pim, *oil_parent(c_oil));
514 :
515 0 : if (PIM_DEBUG_ZEBRA) {
516 0 : more.src = *oil_origin(c_oil);
517 0 : more.grp = *oil_mcastgrp(c_oil);
518 0 : zlog_debug(
519 : "Sending Request for New Channel Oil Information%pSG VIIF %d(%s)",
520 : &more, *oil_parent(c_oil), c_oil->pim->vrf->name);
521 : }
522 :
523 0 : if (!ifp)
524 : return -1;
525 :
526 0 : stream_reset(s);
527 0 : zclient_create_header(s, ZEBRA_IPMR_ROUTE_STATS,
528 0 : c_oil->pim->vrf->vrf_id);
529 0 : stream_putl(s, PIM_AF);
530 0 : stream_write(s, oil_origin(c_oil), sizeof(pim_addr));
531 0 : stream_write(s, oil_mcastgrp(c_oil), sizeof(pim_addr));
532 0 : stream_putl(s, ifp->ifindex);
533 0 : stream_putw_at(s, 0, stream_get_endp(s));
534 :
535 0 : count = stream_get_endp(s);
536 0 : ret = writen(zlookup->sock, s->data, count);
537 0 : if (ret <= 0) {
538 0 : flog_err(
539 : EC_LIB_SOCKET,
540 : "%s: writen() failure: %d writing to zclient lookup socket",
541 : __func__, errno);
542 0 : return -1;
543 : }
544 :
545 0 : s = zlookup->ibuf;
546 :
547 0 : while (command != ZEBRA_IPMR_ROUTE_STATS) {
548 0 : int err;
549 0 : uint16_t length = 0;
550 0 : vrf_id_t vrf_id;
551 0 : uint8_t marker;
552 0 : uint8_t version;
553 :
554 0 : stream_reset(s);
555 0 : err = zclient_read_header(s, zlookup->sock, &length, &marker,
556 : &version, &vrf_id, &command);
557 0 : if (err < 0) {
558 0 : flog_err(EC_LIB_ZAPI_MISSMATCH,
559 : "%s: zclient_read_header() failed", __func__);
560 0 : zclient_lookup_failed(zlookup);
561 0 : return -1;
562 : }
563 : }
564 :
565 0 : stream_get(&sg.src, s, sizeof(pim_addr));
566 0 : stream_get(&sg.grp, s, sizeof(pim_addr));
567 :
568 0 : more.src = *oil_origin(c_oil);
569 0 : more.grp = *oil_mcastgrp(c_oil);
570 0 : if (pim_sgaddr_cmp(sg, more)) {
571 0 : if (PIM_DEBUG_ZEBRA)
572 0 : flog_err(
573 : EC_LIB_ZAPI_MISSMATCH,
574 : "%s: Received wrong %pSG(%s) information requested",
575 : __func__, &more, c_oil->pim->vrf->name);
576 0 : zclient_lookup_failed(zlookup);
577 0 : return -3;
578 : }
579 :
580 0 : stream_get(&lastused, s, sizeof(lastused));
581 : /* signed success value from netlink_talk; currently unused */
582 0 : (void)stream_getl(s);
583 :
584 0 : c_oil->cc.lastused = lastused;
585 :
586 0 : return 0;
587 : }
|