dn_fib.c (19088B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * DECnet An implementation of the DECnet protocol suite for the LINUX 4 * operating system. DECnet is implemented using the BSD Socket 5 * interface as the means of communication with the user level. 6 * 7 * DECnet Routing Forwarding Information Base (Glue/Info List) 8 * 9 * Author: Steve Whitehouse <SteveW@ACM.org> 10 * 11 * 12 * Changes: 13 * Alexey Kuznetsov : SMP locking changes 14 * Steve Whitehouse : Rewrote it... Well to be more correct, I 15 * copied most of it from the ipv4 fib code. 16 * Steve Whitehouse : Updated it in style and fixed a few bugs 17 * which were fixed in the ipv4 code since 18 * this code was copied from it. 19 * 20 */ 21#include <linux/string.h> 22#include <linux/net.h> 23#include <linux/socket.h> 24#include <linux/slab.h> 25#include <linux/sockios.h> 26#include <linux/init.h> 27#include <linux/skbuff.h> 28#include <linux/netlink.h> 29#include <linux/rtnetlink.h> 30#include <linux/proc_fs.h> 31#include <linux/netdevice.h> 32#include <linux/timer.h> 33#include <linux/spinlock.h> 34#include <linux/atomic.h> 35#include <linux/uaccess.h> 36#include <net/neighbour.h> 37#include <net/dst.h> 38#include <net/flow.h> 39#include <net/fib_rules.h> 40#include <net/dn.h> 41#include <net/dn_route.h> 42#include <net/dn_fib.h> 43#include <net/dn_neigh.h> 44#include <net/dn_dev.h> 45#include <net/rtnh.h> 46 47#define RT_MIN_TABLE 1 48 49#define for_fib_info() { struct dn_fib_info *fi;\ 50 for(fi = dn_fib_info_list; fi; fi = fi->fib_next) 51#define endfor_fib_info() } 52 53#define for_nexthops(fi) { int nhsel; const struct dn_fib_nh *nh;\ 54 for(nhsel = 0, nh = (fi)->fib_nh; nhsel < (fi)->fib_nhs; nh++, nhsel++) 55 56#define change_nexthops(fi) { int nhsel; struct dn_fib_nh *nh;\ 57 for(nhsel = 0, nh = (struct dn_fib_nh *)((fi)->fib_nh); nhsel < (fi)->fib_nhs; nh++, nhsel++) 58 59#define endfor_nexthops(fi) } 60 61static DEFINE_SPINLOCK(dn_fib_multipath_lock); 62static struct dn_fib_info *dn_fib_info_list; 63static DEFINE_SPINLOCK(dn_fib_info_lock); 64 65static struct 66{ 67 int error; 68 u8 scope; 69} dn_fib_props[RTN_MAX+1] = { 70 [RTN_UNSPEC] = { .error = 0, .scope = RT_SCOPE_NOWHERE }, 71 [RTN_UNICAST] = { .error = 0, .scope = RT_SCOPE_UNIVERSE }, 72 [RTN_LOCAL] = { .error = 0, .scope = RT_SCOPE_HOST }, 73 [RTN_BROADCAST] = { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE }, 74 [RTN_ANYCAST] = { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE }, 75 [RTN_MULTICAST] = { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE }, 76 [RTN_BLACKHOLE] = { .error = -EINVAL, .scope = RT_SCOPE_UNIVERSE }, 77 [RTN_UNREACHABLE] = { .error = -EHOSTUNREACH, .scope = RT_SCOPE_UNIVERSE }, 78 [RTN_PROHIBIT] = { .error = -EACCES, .scope = RT_SCOPE_UNIVERSE }, 79 [RTN_THROW] = { .error = -EAGAIN, .scope = RT_SCOPE_UNIVERSE }, 80 [RTN_NAT] = { .error = 0, .scope = RT_SCOPE_NOWHERE }, 81 [RTN_XRESOLVE] = { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE }, 82}; 83 84static int dn_fib_sync_down(__le16 local, struct net_device *dev, int force); 85static int dn_fib_sync_up(struct net_device *dev); 86 87void dn_fib_free_info(struct dn_fib_info *fi) 88{ 89 if (fi->fib_dead == 0) { 90 printk(KERN_DEBUG "DECnet: BUG! Attempt to free alive dn_fib_info\n"); 91 return; 92 } 93 94 change_nexthops(fi) { 95 dev_put(nh->nh_dev); 96 nh->nh_dev = NULL; 97 } endfor_nexthops(fi); 98 kfree(fi); 99} 100 101void dn_fib_release_info(struct dn_fib_info *fi) 102{ 103 spin_lock(&dn_fib_info_lock); 104 if (fi && refcount_dec_and_test(&fi->fib_treeref)) { 105 if (fi->fib_next) 106 fi->fib_next->fib_prev = fi->fib_prev; 107 if (fi->fib_prev) 108 fi->fib_prev->fib_next = fi->fib_next; 109 if (fi == dn_fib_info_list) 110 dn_fib_info_list = fi->fib_next; 111 fi->fib_dead = 1; 112 dn_fib_info_put(fi); 113 } 114 spin_unlock(&dn_fib_info_lock); 115} 116 117static inline int dn_fib_nh_comp(const struct dn_fib_info *fi, const struct dn_fib_info *ofi) 118{ 119 const struct dn_fib_nh *onh = ofi->fib_nh; 120 121 for_nexthops(fi) { 122 if (nh->nh_oif != onh->nh_oif || 123 nh->nh_gw != onh->nh_gw || 124 nh->nh_scope != onh->nh_scope || 125 nh->nh_weight != onh->nh_weight || 126 ((nh->nh_flags^onh->nh_flags)&~RTNH_F_DEAD)) 127 return -1; 128 onh++; 129 } endfor_nexthops(fi); 130 return 0; 131} 132 133static inline struct dn_fib_info *dn_fib_find_info(const struct dn_fib_info *nfi) 134{ 135 for_fib_info() { 136 if (fi->fib_nhs != nfi->fib_nhs) 137 continue; 138 if (nfi->fib_protocol == fi->fib_protocol && 139 nfi->fib_prefsrc == fi->fib_prefsrc && 140 nfi->fib_priority == fi->fib_priority && 141 memcmp(nfi->fib_metrics, fi->fib_metrics, sizeof(fi->fib_metrics)) == 0 && 142 ((nfi->fib_flags^fi->fib_flags)&~RTNH_F_DEAD) == 0 && 143 (nfi->fib_nhs == 0 || dn_fib_nh_comp(fi, nfi) == 0)) 144 return fi; 145 } endfor_fib_info(); 146 return NULL; 147} 148 149static int dn_fib_count_nhs(const struct nlattr *attr) 150{ 151 struct rtnexthop *nhp = nla_data(attr); 152 int nhs = 0, nhlen = nla_len(attr); 153 154 while (rtnh_ok(nhp, nhlen)) { 155 nhs++; 156 nhp = rtnh_next(nhp, &nhlen); 157 } 158 159 /* leftover implies invalid nexthop configuration, discard it */ 160 return nhlen > 0 ? 0 : nhs; 161} 162 163static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct nlattr *attr, 164 const struct rtmsg *r) 165{ 166 struct rtnexthop *nhp = nla_data(attr); 167 int nhlen = nla_len(attr); 168 169 change_nexthops(fi) { 170 int attrlen; 171 172 if (!rtnh_ok(nhp, nhlen)) 173 return -EINVAL; 174 175 nh->nh_flags = (r->rtm_flags&~0xFF) | nhp->rtnh_flags; 176 nh->nh_oif = nhp->rtnh_ifindex; 177 nh->nh_weight = nhp->rtnh_hops + 1; 178 179 attrlen = rtnh_attrlen(nhp); 180 if (attrlen > 0) { 181 struct nlattr *gw_attr; 182 183 gw_attr = nla_find((struct nlattr *) (nhp + 1), attrlen, RTA_GATEWAY); 184 nh->nh_gw = gw_attr ? nla_get_le16(gw_attr) : 0; 185 } 186 187 nhp = rtnh_next(nhp, &nhlen); 188 } endfor_nexthops(fi); 189 190 return 0; 191} 192 193 194static int dn_fib_check_nh(const struct rtmsg *r, struct dn_fib_info *fi, struct dn_fib_nh *nh) 195{ 196 int err; 197 198 if (nh->nh_gw) { 199 struct flowidn fld; 200 struct dn_fib_res res; 201 202 if (nh->nh_flags&RTNH_F_ONLINK) { 203 struct net_device *dev; 204 205 if (r->rtm_scope >= RT_SCOPE_LINK) 206 return -EINVAL; 207 if (dnet_addr_type(nh->nh_gw) != RTN_UNICAST) 208 return -EINVAL; 209 if ((dev = __dev_get_by_index(&init_net, nh->nh_oif)) == NULL) 210 return -ENODEV; 211 if (!(dev->flags&IFF_UP)) 212 return -ENETDOWN; 213 nh->nh_dev = dev; 214 dev_hold(dev); 215 nh->nh_scope = RT_SCOPE_LINK; 216 return 0; 217 } 218 219 memset(&fld, 0, sizeof(fld)); 220 fld.daddr = nh->nh_gw; 221 fld.flowidn_oif = nh->nh_oif; 222 fld.flowidn_scope = r->rtm_scope + 1; 223 224 if (fld.flowidn_scope < RT_SCOPE_LINK) 225 fld.flowidn_scope = RT_SCOPE_LINK; 226 227 if ((err = dn_fib_lookup(&fld, &res)) != 0) 228 return err; 229 230 err = -EINVAL; 231 if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) 232 goto out; 233 nh->nh_scope = res.scope; 234 nh->nh_oif = DN_FIB_RES_OIF(res); 235 nh->nh_dev = DN_FIB_RES_DEV(res); 236 if (nh->nh_dev == NULL) 237 goto out; 238 dev_hold(nh->nh_dev); 239 err = -ENETDOWN; 240 if (!(nh->nh_dev->flags & IFF_UP)) 241 goto out; 242 err = 0; 243out: 244 dn_fib_res_put(&res); 245 return err; 246 } else { 247 struct net_device *dev; 248 249 if (nh->nh_flags&(RTNH_F_PERVASIVE|RTNH_F_ONLINK)) 250 return -EINVAL; 251 252 dev = __dev_get_by_index(&init_net, nh->nh_oif); 253 if (dev == NULL || dev->dn_ptr == NULL) 254 return -ENODEV; 255 if (!(dev->flags&IFF_UP)) 256 return -ENETDOWN; 257 nh->nh_dev = dev; 258 dev_hold(nh->nh_dev); 259 nh->nh_scope = RT_SCOPE_HOST; 260 } 261 262 return 0; 263} 264 265 266struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct nlattr *attrs[], 267 const struct nlmsghdr *nlh, int *errp) 268{ 269 int err; 270 struct dn_fib_info *fi = NULL; 271 struct dn_fib_info *ofi; 272 int nhs = 1; 273 274 if (r->rtm_type > RTN_MAX) 275 goto err_inval; 276 277 if (dn_fib_props[r->rtm_type].scope > r->rtm_scope) 278 goto err_inval; 279 280 if (attrs[RTA_MULTIPATH] && 281 (nhs = dn_fib_count_nhs(attrs[RTA_MULTIPATH])) == 0) 282 goto err_inval; 283 284 fi = kzalloc(struct_size(fi, fib_nh, nhs), GFP_KERNEL); 285 err = -ENOBUFS; 286 if (fi == NULL) 287 goto failure; 288 289 fi->fib_protocol = r->rtm_protocol; 290 fi->fib_nhs = nhs; 291 fi->fib_flags = r->rtm_flags; 292 293 if (attrs[RTA_PRIORITY]) 294 fi->fib_priority = nla_get_u32(attrs[RTA_PRIORITY]); 295 296 if (attrs[RTA_METRICS]) { 297 struct nlattr *attr; 298 int rem; 299 300 nla_for_each_nested(attr, attrs[RTA_METRICS], rem) { 301 int type = nla_type(attr); 302 303 if (type) { 304 if (type > RTAX_MAX || type == RTAX_CC_ALGO || 305 nla_len(attr) < 4) 306 goto err_inval; 307 308 fi->fib_metrics[type-1] = nla_get_u32(attr); 309 } 310 } 311 } 312 313 if (attrs[RTA_PREFSRC]) 314 fi->fib_prefsrc = nla_get_le16(attrs[RTA_PREFSRC]); 315 316 if (attrs[RTA_MULTIPATH]) { 317 if ((err = dn_fib_get_nhs(fi, attrs[RTA_MULTIPATH], r)) != 0) 318 goto failure; 319 320 if (attrs[RTA_OIF] && 321 fi->fib_nh->nh_oif != nla_get_u32(attrs[RTA_OIF])) 322 goto err_inval; 323 324 if (attrs[RTA_GATEWAY] && 325 fi->fib_nh->nh_gw != nla_get_le16(attrs[RTA_GATEWAY])) 326 goto err_inval; 327 } else { 328 struct dn_fib_nh *nh = fi->fib_nh; 329 330 if (attrs[RTA_OIF]) 331 nh->nh_oif = nla_get_u32(attrs[RTA_OIF]); 332 333 if (attrs[RTA_GATEWAY]) 334 nh->nh_gw = nla_get_le16(attrs[RTA_GATEWAY]); 335 336 nh->nh_flags = r->rtm_flags; 337 nh->nh_weight = 1; 338 } 339 340 if (r->rtm_type == RTN_NAT) { 341 if (!attrs[RTA_GATEWAY] || nhs != 1 || attrs[RTA_OIF]) 342 goto err_inval; 343 344 fi->fib_nh->nh_gw = nla_get_le16(attrs[RTA_GATEWAY]); 345 goto link_it; 346 } 347 348 if (dn_fib_props[r->rtm_type].error) { 349 if (attrs[RTA_GATEWAY] || attrs[RTA_OIF] || attrs[RTA_MULTIPATH]) 350 goto err_inval; 351 352 goto link_it; 353 } 354 355 if (r->rtm_scope > RT_SCOPE_HOST) 356 goto err_inval; 357 358 if (r->rtm_scope == RT_SCOPE_HOST) { 359 struct dn_fib_nh *nh = fi->fib_nh; 360 361 /* Local address is added */ 362 if (nhs != 1 || nh->nh_gw) 363 goto err_inval; 364 nh->nh_scope = RT_SCOPE_NOWHERE; 365 nh->nh_dev = dev_get_by_index(&init_net, fi->fib_nh->nh_oif); 366 err = -ENODEV; 367 if (nh->nh_dev == NULL) 368 goto failure; 369 } else { 370 change_nexthops(fi) { 371 if ((err = dn_fib_check_nh(r, fi, nh)) != 0) 372 goto failure; 373 } endfor_nexthops(fi) 374 } 375 376 if (fi->fib_prefsrc) { 377 if (r->rtm_type != RTN_LOCAL || !attrs[RTA_DST] || 378 fi->fib_prefsrc != nla_get_le16(attrs[RTA_DST])) 379 if (dnet_addr_type(fi->fib_prefsrc) != RTN_LOCAL) 380 goto err_inval; 381 } 382 383link_it: 384 if ((ofi = dn_fib_find_info(fi)) != NULL) { 385 fi->fib_dead = 1; 386 dn_fib_free_info(fi); 387 refcount_inc(&ofi->fib_treeref); 388 return ofi; 389 } 390 391 refcount_set(&fi->fib_treeref, 1); 392 refcount_set(&fi->fib_clntref, 1); 393 spin_lock(&dn_fib_info_lock); 394 fi->fib_next = dn_fib_info_list; 395 fi->fib_prev = NULL; 396 if (dn_fib_info_list) 397 dn_fib_info_list->fib_prev = fi; 398 dn_fib_info_list = fi; 399 spin_unlock(&dn_fib_info_lock); 400 return fi; 401 402err_inval: 403 err = -EINVAL; 404 405failure: 406 *errp = err; 407 if (fi) { 408 fi->fib_dead = 1; 409 dn_fib_free_info(fi); 410 } 411 412 return NULL; 413} 414 415int dn_fib_semantic_match(int type, struct dn_fib_info *fi, const struct flowidn *fld, struct dn_fib_res *res) 416{ 417 int err = dn_fib_props[type].error; 418 419 if (err == 0) { 420 if (fi->fib_flags & RTNH_F_DEAD) 421 return 1; 422 423 res->fi = fi; 424 425 switch (type) { 426 case RTN_NAT: 427 DN_FIB_RES_RESET(*res); 428 refcount_inc(&fi->fib_clntref); 429 return 0; 430 case RTN_UNICAST: 431 case RTN_LOCAL: 432 for_nexthops(fi) { 433 if (nh->nh_flags & RTNH_F_DEAD) 434 continue; 435 if (!fld->flowidn_oif || 436 fld->flowidn_oif == nh->nh_oif) 437 break; 438 } 439 if (nhsel < fi->fib_nhs) { 440 res->nh_sel = nhsel; 441 refcount_inc(&fi->fib_clntref); 442 return 0; 443 } 444 endfor_nexthops(fi); 445 res->fi = NULL; 446 return 1; 447 default: 448 net_err_ratelimited("DECnet: impossible routing event : dn_fib_semantic_match type=%d\n", 449 type); 450 res->fi = NULL; 451 return -EINVAL; 452 } 453 } 454 return err; 455} 456 457void dn_fib_select_multipath(const struct flowidn *fld, struct dn_fib_res *res) 458{ 459 struct dn_fib_info *fi = res->fi; 460 int w; 461 462 spin_lock_bh(&dn_fib_multipath_lock); 463 if (fi->fib_power <= 0) { 464 int power = 0; 465 change_nexthops(fi) { 466 if (!(nh->nh_flags&RTNH_F_DEAD)) { 467 power += nh->nh_weight; 468 nh->nh_power = nh->nh_weight; 469 } 470 } endfor_nexthops(fi); 471 fi->fib_power = power; 472 if (power < 0) { 473 spin_unlock_bh(&dn_fib_multipath_lock); 474 res->nh_sel = 0; 475 return; 476 } 477 } 478 479 w = jiffies % fi->fib_power; 480 481 change_nexthops(fi) { 482 if (!(nh->nh_flags&RTNH_F_DEAD) && nh->nh_power) { 483 if ((w -= nh->nh_power) <= 0) { 484 nh->nh_power--; 485 fi->fib_power--; 486 res->nh_sel = nhsel; 487 spin_unlock_bh(&dn_fib_multipath_lock); 488 return; 489 } 490 } 491 } endfor_nexthops(fi); 492 res->nh_sel = 0; 493 spin_unlock_bh(&dn_fib_multipath_lock); 494} 495 496static inline u32 rtm_get_table(struct nlattr *attrs[], u8 table) 497{ 498 if (attrs[RTA_TABLE]) 499 table = nla_get_u32(attrs[RTA_TABLE]); 500 501 return table; 502} 503 504static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, 505 struct netlink_ext_ack *extack) 506{ 507 struct net *net = sock_net(skb->sk); 508 struct dn_fib_table *tb; 509 struct rtmsg *r = nlmsg_data(nlh); 510 struct nlattr *attrs[RTA_MAX+1]; 511 int err; 512 513 if (!netlink_capable(skb, CAP_NET_ADMIN)) 514 return -EPERM; 515 516 if (!net_eq(net, &init_net)) 517 return -EINVAL; 518 519 err = nlmsg_parse_deprecated(nlh, sizeof(*r), attrs, RTA_MAX, 520 rtm_dn_policy, extack); 521 if (err < 0) 522 return err; 523 524 tb = dn_fib_get_table(rtm_get_table(attrs, r->rtm_table), 0); 525 if (!tb) 526 return -ESRCH; 527 528 return tb->delete(tb, r, attrs, nlh, &NETLINK_CB(skb)); 529} 530 531static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, 532 struct netlink_ext_ack *extack) 533{ 534 struct net *net = sock_net(skb->sk); 535 struct dn_fib_table *tb; 536 struct rtmsg *r = nlmsg_data(nlh); 537 struct nlattr *attrs[RTA_MAX+1]; 538 int err; 539 540 if (!netlink_capable(skb, CAP_NET_ADMIN)) 541 return -EPERM; 542 543 if (!net_eq(net, &init_net)) 544 return -EINVAL; 545 546 err = nlmsg_parse_deprecated(nlh, sizeof(*r), attrs, RTA_MAX, 547 rtm_dn_policy, extack); 548 if (err < 0) 549 return err; 550 551 tb = dn_fib_get_table(rtm_get_table(attrs, r->rtm_table), 1); 552 if (!tb) 553 return -ENOBUFS; 554 555 return tb->insert(tb, r, attrs, nlh, &NETLINK_CB(skb)); 556} 557 558static void fib_magic(int cmd, int type, __le16 dst, int dst_len, struct dn_ifaddr *ifa) 559{ 560 struct dn_fib_table *tb; 561 struct { 562 struct nlmsghdr nlh; 563 struct rtmsg rtm; 564 } req; 565 struct { 566 struct nlattr hdr; 567 __le16 dst; 568 } dst_attr = { 569 .dst = dst, 570 }; 571 struct { 572 struct nlattr hdr; 573 __le16 prefsrc; 574 } prefsrc_attr = { 575 .prefsrc = ifa->ifa_local, 576 }; 577 struct { 578 struct nlattr hdr; 579 u32 oif; 580 } oif_attr = { 581 .oif = ifa->ifa_dev->dev->ifindex, 582 }; 583 struct nlattr *attrs[RTA_MAX+1] = { 584 [RTA_DST] = (struct nlattr *) &dst_attr, 585 [RTA_PREFSRC] = (struct nlattr * ) &prefsrc_attr, 586 [RTA_OIF] = (struct nlattr *) &oif_attr, 587 }; 588 589 memset(&req.rtm, 0, sizeof(req.rtm)); 590 591 if (type == RTN_UNICAST) 592 tb = dn_fib_get_table(RT_MIN_TABLE, 1); 593 else 594 tb = dn_fib_get_table(RT_TABLE_LOCAL, 1); 595 596 if (tb == NULL) 597 return; 598 599 req.nlh.nlmsg_len = sizeof(req); 600 req.nlh.nlmsg_type = cmd; 601 req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_APPEND; 602 req.nlh.nlmsg_pid = 0; 603 req.nlh.nlmsg_seq = 0; 604 605 req.rtm.rtm_dst_len = dst_len; 606 req.rtm.rtm_table = tb->n; 607 req.rtm.rtm_protocol = RTPROT_KERNEL; 608 req.rtm.rtm_scope = (type != RTN_LOCAL ? RT_SCOPE_LINK : RT_SCOPE_HOST); 609 req.rtm.rtm_type = type; 610 611 if (cmd == RTM_NEWROUTE) 612 tb->insert(tb, &req.rtm, attrs, &req.nlh, NULL); 613 else 614 tb->delete(tb, &req.rtm, attrs, &req.nlh, NULL); 615} 616 617static void dn_fib_add_ifaddr(struct dn_ifaddr *ifa) 618{ 619 620 fib_magic(RTM_NEWROUTE, RTN_LOCAL, ifa->ifa_local, 16, ifa); 621 622#if 0 623 if (!(dev->flags&IFF_UP)) 624 return; 625 /* In the future, we will want to add default routes here */ 626 627#endif 628} 629 630static void dn_fib_del_ifaddr(struct dn_ifaddr *ifa) 631{ 632 int found_it = 0; 633 struct net_device *dev; 634 struct dn_dev *dn_db; 635 struct dn_ifaddr *ifa2; 636 637 ASSERT_RTNL(); 638 639 /* Scan device list */ 640 rcu_read_lock(); 641 for_each_netdev_rcu(&init_net, dev) { 642 dn_db = rcu_dereference(dev->dn_ptr); 643 if (dn_db == NULL) 644 continue; 645 for (ifa2 = rcu_dereference(dn_db->ifa_list); 646 ifa2 != NULL; 647 ifa2 = rcu_dereference(ifa2->ifa_next)) { 648 if (ifa2->ifa_local == ifa->ifa_local) { 649 found_it = 1; 650 break; 651 } 652 } 653 } 654 rcu_read_unlock(); 655 656 if (found_it == 0) { 657 fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 16, ifa); 658 659 if (dnet_addr_type(ifa->ifa_local) != RTN_LOCAL) { 660 if (dn_fib_sync_down(ifa->ifa_local, NULL, 0)) 661 dn_fib_flush(); 662 } 663 } 664} 665 666static void dn_fib_disable_addr(struct net_device *dev, int force) 667{ 668 if (dn_fib_sync_down(0, dev, force)) 669 dn_fib_flush(); 670 dn_rt_cache_flush(0); 671 neigh_ifdown(&dn_neigh_table, dev); 672} 673 674static int dn_fib_dnaddr_event(struct notifier_block *this, unsigned long event, void *ptr) 675{ 676 struct dn_ifaddr *ifa = (struct dn_ifaddr *)ptr; 677 678 switch (event) { 679 case NETDEV_UP: 680 dn_fib_add_ifaddr(ifa); 681 dn_fib_sync_up(ifa->ifa_dev->dev); 682 dn_rt_cache_flush(-1); 683 break; 684 case NETDEV_DOWN: 685 dn_fib_del_ifaddr(ifa); 686 if (ifa->ifa_dev && ifa->ifa_dev->ifa_list == NULL) { 687 dn_fib_disable_addr(ifa->ifa_dev->dev, 1); 688 } else { 689 dn_rt_cache_flush(-1); 690 } 691 break; 692 } 693 return NOTIFY_DONE; 694} 695 696static int dn_fib_sync_down(__le16 local, struct net_device *dev, int force) 697{ 698 int ret = 0; 699 int scope = RT_SCOPE_NOWHERE; 700 701 if (force) 702 scope = -1; 703 704 for_fib_info() { 705 /* 706 * This makes no sense for DECnet.... we will almost 707 * certainly have more than one local address the same 708 * over all our interfaces. It needs thinking about 709 * some more. 710 */ 711 if (local && fi->fib_prefsrc == local) { 712 fi->fib_flags |= RTNH_F_DEAD; 713 ret++; 714 } else if (dev && fi->fib_nhs) { 715 int dead = 0; 716 717 change_nexthops(fi) { 718 if (nh->nh_flags&RTNH_F_DEAD) 719 dead++; 720 else if (nh->nh_dev == dev && 721 nh->nh_scope != scope) { 722 spin_lock_bh(&dn_fib_multipath_lock); 723 nh->nh_flags |= RTNH_F_DEAD; 724 fi->fib_power -= nh->nh_power; 725 nh->nh_power = 0; 726 spin_unlock_bh(&dn_fib_multipath_lock); 727 dead++; 728 } 729 } endfor_nexthops(fi) 730 if (dead == fi->fib_nhs) { 731 fi->fib_flags |= RTNH_F_DEAD; 732 ret++; 733 } 734 } 735 } endfor_fib_info(); 736 return ret; 737} 738 739 740static int dn_fib_sync_up(struct net_device *dev) 741{ 742 int ret = 0; 743 744 if (!(dev->flags&IFF_UP)) 745 return 0; 746 747 for_fib_info() { 748 int alive = 0; 749 750 change_nexthops(fi) { 751 if (!(nh->nh_flags&RTNH_F_DEAD)) { 752 alive++; 753 continue; 754 } 755 if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP)) 756 continue; 757 if (nh->nh_dev != dev || dev->dn_ptr == NULL) 758 continue; 759 alive++; 760 spin_lock_bh(&dn_fib_multipath_lock); 761 nh->nh_power = 0; 762 nh->nh_flags &= ~RTNH_F_DEAD; 763 spin_unlock_bh(&dn_fib_multipath_lock); 764 } endfor_nexthops(fi); 765 766 if (alive > 0) { 767 fi->fib_flags &= ~RTNH_F_DEAD; 768 ret++; 769 } 770 } endfor_fib_info(); 771 return ret; 772} 773 774static struct notifier_block dn_fib_dnaddr_notifier = { 775 .notifier_call = dn_fib_dnaddr_event, 776}; 777 778void __exit dn_fib_cleanup(void) 779{ 780 dn_fib_table_cleanup(); 781 dn_fib_rules_cleanup(); 782 783 unregister_dnaddr_notifier(&dn_fib_dnaddr_notifier); 784} 785 786 787void __init dn_fib_init(void) 788{ 789 dn_fib_table_init(); 790 dn_fib_rules_init(); 791 792 register_dnaddr_notifier(&dn_fib_dnaddr_notifier); 793 794 rtnl_register_module(THIS_MODULE, PF_DECnet, RTM_NEWROUTE, 795 dn_fib_rtm_newroute, NULL, 0); 796 rtnl_register_module(THIS_MODULE, PF_DECnet, RTM_DELROUTE, 797 dn_fib_rtm_delroute, NULL, 0); 798}