blob: 98548e79f6918796f7280a32e55ce8fc1e4fc83d [file] [log] [blame]
Everton Marques871dbcf2009-08-11 15:43:05 -03001/*
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 $QuaggaId: $Format:%an, %ai, %h$ $
21*/
22
23#include <zebra.h>
24#include "zebra/rib.h"
25
26#include "log.h"
27#include "prefix.h"
28#include "zclient.h"
29#include "stream.h"
30#include "network.h"
31#include "thread.h"
32
33#include "pimd.h"
34#include "pim_pim.h"
35#include "pim_str.h"
36#include "pim_zlookup.h"
37
38extern int zclient_debug;
39
40static void zclient_lookup_sched(struct zclient *zlookup, int delay);
41
42/* Connect to zebra for nexthop lookup. */
43static int zclient_lookup_connect(struct thread *t)
44{
45 struct zclient *zlookup;
46
47 zlookup = THREAD_ARG(t);
48 zlookup->t_connect = NULL;
49
50 if (zlookup->sock >= 0) {
51 return 0;
52 }
53
54#ifdef HAVE_TCP_ZEBRA
55 zlog_debug("%s: FIXME blocking connect: zclient_socket()",
56 __PRETTY_FUNCTION__);
57 zlookup->sock = zclient_socket();
58 if (zlookup->sock < 0) {
59 zlog_warn("%s: failure connecting TCP socket %s,%d",
60 __PRETTY_FUNCTION__, "127.0.0.1", ZEBRA_PORT);
61 }
62 else if (zclient_debug) {
63 zlog_notice("%s: connected TCP socket %s,%d",
64 __PRETTY_FUNCTION__, "127.0.0.1", ZEBRA_PORT);
65 }
66#else
67 zlog_debug("%s: FIXME blocking connect: zclient_socket_un()",
68 __PRETTY_FUNCTION__);
69 zlookup->sock = zclient_socket_un(ZEBRA_SERV_PATH);
70 if (zlookup->sock < 0) {
71 zlog_warn("%s: failure connecting UNIX socket %s",
72 __PRETTY_FUNCTION__, ZEBRA_SERV_PATH);
73 }
74 else if (zclient_debug) {
75 zlog_notice("%s: connected UNIX socket %s",
76 __PRETTY_FUNCTION__, ZEBRA_SERV_PATH);
77 }
78#endif /* HAVE_TCP_ZEBRA */
79
80 zassert(!zlookup->t_connect);
81 if (zlookup->sock < 0) {
82 /* Since last connect failed, retry within 10 secs */
83 zclient_lookup_sched(zlookup, 10);
84 return -1;
85 }
86
87 return 0;
88}
89
90/* Schedule connection with delay. */
91static void zclient_lookup_sched(struct zclient *zlookup, int delay)
92{
93 zassert(!zlookup->t_connect);
94
95 THREAD_TIMER_ON(master, zlookup->t_connect,
96 zclient_lookup_connect,
97 zlookup, delay);
98
99 zlog_notice("%s: zclient lookup connection scheduled for %d seconds",
100 __PRETTY_FUNCTION__, delay);
101}
102
103/* Schedule connection for now. */
104static void zclient_lookup_sched_now(struct zclient *zlookup)
105{
106 zassert(!zlookup->t_connect);
107
108 zlookup->t_connect = thread_add_event(master, zclient_lookup_connect,
109 zlookup, 0);
110
111 zlog_notice("%s: zclient lookup immediate connection scheduled",
112 __PRETTY_FUNCTION__);
113}
114
115/* Schedule reconnection, if needed. */
116static void zclient_lookup_reconnect(struct zclient *zlookup)
117{
118 if (zlookup->t_connect) {
119 return;
120 }
121
122 zclient_lookup_sched_now(zlookup);
123}
124
125struct zclient *zclient_lookup_new()
126{
127 struct zclient *zlookup;
128
129 zlookup = zclient_new();
130 if (!zlookup) {
131 zlog_err("%s: zclient_new() failure",
132 __PRETTY_FUNCTION__);
133 return 0;
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 return zlookup;
147}
148
149static int zclient_read_nexthop(struct zclient *zlookup,
150 struct pim_zlookup_nexthop nexthop_tab[],
151 const int tab_size,
152 struct in_addr addr)
153{
154 int num_ifindex = 0;
155 struct stream *s;
156 const uint16_t MIN_LEN = 14; /* getc=1 getc=1 getw=2 getipv4=4 getc=1 getl=4 getc=1 */
157 uint16_t length, len;
158 u_char marker;
159 u_char version;
160 uint16_t command;
161 int nbytes;
162 struct in_addr raddr;
163 uint8_t distance;
164 uint32_t metric;
165 int nexthop_num;
166 int i;
167
168 if (PIM_DEBUG_ZEBRA) {
169 char addr_str[100];
170 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
171 zlog_debug("%s: addr=%s",
172 __PRETTY_FUNCTION__,
173 addr_str);
174 }
175
176 s = zlookup->ibuf;
177 stream_reset(s);
178
179 nbytes = stream_read(s, zlookup->sock, 2);
180 if (nbytes < 2) {
181 zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d",
182 __FILE__, __PRETTY_FUNCTION__, nbytes);
183 close(zlookup->sock);
184 zlookup->sock = -1;
185 zclient_lookup_reconnect(zlookup);
186 return -1;
187 }
188 length = stream_getw(s);
189
190 len = length - 2;
191
192 if (len < MIN_LEN) {
193 zlog_err("%s %s: failure reading zclient lookup socket: len=%d < MIN_LEN=%d",
194 __FILE__, __PRETTY_FUNCTION__, len, MIN_LEN);
195 close(zlookup->sock);
196 zlookup->sock = -1;
197 zclient_lookup_reconnect(zlookup);
198 return -2;
199 }
200
201 nbytes = stream_read(s, zlookup->sock, len);
202 if (nbytes < (length - 2)) {
203 zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d < len=%d",
204 __FILE__, __PRETTY_FUNCTION__, nbytes, len);
205 close(zlookup->sock);
206 zlookup->sock = -1;
207 zclient_lookup_reconnect(zlookup);
208 return -3;
209 }
210 marker = stream_getc(s);
211 version = stream_getc(s);
212
213 if (version != ZSERV_VERSION || marker != ZEBRA_HEADER_MARKER) {
214 zlog_err("%s: socket %d version mismatch, marker %d, version %d",
215 __func__, zlookup->sock, marker, version);
216 return -4;
217 }
218
219 command = stream_getw(s);
220 if (command != ZEBRA_IPV4_NEXTHOP_LOOKUP_V2) {
221 zlog_err("%s: socket %d command mismatch: %d",
222 __func__, zlookup->sock, command);
223 return -5;
224 }
225
226 raddr.s_addr = stream_get_ipv4(s);
227
228 if (raddr.s_addr != addr.s_addr) {
229 char addr_str[100];
230 char raddr_str[100];
231 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
232 pim_inet4_dump("<raddr?>", raddr, raddr_str, sizeof(raddr_str));
233 zlog_warn("%s: address mismatch: addr=%s raddr=%s",
234 __PRETTY_FUNCTION__,
235 addr_str, raddr_str);
236 /* warning only */
237 }
238
239 distance = stream_getc(s);
240 metric = stream_getl(s);
241 nexthop_num = stream_getc(s);
242
243 if (nexthop_num < 1) {
244 zlog_err("%s: socket %d bad nexthop_num=%d",
245 __func__, zlookup->sock, nexthop_num);
246 return -6;
247 }
248
249 len -= MIN_LEN;
250
251 for (i = 0; i < nexthop_num; ++i) {
252 enum nexthop_types_t nexthop_type;
253
254 if (len < 1) {
255 zlog_err("%s: socket %d empty input expecting nexthop_type: len=%d",
256 __func__, zlookup->sock, len);
257 return -7;
258 }
259
260 nexthop_type = stream_getc(s);
261 --len;
262
263 switch (nexthop_type) {
264 case ZEBRA_NEXTHOP_IFINDEX:
265 case ZEBRA_NEXTHOP_IFNAME:
266 case ZEBRA_NEXTHOP_IPV4_IFINDEX:
267 if (num_ifindex >= tab_size) {
268 char addr_str[100];
269 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
270 zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
271 __FILE__, __PRETTY_FUNCTION__,
272 (num_ifindex + 1), tab_size, addr_str);
273 return num_ifindex;
274 }
275 if (nexthop_type == ZEBRA_NEXTHOP_IPV4_IFINDEX) {
276 if (len < 4) {
277 zlog_err("%s: socket %d short input expecting nexthop IPv4-addr: len=%d",
278 __func__, zlookup->sock, len);
279 return -8;
280 }
281 nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
282 len -= 4;
283 }
284 else {
285 nexthop_tab[num_ifindex].nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
286 }
287 nexthop_tab[num_ifindex].ifindex = stream_getl(s);
288 nexthop_tab[num_ifindex].protocol_distance = distance;
289 nexthop_tab[num_ifindex].route_metric = metric;
290 ++num_ifindex;
291 break;
292 case ZEBRA_NEXTHOP_IPV4:
293 if (num_ifindex >= tab_size) {
294 char addr_str[100];
295 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
296 zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
297 __FILE__, __PRETTY_FUNCTION__,
298 (num_ifindex + 1), tab_size, addr_str);
299 return num_ifindex;
300 }
301 nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
302 len -= 4;
303 nexthop_tab[num_ifindex].ifindex = 0;
304 nexthop_tab[num_ifindex].protocol_distance = distance;
305 nexthop_tab[num_ifindex].route_metric = metric;
306 {
307 char addr_str[100];
308 char nexthop_str[100];
309 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
310 pim_inet4_dump("<nexthop?>", nexthop_tab[num_ifindex].nexthop_addr, nexthop_str, sizeof(nexthop_str));
311 zlog_warn("%s %s: zebra returned recursive nexthop %s for address %s",
312 __FILE__, __PRETTY_FUNCTION__,
313 nexthop_str, addr_str);
314 }
315 ++num_ifindex;
316 break;
317 default:
318 /* do nothing */
319 {
320 char addr_str[100];
321 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
322 zlog_warn("%s %s: found non-ifindex nexthop type=%d for address %s",
323 __FILE__, __PRETTY_FUNCTION__,
324 nexthop_type, addr_str);
325 }
326 break;
327 }
328 }
329
330 return num_ifindex;
331}
332
333static int zclient_lookup_nexthop_once(struct zclient *zlookup,
334 struct pim_zlookup_nexthop nexthop_tab[],
335 const int tab_size,
336 struct in_addr addr)
337{
338 struct stream *s;
339 int ret;
340
341 if (PIM_DEBUG_ZEBRA) {
342 char addr_str[100];
343 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
344 zlog_debug("%s: addr=%s",
345 __PRETTY_FUNCTION__,
346 addr_str);
347 }
348
349 /* Check socket. */
350 if (zlookup->sock < 0) {
351 zlog_err("%s %s: zclient lookup socket is not connected",
352 __FILE__, __PRETTY_FUNCTION__);
353 zclient_lookup_reconnect(zlookup);
354 return -1;
355 }
356
357 s = zlookup->obuf;
358 stream_reset(s);
359 zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_V2);
360 stream_put_in_addr(s, &addr);
361 stream_putw_at(s, 0, stream_get_endp(s));
362
363 ret = writen(zlookup->sock, s->data, stream_get_endp(s));
364 if (ret < 0) {
365 zlog_err("%s %s: writen() failure writing to zclient lookup socket",
366 __FILE__, __PRETTY_FUNCTION__);
367 close(zlookup->sock);
368 zlookup->sock = -1;
369 zclient_lookup_reconnect(zlookup);
370 return -2;
371 }
372 if (ret == 0) {
373 zlog_err("%s %s: connection closed on zclient lookup socket",
374 __FILE__, __PRETTY_FUNCTION__);
375 close(zlookup->sock);
376 zlookup->sock = -1;
377 zclient_lookup_reconnect(zlookup);
378 return -3;
379 }
380
381 return zclient_read_nexthop(zlookup, nexthop_tab,
382 tab_size, addr);
383}
384
385int zclient_lookup_nexthop(struct zclient *zlookup,
386 struct pim_zlookup_nexthop nexthop_tab[],
387 const int tab_size,
388 struct in_addr addr,
389 int max_lookup)
390{
391 int lookup;
392
393 for (lookup = 0; lookup < max_lookup; ++lookup) {
394 int num_ifindex;
395 int first_ifindex;
396 struct in_addr nexthop_addr;
397
398 num_ifindex = zclient_lookup_nexthop_once(qpim_zclient_lookup, nexthop_tab,
399 PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr);
400 if (num_ifindex < 1) {
401 char addr_str[100];
402 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
403 zlog_warn("%s %s: lookup=%d/%d: could not find nexthop ifindex for address %s",
404 __FILE__, __PRETTY_FUNCTION__,
405 lookup, max_lookup, addr_str);
406 return -1;
407 }
408
409 /*
410 FIXME: Non-recursive nexthop ensured only for first ifindex.
411 However, recursive route lookup should really be fixed in zebra daemon.
412 See also TODO T24.
413 */
414 first_ifindex = nexthop_tab[0].ifindex;
415 nexthop_addr = nexthop_tab[0].nexthop_addr;
416 if (first_ifindex > 0) {
417 /* found: first ifindex is non-recursive nexthop */
418
419 if (lookup > 0) {
420 /* Report non-recursive success after first lookup */
421 char addr_str[100];
422 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
423 zlog_info("%s %s: lookup=%d/%d: found non-recursive ifindex=%d for address %s",
424 __FILE__, __PRETTY_FUNCTION__,
425 lookup, max_lookup, first_ifindex, addr_str);
426
427 /* use last address as nexthop address */
428 nexthop_tab[0].nexthop_addr = addr;
429 }
430
431 return num_ifindex;
432 }
433
434 {
435 char addr_str[100];
436 char nexthop_str[100];
437 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
438 pim_inet4_dump("<nexthop?>", nexthop_addr, nexthop_str, sizeof(nexthop_str));
439 zlog_warn("%s %s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s",
440 __FILE__, __PRETTY_FUNCTION__,
441 lookup, max_lookup, nexthop_str, addr_str);
442 }
443
444 addr = nexthop_addr; /* use nexthop addr for recursive lookup */
445
446 } /* for (max_lookup) */
447
448 char addr_str[100];
449 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
450 zlog_warn("%s %s: lookup=%d/%d: failure searching recursive nexthop ifindex for address %s",
451 __FILE__, __PRETTY_FUNCTION__,
452 lookup, max_lookup, addr_str);
453
454 return -2;
455}