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