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