]> git.proxmox.com Git - mirror_frr.git/blame - zebra/connected.c
Revert "zebra: When shutting down an interface immediately notify about rnh"
[mirror_frr.git] / zebra / connected.c
CommitLineData
718e3744 1/*
2 * Address linked list routine.
3 * Copyright (C) 1997, 98 Kunihiro Ishiguro
4 *
5 * This file is part of GNU Zebra.
6 *
7 * GNU Zebra is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2, or (at your option) any
10 * later version.
11 *
12 * GNU Zebra is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
896014f4
DL
17 * You should have received a copy of the GNU General Public License along
18 * with this program; see the file COPYING; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
718e3744 20 */
21
22#include <zebra.h>
23
24#include "prefix.h"
25#include "linklist.h"
26#include "if.h"
27#include "table.h"
28#include "rib.h"
29#include "table.h"
30#include "log.h"
0752ef0b 31#include "memory.h"
4a1ab8e4 32#include "zebra_memory.h"
718e3744 33
82f97584 34#include "vty.h"
b84c7253 35#include "zebra/debug.h"
718e3744 36#include "zebra/zserv.h"
37#include "zebra/redistribute.h"
eef1fe11 38#include "zebra/interface.h"
a1ac18c4 39#include "zebra/connected.h"
b6120505 40#include "zebra/rtadv.h"
40c7bdb0 41#include "zebra/zebra_mpls.h"
939fba27 42#include "zebra/debug.h"
9df414fe 43#include "zebra/zebra_errors.h"
e4876266 44#include "zebra/zebra_router.h"
6b0655a2 45
02b4805f 46/* communicate the withdrawal of a connected address */
d62a17ae 47static void connected_withdraw(struct connected *ifc)
ca16218d 48{
d62a17ae 49 if (!ifc)
50 return;
ca16218d 51
d62a17ae 52 /* Update interface address information to protocol daemon. */
53 if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) {
54 zebra_interface_address_delete_update(ifc->ifp, ifc);
ca16218d 55
d62a17ae 56 if (ifc->address->family == AF_INET)
57 if_subnet_delete(ifc->ifp, ifc);
9db047fc 58
11461c63 59 connected_down(ifc->ifp, ifc);
ca16218d 60
d62a17ae 61 UNSET_FLAG(ifc->conf, ZEBRA_IFC_REAL);
62 }
ca16218d 63
d62a17ae 64 /* The address is not in the kernel anymore, so clear the flag */
65 UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
f7f740fe 66
d62a17ae 67 if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) {
68 listnode_delete(ifc->ifp->connected, ifc);
721c0857 69 connected_free(&ifc);
d62a17ae 70 }
ca16218d 71}
72
d62a17ae 73static void connected_announce(struct interface *ifp, struct connected *ifc)
ca16218d 74{
d62a17ae 75 if (!ifc)
76 return;
77
b0fa6f6a
CS
78 if (!if_is_loopback(ifp) && ifc->address->family == AF_INET &&
79 !IS_ZEBRA_IF_VRF(ifp)) {
d62a17ae 80 if (ifc->address->prefixlen == 32)
81 SET_FLAG(ifc->flags, ZEBRA_IFA_UNNUMBERED);
82 else
83 UNSET_FLAG(ifc->flags, ZEBRA_IFA_UNNUMBERED);
84 }
85
86 listnode_add(ifp->connected, ifc);
87
88 /* Update interface address information to protocol daemon. */
89 if (ifc->address->family == AF_INET)
90 if_subnet_add(ifp, ifc);
91
92 zebra_interface_address_add_update(ifp, ifc);
93
94 if (if_is_operative(ifp)) {
ae87977c 95 connected_up(ifp, ifc);
d62a17ae 96 }
ca16218d 97}
6b0655a2 98
718e3744 99/* If same interface address is already exist... */
abffde07
DL
100struct connected *connected_check(struct interface *ifp,
101 union prefixconstptr pu)
718e3744 102{
abffde07 103 const struct prefix *p = pu.p;
d62a17ae 104 struct connected *ifc;
105 struct listnode *node;
718e3744 106
d62a17ae 107 for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc))
108 if (prefix_same(ifc->address, p))
109 return ifc;
718e3744 110
d62a17ae 111 return NULL;
718e3744 112}
113
abffde07
DL
114/* same, but with peer address */
115struct connected *connected_check_ptp(struct interface *ifp,
116 union prefixconstptr pu,
117 union prefixconstptr du)
118{
119 const struct prefix *p = pu.p;
120 const struct prefix *d = du.p;
121 struct connected *ifc;
122 struct listnode *node;
123
abffde07
DL
124 for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
125 if (!prefix_same(ifc->address, p))
126 continue;
127 if (!CONNECTED_PEER(ifc) && !d)
128 return ifc;
129 if (CONNECTED_PEER(ifc) && d
130 && prefix_same(ifc->destination, d))
131 return ifc;
132 }
133
134 return NULL;
135}
136
02b4805f 137/* Check if two ifc's describe the same address in the same state */
d62a17ae 138static int connected_same(struct connected *ifc1, struct connected *ifc2)
74ecdc9e 139{
d62a17ae 140 if (ifc1->ifp != ifc2->ifp)
141 return 0;
142
0f3af738
JW
143 if (ifc1->flags != ifc2->flags)
144 return 0;
145
146 if (ifc1->conf != ifc2->conf)
147 return 0;
148
d62a17ae 149 if (ifc1->destination)
150 if (!ifc2->destination)
151 return 0;
152 if (ifc2->destination)
153 if (!ifc1->destination)
154 return 0;
155
156 if (ifc1->destination && ifc2->destination)
157 if (!prefix_same(ifc1->destination, ifc2->destination))
158 return 0;
159
d62a17ae 160 return 1;
74ecdc9e
PJ
161}
162
d7f5dad6
CF
163/* Handle changes to addresses and send the neccesary announcements
164 * to clients. */
d62a17ae 165static void connected_update(struct interface *ifp, struct connected *ifc)
74ecdc9e 166{
d62a17ae 167 struct connected *current;
168
169 /* Check same connected route. */
abffde07
DL
170 current = connected_check_ptp(ifp, ifc->address, ifc->destination);
171 if (current) {
d62a17ae 172 if (CHECK_FLAG(current->conf, ZEBRA_IFC_CONFIGURED))
173 SET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED);
174
175 /* Avoid spurious withdraws, this might be just the kernel
176 * 'reflecting'
177 * back an address we have already added.
178 */
179 if (connected_same(current, ifc)) {
180 /* nothing to do */
721c0857 181 connected_free(&ifc);
d62a17ae 182 return;
183 }
184
185 /* Clear the configured flag on the old ifc, so it will be freed
186 * by
187 * connected withdraw. */
188 UNSET_FLAG(current->conf, ZEBRA_IFC_CONFIGURED);
189 connected_withdraw(
190 current); /* implicit withdraw - freebsd does this */
191 }
192
193 /* If the connected is new or has changed, announce it, if it is usable
194 */
195 if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL))
196 connected_announce(ifp, ifc);
74ecdc9e
PJ
197}
198
718e3744 199/* Called from if_up(). */
ae87977c 200void connected_up(struct interface *ifp, struct connected *ifc)
718e3744 201{
ae87977c 202 afi_t afi;
49085521 203 struct prefix p = {0};
fd36be7e 204 struct nexthop nh = {
4a7371e9
DS
205 .type = NEXTHOP_TYPE_IFINDEX,
206 .ifindex = ifp->ifindex,
a36898e7 207 .vrf_id = ifp->vrf_id,
fd36be7e 208 };
56e78254 209 struct zebra_vrf *zvrf;
cde1af84 210 uint32_t metric;
e4876266 211 uint32_t flags = 0;
d62a17ae 212
a36898e7 213 zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
56e78254 214 if (!zvrf) {
15569c58
DA
215 flog_err(
216 EC_ZEBRA_VRF_NOT_FOUND,
217 "%s: Received Up for interface but no associated zvrf: %d",
218 __func__, ifp->vrf_id);
56e78254
DS
219 return;
220 }
d62a17ae 221 if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL))
222 return;
223
ae87977c 224 PREFIX_COPY(&p, CONNECTED_PREFIX(ifc));
d62a17ae 225
226 /* Apply mask to the network. */
227 apply_mask(&p);
228
ae87977c
DS
229 afi = family2afi(p.family);
230
231 switch (afi) {
232 case AFI_IP:
233 /*
234 * In case of connected address is 0.0.0.0/0 we treat it tunnel
235 * address.
236 */
237 if (prefix_ipv4_any((struct prefix_ipv4 *)&p))
238 return;
239 break;
240 case AFI_IP6:
70bc8385 241#ifndef GNU_LINUX
ae87977c
DS
242 /* XXX: It is already done by rib_bogus_ipv6 within rib_add */
243 if (IN6_IS_ADDR_UNSPECIFIED(&p.u.prefix6))
244 return;
245#endif
246 break;
247 default:
e914ccbe 248 flog_warn(EC_ZEBRA_CONNECTED_AFI_UNKNOWN,
9df414fe 249 "Received unknown AFI: %s", afi2str(afi));
d62a17ae 250 return;
ae87977c
DS
251 break;
252 }
d62a17ae 253
cde1af84
AK
254 metric = (ifc->metric < (uint32_t)METRIC_MAX) ?
255 ifc->metric : ifp->metric;
d62a17ae 256
e4876266
DS
257 /*
258 * Since we are hand creating the connected routes
259 * in our main routing table, *if* we are working
260 * in an offloaded environment then we need to
261 * pretend like the route is offloaded so everything
262 * else will work
263 */
264 if (zrouter.asic_offloaded)
265 flags |= ZEBRA_FLAG_OFFLOADED;
266
267 rib_add(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0,
268 flags, &p, NULL, &nh, 0, zvrf->table_id, metric, 0, 0, 0);
269
270 rib_add(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0,
271 flags, &p, NULL, &nh, 0, zvrf->table_id, metric, 0, 0, 0);
d62a17ae 272
d62a17ae 273 /* Schedule LSP forwarding entries for processing, if appropriate. */
56e78254 274 if (zvrf->vrf->vrf_id == VRF_DEFAULT) {
2dbe669b 275 if (IS_ZEBRA_DEBUG_MPLS)
996c9314 276 zlog_debug(
2dbe669b
DA
277 "%u: IF %s IP %pFX address add/up, scheduling MPLS processing",
278 zvrf->vrf->vrf_id, ifp->name, &p);
56e78254 279 mpls_mark_lsps_for_processing(zvrf, &p);
d62a17ae 280 }
718e3744 281}
282
283/* Add connected IPv4 route to the interface. */
d62a17ae 284void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr,
0f3af738 285 uint16_t prefixlen, struct in_addr *dest,
cde1af84 286 const char *label, uint32_t metric)
718e3744 287{
d62a17ae 288 struct prefix_ipv4 *p;
289 struct connected *ifc;
290
291 if (ipv4_martian(addr))
292 return;
293
294 /* Make connected structure. */
295 ifc = connected_new();
296 ifc->ifp = ifp;
297 ifc->flags = flags;
cde1af84 298 ifc->metric = metric;
d62a17ae 299 /* If we get a notification from the kernel,
300 * we can safely assume the address is known to the kernel */
301 SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
302
303 /* Allocate new connected address. */
304 p = prefix_ipv4_new();
305 p->family = AF_INET;
306 p->prefix = *addr;
abffde07
DL
307 p->prefixlen = CHECK_FLAG(flags, ZEBRA_IFA_PEER) ? IPV4_MAX_PREFIXLEN
308 : prefixlen;
d62a17ae 309 ifc->address = (struct prefix *)p;
310
0f3af738
JW
311 /* If there is a peer address. */
312 if (CONNECTED_PEER(ifc)) {
d62a17ae 313 /* validate the destination address */
0f3af738
JW
314 if (dest) {
315 p = prefix_ipv4_new();
316 p->family = AF_INET;
317 p->prefix = *dest;
318 p->prefixlen = prefixlen;
319 ifc->destination = (struct prefix *)p;
320
321 if (IPV4_ADDR_SAME(addr, dest))
9df414fe 322 flog_warn(
e914ccbe 323 EC_ZEBRA_IFACE_SAME_LOCAL_AS_PEER,
9bcef951
MS
324 "warning: interface %s has same local and peer address %pI4, routing protocols may malfunction",
325 ifp->name, addr);
d62a17ae 326 } else {
9df414fe 327 zlog_debug(
3efd0893 328 "warning: %s called for interface %s with peer flag set, but no peer address supplied",
d62a17ae 329 __func__, ifp->name);
330 UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER);
331 }
e4529636
AS
332 }
333
0f3af738
JW
334 /* no destination address was supplied */
335 if (!dest && (prefixlen == IPV4_MAX_PREFIXLEN)
336 && if_is_pointopoint(ifp))
337 zlog_debug(
9bcef951
MS
338 "warning: PtP interface %s with addr %pI4/%d needs a peer address",
339 ifp->name, addr, prefixlen);
0f3af738 340
d62a17ae 341 /* Label of this address. */
342 if (label)
343 ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label);
718e3744 344
d62a17ae 345 /* For all that I know an IPv4 address is always ready when we receive
346 * the notification. So it should be safe to set the REAL flag here. */
347 SET_FLAG(ifc->conf, ZEBRA_IFC_REAL);
718e3744 348
d62a17ae 349 connected_update(ifp, ifc);
718e3744 350}
351
11461c63 352void connected_down(struct interface *ifp, struct connected *ifc)
718e3744 353{
11461c63 354 afi_t afi;
d62a17ae 355 struct prefix p;
fd36be7e 356 struct nexthop nh = {
4a7371e9
DS
357 .type = NEXTHOP_TYPE_IFINDEX,
358 .ifindex = ifp->ifindex,
a36898e7 359 .vrf_id = ifp->vrf_id,
fd36be7e 360 };
56e78254
DS
361 struct zebra_vrf *zvrf;
362
a36898e7 363 zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
56e78254 364 if (!zvrf) {
15569c58
DA
365 flog_err(
366 EC_ZEBRA_VRF_NOT_FOUND,
367 "%s: Received Up for interface but no associated zvrf: %d",
368 __func__, ifp->vrf_id);
56e78254
DS
369 return;
370 }
718e3744 371
d62a17ae 372 if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL))
373 return;
718e3744 374
11461c63 375 PREFIX_COPY(&p, CONNECTED_PREFIX(ifc));
718e3744 376
d62a17ae 377 /* Apply mask to the network. */
378 apply_mask(&p);
718e3744 379
11461c63
DS
380 afi = family2afi(p.family);
381
382 switch (afi) {
383 case AFI_IP:
384 /*
385 * In case of connected address is 0.0.0.0/0 we treat it tunnel
386 * address.
387 */
388 if (prefix_ipv4_any((struct prefix_ipv4 *)&p))
389 return;
390 break;
391 case AFI_IP6:
392 if (IN6_IS_ADDR_UNSPECIFIED(&p.u.prefix6))
393 return;
394 break;
395 default:
14a4d9d0 396 zlog_warn("Unknown AFI: %s", afi2str(afi));
11461c63
DS
397 break;
398 }
718e3744 399
11461c63
DS
400 /*
401 * Same logic as for connected_up(): push the changes into the
402 * head.
403 */
bc541126 404 rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0,
3ceae22b 405 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false);
718e3744 406
56e78254 407 rib_delete(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT,
3ceae22b 408 0, 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false);
42cb6b66 409
d62a17ae 410 /* Schedule LSP forwarding entries for processing, if appropriate. */
56e78254 411 if (zvrf->vrf->vrf_id == VRF_DEFAULT) {
2dbe669b 412 if (IS_ZEBRA_DEBUG_MPLS)
996c9314 413 zlog_debug(
2dbe669b
DA
414 "%u: IF %s IP %pFX address down, scheduling MPLS processing",
415 zvrf->vrf->vrf_id, ifp->name, &p);
56e78254 416 mpls_mark_lsps_for_processing(zvrf, &p);
d62a17ae 417 }
718e3744 418}
419
dc7cd304
DS
420static void connected_delete_helper(struct connected *ifc, struct prefix *p)
421{
422 struct interface *ifp;
423
424 if (!ifc)
425 return;
426 ifp = ifc->ifp;
427
428 connected_withdraw(ifc);
429
dc7cd304 430 /* Schedule LSP forwarding entries for processing, if appropriate. */
a36898e7 431 if (ifp->vrf_id == VRF_DEFAULT) {
2dbe669b 432 if (IS_ZEBRA_DEBUG_MPLS)
996c9314 433 zlog_debug(
2dbe669b
DA
434 "%u: IF %s IP %pFX address delete, scheduling MPLS processing",
435 ifp->vrf_id, ifp->name, p);
a36898e7 436 mpls_mark_lsps_for_processing(vrf_info_lookup(ifp->vrf_id), p);
dc7cd304
DS
437 }
438}
439
718e3744 440/* Delete connected IPv4 route to the interface. */
d62a17ae 441void connected_delete_ipv4(struct interface *ifp, int flags,
f93eee44 442 struct in_addr *addr, uint16_t prefixlen,
0f3af738 443 struct in_addr *dest)
718e3744 444{
dc7cd304 445 struct prefix p, d;
d62a17ae 446 struct connected *ifc;
447
dc7cd304 448 memset(&p, 0, sizeof(struct prefix));
d62a17ae 449 p.family = AF_INET;
dc7cd304 450 p.u.prefix4 = *addr;
abffde07
DL
451 p.prefixlen = CHECK_FLAG(flags, ZEBRA_IFA_PEER) ? IPV4_MAX_PREFIXLEN
452 : prefixlen;
453
0f3af738 454 if (dest) {
dc7cd304 455 memset(&d, 0, sizeof(struct prefix));
abffde07 456 d.family = AF_INET;
0f3af738 457 d.u.prefix4 = *dest;
abffde07 458 d.prefixlen = prefixlen;
dc7cd304 459 ifc = connected_check_ptp(ifp, &p, &d);
abffde07 460 } else
dc7cd304 461 ifc = connected_check_ptp(ifp, &p, NULL);
d62a17ae 462
dc7cd304 463 connected_delete_helper(ifc, &p);
718e3744 464}
465
718e3744 466/* Add connected IPv6 route to the interface. */
d62a17ae 467void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr,
0f3af738 468 struct in6_addr *dest, uint16_t prefixlen,
cde1af84 469 const char *label, uint32_t metric)
718e3744 470{
d62a17ae 471 struct prefix_ipv6 *p;
472 struct connected *ifc;
473
474 if (ipv6_martian(addr))
475 return;
476
477 /* Make connected structure. */
478 ifc = connected_new();
479 ifc->ifp = ifp;
480 ifc->flags = flags;
cde1af84 481 ifc->metric = metric;
d62a17ae 482 /* If we get a notification from the kernel,
483 * we can safely assume the address is known to the kernel */
484 SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
485
486 /* Allocate new connected address. */
487 p = prefix_ipv6_new();
488 p->family = AF_INET6;
489 IPV6_ADDR_COPY(&p->prefix, addr);
490 p->prefixlen = prefixlen;
491 ifc->address = (struct prefix *)p;
492
2a855763
DS
493 /* Add global ipv6 address to the RA prefix list */
494 if (!IN6_IS_ADDR_LINKLOCAL(&p->prefix))
495 rtadv_add_prefix(ifp->info, p);
496
0f3af738 497 if (dest) {
60c0687a
DS
498 p = prefix_ipv6_new();
499 p->family = AF_INET6;
0f3af738 500 IPV6_ADDR_COPY(&p->prefix, dest);
60c0687a
DS
501 p->prefixlen = prefixlen;
502 ifc->destination = (struct prefix *)p;
f52d0a1a
DS
503 } else {
504 if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER)) {
9df414fe
QY
505 zlog_debug(
506 "warning: %s called for interface %s with peer flag set, but no peer address supplied",
507 __func__, ifp->name);
f52d0a1a
DS
508 UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER);
509 }
60c0687a
DS
510 }
511
d62a17ae 512 /* Label of this address. */
513 if (label)
514 ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label);
515
516 /* On Linux, we only get here when DAD is complete, therefore we can set
517 * ZEBRA_IFC_REAL.
518 *
519 * On BSD, there currently doesn't seem to be a way to check for
520 * completion of
521 * DAD, so we replicate the old behaviour and set ZEBRA_IFC_REAL,
522 * although DAD
523 * might still be running.
524 */
525 SET_FLAG(ifc->conf, ZEBRA_IFC_REAL);
526 connected_update(ifp, ifc);
718e3744 527}
528
d62a17ae 529void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address,
0f3af738 530 struct in6_addr *dest, uint16_t prefixlen)
718e3744 531{
60c0687a 532 struct prefix p, d;
d62a17ae 533 struct connected *ifc;
534
dc7cd304 535 memset(&p, 0, sizeof(struct prefix));
d62a17ae 536 p.family = AF_INET6;
dc7cd304 537 memcpy(&p.u.prefix6, address, sizeof(struct in6_addr));
d62a17ae 538 p.prefixlen = prefixlen;
539
2a855763
DS
540 /* Delete global ipv6 address from RA prefix list */
541 if (!IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6))
542 rtadv_delete_prefix(ifp->info, &p);
543
0f3af738 544 if (dest) {
60c0687a
DS
545 memset(&d, 0, sizeof(struct prefix));
546 d.family = AF_INET6;
0f3af738 547 IPV6_ADDR_COPY(&d.u.prefix6, dest);
60c0687a
DS
548 d.prefixlen = prefixlen;
549 ifc = connected_check_ptp(ifp, &p, &d);
550 } else
551 ifc = connected_check_ptp(ifp, &p, NULL);
d62a17ae 552
dc7cd304 553 connected_delete_helper(ifc, &p);
718e3744 554}
d44ca835 555
d62a17ae 556int connected_is_unnumbered(struct interface *ifp)
d44ca835 557{
d62a17ae 558 struct connected *connected;
559 struct listnode *node;
560
561 for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) {
562 if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL)
563 && connected->address->family == AF_INET)
564 return CHECK_FLAG(connected->flags,
565 ZEBRA_IFA_UNNUMBERED);
566 }
a79fdd65 567 return 0;
d44ca835 568}