]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_zlookup.c
Merge pull request #28 from opensourcerouting/dev/osr/rename-part1
[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
31 #include "pimd.h"
32 #include "pim_pim.h"
33 #include "pim_str.h"
34 #include "pim_zlookup.h"
35
36 extern int zclient_debug;
37
38 static void zclient_lookup_sched(struct zclient *zlookup, int delay);
39
40 /* Connect to zebra for nexthop lookup. */
41 static int zclient_lookup_connect(struct thread *t)
42 {
43 struct zclient *zlookup;
44
45 zlookup = THREAD_ARG(t);
46 zlookup->t_connect = NULL;
47
48 if (zlookup->sock >= 0) {
49 return 0;
50 }
51
52 if (zclient_socket_connect(zlookup) < 0) {
53 ++zlookup->fail;
54 zlog_warn("%s: failure connecting zclient socket: failures=%d",
55 __PRETTY_FUNCTION__, zlookup->fail);
56 }
57 else {
58 zlookup->fail = 0; /* reset counter on connection */
59 }
60
61 zassert(!zlookup->t_connect);
62 if (zlookup->sock < 0) {
63 /* Since last connect failed, retry within 10 secs */
64 zclient_lookup_sched(zlookup, 10);
65 return -1;
66 }
67
68 return 0;
69 }
70
71 /* Schedule connection with delay. */
72 static void zclient_lookup_sched(struct zclient *zlookup, int delay)
73 {
74 zassert(!zlookup->t_connect);
75
76 THREAD_TIMER_ON(master, zlookup->t_connect,
77 zclient_lookup_connect,
78 zlookup, delay);
79
80 zlog_notice("%s: zclient lookup connection scheduled for %d seconds",
81 __PRETTY_FUNCTION__, delay);
82 }
83
84 /* Schedule connection for now. */
85 static void zclient_lookup_sched_now(struct zclient *zlookup)
86 {
87 zassert(!zlookup->t_connect);
88
89 zlookup->t_connect = thread_add_event(master, zclient_lookup_connect,
90 zlookup, 0);
91
92 zlog_notice("%s: zclient lookup immediate connection scheduled",
93 __PRETTY_FUNCTION__);
94 }
95
96 /* Schedule reconnection, if needed. */
97 static void zclient_lookup_reconnect(struct zclient *zlookup)
98 {
99 if (zlookup->t_connect) {
100 return;
101 }
102
103 zclient_lookup_sched_now(zlookup);
104 }
105
106 static void zclient_lookup_failed(struct zclient *zlookup)
107 {
108 if (zlookup->sock >= 0) {
109 if (close(zlookup->sock)) {
110 zlog_warn("%s: closing fd=%d: errno=%d %s", __func__, zlookup->sock,
111 errno, safe_strerror(errno));
112 }
113 zlookup->sock = -1;
114 }
115
116 zclient_lookup_reconnect(zlookup);
117 }
118
119 struct zclient *zclient_lookup_new()
120 {
121 struct zclient *zlookup;
122
123 zlookup = zclient_new (master);
124 if (!zlookup) {
125 zlog_err("%s: zclient_new() failure",
126 __PRETTY_FUNCTION__);
127 return 0;
128 }
129
130 zlookup->sock = -1;
131 zlookup->ibuf = stream_new(ZEBRA_MAX_PACKET_SIZ);
132 zlookup->obuf = stream_new(ZEBRA_MAX_PACKET_SIZ);
133 zlookup->t_connect = 0;
134
135 zclient_lookup_sched_now(zlookup);
136
137 zlog_notice("%s: zclient lookup socket initialized",
138 __PRETTY_FUNCTION__);
139
140 return zlookup;
141 }
142
143 static int zclient_read_nexthop(struct zclient *zlookup,
144 struct pim_zlookup_nexthop nexthop_tab[],
145 const int tab_size,
146 struct in_addr addr)
147 {
148 int num_ifindex = 0;
149 struct stream *s;
150 const uint16_t MIN_LEN = 10; /* getipv4=4 getc=1 getl=4 getc=1 */
151 uint16_t length;
152 u_char marker;
153 u_char version;
154 vrf_id_t vrf_id;
155 uint16_t command = 0;
156 struct in_addr raddr;
157 uint8_t distance;
158 uint32_t metric;
159 int nexthop_num;
160 int i, err;
161
162 if (PIM_DEBUG_ZEBRA) {
163 char addr_str[100];
164 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
165 zlog_debug("%s: addr=%s",
166 __PRETTY_FUNCTION__,
167 addr_str);
168 }
169
170 s = zlookup->ibuf;
171
172 while (command != ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB)
173 {
174 stream_reset(s);
175 err = zclient_read_header (s, zlookup->sock, &length, &marker, &version,
176 &vrf_id, &command);
177 if (err < 0) {
178 zlog_err("%s %s: zclient_read_header() failed",
179 __FILE__, __PRETTY_FUNCTION__);
180 zclient_lookup_failed(zlookup);
181 return -1;
182 }
183
184 if (length < MIN_LEN) {
185 zlog_err("%s %s: failure reading zclient lookup socket: len=%d < MIN_LEN=%d",
186 __FILE__, __PRETTY_FUNCTION__, length, MIN_LEN);
187 zclient_lookup_failed(zlookup);
188 return -2;
189 }
190 }
191
192 raddr.s_addr = stream_get_ipv4(s);
193
194 if (raddr.s_addr != addr.s_addr) {
195 char addr_str[100];
196 char raddr_str[100];
197 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
198 pim_inet4_dump("<raddr?>", raddr, raddr_str, sizeof(raddr_str));
199 zlog_warn("%s: address mismatch: addr=%s raddr=%s",
200 __PRETTY_FUNCTION__,
201 addr_str, raddr_str);
202 /* warning only */
203 }
204
205 distance = stream_getc(s);
206 metric = stream_getl(s);
207 nexthop_num = stream_getc(s);
208
209 if (nexthop_num < 1) {
210 zlog_err("%s: socket %d bad nexthop_num=%d",
211 __func__, zlookup->sock, nexthop_num);
212 return -6;
213 }
214
215 length -= MIN_LEN;
216
217 for (i = 0; i < nexthop_num; ++i) {
218 enum nexthop_types_t nexthop_type;
219
220 if (length < 1) {
221 zlog_err("%s: socket %d empty input expecting nexthop_type: len=%d",
222 __func__, zlookup->sock, length);
223 return -7;
224 }
225
226 nexthop_type = stream_getc(s);
227 --length;
228
229 switch (nexthop_type) {
230 case NEXTHOP_TYPE_IFINDEX:
231 case NEXTHOP_TYPE_IPV4_IFINDEX:
232 if (num_ifindex >= tab_size) {
233 char addr_str[100];
234 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
235 zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
236 __FILE__, __PRETTY_FUNCTION__,
237 (num_ifindex + 1), tab_size, addr_str);
238 return num_ifindex;
239 }
240 if (nexthop_type == NEXTHOP_TYPE_IPV4_IFINDEX) {
241 if (length < 4) {
242 zlog_err("%s: socket %d short input expecting nexthop IPv4-addr: len=%d",
243 __func__, zlookup->sock, length);
244 return -8;
245 }
246 nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
247 length -= 4;
248 }
249 else {
250 nexthop_tab[num_ifindex].nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
251 }
252 nexthop_tab[num_ifindex].ifindex = stream_getl(s);
253 nexthop_tab[num_ifindex].protocol_distance = distance;
254 nexthop_tab[num_ifindex].route_metric = metric;
255 ++num_ifindex;
256 break;
257 case NEXTHOP_TYPE_IPV4:
258 if (num_ifindex >= tab_size) {
259 char addr_str[100];
260 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
261 zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
262 __FILE__, __PRETTY_FUNCTION__,
263 (num_ifindex + 1), tab_size, addr_str);
264 return num_ifindex;
265 }
266 nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
267 length -= 4;
268 nexthop_tab[num_ifindex].ifindex = 0;
269 nexthop_tab[num_ifindex].protocol_distance = distance;
270 nexthop_tab[num_ifindex].route_metric = metric;
271 if (PIM_DEBUG_ZEBRA) {
272 char addr_str[100];
273 char nexthop_str[100];
274 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
275 pim_inet4_dump("<nexthop?>", nexthop_tab[num_ifindex].nexthop_addr, nexthop_str, sizeof(nexthop_str));
276 zlog_debug("%s %s: zebra returned recursive nexthop %s for address %s",
277 __FILE__, __PRETTY_FUNCTION__,
278 nexthop_str, addr_str);
279 }
280 ++num_ifindex;
281 break;
282 default:
283 /* do nothing */
284 {
285 char addr_str[100];
286 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
287 zlog_warn("%s %s: found non-ifindex nexthop type=%d for address %s",
288 __FILE__, __PRETTY_FUNCTION__,
289 nexthop_type, addr_str);
290 }
291 break;
292 }
293 }
294
295 return num_ifindex;
296 }
297
298 static int zclient_lookup_nexthop_once(struct zclient *zlookup,
299 struct pim_zlookup_nexthop nexthop_tab[],
300 const int tab_size,
301 struct in_addr addr)
302 {
303 struct stream *s;
304 int ret;
305
306 if (PIM_DEBUG_ZEBRA) {
307 char addr_str[100];
308 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
309 zlog_debug("%s: addr=%s",
310 __PRETTY_FUNCTION__,
311 addr_str);
312 }
313
314 /* Check socket. */
315 if (zlookup->sock < 0) {
316 zlog_err("%s %s: zclient lookup socket is not connected",
317 __FILE__, __PRETTY_FUNCTION__);
318 zclient_lookup_failed(zlookup);
319 return -1;
320 }
321
322 s = zlookup->obuf;
323 stream_reset(s);
324 zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, VRF_DEFAULT);
325 stream_put_in_addr(s, &addr);
326 stream_putw_at(s, 0, stream_get_endp(s));
327
328 ret = writen(zlookup->sock, s->data, stream_get_endp(s));
329 if (ret < 0) {
330 zlog_err("%s %s: writen() failure writing to zclient lookup socket",
331 __FILE__, __PRETTY_FUNCTION__);
332 zclient_lookup_failed(zlookup);
333 return -2;
334 }
335 if (ret == 0) {
336 zlog_err("%s %s: connection closed on zclient lookup socket",
337 __FILE__, __PRETTY_FUNCTION__);
338 zclient_lookup_failed(zlookup);
339 return -3;
340 }
341
342 return zclient_read_nexthop(zlookup, nexthop_tab,
343 tab_size, addr);
344 }
345
346 int zclient_lookup_nexthop(struct zclient *zlookup,
347 struct pim_zlookup_nexthop nexthop_tab[],
348 const int tab_size,
349 struct in_addr addr,
350 int max_lookup)
351 {
352 int lookup;
353 uint32_t route_metric = 0xFFFFFFFF;
354 uint8_t protocol_distance = 0xFF;
355
356 for (lookup = 0; lookup < max_lookup; ++lookup) {
357 int num_ifindex;
358 int first_ifindex;
359 struct in_addr nexthop_addr;
360
361 num_ifindex = zclient_lookup_nexthop_once(qpim_zclient_lookup, 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 = 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_inet4_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; /* 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 }