diff options
Diffstat (limited to 'net/ipv4/fib_semantics.c')
-rw-r--r-- | net/ipv4/fib_semantics.c | 518 |
1 files changed, 187 insertions, 331 deletions
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 51738000f3dc..2ead09543f68 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c | |||
@@ -33,7 +33,6 @@ | |||
33 | #include <linux/if_arp.h> | 33 | #include <linux/if_arp.h> |
34 | #include <linux/proc_fs.h> | 34 | #include <linux/proc_fs.h> |
35 | #include <linux/skbuff.h> | 35 | #include <linux/skbuff.h> |
36 | #include <linux/netlink.h> | ||
37 | #include <linux/init.h> | 36 | #include <linux/init.h> |
38 | 37 | ||
39 | #include <net/arp.h> | 38 | #include <net/arp.h> |
@@ -44,12 +43,14 @@ | |||
44 | #include <net/sock.h> | 43 | #include <net/sock.h> |
45 | #include <net/ip_fib.h> | 44 | #include <net/ip_fib.h> |
46 | #include <net/ip_mp_alg.h> | 45 | #include <net/ip_mp_alg.h> |
46 | #include <net/netlink.h> | ||
47 | #include <net/nexthop.h> | ||
47 | 48 | ||
48 | #include "fib_lookup.h" | 49 | #include "fib_lookup.h" |
49 | 50 | ||
50 | #define FSprintk(a...) | 51 | #define FSprintk(a...) |
51 | 52 | ||
52 | static DEFINE_RWLOCK(fib_info_lock); | 53 | static DEFINE_SPINLOCK(fib_info_lock); |
53 | static struct hlist_head *fib_info_hash; | 54 | static struct hlist_head *fib_info_hash; |
54 | static struct hlist_head *fib_info_laddrhash; | 55 | static struct hlist_head *fib_info_laddrhash; |
55 | static unsigned int fib_hash_size; | 56 | static unsigned int fib_hash_size; |
@@ -159,7 +160,7 @@ void free_fib_info(struct fib_info *fi) | |||
159 | 160 | ||
160 | void fib_release_info(struct fib_info *fi) | 161 | void fib_release_info(struct fib_info *fi) |
161 | { | 162 | { |
162 | write_lock_bh(&fib_info_lock); | 163 | spin_lock_bh(&fib_info_lock); |
163 | if (fi && --fi->fib_treeref == 0) { | 164 | if (fi && --fi->fib_treeref == 0) { |
164 | hlist_del(&fi->fib_hash); | 165 | hlist_del(&fi->fib_hash); |
165 | if (fi->fib_prefsrc) | 166 | if (fi->fib_prefsrc) |
@@ -172,7 +173,7 @@ void fib_release_info(struct fib_info *fi) | |||
172 | fi->fib_dead = 1; | 173 | fi->fib_dead = 1; |
173 | fib_info_put(fi); | 174 | fib_info_put(fi); |
174 | } | 175 | } |
175 | write_unlock_bh(&fib_info_lock); | 176 | spin_unlock_bh(&fib_info_lock); |
176 | } | 177 | } |
177 | 178 | ||
178 | static __inline__ int nh_comp(const struct fib_info *fi, const struct fib_info *ofi) | 179 | static __inline__ int nh_comp(const struct fib_info *fi, const struct fib_info *ofi) |
@@ -254,7 +255,7 @@ int ip_fib_check_default(u32 gw, struct net_device *dev) | |||
254 | struct fib_nh *nh; | 255 | struct fib_nh *nh; |
255 | unsigned int hash; | 256 | unsigned int hash; |
256 | 257 | ||
257 | read_lock(&fib_info_lock); | 258 | spin_lock(&fib_info_lock); |
258 | 259 | ||
259 | hash = fib_devindex_hashfn(dev->ifindex); | 260 | hash = fib_devindex_hashfn(dev->ifindex); |
260 | head = &fib_info_devhash[hash]; | 261 | head = &fib_info_devhash[hash]; |
@@ -262,41 +263,41 @@ int ip_fib_check_default(u32 gw, struct net_device *dev) | |||
262 | if (nh->nh_dev == dev && | 263 | if (nh->nh_dev == dev && |
263 | nh->nh_gw == gw && | 264 | nh->nh_gw == gw && |
264 | !(nh->nh_flags&RTNH_F_DEAD)) { | 265 | !(nh->nh_flags&RTNH_F_DEAD)) { |
265 | read_unlock(&fib_info_lock); | 266 | spin_unlock(&fib_info_lock); |
266 | return 0; | 267 | return 0; |
267 | } | 268 | } |
268 | } | 269 | } |
269 | 270 | ||
270 | read_unlock(&fib_info_lock); | 271 | spin_unlock(&fib_info_lock); |
271 | 272 | ||
272 | return -1; | 273 | return -1; |
273 | } | 274 | } |
274 | 275 | ||
275 | void rtmsg_fib(int event, u32 key, struct fib_alias *fa, | 276 | void rtmsg_fib(int event, u32 key, struct fib_alias *fa, |
276 | int z, int tb_id, | 277 | int dst_len, u32 tb_id, struct nl_info *info) |
277 | struct nlmsghdr *n, struct netlink_skb_parms *req) | ||
278 | { | 278 | { |
279 | struct sk_buff *skb; | 279 | struct sk_buff *skb; |
280 | u32 pid = req ? req->pid : n->nlmsg_pid; | 280 | int payload = sizeof(struct rtmsg) + 256; |
281 | int size = NLMSG_SPACE(sizeof(struct rtmsg)+256); | 281 | u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; |
282 | 282 | int err = -ENOBUFS; | |
283 | skb = alloc_skb(size, GFP_KERNEL); | 283 | |
284 | if (!skb) | 284 | skb = nlmsg_new(nlmsg_total_size(payload), GFP_KERNEL); |
285 | return; | 285 | if (skb == NULL) |
286 | 286 | goto errout; | |
287 | if (fib_dump_info(skb, pid, n->nlmsg_seq, event, tb_id, | 287 | |
288 | fa->fa_type, fa->fa_scope, &key, z, | 288 | err = fib_dump_info(skb, info->pid, seq, event, tb_id, |
289 | fa->fa_tos, | 289 | fa->fa_type, fa->fa_scope, key, dst_len, |
290 | fa->fa_info, 0) < 0) { | 290 | fa->fa_tos, fa->fa_info, 0); |
291 | if (err < 0) { | ||
291 | kfree_skb(skb); | 292 | kfree_skb(skb); |
292 | return; | 293 | goto errout; |
293 | } | 294 | } |
294 | NETLINK_CB(skb).dst_group = RTNLGRP_IPV4_ROUTE; | 295 | |
295 | if (n->nlmsg_flags&NLM_F_ECHO) | 296 | err = rtnl_notify(skb, info->pid, RTNLGRP_IPV4_ROUTE, |
296 | atomic_inc(&skb->users); | 297 | info->nlh, GFP_KERNEL); |
297 | netlink_broadcast(rtnl, skb, pid, RTNLGRP_IPV4_ROUTE, GFP_KERNEL); | 298 | errout: |
298 | if (n->nlmsg_flags&NLM_F_ECHO) | 299 | if (err < 0) |
299 | netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT); | 300 | rtnl_set_sk_err(RTNLGRP_IPV4_ROUTE, err); |
300 | } | 301 | } |
301 | 302 | ||
302 | /* Return the first fib alias matching TOS with | 303 | /* Return the first fib alias matching TOS with |
@@ -342,102 +343,100 @@ int fib_detect_death(struct fib_info *fi, int order, | |||
342 | 343 | ||
343 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 344 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
344 | 345 | ||
345 | static u32 fib_get_attr32(struct rtattr *attr, int attrlen, int type) | 346 | static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining) |
346 | { | ||
347 | while (RTA_OK(attr,attrlen)) { | ||
348 | if (attr->rta_type == type) | ||
349 | return *(u32*)RTA_DATA(attr); | ||
350 | attr = RTA_NEXT(attr, attrlen); | ||
351 | } | ||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static int | ||
356 | fib_count_nexthops(struct rtattr *rta) | ||
357 | { | 347 | { |
358 | int nhs = 0; | 348 | int nhs = 0; |
359 | struct rtnexthop *nhp = RTA_DATA(rta); | ||
360 | int nhlen = RTA_PAYLOAD(rta); | ||
361 | 349 | ||
362 | while (nhlen >= (int)sizeof(struct rtnexthop)) { | 350 | while (rtnh_ok(rtnh, remaining)) { |
363 | if ((nhlen -= nhp->rtnh_len) < 0) | ||
364 | return 0; | ||
365 | nhs++; | 351 | nhs++; |
366 | nhp = RTNH_NEXT(nhp); | 352 | rtnh = rtnh_next(rtnh, &remaining); |
367 | }; | 353 | } |
368 | return nhs; | 354 | |
355 | /* leftover implies invalid nexthop configuration, discard it */ | ||
356 | return remaining > 0 ? 0 : nhs; | ||
369 | } | 357 | } |
370 | 358 | ||
371 | static int | 359 | static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh, |
372 | fib_get_nhs(struct fib_info *fi, const struct rtattr *rta, const struct rtmsg *r) | 360 | int remaining, struct fib_config *cfg) |
373 | { | 361 | { |
374 | struct rtnexthop *nhp = RTA_DATA(rta); | ||
375 | int nhlen = RTA_PAYLOAD(rta); | ||
376 | |||
377 | change_nexthops(fi) { | 362 | change_nexthops(fi) { |
378 | int attrlen = nhlen - sizeof(struct rtnexthop); | 363 | int attrlen; |
379 | if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0) | 364 | |
365 | if (!rtnh_ok(rtnh, remaining)) | ||
380 | return -EINVAL; | 366 | return -EINVAL; |
381 | nh->nh_flags = (r->rtm_flags&~0xFF) | nhp->rtnh_flags; | 367 | |
382 | nh->nh_oif = nhp->rtnh_ifindex; | 368 | nh->nh_flags = (cfg->fc_flags & ~0xFF) | rtnh->rtnh_flags; |
383 | nh->nh_weight = nhp->rtnh_hops + 1; | 369 | nh->nh_oif = rtnh->rtnh_ifindex; |
384 | if (attrlen) { | 370 | nh->nh_weight = rtnh->rtnh_hops + 1; |
385 | nh->nh_gw = fib_get_attr32(RTNH_DATA(nhp), attrlen, RTA_GATEWAY); | 371 | |
372 | attrlen = rtnh_attrlen(rtnh); | ||
373 | if (attrlen > 0) { | ||
374 | struct nlattr *nla, *attrs = rtnh_attrs(rtnh); | ||
375 | |||
376 | nla = nla_find(attrs, attrlen, RTA_GATEWAY); | ||
377 | nh->nh_gw = nla ? nla_get_u32(nla) : 0; | ||
386 | #ifdef CONFIG_NET_CLS_ROUTE | 378 | #ifdef CONFIG_NET_CLS_ROUTE |
387 | nh->nh_tclassid = fib_get_attr32(RTNH_DATA(nhp), attrlen, RTA_FLOW); | 379 | nla = nla_find(attrs, attrlen, RTA_FLOW); |
380 | nh->nh_tclassid = nla ? nla_get_u32(nla) : 0; | ||
388 | #endif | 381 | #endif |
389 | } | 382 | } |
390 | nhp = RTNH_NEXT(nhp); | 383 | |
384 | rtnh = rtnh_next(rtnh, &remaining); | ||
391 | } endfor_nexthops(fi); | 385 | } endfor_nexthops(fi); |
386 | |||
392 | return 0; | 387 | return 0; |
393 | } | 388 | } |
394 | 389 | ||
395 | #endif | 390 | #endif |
396 | 391 | ||
397 | int fib_nh_match(struct rtmsg *r, struct nlmsghdr *nlh, struct kern_rta *rta, | 392 | int fib_nh_match(struct fib_config *cfg, struct fib_info *fi) |
398 | struct fib_info *fi) | ||
399 | { | 393 | { |
400 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 394 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
401 | struct rtnexthop *nhp; | 395 | struct rtnexthop *rtnh; |
402 | int nhlen; | 396 | int remaining; |
403 | #endif | 397 | #endif |
404 | 398 | ||
405 | if (rta->rta_priority && | 399 | if (cfg->fc_priority && cfg->fc_priority != fi->fib_priority) |
406 | *rta->rta_priority != fi->fib_priority) | ||
407 | return 1; | 400 | return 1; |
408 | 401 | ||
409 | if (rta->rta_oif || rta->rta_gw) { | 402 | if (cfg->fc_oif || cfg->fc_gw) { |
410 | if ((!rta->rta_oif || *rta->rta_oif == fi->fib_nh->nh_oif) && | 403 | if ((!cfg->fc_oif || cfg->fc_oif == fi->fib_nh->nh_oif) && |
411 | (!rta->rta_gw || memcmp(rta->rta_gw, &fi->fib_nh->nh_gw, 4) == 0)) | 404 | (!cfg->fc_gw || cfg->fc_gw == fi->fib_nh->nh_gw)) |
412 | return 0; | 405 | return 0; |
413 | return 1; | 406 | return 1; |
414 | } | 407 | } |
415 | 408 | ||
416 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 409 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
417 | if (rta->rta_mp == NULL) | 410 | if (cfg->fc_mp == NULL) |
418 | return 0; | 411 | return 0; |
419 | nhp = RTA_DATA(rta->rta_mp); | 412 | |
420 | nhlen = RTA_PAYLOAD(rta->rta_mp); | 413 | rtnh = cfg->fc_mp; |
414 | remaining = cfg->fc_mp_len; | ||
421 | 415 | ||
422 | for_nexthops(fi) { | 416 | for_nexthops(fi) { |
423 | int attrlen = nhlen - sizeof(struct rtnexthop); | 417 | int attrlen; |
424 | u32 gw; | ||
425 | 418 | ||
426 | if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0) | 419 | if (!rtnh_ok(rtnh, remaining)) |
427 | return -EINVAL; | 420 | return -EINVAL; |
428 | if (nhp->rtnh_ifindex && nhp->rtnh_ifindex != nh->nh_oif) | 421 | |
422 | if (rtnh->rtnh_ifindex && rtnh->rtnh_ifindex != nh->nh_oif) | ||
429 | return 1; | 423 | return 1; |
430 | if (attrlen) { | 424 | |
431 | gw = fib_get_attr32(RTNH_DATA(nhp), attrlen, RTA_GATEWAY); | 425 | attrlen = rtnh_attrlen(rtnh); |
432 | if (gw && gw != nh->nh_gw) | 426 | if (attrlen < 0) { |
427 | struct nlattr *nla, *attrs = rtnh_attrs(rtnh); | ||
428 | |||
429 | nla = nla_find(attrs, attrlen, RTA_GATEWAY); | ||
430 | if (nla && nla_get_u32(nla) != nh->nh_gw) | ||
433 | return 1; | 431 | return 1; |
434 | #ifdef CONFIG_NET_CLS_ROUTE | 432 | #ifdef CONFIG_NET_CLS_ROUTE |
435 | gw = fib_get_attr32(RTNH_DATA(nhp), attrlen, RTA_FLOW); | 433 | nla = nla_find(attrs, attrlen, RTA_FLOW); |
436 | if (gw && gw != nh->nh_tclassid) | 434 | if (nla && nla_get_u32(nla) != nh->nh_tclassid) |
437 | return 1; | 435 | return 1; |
438 | #endif | 436 | #endif |
439 | } | 437 | } |
440 | nhp = RTNH_NEXT(nhp); | 438 | |
439 | rtnh = rtnh_next(rtnh, &remaining); | ||
441 | } endfor_nexthops(fi); | 440 | } endfor_nexthops(fi); |
442 | #endif | 441 | #endif |
443 | return 0; | 442 | return 0; |
@@ -488,7 +487,8 @@ int fib_nh_match(struct rtmsg *r, struct nlmsghdr *nlh, struct kern_rta *rta, | |||
488 | |-> {local prefix} (terminal node) | 487 | |-> {local prefix} (terminal node) |
489 | */ | 488 | */ |
490 | 489 | ||
491 | static int fib_check_nh(const struct rtmsg *r, struct fib_info *fi, struct fib_nh *nh) | 490 | static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi, |
491 | struct fib_nh *nh) | ||
492 | { | 492 | { |
493 | int err; | 493 | int err; |
494 | 494 | ||
@@ -502,7 +502,7 @@ static int fib_check_nh(const struct rtmsg *r, struct fib_info *fi, struct fib_n | |||
502 | if (nh->nh_flags&RTNH_F_ONLINK) { | 502 | if (nh->nh_flags&RTNH_F_ONLINK) { |
503 | struct net_device *dev; | 503 | struct net_device *dev; |
504 | 504 | ||
505 | if (r->rtm_scope >= RT_SCOPE_LINK) | 505 | if (cfg->fc_scope >= RT_SCOPE_LINK) |
506 | return -EINVAL; | 506 | return -EINVAL; |
507 | if (inet_addr_type(nh->nh_gw) != RTN_UNICAST) | 507 | if (inet_addr_type(nh->nh_gw) != RTN_UNICAST) |
508 | return -EINVAL; | 508 | return -EINVAL; |
@@ -516,10 +516,15 @@ static int fib_check_nh(const struct rtmsg *r, struct fib_info *fi, struct fib_n | |||
516 | return 0; | 516 | return 0; |
517 | } | 517 | } |
518 | { | 518 | { |
519 | struct flowi fl = { .nl_u = { .ip4_u = | 519 | struct flowi fl = { |
520 | { .daddr = nh->nh_gw, | 520 | .nl_u = { |
521 | .scope = r->rtm_scope + 1 } }, | 521 | .ip4_u = { |
522 | .oif = nh->nh_oif }; | 522 | .daddr = nh->nh_gw, |
523 | .scope = cfg->fc_scope + 1, | ||
524 | }, | ||
525 | }, | ||
526 | .oif = nh->nh_oif, | ||
527 | }; | ||
523 | 528 | ||
524 | /* It is not necessary, but requires a bit of thinking */ | 529 | /* It is not necessary, but requires a bit of thinking */ |
525 | if (fl.fl4_scope < RT_SCOPE_LINK) | 530 | if (fl.fl4_scope < RT_SCOPE_LINK) |
@@ -598,7 +603,7 @@ static void fib_hash_move(struct hlist_head *new_info_hash, | |||
598 | unsigned int old_size = fib_hash_size; | 603 | unsigned int old_size = fib_hash_size; |
599 | unsigned int i, bytes; | 604 | unsigned int i, bytes; |
600 | 605 | ||
601 | write_lock_bh(&fib_info_lock); | 606 | spin_lock_bh(&fib_info_lock); |
602 | old_info_hash = fib_info_hash; | 607 | old_info_hash = fib_info_hash; |
603 | old_laddrhash = fib_info_laddrhash; | 608 | old_laddrhash = fib_info_laddrhash; |
604 | fib_hash_size = new_size; | 609 | fib_hash_size = new_size; |
@@ -639,46 +644,35 @@ static void fib_hash_move(struct hlist_head *new_info_hash, | |||
639 | } | 644 | } |
640 | fib_info_laddrhash = new_laddrhash; | 645 | fib_info_laddrhash = new_laddrhash; |
641 | 646 | ||
642 | write_unlock_bh(&fib_info_lock); | 647 | spin_unlock_bh(&fib_info_lock); |
643 | 648 | ||
644 | bytes = old_size * sizeof(struct hlist_head *); | 649 | bytes = old_size * sizeof(struct hlist_head *); |
645 | fib_hash_free(old_info_hash, bytes); | 650 | fib_hash_free(old_info_hash, bytes); |
646 | fib_hash_free(old_laddrhash, bytes); | 651 | fib_hash_free(old_laddrhash, bytes); |
647 | } | 652 | } |
648 | 653 | ||
649 | struct fib_info * | 654 | struct fib_info *fib_create_info(struct fib_config *cfg) |
650 | fib_create_info(const struct rtmsg *r, struct kern_rta *rta, | ||
651 | const struct nlmsghdr *nlh, int *errp) | ||
652 | { | 655 | { |
653 | int err; | 656 | int err; |
654 | struct fib_info *fi = NULL; | 657 | struct fib_info *fi = NULL; |
655 | struct fib_info *ofi; | 658 | struct fib_info *ofi; |
656 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | ||
657 | int nhs = 1; | 659 | int nhs = 1; |
658 | #else | ||
659 | const int nhs = 1; | ||
660 | #endif | ||
661 | #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED | ||
662 | u32 mp_alg = IP_MP_ALG_NONE; | ||
663 | #endif | ||
664 | 660 | ||
665 | /* Fast check to catch the most weird cases */ | 661 | /* Fast check to catch the most weird cases */ |
666 | if (fib_props[r->rtm_type].scope > r->rtm_scope) | 662 | if (fib_props[cfg->fc_type].scope > cfg->fc_scope) |
667 | goto err_inval; | 663 | goto err_inval; |
668 | 664 | ||
669 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 665 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
670 | if (rta->rta_mp) { | 666 | if (cfg->fc_mp) { |
671 | nhs = fib_count_nexthops(rta->rta_mp); | 667 | nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len); |
672 | if (nhs == 0) | 668 | if (nhs == 0) |
673 | goto err_inval; | 669 | goto err_inval; |
674 | } | 670 | } |
675 | #endif | 671 | #endif |
676 | #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED | 672 | #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED |
677 | if (rta->rta_mp_alg) { | 673 | if (cfg->fc_mp_alg) { |
678 | mp_alg = *rta->rta_mp_alg; | 674 | if (cfg->fc_mp_alg < IP_MP_ALG_NONE || |
679 | 675 | cfg->fc_mp_alg > IP_MP_ALG_MAX) | |
680 | if (mp_alg < IP_MP_ALG_NONE || | ||
681 | mp_alg > IP_MP_ALG_MAX) | ||
682 | goto err_inval; | 676 | goto err_inval; |
683 | } | 677 | } |
684 | #endif | 678 | #endif |
@@ -714,43 +708,42 @@ fib_create_info(const struct rtmsg *r, struct kern_rta *rta, | |||
714 | goto failure; | 708 | goto failure; |
715 | fib_info_cnt++; | 709 | fib_info_cnt++; |
716 | 710 | ||
717 | fi->fib_protocol = r->rtm_protocol; | 711 | fi->fib_protocol = cfg->fc_protocol; |
712 | fi->fib_flags = cfg->fc_flags; | ||
713 | fi->fib_priority = cfg->fc_priority; | ||
714 | fi->fib_prefsrc = cfg->fc_prefsrc; | ||
718 | 715 | ||
719 | fi->fib_nhs = nhs; | 716 | fi->fib_nhs = nhs; |
720 | change_nexthops(fi) { | 717 | change_nexthops(fi) { |
721 | nh->nh_parent = fi; | 718 | nh->nh_parent = fi; |
722 | } endfor_nexthops(fi) | 719 | } endfor_nexthops(fi) |
723 | 720 | ||
724 | fi->fib_flags = r->rtm_flags; | 721 | if (cfg->fc_mx) { |
725 | if (rta->rta_priority) | 722 | struct nlattr *nla; |
726 | fi->fib_priority = *rta->rta_priority; | 723 | int remaining; |
727 | if (rta->rta_mx) { | 724 | |
728 | int attrlen = RTA_PAYLOAD(rta->rta_mx); | 725 | nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { |
729 | struct rtattr *attr = RTA_DATA(rta->rta_mx); | 726 | int type = nla->nla_type; |
730 | 727 | ||
731 | while (RTA_OK(attr, attrlen)) { | 728 | if (type) { |
732 | unsigned flavor = attr->rta_type; | 729 | if (type > RTAX_MAX) |
733 | if (flavor) { | ||
734 | if (flavor > RTAX_MAX) | ||
735 | goto err_inval; | 730 | goto err_inval; |
736 | fi->fib_metrics[flavor-1] = *(unsigned*)RTA_DATA(attr); | 731 | fi->fib_metrics[type - 1] = nla_get_u32(nla); |
737 | } | 732 | } |
738 | attr = RTA_NEXT(attr, attrlen); | ||
739 | } | 733 | } |
740 | } | 734 | } |
741 | if (rta->rta_prefsrc) | ||
742 | memcpy(&fi->fib_prefsrc, rta->rta_prefsrc, 4); | ||
743 | 735 | ||
744 | if (rta->rta_mp) { | 736 | if (cfg->fc_mp) { |
745 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 737 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
746 | if ((err = fib_get_nhs(fi, rta->rta_mp, r)) != 0) | 738 | err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg); |
739 | if (err != 0) | ||
747 | goto failure; | 740 | goto failure; |
748 | if (rta->rta_oif && fi->fib_nh->nh_oif != *rta->rta_oif) | 741 | if (cfg->fc_oif && fi->fib_nh->nh_oif != cfg->fc_oif) |
749 | goto err_inval; | 742 | goto err_inval; |
750 | if (rta->rta_gw && memcmp(&fi->fib_nh->nh_gw, rta->rta_gw, 4)) | 743 | if (cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw) |
751 | goto err_inval; | 744 | goto err_inval; |
752 | #ifdef CONFIG_NET_CLS_ROUTE | 745 | #ifdef CONFIG_NET_CLS_ROUTE |
753 | if (rta->rta_flow && memcmp(&fi->fib_nh->nh_tclassid, rta->rta_flow, 4)) | 746 | if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow) |
754 | goto err_inval; | 747 | goto err_inval; |
755 | #endif | 748 | #endif |
756 | #else | 749 | #else |
@@ -758,34 +751,32 @@ fib_create_info(const struct rtmsg *r, struct kern_rta *rta, | |||
758 | #endif | 751 | #endif |
759 | } else { | 752 | } else { |
760 | struct fib_nh *nh = fi->fib_nh; | 753 | struct fib_nh *nh = fi->fib_nh; |
761 | if (rta->rta_oif) | 754 | |
762 | nh->nh_oif = *rta->rta_oif; | 755 | nh->nh_oif = cfg->fc_oif; |
763 | if (rta->rta_gw) | 756 | nh->nh_gw = cfg->fc_gw; |
764 | memcpy(&nh->nh_gw, rta->rta_gw, 4); | 757 | nh->nh_flags = cfg->fc_flags; |
765 | #ifdef CONFIG_NET_CLS_ROUTE | 758 | #ifdef CONFIG_NET_CLS_ROUTE |
766 | if (rta->rta_flow) | 759 | nh->nh_tclassid = cfg->fc_flow; |
767 | memcpy(&nh->nh_tclassid, rta->rta_flow, 4); | ||
768 | #endif | 760 | #endif |
769 | nh->nh_flags = r->rtm_flags; | ||
770 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 761 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
771 | nh->nh_weight = 1; | 762 | nh->nh_weight = 1; |
772 | #endif | 763 | #endif |
773 | } | 764 | } |
774 | 765 | ||
775 | #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED | 766 | #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED |
776 | fi->fib_mp_alg = mp_alg; | 767 | fi->fib_mp_alg = cfg->fc_mp_alg; |
777 | #endif | 768 | #endif |
778 | 769 | ||
779 | if (fib_props[r->rtm_type].error) { | 770 | if (fib_props[cfg->fc_type].error) { |
780 | if (rta->rta_gw || rta->rta_oif || rta->rta_mp) | 771 | if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) |
781 | goto err_inval; | 772 | goto err_inval; |
782 | goto link_it; | 773 | goto link_it; |
783 | } | 774 | } |
784 | 775 | ||
785 | if (r->rtm_scope > RT_SCOPE_HOST) | 776 | if (cfg->fc_scope > RT_SCOPE_HOST) |
786 | goto err_inval; | 777 | goto err_inval; |
787 | 778 | ||
788 | if (r->rtm_scope == RT_SCOPE_HOST) { | 779 | if (cfg->fc_scope == RT_SCOPE_HOST) { |
789 | struct fib_nh *nh = fi->fib_nh; | 780 | struct fib_nh *nh = fi->fib_nh; |
790 | 781 | ||
791 | /* Local address is added. */ | 782 | /* Local address is added. */ |
@@ -798,14 +789,14 @@ fib_create_info(const struct rtmsg *r, struct kern_rta *rta, | |||
798 | goto failure; | 789 | goto failure; |
799 | } else { | 790 | } else { |
800 | change_nexthops(fi) { | 791 | change_nexthops(fi) { |
801 | if ((err = fib_check_nh(r, fi, nh)) != 0) | 792 | if ((err = fib_check_nh(cfg, fi, nh)) != 0) |
802 | goto failure; | 793 | goto failure; |
803 | } endfor_nexthops(fi) | 794 | } endfor_nexthops(fi) |
804 | } | 795 | } |
805 | 796 | ||
806 | if (fi->fib_prefsrc) { | 797 | if (fi->fib_prefsrc) { |
807 | if (r->rtm_type != RTN_LOCAL || rta->rta_dst == NULL || | 798 | if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || |
808 | memcmp(&fi->fib_prefsrc, rta->rta_dst, 4)) | 799 | fi->fib_prefsrc != cfg->fc_dst) |
809 | if (inet_addr_type(fi->fib_prefsrc) != RTN_LOCAL) | 800 | if (inet_addr_type(fi->fib_prefsrc) != RTN_LOCAL) |
810 | goto err_inval; | 801 | goto err_inval; |
811 | } | 802 | } |
@@ -820,7 +811,7 @@ link_it: | |||
820 | 811 | ||
821 | fi->fib_treeref++; | 812 | fi->fib_treeref++; |
822 | atomic_inc(&fi->fib_clntref); | 813 | atomic_inc(&fi->fib_clntref); |
823 | write_lock_bh(&fib_info_lock); | 814 | spin_lock_bh(&fib_info_lock); |
824 | hlist_add_head(&fi->fib_hash, | 815 | hlist_add_head(&fi->fib_hash, |
825 | &fib_info_hash[fib_info_hashfn(fi)]); | 816 | &fib_info_hash[fib_info_hashfn(fi)]); |
826 | if (fi->fib_prefsrc) { | 817 | if (fi->fib_prefsrc) { |
@@ -839,19 +830,19 @@ link_it: | |||
839 | head = &fib_info_devhash[hash]; | 830 | head = &fib_info_devhash[hash]; |
840 | hlist_add_head(&nh->nh_hash, head); | 831 | hlist_add_head(&nh->nh_hash, head); |
841 | } endfor_nexthops(fi) | 832 | } endfor_nexthops(fi) |
842 | write_unlock_bh(&fib_info_lock); | 833 | spin_unlock_bh(&fib_info_lock); |
843 | return fi; | 834 | return fi; |
844 | 835 | ||
845 | err_inval: | 836 | err_inval: |
846 | err = -EINVAL; | 837 | err = -EINVAL; |
847 | 838 | ||
848 | failure: | 839 | failure: |
849 | *errp = err; | ||
850 | if (fi) { | 840 | if (fi) { |
851 | fi->fib_dead = 1; | 841 | fi->fib_dead = 1; |
852 | free_fib_info(fi); | 842 | free_fib_info(fi); |
853 | } | 843 | } |
854 | return NULL; | 844 | |
845 | return ERR_PTR(err); | ||
855 | } | 846 | } |
856 | 847 | ||
857 | /* Note! fib_semantic_match intentionally uses RCU list functions. */ | 848 | /* Note! fib_semantic_match intentionally uses RCU list functions. */ |
@@ -937,224 +928,89 @@ u32 __fib_res_prefsrc(struct fib_result *res) | |||
937 | return inet_select_addr(FIB_RES_DEV(*res), FIB_RES_GW(*res), res->scope); | 928 | return inet_select_addr(FIB_RES_DEV(*res), FIB_RES_GW(*res), res->scope); |
938 | } | 929 | } |
939 | 930 | ||
940 | int | 931 | int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, |
941 | fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, | 932 | u32 tb_id, u8 type, u8 scope, u32 dst, int dst_len, u8 tos, |
942 | u8 tb_id, u8 type, u8 scope, void *dst, int dst_len, u8 tos, | 933 | struct fib_info *fi, unsigned int flags) |
943 | struct fib_info *fi, unsigned int flags) | ||
944 | { | 934 | { |
935 | struct nlmsghdr *nlh; | ||
945 | struct rtmsg *rtm; | 936 | struct rtmsg *rtm; |
946 | struct nlmsghdr *nlh; | ||
947 | unsigned char *b = skb->tail; | ||
948 | 937 | ||
949 | nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*rtm), flags); | 938 | nlh = nlmsg_put(skb, pid, seq, event, sizeof(*rtm), flags); |
950 | rtm = NLMSG_DATA(nlh); | 939 | if (nlh == NULL) |
940 | return -ENOBUFS; | ||
941 | |||
942 | rtm = nlmsg_data(nlh); | ||
951 | rtm->rtm_family = AF_INET; | 943 | rtm->rtm_family = AF_INET; |
952 | rtm->rtm_dst_len = dst_len; | 944 | rtm->rtm_dst_len = dst_len; |
953 | rtm->rtm_src_len = 0; | 945 | rtm->rtm_src_len = 0; |
954 | rtm->rtm_tos = tos; | 946 | rtm->rtm_tos = tos; |
955 | rtm->rtm_table = tb_id; | 947 | rtm->rtm_table = tb_id; |
948 | NLA_PUT_U32(skb, RTA_TABLE, tb_id); | ||
956 | rtm->rtm_type = type; | 949 | rtm->rtm_type = type; |
957 | rtm->rtm_flags = fi->fib_flags; | 950 | rtm->rtm_flags = fi->fib_flags; |
958 | rtm->rtm_scope = scope; | 951 | rtm->rtm_scope = scope; |
959 | if (rtm->rtm_dst_len) | ||
960 | RTA_PUT(skb, RTA_DST, 4, dst); | ||
961 | rtm->rtm_protocol = fi->fib_protocol; | 952 | rtm->rtm_protocol = fi->fib_protocol; |
953 | |||
954 | if (rtm->rtm_dst_len) | ||
955 | NLA_PUT_U32(skb, RTA_DST, dst); | ||
956 | |||
962 | if (fi->fib_priority) | 957 | if (fi->fib_priority) |
963 | RTA_PUT(skb, RTA_PRIORITY, 4, &fi->fib_priority); | 958 | NLA_PUT_U32(skb, RTA_PRIORITY, fi->fib_priority); |
959 | |||
964 | if (rtnetlink_put_metrics(skb, fi->fib_metrics) < 0) | 960 | if (rtnetlink_put_metrics(skb, fi->fib_metrics) < 0) |
965 | goto rtattr_failure; | 961 | goto nla_put_failure; |
962 | |||
966 | if (fi->fib_prefsrc) | 963 | if (fi->fib_prefsrc) |
967 | RTA_PUT(skb, RTA_PREFSRC, 4, &fi->fib_prefsrc); | 964 | NLA_PUT_U32(skb, RTA_PREFSRC, fi->fib_prefsrc); |
965 | |||
968 | if (fi->fib_nhs == 1) { | 966 | if (fi->fib_nhs == 1) { |
969 | if (fi->fib_nh->nh_gw) | 967 | if (fi->fib_nh->nh_gw) |
970 | RTA_PUT(skb, RTA_GATEWAY, 4, &fi->fib_nh->nh_gw); | 968 | NLA_PUT_U32(skb, RTA_GATEWAY, fi->fib_nh->nh_gw); |
969 | |||
971 | if (fi->fib_nh->nh_oif) | 970 | if (fi->fib_nh->nh_oif) |
972 | RTA_PUT(skb, RTA_OIF, sizeof(int), &fi->fib_nh->nh_oif); | 971 | NLA_PUT_U32(skb, RTA_OIF, fi->fib_nh->nh_oif); |
973 | #ifdef CONFIG_NET_CLS_ROUTE | 972 | #ifdef CONFIG_NET_CLS_ROUTE |
974 | if (fi->fib_nh[0].nh_tclassid) | 973 | if (fi->fib_nh[0].nh_tclassid) |
975 | RTA_PUT(skb, RTA_FLOW, 4, &fi->fib_nh[0].nh_tclassid); | 974 | NLA_PUT_U32(skb, RTA_FLOW, fi->fib_nh[0].nh_tclassid); |
976 | #endif | 975 | #endif |
977 | } | 976 | } |
978 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 977 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
979 | if (fi->fib_nhs > 1) { | 978 | if (fi->fib_nhs > 1) { |
980 | struct rtnexthop *nhp; | 979 | struct rtnexthop *rtnh; |
981 | struct rtattr *mp_head; | 980 | struct nlattr *mp; |
982 | if (skb_tailroom(skb) <= RTA_SPACE(0)) | 981 | |
983 | goto rtattr_failure; | 982 | mp = nla_nest_start(skb, RTA_MULTIPATH); |
984 | mp_head = (struct rtattr*)skb_put(skb, RTA_SPACE(0)); | 983 | if (mp == NULL) |
984 | goto nla_put_failure; | ||
985 | 985 | ||
986 | for_nexthops(fi) { | 986 | for_nexthops(fi) { |
987 | if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4)) | 987 | rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); |
988 | goto rtattr_failure; | 988 | if (rtnh == NULL) |
989 | nhp = (struct rtnexthop*)skb_put(skb, RTA_ALIGN(sizeof(*nhp))); | 989 | goto nla_put_failure; |
990 | nhp->rtnh_flags = nh->nh_flags & 0xFF; | 990 | |
991 | nhp->rtnh_hops = nh->nh_weight-1; | 991 | rtnh->rtnh_flags = nh->nh_flags & 0xFF; |
992 | nhp->rtnh_ifindex = nh->nh_oif; | 992 | rtnh->rtnh_hops = nh->nh_weight - 1; |
993 | rtnh->rtnh_ifindex = nh->nh_oif; | ||
994 | |||
993 | if (nh->nh_gw) | 995 | if (nh->nh_gw) |
994 | RTA_PUT(skb, RTA_GATEWAY, 4, &nh->nh_gw); | 996 | NLA_PUT_U32(skb, RTA_GATEWAY, nh->nh_gw); |
995 | #ifdef CONFIG_NET_CLS_ROUTE | 997 | #ifdef CONFIG_NET_CLS_ROUTE |
996 | if (nh->nh_tclassid) | 998 | if (nh->nh_tclassid) |
997 | RTA_PUT(skb, RTA_FLOW, 4, &nh->nh_tclassid); | 999 | NLA_PUT_U32(skb, RTA_FLOW, nh->nh_tclassid); |
998 | #endif | 1000 | #endif |
999 | nhp->rtnh_len = skb->tail - (unsigned char*)nhp; | 1001 | /* length of rtnetlink header + attributes */ |
1002 | rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *) rtnh; | ||
1000 | } endfor_nexthops(fi); | 1003 | } endfor_nexthops(fi); |
1001 | mp_head->rta_type = RTA_MULTIPATH; | ||
1002 | mp_head->rta_len = skb->tail - (u8*)mp_head; | ||
1003 | } | ||
1004 | #endif | ||
1005 | nlh->nlmsg_len = skb->tail - b; | ||
1006 | return skb->len; | ||
1007 | |||
1008 | nlmsg_failure: | ||
1009 | rtattr_failure: | ||
1010 | skb_trim(skb, b - skb->data); | ||
1011 | return -1; | ||
1012 | } | ||
1013 | |||
1014 | #ifndef CONFIG_IP_NOSIOCRT | ||
1015 | |||
1016 | int | ||
1017 | fib_convert_rtentry(int cmd, struct nlmsghdr *nl, struct rtmsg *rtm, | ||
1018 | struct kern_rta *rta, struct rtentry *r) | ||
1019 | { | ||
1020 | int plen; | ||
1021 | u32 *ptr; | ||
1022 | |||
1023 | memset(rtm, 0, sizeof(*rtm)); | ||
1024 | memset(rta, 0, sizeof(*rta)); | ||
1025 | |||
1026 | if (r->rt_dst.sa_family != AF_INET) | ||
1027 | return -EAFNOSUPPORT; | ||
1028 | |||
1029 | /* Check mask for validity: | ||
1030 | a) it must be contiguous. | ||
1031 | b) destination must have all host bits clear. | ||
1032 | c) if application forgot to set correct family (AF_INET), | ||
1033 | reject request unless it is absolutely clear i.e. | ||
1034 | both family and mask are zero. | ||
1035 | */ | ||
1036 | plen = 32; | ||
1037 | ptr = &((struct sockaddr_in*)&r->rt_dst)->sin_addr.s_addr; | ||
1038 | if (!(r->rt_flags&RTF_HOST)) { | ||
1039 | u32 mask = ((struct sockaddr_in*)&r->rt_genmask)->sin_addr.s_addr; | ||
1040 | if (r->rt_genmask.sa_family != AF_INET) { | ||
1041 | if (mask || r->rt_genmask.sa_family) | ||
1042 | return -EAFNOSUPPORT; | ||
1043 | } | ||
1044 | if (bad_mask(mask, *ptr)) | ||
1045 | return -EINVAL; | ||
1046 | plen = inet_mask_len(mask); | ||
1047 | } | ||
1048 | |||
1049 | nl->nlmsg_flags = NLM_F_REQUEST; | ||
1050 | nl->nlmsg_pid = 0; | ||
1051 | nl->nlmsg_seq = 0; | ||
1052 | nl->nlmsg_len = NLMSG_LENGTH(sizeof(*rtm)); | ||
1053 | if (cmd == SIOCDELRT) { | ||
1054 | nl->nlmsg_type = RTM_DELROUTE; | ||
1055 | nl->nlmsg_flags = 0; | ||
1056 | } else { | ||
1057 | nl->nlmsg_type = RTM_NEWROUTE; | ||
1058 | nl->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE; | ||
1059 | rtm->rtm_protocol = RTPROT_BOOT; | ||
1060 | } | ||
1061 | |||
1062 | rtm->rtm_dst_len = plen; | ||
1063 | rta->rta_dst = ptr; | ||
1064 | |||
1065 | if (r->rt_metric) { | ||
1066 | *(u32*)&r->rt_pad3 = r->rt_metric - 1; | ||
1067 | rta->rta_priority = (u32*)&r->rt_pad3; | ||
1068 | } | ||
1069 | if (r->rt_flags&RTF_REJECT) { | ||
1070 | rtm->rtm_scope = RT_SCOPE_HOST; | ||
1071 | rtm->rtm_type = RTN_UNREACHABLE; | ||
1072 | return 0; | ||
1073 | } | ||
1074 | rtm->rtm_scope = RT_SCOPE_NOWHERE; | ||
1075 | rtm->rtm_type = RTN_UNICAST; | ||
1076 | |||
1077 | if (r->rt_dev) { | ||
1078 | char *colon; | ||
1079 | struct net_device *dev; | ||
1080 | char devname[IFNAMSIZ]; | ||
1081 | |||
1082 | if (copy_from_user(devname, r->rt_dev, IFNAMSIZ-1)) | ||
1083 | return -EFAULT; | ||
1084 | devname[IFNAMSIZ-1] = 0; | ||
1085 | colon = strchr(devname, ':'); | ||
1086 | if (colon) | ||
1087 | *colon = 0; | ||
1088 | dev = __dev_get_by_name(devname); | ||
1089 | if (!dev) | ||
1090 | return -ENODEV; | ||
1091 | rta->rta_oif = &dev->ifindex; | ||
1092 | if (colon) { | ||
1093 | struct in_ifaddr *ifa; | ||
1094 | struct in_device *in_dev = __in_dev_get_rtnl(dev); | ||
1095 | if (!in_dev) | ||
1096 | return -ENODEV; | ||
1097 | *colon = ':'; | ||
1098 | for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) | ||
1099 | if (strcmp(ifa->ifa_label, devname) == 0) | ||
1100 | break; | ||
1101 | if (ifa == NULL) | ||
1102 | return -ENODEV; | ||
1103 | rta->rta_prefsrc = &ifa->ifa_local; | ||
1104 | } | ||
1105 | } | ||
1106 | 1004 | ||
1107 | ptr = &((struct sockaddr_in*)&r->rt_gateway)->sin_addr.s_addr; | 1005 | nla_nest_end(skb, mp); |
1108 | if (r->rt_gateway.sa_family == AF_INET && *ptr) { | ||
1109 | rta->rta_gw = ptr; | ||
1110 | if (r->rt_flags&RTF_GATEWAY && inet_addr_type(*ptr) == RTN_UNICAST) | ||
1111 | rtm->rtm_scope = RT_SCOPE_UNIVERSE; | ||
1112 | } | 1006 | } |
1007 | #endif | ||
1008 | return nlmsg_end(skb, nlh); | ||
1113 | 1009 | ||
1114 | if (cmd == SIOCDELRT) | 1010 | nla_put_failure: |
1115 | return 0; | 1011 | return nlmsg_cancel(skb, nlh); |
1116 | |||
1117 | if (r->rt_flags&RTF_GATEWAY && rta->rta_gw == NULL) | ||
1118 | return -EINVAL; | ||
1119 | |||
1120 | if (rtm->rtm_scope == RT_SCOPE_NOWHERE) | ||
1121 | rtm->rtm_scope = RT_SCOPE_LINK; | ||
1122 | |||
1123 | if (r->rt_flags&(RTF_MTU|RTF_WINDOW|RTF_IRTT)) { | ||
1124 | struct rtattr *rec; | ||
1125 | struct rtattr *mx = kmalloc(RTA_LENGTH(3*RTA_LENGTH(4)), GFP_KERNEL); | ||
1126 | if (mx == NULL) | ||
1127 | return -ENOMEM; | ||
1128 | rta->rta_mx = mx; | ||
1129 | mx->rta_type = RTA_METRICS; | ||
1130 | mx->rta_len = RTA_LENGTH(0); | ||
1131 | if (r->rt_flags&RTF_MTU) { | ||
1132 | rec = (void*)((char*)mx + RTA_ALIGN(mx->rta_len)); | ||
1133 | rec->rta_type = RTAX_ADVMSS; | ||
1134 | rec->rta_len = RTA_LENGTH(4); | ||
1135 | mx->rta_len += RTA_LENGTH(4); | ||
1136 | *(u32*)RTA_DATA(rec) = r->rt_mtu - 40; | ||
1137 | } | ||
1138 | if (r->rt_flags&RTF_WINDOW) { | ||
1139 | rec = (void*)((char*)mx + RTA_ALIGN(mx->rta_len)); | ||
1140 | rec->rta_type = RTAX_WINDOW; | ||
1141 | rec->rta_len = RTA_LENGTH(4); | ||
1142 | mx->rta_len += RTA_LENGTH(4); | ||
1143 | *(u32*)RTA_DATA(rec) = r->rt_window; | ||
1144 | } | ||
1145 | if (r->rt_flags&RTF_IRTT) { | ||
1146 | rec = (void*)((char*)mx + RTA_ALIGN(mx->rta_len)); | ||
1147 | rec->rta_type = RTAX_RTT; | ||
1148 | rec->rta_len = RTA_LENGTH(4); | ||
1149 | mx->rta_len += RTA_LENGTH(4); | ||
1150 | *(u32*)RTA_DATA(rec) = r->rt_irtt<<3; | ||
1151 | } | ||
1152 | } | ||
1153 | return 0; | ||
1154 | } | 1012 | } |
1155 | 1013 | ||
1156 | #endif | ||
1157 | |||
1158 | /* | 1014 | /* |
1159 | Update FIB if: | 1015 | Update FIB if: |
1160 | - local address disappeared -> we must delete all the entries | 1016 | - local address disappeared -> we must delete all the entries |