]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_zlookup.c
Merge remote-tracking branch 'origin/master' into EIGRP
[mirror_frr.git] / pimd / pim_zlookup.c
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
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
21 #include <zebra.h>
22 #include "zebra/rib.h"
23
24 #include "log.h"
25 #include "prefix.h"
26 #include "zclient.h"
27 #include "stream.h"
28 #include "network.h"
29 #include "thread.h"
30 #include "prefix.h"
31 #include "vty.h"
32
33 #include "pimd.h"
34 #include "pim_iface.h"
35 #include "pim_pim.h"
36 #include "pim_str.h"
37 #include "pim_oil.h"
38 #include "pim_zlookup.h"
39
40 static struct zclient *zlookup = NULL;
41
42 static void zclient_lookup_sched(struct zclient *zlookup, int delay);
43
44 /* Connect to zebra for nexthop lookup. */
45 static int zclient_lookup_connect(struct thread *t)
46 {
47 struct zclient *zlookup;
48
49 zlookup = THREAD_ARG(t);
50 zlookup->t_connect = NULL;
51
52 if (zlookup->sock >= 0) {
53 return 0;
54 }
55
56 if (zclient_socket_connect(zlookup) < 0) {
57 ++zlookup->fail;
58 zlog_warn("%s: failure connecting zclient socket: failures=%d",
59 __PRETTY_FUNCTION__, zlookup->fail);
60 }
61 else {
62 zlookup->fail = 0; /* reset counter on connection */
63 }
64
65 zassert(!zlookup->t_connect);
66 if (zlookup->sock < 0) {
67 /* Since last connect failed, retry within 10 secs */
68 zclient_lookup_sched(zlookup, 10);
69 return -1;
70 }
71
72 return 0;
73 }
74
75 /* Schedule connection with delay. */
76 static void zclient_lookup_sched(struct zclient *zlookup, int delay)
77 {
78 zassert(!zlookup->t_connect);
79
80 THREAD_TIMER_ON(master, zlookup->t_connect,
81 zclient_lookup_connect,
82 zlookup, delay);
83
84 zlog_notice("%s: zclient lookup connection scheduled for %d seconds",
85 __PRETTY_FUNCTION__, delay);
86 }
87
88 /* Schedule connection for now. */
89 static void zclient_lookup_sched_now(struct zclient *zlookup)
90 {
91 zassert(!zlookup->t_connect);
92
93 zlookup->t_connect = thread_add_event(master, zclient_lookup_connect,
94 zlookup, 0);
95
96 zlog_notice("%s: zclient lookup immediate connection scheduled",
97 __PRETTY_FUNCTION__);
98 }
99
100 /* Schedule reconnection, if needed. */
101 static void zclient_lookup_reconnect(struct zclient *zlookup)
102 {
103 if (zlookup->t_connect) {
104 return;
105 }
106
107 zclient_lookup_sched_now(zlookup);
108 }
109
110 static void zclient_lookup_failed(struct zclient *zlookup)
111 {
112 if (zlookup->sock >= 0) {
113 if (close(zlookup->sock)) {
114 zlog_warn("%s: closing fd=%d: errno=%d %s", __func__, zlookup->sock,
115 errno, safe_strerror(errno));
116 }
117 zlookup->sock = -1;
118 }
119
120 zclient_lookup_reconnect(zlookup);
121 }
122
123 void
124 zclient_lookup_free (void)
125 {
126 zclient_free (zlookup);
127 zlookup = NULL;
128 }
129
130 void
131 zclient_lookup_new (void)
132 {
133 zlookup = zclient_new (master);
134 if (!zlookup) {
135 zlog_err("%s: zclient_new() failure",
136 __PRETTY_FUNCTION__);
137 return;
138 }
139
140 zlookup->sock = -1;
141 zlookup->t_connect = NULL;
142
143 zclient_lookup_sched_now(zlookup);
144
145 zlog_notice("%s: zclient lookup socket initialized",
146 __PRETTY_FUNCTION__);
147
148 }
149
150 static int zclient_read_nexthop(struct zclient *zlookup,
151 struct pim_zlookup_nexthop nexthop_tab[],
152 const int tab_size,
153 struct in_addr addr)
154 {
155 int num_ifindex = 0;
156 struct stream *s;
157 const uint16_t MIN_LEN = 10; /* getipv4=4 getc=1 getl=4 getc=1 */
158 uint16_t length;
159 u_char marker;
160 u_char version;
161 vrf_id_t vrf_id;
162 uint16_t command = 0;
163 struct in_addr raddr;
164 uint8_t distance;
165 uint32_t metric;
166 int nexthop_num;
167 int i, err;
168
169 if (PIM_DEBUG_PIM_TRACE_DETAIL) {
170 char addr_str[INET_ADDRSTRLEN];
171 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
172 zlog_debug("%s: addr=%s",
173 __PRETTY_FUNCTION__,
174 addr_str);
175 }
176
177 s = zlookup->ibuf;
178
179 while (command != ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB)
180 {
181 stream_reset(s);
182 err = zclient_read_header (s, zlookup->sock, &length, &marker, &version,
183 &vrf_id, &command);
184 if (err < 0) {
185 zlog_err("%s %s: zclient_read_header() failed",
186 __FILE__, __PRETTY_FUNCTION__);
187 zclient_lookup_failed(zlookup);
188 return -1;
189 }
190
191 if (length < MIN_LEN) {
192 zlog_err("%s %s: failure reading zclient lookup socket: len=%d < MIN_LEN=%d",
193 __FILE__, __PRETTY_FUNCTION__, length, MIN_LEN);
194 zclient_lookup_failed(zlookup);
195 return -2;
196 }
197 }
198
199 raddr.s_addr = stream_get_ipv4(s);
200
201 if (raddr.s_addr != addr.s_addr) {
202 char addr_str[INET_ADDRSTRLEN];
203 char raddr_str[INET_ADDRSTRLEN];
204 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
205 pim_inet4_dump("<raddr?>", raddr, raddr_str, sizeof(raddr_str));
206 zlog_warn("%s: address mismatch: addr=%s raddr=%s",
207 __PRETTY_FUNCTION__,
208 addr_str, raddr_str);
209 /* warning only */
210 }
211
212 distance = stream_getc(s);
213 metric = stream_getl(s);
214 nexthop_num = stream_getc(s);
215
216 if (nexthop_num < 1) {
217 zlog_err("%s: socket %d bad nexthop_num=%d",
218 __func__, zlookup->sock, nexthop_num);
219 return -6;
220 }
221
222 for (i = 0; i < nexthop_num; ++i) {
223 enum nexthop_types_t nexthop_type;
224 struct pim_neighbor *nbr;
225
226 nexthop_type = stream_getc(s);
227 if (num_ifindex >= tab_size) {
228 char addr_str[INET_ADDRSTRLEN];
229 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
230 zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
231 __FILE__, __PRETTY_FUNCTION__,
232 (num_ifindex + 1), tab_size, addr_str);
233 return num_ifindex;
234 }
235 switch (nexthop_type) {
236 case NEXTHOP_TYPE_IFINDEX:
237 case NEXTHOP_TYPE_IPV4_IFINDEX:
238 case NEXTHOP_TYPE_IPV4:
239 nexthop_tab[num_ifindex].nexthop_addr.family = AF_INET;
240 if (nexthop_type == NEXTHOP_TYPE_IPV4_IFINDEX ||
241 nexthop_type == NEXTHOP_TYPE_IPV4) {
242 nexthop_tab[num_ifindex].nexthop_addr.u.prefix4.s_addr = stream_get_ipv4(s);
243 }
244 else {
245 nexthop_tab[num_ifindex].nexthop_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
246 }
247 nexthop_tab[num_ifindex].ifindex = stream_getl(s);
248 nexthop_tab[num_ifindex].protocol_distance = distance;
249 nexthop_tab[num_ifindex].route_metric = metric;
250 ++num_ifindex;
251 break;
252 case NEXTHOP_TYPE_IPV6_IFINDEX:
253 nexthop_tab[num_ifindex].nexthop_addr.family = AF_INET6;
254 stream_get (&nexthop_tab[num_ifindex].nexthop_addr.u.prefix6, s, 16);
255 nexthop_tab[num_ifindex].ifindex = stream_getl (s);
256 nbr = pim_neighbor_find_if (if_lookup_by_index (nexthop_tab[num_ifindex].ifindex, VRF_DEFAULT));
257 if (nbr)
258 {
259 nexthop_tab[num_ifindex].nexthop_addr.family = AF_INET;
260 nexthop_tab[num_ifindex].nexthop_addr.u.prefix4 = nbr->source_addr;
261 }
262 ++num_ifindex;
263 break;
264 default:
265 /* do nothing */
266 {
267 char addr_str[INET_ADDRSTRLEN];
268 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
269 zlog_warn("%s %s: found non-ifindex nexthop type=%d for address %s",
270 __FILE__, __PRETTY_FUNCTION__,
271 nexthop_type, addr_str);
272 }
273 break;
274 }
275 }
276
277 return num_ifindex;
278 }
279
280 static int
281 zclient_lookup_nexthop_once (struct pim_zlookup_nexthop nexthop_tab[],
282 const int tab_size,
283 struct in_addr addr)
284 {
285 struct stream *s;
286 int ret;
287
288 if (PIM_DEBUG_PIM_TRACE_DETAIL) {
289 char addr_str[INET_ADDRSTRLEN];
290 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
291 zlog_debug("%s: addr=%s",
292 __PRETTY_FUNCTION__,
293 addr_str);
294 }
295
296 /* Check socket. */
297 if (zlookup->sock < 0) {
298 zlog_err("%s %s: zclient lookup socket is not connected",
299 __FILE__, __PRETTY_FUNCTION__);
300 zclient_lookup_failed(zlookup);
301 return -1;
302 }
303
304 s = zlookup->obuf;
305 stream_reset(s);
306 zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, VRF_DEFAULT);
307 stream_put_in_addr(s, &addr);
308 stream_putw_at(s, 0, stream_get_endp(s));
309
310 ret = writen(zlookup->sock, s->data, stream_get_endp(s));
311 if (ret < 0) {
312 zlog_err("%s %s: writen() failure: %d writing to zclient lookup socket",
313 __FILE__, __PRETTY_FUNCTION__, errno);
314 zclient_lookup_failed(zlookup);
315 return -2;
316 }
317 if (ret == 0) {
318 zlog_err("%s %s: connection closed on zclient lookup socket",
319 __FILE__, __PRETTY_FUNCTION__);
320 zclient_lookup_failed(zlookup);
321 return -3;
322 }
323
324 return zclient_read_nexthop(zlookup, nexthop_tab,
325 tab_size, addr);
326 }
327
328 int
329 zclient_lookup_nexthop (struct pim_zlookup_nexthop nexthop_tab[],
330 const int tab_size,
331 struct in_addr addr,
332 int max_lookup)
333 {
334 int lookup;
335 uint32_t route_metric = 0xFFFFFFFF;
336 uint8_t protocol_distance = 0xFF;
337
338 qpim_nexthop_lookups++;
339
340 for (lookup = 0; lookup < max_lookup; ++lookup) {
341 int num_ifindex;
342 int first_ifindex;
343 struct prefix nexthop_addr;
344
345 num_ifindex = zclient_lookup_nexthop_once(nexthop_tab,
346 tab_size, addr);
347 if (num_ifindex < 1) {
348 if (PIM_DEBUG_ZEBRA) {
349 char addr_str[INET_ADDRSTRLEN];
350 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
351 zlog_debug("%s %s: lookup=%d/%d: could not find nexthop ifindex for address %s",
352 __FILE__, __PRETTY_FUNCTION__,
353 lookup, max_lookup, addr_str);
354 }
355 return -1;
356 }
357
358 if (lookup < 1) {
359 /* this is the non-recursive lookup - save original metric/distance */
360 route_metric = nexthop_tab[0].route_metric;
361 protocol_distance = nexthop_tab[0].protocol_distance;
362 }
363
364 /*
365 * FIXME: Non-recursive nexthop ensured only for first ifindex.
366 * However, recursive route lookup should really be fixed in zebra daemon.
367 * See also TODO T24.
368 *
369 * So Zebra for NEXTHOP_TYPE_IPV4 returns the ifindex now since
370 * it was being stored. This Doesn't solve all cases of
371 * recursive lookup but for the most common types it does.
372 */
373 first_ifindex = nexthop_tab[0].ifindex;
374 nexthop_addr = nexthop_tab[0].nexthop_addr;
375 if (first_ifindex > 0) {
376 /* found: first ifindex is non-recursive nexthop */
377
378 if (lookup > 0) {
379 /* Report non-recursive success after first lookup */
380 if (PIM_DEBUG_ZEBRA) {
381 char addr_str[INET_ADDRSTRLEN];
382 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
383 zlog_debug("%s %s: lookup=%d/%d: found non-recursive ifindex=%d for address %s dist=%d met=%d",
384 __FILE__, __PRETTY_FUNCTION__,
385 lookup, max_lookup, first_ifindex, addr_str,
386 nexthop_tab[0].protocol_distance,
387 nexthop_tab[0].route_metric);
388 }
389
390 /* use last address as nexthop address */
391 nexthop_tab[0].nexthop_addr.u.prefix4 = addr;
392
393 /* report original route metric/distance */
394 nexthop_tab[0].route_metric = route_metric;
395 nexthop_tab[0].protocol_distance = protocol_distance;
396 }
397
398 return num_ifindex;
399 }
400
401 if (PIM_DEBUG_ZEBRA) {
402 char addr_str[INET_ADDRSTRLEN];
403 char nexthop_str[PREFIX_STRLEN];
404 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
405 pim_addr_dump("<nexthop?>", &nexthop_addr, nexthop_str, sizeof(nexthop_str));
406 zlog_debug("%s %s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s dist=%d met=%d",
407 __FILE__, __PRETTY_FUNCTION__,
408 lookup, max_lookup, nexthop_str, addr_str,
409 nexthop_tab[0].protocol_distance,
410 nexthop_tab[0].route_metric);
411 }
412
413 addr = nexthop_addr.u.prefix4; /* use nexthop addr for recursive lookup */
414
415 } /* for (max_lookup) */
416
417 if (PIM_DEBUG_ZEBRA) {
418 char addr_str[INET_ADDRSTRLEN];
419 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
420 zlog_warn("%s %s: lookup=%d/%d: failure searching recursive nexthop ifindex for address %s",
421 __FILE__, __PRETTY_FUNCTION__,
422 lookup, max_lookup, addr_str);
423 }
424
425 return -2;
426 }
427
428 void
429 pim_zlookup_show_ip_multicast (struct vty *vty)
430 {
431 vty_out(vty, "Zclient lookup socket: ");
432 if (zlookup) {
433 vty_out(vty, "%d failures=%d%s", zlookup->sock,
434 zlookup->fail, VTY_NEWLINE);
435 }
436 else {
437 vty_out(vty, "<null zclient>%s", VTY_NEWLINE);
438 }
439 }
440
441 int
442 pim_zlookup_sg_statistics (struct channel_oil *c_oil)
443 {
444 struct stream *s = zlookup->obuf;
445 uint16_t command = 0;
446 unsigned long long lastused;
447 struct prefix_sg sg;
448 int count = 0;
449 int ret;
450 struct interface *ifp = pim_if_find_by_vif_index (c_oil->oil.mfcc_parent);
451
452 if (PIM_DEBUG_ZEBRA)
453 {
454 struct prefix_sg more;
455
456 more.src = c_oil->oil.mfcc_origin;
457 more.grp = c_oil->oil.mfcc_mcastgrp;
458 zlog_debug ("Sending Request for New Channel Oil Information(%s) VIIF %d",
459 pim_str_sg_dump (&more), c_oil->oil.mfcc_parent);
460 }
461
462 if (!ifp)
463 return -1;
464
465 stream_reset (s);
466 zclient_create_header (s, ZEBRA_IPMR_ROUTE_STATS, VRF_DEFAULT);
467 stream_put_in_addr (s, &c_oil->oil.mfcc_origin);
468 stream_put_in_addr (s, &c_oil->oil.mfcc_mcastgrp);
469 stream_putl (s, ifp->ifindex);
470 stream_putw_at(s, 0, stream_get_endp(s));
471
472 count = stream_get_endp (s);
473 ret = writen (zlookup->sock, s->data, count);
474 if (ret <= 0)
475 {
476 zlog_err("%s %s: writen() failure: %d writing to zclient lookup socket",
477 __FILE__, __PRETTY_FUNCTION__, errno);
478 return -1;
479 }
480
481 s = zlookup->ibuf;
482
483 while (command != ZEBRA_IPMR_ROUTE_STATS)
484 {
485 int err;
486 uint16_t length = 0;
487 vrf_id_t vrf_id;
488 u_char marker;
489 u_char version;
490
491 stream_reset (s);
492 err = zclient_read_header (s, zlookup->sock, &length, &marker, &version,
493 &vrf_id, &command);
494 if (err < 0)
495 {
496 zlog_err ("%s %s: zclient_read_header() failed",
497 __FILE__, __PRETTY_FUNCTION__);
498 zclient_lookup_failed(zlookup);
499 return -1;
500 }
501 }
502
503 sg.src.s_addr = stream_get_ipv4 (s);
504 sg.grp.s_addr = stream_get_ipv4 (s);
505 if (sg.src.s_addr != c_oil->oil.mfcc_origin.s_addr ||
506 sg.grp.s_addr != c_oil->oil.mfcc_mcastgrp.s_addr)
507 {
508 zlog_err ("%s: Received wrong %s information",
509 __PRETTY_FUNCTION__, pim_str_sg_dump (&sg));
510 zclient_lookup_failed (zlookup);
511 return -3;
512 }
513
514 stream_get (&lastused, s, sizeof (lastused));
515 ret = stream_getl (s);
516
517 if (PIM_DEBUG_ZEBRA)
518 zlog_debug ("Received %lld for %s success: %d", lastused, pim_str_sg_dump (&sg), ret);
519
520 c_oil->cc.lastused = lastused;
521
522 return 0;
523
524 }