diff options
Diffstat (limited to 'net/ipv6/route.c')
-rw-r--r-- | net/ipv6/route.c | 43 |
1 files changed, 34 insertions, 9 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 28ee43e78..5188de864 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: route.c,v 1.19 1997/12/13 21:53:16 kuznet Exp $ + * $Id: route.c,v 1.25 1998/03/15 03:31:47 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -85,18 +85,18 @@ struct dst_ops ip6_dst_ops = { }; struct rt6_info ip6_null_entry = { - {{NULL, ATOMIC_INIT(0), ATOMIC_INIT(0), NULL, + {{NULL, ATOMIC_INIT(1), ATOMIC_INIT(1), NULL, -1, 0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL, ip6_pkt_discard, ip6_pkt_discard, &ip6_dst_ops}}, NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0U, - 0, 255, {NULL}, {{{{0}}}, 128}, {{{{0}}}, 128} + 255, 0, {NULL}, {{{{0}}}, 0}, {{{{0}}}, 0} }; struct fib6_node ip6_routing_table = { NULL, NULL, NULL, NULL, &ip6_null_entry, - 0, RTN_ROOT|RTN_TL_ROOT, 0 + 0, RTN_ROOT|RTN_TL_ROOT|RTN_RTINFO, 0 }; #ifdef CONFIG_RT6_POLICY @@ -709,14 +709,14 @@ struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err) if (rt == NULL) { RDBG(("dalloc fails, ")); *err = -ENOMEM; - goto out; + return NULL; } rt->u.dst.obsolete = -1; rt->rt6i_expires = rtmsg->rtmsg_info; addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst); - + if (addr_type & IPV6_ADDR_MULTICAST) { RDBG(("MCAST, ")); rt->u.dst.input = ip6_mc_input; @@ -743,6 +743,21 @@ struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err) rt->rt6i_src.plen = rtmsg->rtmsg_src_len; ipv6_wash_prefix(&rt->rt6i_src.addr, rt->rt6i_src.plen); + /* We cannot add true routes via loopback here, + they would result in kernel looping; promote them to reject routes + */ + if ((rtmsg->rtmsg_flags&RTF_REJECT) || + (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) { + dev = dev_get("lo"); + rt->u.dst.output = ip6_pkt_discard; + rt->u.dst.input = ip6_pkt_discard; + rt->u.dst.error = -ENETUNREACH; + rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; + rt->rt6i_metric = rtmsg->rtmsg_metric; + rt->rt6i_dev = dev; + goto install_route; + } + if (rtmsg->rtmsg_flags & RTF_GATEWAY) { struct in6_addr *gw_addr; int gwa_type; @@ -773,7 +788,7 @@ struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err) } dev = grt->rt6i_dev; } - if (dev == NULL) { + if (dev == NULL || (dev->flags&IFF_LOOPBACK)) { *err = -EINVAL; goto out; } @@ -805,6 +820,7 @@ struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err) rt->rt6i_hoplimit = ipv6_get_hoplimit(dev); rt->rt6i_flags = rtmsg->rtmsg_flags; +install_route: RDBG(("rt6ins(%p) ", rt)); rt6_lock(); @@ -1421,6 +1437,7 @@ int ipv6_route_ioctl(unsigned int cmd, void *arg) int ip6_pkt_discard(struct sk_buff *skb) { ipv6_statistics.Ip6OutNoRoutes++; + icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev); kfree_skb(skb); return 0; } @@ -1671,7 +1688,8 @@ static int inet6_rtm_to_rtmsg(struct rtmsg *r, struct rtattr **rta, rtmsg->rtmsg_dst_len = r->rtm_dst_len; rtmsg->rtmsg_src_len = r->rtm_src_len; rtmsg->rtmsg_flags = RTF_UP; - rtmsg->rtmsg_metric = IP6_RT_PRIO_USER; + if (r->rtm_type == RTN_UNREACHABLE) + rtmsg->rtmsg_flags |= RTF_REJECT; if (rta[RTA_GATEWAY-1]) { if (rta[RTA_GATEWAY-1]->rta_len != RTA_LENGTH(16)) @@ -1754,7 +1772,12 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt, rtm->rtm_src_len = rt->rt6i_src.plen; rtm->rtm_tos = 0; rtm->rtm_table = RT_TABLE_MAIN; - rtm->rtm_type = RTN_UNICAST; + if (rt->rt6i_flags&RTF_REJECT) + rtm->rtm_type = RTN_UNREACHABLE; + else if (rt->rt6i_dev && (rt->rt6i_dev->flags&IFF_LOOPBACK)) + rtm->rtm_type = RTN_LOCAL; + else + rtm->rtm_type = RTN_UNICAST; rtm->rtm_flags = 0; rtm->rtm_scope = RT_SCOPE_UNIVERSE; #ifdef CONFIG_RTNL_OLD_IFINFO @@ -1795,6 +1818,8 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt, if (rt->u.dst.rtt) RTA_PUT(skb, RTAX_RTT, sizeof(unsigned), &rt->u.dst.rtt); mx->rta_len = skb->tail - (u8*)mx; + if (mx->rta_len == RTA_LENGTH(0)) + skb_trim(skb, (u8*)mx - skb->data); #endif if (rt->u.dst.neighbour) RTA_PUT(skb, RTA_GATEWAY, 16, &rt->u.dst.neighbour->primary_key); |