Example #1
0
/*
 * Ethernet router hello message received
 */
int dn_neigh_router_hello(struct sk_buff *skb)
{
    struct rtnode_hello_message *msg = (struct rtnode_hello_message *)skb->data;

    struct neighbour *neigh;
    struct dn_neigh *dn;
    struct dn_dev *dn_db;
    dn_address src;

    src = dn_htons(dn_eth2dn(msg->id));

    neigh = __neigh_lookup(&dn_neigh_table, &src, skb->dev, 1);

    dn = (struct dn_neigh *)neigh;

    if (neigh) {
        write_lock(&neigh->lock);

        neigh->used = jiffies;
        dn_db = (struct dn_dev *)neigh->dev->dn_ptr;

        if (!(neigh->nud_state & NUD_PERMANENT)) {
            neigh->updated = jiffies;

            if (neigh->dev->type == ARPHRD_ETHER)
                memcpy(neigh->ha, &skb->mac.ethernet->h_source, ETH_ALEN);

            dn->blksize  = dn_ntohs(msg->blksize);
            dn->priority = msg->priority;

            dn->flags &= ~DN_NDFLAG_P3;

            switch(msg->iinfo & DN_RT_INFO_TYPE) {
            case DN_RT_INFO_L1RT:
                dn->flags &=~DN_NDFLAG_R2;
                dn->flags |= DN_NDFLAG_R1;
                break;
            case DN_RT_INFO_L2RT:
                dn->flags |= DN_NDFLAG_R2;
            }
        }

        if (!dn_db->router) {
            dn_db->router = neigh_clone(neigh);
        } else {
            if (msg->priority > ((struct dn_neigh *)dn_db->router)->priority)
                neigh_release(xchg(&dn_db->router, neigh_clone(neigh)));
        }
        write_unlock(&neigh->lock);
        neigh_release(neigh);
    }

    kfree_skb(skb);
    return 0;
}
Example #2
0
static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
			  const struct flowi *fl)
{
	struct rt6_info *rt = (struct rt6_info*)xdst->route;

	xdst->u.dst.dev = dev;
	dev_hold(dev);

	xdst->u.rt6.rt6i_idev = in6_dev_get(dev);
	if (!xdst->u.rt6.rt6i_idev) {
		dev_put(dev);
		return -ENODEV;
	}

	rt6_transfer_peer(&xdst->u.rt6, rt);

	/* Sheit... I remember I did this right. Apparently,
	 * it was magically lost, so this code needs audit */
	xdst->u.rt6.n = neigh_clone(rt->n);
	xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST |
						   RTF_LOCAL);
	xdst->u.rt6.rt6i_metric = rt->rt6i_metric;
	xdst->u.rt6.rt6i_node = rt->rt6i_node;
	if (rt->rt6i_node)
		xdst->route_cookie = rt->rt6i_node->fn_sernum;
	xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway;
	xdst->u.rt6.rt6i_dst = rt->rt6i_dst;
	xdst->u.rt6.rt6i_src = rt->rt6i_src;

	return 0;
}
Example #3
0
/* Create a child dst_entry with locked interface MTU, and attach it to `dst'. 
   This is needed on newer linux kernels and IP_ONLY_INTERCEPTOR builds,
   where the IP stack fragments packets to path MTU after ssh_interceptor_send.
*/
static struct dst_entry * 
interceptor_route_create_child_dst(struct dst_entry *dst)
{
  struct dst_entry *child;

  /* Allocate a dst_entry and copy relevant fields from dst. */
  child = dst_alloc(dst->ops);
  if (child == NULL)
    return NULL;
  
  child->input = dst->input;
  child->output = dst->output;
  
  /* Child is not added to dst hash, and linux native IPsec is disabled. */
  child->flags |= (DST_NOHASH | DST_NOPOLICY | DST_NOXFRM);
  
  /* Copy route metrics and lock MTU to interface MTU. */
  memcpy(child->metrics, dst->metrics, sizeof(child->metrics));
  child->metrics[RTAX_LOCK-1] |= 1 << RTAX_MTU;
  
#ifdef CONFIG_NET_CLS_ROUTE  
  child->tclassid = dst->tclassid;
#endif /* CONFIG_NET_CLS_ROUTE */
  
  child->xfrm = NULL;
  
  if (dst->hh)
    {
      atomic_inc(&dst->hh->hh_refcnt);
      child->hh = dst->hh;
    }

  if (dst->neighbour)
    {
      child->neighbour = neigh_clone(dst->neighbour);
    }

  if (dst->dev)
    {
      dev_hold(dst->dev);
      child->dev = dst->dev;
    }

  SSH_ASSERT(dst->child == NULL);
  dst->child = dst_clone(child);
  
  SSH_DEBUG(SSH_D_MIDOK, ("Allocated child %p dst_entry for dst %p mtu %d",
			  child, dst, dst_mtu(dst)));

  return child;
}
static int
__xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
		      struct flowi *fl, struct dst_entry **dst_p)
{
	struct dst_entry *dst, *dst_prev;
	struct rtable *rt0 = (struct rtable*)(*dst_p);
	struct rtable *rt = rt0;
	struct flowi fl_tunnel = {
		.nl_u = {
			.ip4_u = {
				.saddr = fl->fl4_src,
				.daddr = fl->fl4_dst,
				.tos = fl->fl4_tos
			}
		}
	};
	int i;
	int err;
	int header_len = 0;
	int trailer_len = 0;

	dst = dst_prev = NULL;
	dst_hold(&rt->u.dst);

	for (i = 0; i < nx; i++) {
		struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops);
		struct xfrm_dst *xdst;

		if (unlikely(dst1 == NULL)) {
			err = -ENOBUFS;
			dst_release(&rt->u.dst);
			goto error;
		}

		if (!dst)
			dst = dst1;
		else {
			dst_prev->child = dst1;
			dst1->flags |= DST_NOHASH;
			dst_clone(dst1);
		}

		xdst = (struct xfrm_dst *)dst1;
		xdst->route = &rt->u.dst;
		xdst->genid = xfrm[i]->genid;

		dst1->next = dst_prev;
		dst_prev = dst1;

		header_len += xfrm[i]->props.header_len;
		trailer_len += xfrm[i]->props.trailer_len;

		if (xfrm[i]->props.mode == XFRM_MODE_TUNNEL) {
			unsigned short encap_family = xfrm[i]->props.family;
			switch(encap_family) {
			case AF_INET:
				fl_tunnel.fl4_dst = xfrm[i]->id.daddr.a4;
				fl_tunnel.fl4_src = xfrm[i]->props.saddr.a4;
				break;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
			case AF_INET6:
				ipv6_addr_copy(&fl_tunnel.fl6_dst, (struct in6_addr*)&xfrm[i]->id.daddr.a6);
				ipv6_addr_copy(&fl_tunnel.fl6_src, (struct in6_addr*)&xfrm[i]->props.saddr.a6);
				break;
#endif
			default:
				BUG_ON(1);
			}
			err = xfrm_dst_lookup((struct xfrm_dst **)&rt,
					      &fl_tunnel, encap_family);
			if (err)
				goto error;
		} else
			dst_hold(&rt->u.dst);
	}

	dst_prev->child = &rt->u.dst;
	dst->path = &rt->u.dst;

	*dst_p = dst;
	dst = dst_prev;

	dst_prev = *dst_p;
	i = 0;
	for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
		struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;
		struct xfrm_state_afinfo *afinfo;
		x->u.rt.fl = *fl;

		dst_prev->xfrm = xfrm[i++];
		dst_prev->dev = rt->u.dst.dev;
		if (rt->u.dst.dev)
			dev_hold(rt->u.dst.dev);
		dst_prev->obsolete	= -1;
		dst_prev->flags	       |= DST_HOST;
		dst_prev->lastuse	= jiffies;
		dst_prev->header_len	= header_len;
		dst_prev->nfheader_len	= 0;
		dst_prev->trailer_len	= trailer_len;
		memcpy(&dst_prev->metrics, &x->route->metrics, sizeof(dst_prev->metrics));

		/* Copy neighbout for reachability confirmation */
		dst_prev->neighbour	= neigh_clone(rt->u.dst.neighbour);
		dst_prev->input		= rt->u.dst.input;
		/* XXX: When IPv6 module can be unloaded, we should manage reference
		 * to xfrm6_output in afinfo->output. Miyazawa
		 * */
		afinfo = xfrm_state_get_afinfo(dst_prev->xfrm->props.family);
		if (!afinfo) {
			dst = *dst_p;
			err = -EAFNOSUPPORT;
			goto error;
		}
		dst_prev->output = afinfo->output;
		xfrm_state_put_afinfo(afinfo);
		if (dst_prev->xfrm->props.family == AF_INET && rt->peer)
			atomic_inc(&rt->peer->refcnt);
		x->u.rt.peer = rt->peer;
		/* Sheit... I remember I did this right. Apparently,
		 * it was magically lost, so this code needs audit */
		x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
		x->u.rt.rt_type = rt->rt_type;
		x->u.rt.rt_src = rt0->rt_src;
		x->u.rt.rt_dst = rt0->rt_dst;
		x->u.rt.rt_gateway = rt->rt_gateway;
		x->u.rt.rt_spec_dst = rt0->rt_spec_dst;
		x->u.rt.idev = rt0->idev;
		in_dev_hold(rt0->idev);
		header_len -= x->u.dst.xfrm->props.header_len;
		trailer_len -= x->u.dst.xfrm->props.trailer_len;
	}

	xfrm_init_pmtu(dst);
	return 0;

error:
	if (dst)
		dst_free(dst);
	return err;
}
Example #5
0
/*
 *	Handle redirects
 */
void rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr,
		  struct neighbour *neigh, int on_link)
{
	struct rt6_info *rt, *nrt;

	/* Locate old route to this destination. */
	rt = rt6_lookup(dest, NULL, neigh->dev->ifindex, 1);

	if (rt == NULL)
		return;

	if (neigh->dev != rt->rt6i_dev)
		goto out;

	/* Redirect received -> path was valid.
	   Look, redirects are sent only in response to data packets,
	   so that this nexthop apparently is reachable. --ANK
	 */
	dst_confirm(&rt->u.dst);

	/* Duplicate redirect: silently ignore. */
	if (neigh == rt->u.dst.neighbour)
		goto out;

	/* Current route is on-link; redirect is always invalid.
	   
	   Seems, previous statement is not true. It could
	   be node, which looks for us as on-link (f.e. proxy ndisc)
	   But then router serving it might decide, that we should
	   know truth 8)8) --ANK (980726).
	 */
	if (!(rt->rt6i_flags&RTF_GATEWAY))
		goto out;

	/*
	 *	RFC 1970 specifies that redirects should only be
	 *	accepted if they come from the nexthop to the target.
	 *	Due to the way default routers are chosen, this notion
	 *	is a bit fuzzy and one might need to check all default
	 *	routers.
	 */

	if (ipv6_addr_cmp(saddr, &rt->rt6i_gateway)) {
		if (rt->rt6i_flags & RTF_DEFAULT) {
			struct rt6_info *rt1;

			read_lock(&rt6_lock);
			for (rt1 = ip6_routing_table.leaf; rt1; rt1 = rt1->u.next) {
				if (!ipv6_addr_cmp(saddr, &rt1->rt6i_gateway)) {
					dst_hold(&rt1->u.dst);
					dst_release(&rt->u.dst);
					read_unlock(&rt6_lock);
					rt = rt1;
					goto source_ok;
				}
			}
			read_unlock(&rt6_lock);
		}
		if (net_ratelimit())
			printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
			       "for redirect target\n");
		goto out;
	}

source_ok:

	/*
	 *	We have finally decided to accept it.
	 */

	nrt = ip6_rt_copy(rt);
	if (nrt == NULL)
		goto out;

	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
	if (on_link)
		nrt->rt6i_flags &= ~RTF_GATEWAY;

	ipv6_addr_copy(&nrt->rt6i_dst.addr, dest);
	nrt->rt6i_dst.plen = 128;
	nrt->u.dst.flags |= DST_HOST;

	ipv6_addr_copy(&nrt->rt6i_gateway, (struct in6_addr*)neigh->primary_key);
	nrt->rt6i_nexthop = neigh_clone(neigh);
	/* Reset pmtu, it may be better */
	nrt->u.dst.pmtu = ipv6_get_mtu(neigh->dev);
	nrt->u.dst.advmss = max_t(unsigned int, nrt->u.dst.pmtu - 60, ip6_rt_min_advmss);
	if (rt->u.dst.advmss > 65535-20)
		rt->u.dst.advmss = 65535;
	nrt->rt6i_hoplimit = ipv6_get_hoplimit(neigh->dev);

	if (rt6_ins(nrt, NULL))
		goto out;

	if (rt->rt6i_flags&RTF_CACHE) {
		ip6_del_rt(rt, NULL);
		return;
	}

out:
        dst_release(&rt->u.dst);
	return;
}
Example #6
0
void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr,
			struct net_device *dev, u32 pmtu)
{
	struct rt6_info *rt, *nrt;

	if (pmtu < IPV6_MIN_MTU) {
		if (net_ratelimit())
			printk(KERN_DEBUG "rt6_pmtu_discovery: invalid MTU value %d\n",
			       pmtu);
		/* According to RFC1981, the PMTU is set to the IPv6 minimum
		   link MTU if the node receives a Packet Too Big message
		   reporting next-hop MTU that is less than the IPv6 minimum MTU.
		 */	
		pmtu = IPV6_MIN_MTU;
	}

	rt = rt6_lookup(daddr, saddr, dev->ifindex, 0);

	if (rt == NULL)
		return;

	if (pmtu >= rt->u.dst.pmtu)
		goto out;

	/* New mtu received -> path was valid.
	   They are sent only in response to data packets,
	   so that this nexthop apparently is reachable. --ANK
	 */
	dst_confirm(&rt->u.dst);

	/* Host route. If it is static, it would be better
	   not to override it, but add new one, so that
	   when cache entry will expire old pmtu
	   would return automatically.
	 */
	if (rt->rt6i_flags & RTF_CACHE) {
		rt->u.dst.pmtu = pmtu;
		dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires);
		rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES;
		goto out;
	}

	/* Network route.
	   Two cases are possible:
	   1. It is connected route. Action: COW
	   2. It is gatewayed route or NONEXTHOP route. Action: clone it.
	 */
	if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) {
		nrt = rt6_cow(rt, daddr, saddr);
		if (!nrt->u.dst.error) {
			nrt->u.dst.pmtu = pmtu;
			/* According to RFC 1981, detecting PMTU increase shouldn't be
			   happened within 5 mins, the recommended timer is 10 mins.
			   Here this route expiration time is set to ip6_rt_mtu_expires 
			   which is 10 mins. After 10 mins the decreased pmtu is expired
			   and detecting PMTU increase will be automatically happened.
			 */
			dst_set_expires(&nrt->u.dst, ip6_rt_mtu_expires);
			nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES;
			dst_release(&nrt->u.dst);
		}
	} else {
		nrt = ip6_rt_copy(rt);
		if (nrt == NULL)
			goto out;
		ipv6_addr_copy(&nrt->rt6i_dst.addr, daddr);
		nrt->rt6i_dst.plen = 128;
		nrt->u.dst.flags |= DST_HOST;
		nrt->rt6i_nexthop = neigh_clone(rt->rt6i_nexthop);
		dst_set_expires(&nrt->u.dst, ip6_rt_mtu_expires);
		nrt->rt6i_flags |= RTF_DYNAMIC|RTF_CACHE|RTF_EXPIRES;
		nrt->u.dst.pmtu = pmtu;
		rt6_ins(nrt, NULL);
	}

out:
	dst_release(&rt->u.dst);
}
static int
__xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
		      struct flowi *fl, struct dst_entry **dst_p)
{
	struct dst_entry *dst, *dst_prev;
	struct rt6_info *rt0 = (struct rt6_info*)(*dst_p);
	struct rt6_info *rt  = rt0;
	struct in6_addr *remote = &fl->fl6_dst;
	struct in6_addr *local  = &fl->fl6_src;
	int i;
	int err = 0;
	int header_len = 0;
	int trailer_len = 0;

	dst = dst_prev = NULL;

	for (i = 0; i < nx; i++) {
		struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops);

		if (unlikely(dst1 == NULL)) {
			err = -ENOBUFS;
			goto error;
		}

		if (!dst)
			dst = dst1;
		else {
			dst_prev->child = dst1;
			dst1->flags |= DST_NOHASH;
			dst_clone(dst1);
		}
		dst_prev = dst1;
		if (xfrm[i]->props.mode) {
			remote = (struct in6_addr*)&xfrm[i]->id.daddr;
			local  = (struct in6_addr*)&xfrm[i]->props.saddr;
		}
		header_len += xfrm[i]->props.header_len;
		trailer_len += xfrm[i]->props.trailer_len;
	}

	if (ipv6_addr_cmp(remote, &fl->fl6_dst)) {
		struct flowi fl_tunnel;

		memset(&fl_tunnel, 0, sizeof(fl_tunnel));
		ipv6_addr_copy(&fl_tunnel.fl6_dst, remote);
		ipv6_addr_copy(&fl_tunnel.fl6_src, local);

		err = xfrm_dst_lookup((struct xfrm_dst **) &rt,
				      &fl_tunnel, AF_INET6);
		if (err)
			goto error;
	} else {
		dst_hold(&rt->u.dst);
	}
	dst_prev->child = &rt->u.dst;
	i = 0;
	for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
		struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;

		dst_prev->xfrm = xfrm[i++];
		dst_prev->dev = rt->u.dst.dev;
		if (rt->u.dst.dev)
			dev_hold(rt->u.dst.dev);
		dst_prev->obsolete	= -1;
		dst_prev->flags	       |= DST_HOST;
		dst_prev->lastuse	= jiffies;
		dst_prev->header_len	= header_len;
		dst_prev->trailer_len	= trailer_len;
		memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics));
		dst_prev->path		= &rt->u.dst;

		/* Copy neighbour for reachability confirmation */
		dst_prev->neighbour	= neigh_clone(rt->u.dst.neighbour);
		dst_prev->input		= rt->u.dst.input;
		dst_prev->output	= dst_prev->xfrm->type->output;
		/* Sheit... I remember I did this right. Apparently,
		 * it was magically lost, so this code needs audit */
		x->u.rt6.rt6i_flags    = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
		x->u.rt6.rt6i_metric   = rt0->rt6i_metric;
		x->u.rt6.rt6i_node     = rt0->rt6i_node;
		x->u.rt6.rt6i_gateway  = rt0->rt6i_gateway;
		memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway)); 
		x->u.rt6.rt6i_dst      = rt0->rt6i_dst;
		x->u.rt6.rt6i_src      = rt0->rt6i_src;	
		header_len -= x->u.dst.xfrm->props.header_len;
		trailer_len -= x->u.dst.xfrm->props.trailer_len;
	}
	*dst_p = dst;
	return 0;

error:
	if (dst)
		dst_free(dst);
	return err;
}
Example #8
0
static int dn_route_output_slow(struct dst_entry **pprt, const struct flowi *oldflp, int try_hard)
{
	struct flowi fl = { .nl_u = { .dn_u = 
				      { .daddr = oldflp->fld_dst,
					.saddr = oldflp->fld_src,
					.scope = RT_SCOPE_UNIVERSE,
#ifdef CONFIG_DECNET_ROUTE_FWMARK
					.fwmark = oldflp->fld_fwmark
#endif
				     } },
			    .iif = loopback_dev.ifindex,
			    .oif = oldflp->oif };
	struct dn_route *rt = NULL;
	struct net_device *dev_out = NULL;
	struct neighbour *neigh = NULL;
	unsigned hash;
	unsigned flags = 0;
	struct dn_fib_res res = { .fi = NULL, .type = RTN_UNICAST };
	int err;
	int free_res = 0;
	__le16 gateway = 0;

	if (decnet_debug_level & 16)
		printk(KERN_DEBUG
		       "dn_route_output_slow: dst=%04x src=%04x mark=%d"
		       " iif=%d oif=%d\n", dn_ntohs(oldflp->fld_dst),
		       dn_ntohs(oldflp->fld_src),
                       oldflp->fld_fwmark, loopback_dev.ifindex, oldflp->oif);

	/* If we have an output interface, verify its a DECnet device */
	if (oldflp->oif) {
		dev_out = dev_get_by_index(oldflp->oif);
		err = -ENODEV;
		if (dev_out && dev_out->dn_ptr == NULL) {
			dev_put(dev_out);
			dev_out = NULL;
		}
		if (dev_out == NULL)
			goto out;
	}

	/* If we have a source address, verify that its a local address */
	if (oldflp->fld_src) {
		err = -EADDRNOTAVAIL;

		if (dev_out) {
			if (dn_dev_islocal(dev_out, oldflp->fld_src))
				goto source_ok;
			dev_put(dev_out);
			goto out;
		}
		read_lock(&dev_base_lock);
		for(dev_out = dev_base; dev_out; dev_out = dev_out->next) {
			if (!dev_out->dn_ptr)
				continue;
			if (!dn_dev_islocal(dev_out, oldflp->fld_src))
				continue;
			if ((dev_out->flags & IFF_LOOPBACK) &&
			    oldflp->fld_dst &&
			    !dn_dev_islocal(dev_out, oldflp->fld_dst))
				continue;
			break;
		}
		read_unlock(&dev_base_lock);
		if (dev_out == NULL)
			goto out;
		dev_hold(dev_out);
source_ok:
		;
	}

	/* No destination? Assume its local */
	if (!fl.fld_dst) {
		fl.fld_dst = fl.fld_src;

		err = -EADDRNOTAVAIL;
		if (dev_out)
			dev_put(dev_out);
		dev_out = &loopback_dev;
		dev_hold(dev_out);
		if (!fl.fld_dst) {
			fl.fld_dst =
			fl.fld_src = dnet_select_source(dev_out, 0,
						       RT_SCOPE_HOST);
			if (!fl.fld_dst)
				goto out;
		}
		fl.oif = loopback_dev.ifindex;
		res.type = RTN_LOCAL;
		goto make_route;
	}

	if (decnet_debug_level & 16)
		printk(KERN_DEBUG
		       "dn_route_output_slow: initial checks complete."
		       " dst=%o4x src=%04x oif=%d try_hard=%d\n",
		       dn_ntohs(fl.fld_dst), dn_ntohs(fl.fld_src),
		       fl.oif, try_hard);

	/*
	 * N.B. If the kernel is compiled without router support then
	 * dn_fib_lookup() will evaluate to non-zero so this if () block
	 * will always be executed.
	 */
	err = -ESRCH;
	if (try_hard || (err = dn_fib_lookup(&fl, &res)) != 0) {
		struct dn_dev *dn_db;
		if (err != -ESRCH)
			goto out;
		/*
	 	 * Here the fallback is basically the standard algorithm for 
		 * routing in endnodes which is described in the DECnet routing
		 * docs
		 *
		 * If we are not trying hard, look in neighbour cache.
		 * The result is tested to ensure that if a specific output
		 * device/source address was requested, then we honour that 
		 * here
		 */
		if (!try_hard) {
			neigh = neigh_lookup_nodev(&dn_neigh_table, &fl.fld_dst);
			if (neigh) {
				if ((oldflp->oif && 
				    (neigh->dev->ifindex != oldflp->oif)) ||
				    (oldflp->fld_src &&
				    (!dn_dev_islocal(neigh->dev,
						      oldflp->fld_src)))) {
					neigh_release(neigh);
					neigh = NULL;
				} else {
					if (dev_out)
						dev_put(dev_out);
					if (dn_dev_islocal(neigh->dev, fl.fld_dst)) {
						dev_out = &loopback_dev;
						res.type = RTN_LOCAL;
					} else {
						dev_out = neigh->dev;
					}
					dev_hold(dev_out);
					goto select_source;
				}
			}
		}

		/* Not there? Perhaps its a local address */
		if (dev_out == NULL)
			dev_out = dn_dev_get_default();
		err = -ENODEV;
		if (dev_out == NULL)
			goto out;
		dn_db = dev_out->dn_ptr;
		/* Possible improvement - check all devices for local addr */
		if (dn_dev_islocal(dev_out, fl.fld_dst)) {
			dev_put(dev_out);
			dev_out = &loopback_dev;
			dev_hold(dev_out);
			res.type = RTN_LOCAL;
			goto select_source;
		}
		/* Not local either.... try sending it to the default router */
		neigh = neigh_clone(dn_db->router);
		BUG_ON(neigh && neigh->dev != dev_out);

		/* Ok then, we assume its directly connected and move on */
select_source:
		if (neigh)
			gateway = ((struct dn_neigh *)neigh)->addr;
		if (gateway == 0)
			gateway = fl.fld_dst;
		if (fl.fld_src == 0) {
			fl.fld_src = dnet_select_source(dev_out, gateway,
							 res.type == RTN_LOCAL ?
							 RT_SCOPE_HOST : 
							 RT_SCOPE_LINK);
			if (fl.fld_src == 0 && res.type != RTN_LOCAL)
				goto e_addr;
		}
		fl.oif = dev_out->ifindex;
		goto make_route;
	}
	free_res = 1;

	if (res.type == RTN_NAT)
		goto e_inval;

	if (res.type == RTN_LOCAL) {
		if (!fl.fld_src)
			fl.fld_src = fl.fld_dst;
		if (dev_out)
			dev_put(dev_out);
		dev_out = &loopback_dev;
		dev_hold(dev_out);
		fl.oif = dev_out->ifindex;
		if (res.fi)
			dn_fib_info_put(res.fi);
		res.fi = NULL;
		goto make_route;
	}

	if (res.fi->fib_nhs > 1 && fl.oif == 0)
		dn_fib_select_multipath(&fl, &res);

	/* 
	 * We could add some logic to deal with default routes here and
	 * get rid of some of the special casing above.
	 */

	if (!fl.fld_src)
		fl.fld_src = DN_FIB_RES_PREFSRC(res);
	
	if (dev_out)
		dev_put(dev_out);
	dev_out = DN_FIB_RES_DEV(res);
	dev_hold(dev_out);
	fl.oif = dev_out->ifindex;
	gateway = DN_FIB_RES_GW(res);

make_route:
	if (dev_out->flags & IFF_LOOPBACK)
		flags |= RTCF_LOCAL;

	rt = dst_alloc(&dn_dst_ops);
	if (rt == NULL)
		goto e_nobufs;

	atomic_set(&rt->u.dst.__refcnt, 1);
	rt->u.dst.flags   = DST_HOST;

	rt->fl.fld_src    = oldflp->fld_src;
	rt->fl.fld_dst    = oldflp->fld_dst;
	rt->fl.oif        = oldflp->oif;
	rt->fl.iif        = 0;
#ifdef CONFIG_DECNET_ROUTE_FWMARK
	rt->fl.fld_fwmark = oldflp->fld_fwmark;
#endif

	rt->rt_saddr      = fl.fld_src;
	rt->rt_daddr      = fl.fld_dst;
	rt->rt_gateway    = gateway ? gateway : fl.fld_dst;
	rt->rt_local_src  = fl.fld_src;

	rt->rt_dst_map    = fl.fld_dst;
	rt->rt_src_map    = fl.fld_src;

	rt->u.dst.dev = dev_out;
	dev_hold(dev_out);
	rt->u.dst.neighbour = neigh;
	neigh = NULL;

	rt->u.dst.lastuse = jiffies;
	rt->u.dst.output  = dn_output;
	rt->u.dst.input   = dn_rt_bug;
	rt->rt_flags      = flags;
	if (flags & RTCF_LOCAL)
		rt->u.dst.input = dn_nsp_rx;

	err = dn_rt_set_next_hop(rt, &res);
	if (err)
		goto e_neighbour;

	hash = dn_hash(rt->fl.fld_src, rt->fl.fld_dst);
	dn_insert_route(rt, hash, (struct dn_route **)pprt);

done:
	if (neigh)
		neigh_release(neigh);
	if (free_res)
		dn_fib_res_put(&res);
	if (dev_out)
		dev_put(dev_out);
out:
	return err;

e_addr:
        err = -EADDRNOTAVAIL;
        goto done;
e_inval:
	err = -EINVAL;
	goto done;
e_nobufs:
	err = -ENOBUFS;
	goto done;
e_neighbour:
	dst_free(&rt->u.dst);
	goto e_nobufs;
}
Example #9
0
static int dn_route_input_slow(struct sk_buff *skb)
{
	struct dn_route *rt = NULL;
	struct dn_skb_cb *cb = DN_SKB_CB(skb);
	struct net_device *in_dev = skb->dev;
	struct net_device *out_dev = NULL;
	struct dn_dev *dn_db;
	struct neighbour *neigh = NULL;
	unsigned hash;
	int flags = 0;
	__le16 gateway = 0;
	__le16 local_src = 0;
	struct flowi fl = { .nl_u = { .dn_u = 
				     { .daddr = cb->dst,
				       .saddr = cb->src,
				       .scope = RT_SCOPE_UNIVERSE,
#ifdef CONFIG_DECNET_ROUTE_FWMARK
				       .fwmark = skb->nfmark
#endif
				    } },
			    .iif = skb->dev->ifindex };
	struct dn_fib_res res = { .fi = NULL, .type = RTN_UNREACHABLE };
	int err = -EINVAL;
	int free_res = 0;

	dev_hold(in_dev);

	if ((dn_db = in_dev->dn_ptr) == NULL)
		goto out;

	/* Zero source addresses are not allowed */
	if (fl.fld_src == 0)
		goto out;

	/*
	 * In this case we've just received a packet from a source
	 * outside ourselves pretending to come from us. We don't
	 * allow it any further to prevent routing loops, spoofing and
	 * other nasties. Loopback packets already have the dst attached
	 * so this only affects packets which have originated elsewhere.
	 */
	err  = -ENOTUNIQ;
	if (dn_dev_islocal(in_dev, cb->src))
		goto out;

	err = dn_fib_lookup(&fl, &res);
	if (err) {
		if (err != -ESRCH)
			goto out;
		/*
		 * Is the destination us ?
		 */
		if (!dn_dev_islocal(in_dev, cb->dst))
			goto e_inval;

		res.type = RTN_LOCAL;
	} else {
		__le16 src_map = fl.fld_src;
		free_res = 1;

		out_dev = DN_FIB_RES_DEV(res);
		if (out_dev == NULL) {
			if (net_ratelimit())
				printk(KERN_CRIT "Bug in dn_route_input_slow() "
						 "No output device\n");
			goto e_inval;
		}
		dev_hold(out_dev);

		if (res.r)
			src_map = fl.fld_src; /* no NAT support for now */

		gateway = DN_FIB_RES_GW(res);
		if (res.type == RTN_NAT) {
			fl.fld_dst = dn_fib_rules_map_destination(fl.fld_dst, &res);
			dn_fib_res_put(&res);
			free_res = 0;
			if (dn_fib_lookup(&fl, &res))
				goto e_inval;
			free_res = 1;
			if (res.type != RTN_UNICAST)
				goto e_inval;
			flags |= RTCF_DNAT;
			gateway = fl.fld_dst;
		}
		fl.fld_src = src_map;
	}

	switch(res.type) {
	case RTN_UNICAST:
		/*
		 * Forwarding check here, we only check for forwarding
		 * being turned off, if you want to only forward intra
		 * area, its up to you to set the routing tables up
		 * correctly.
		 */
		if (dn_db->parms.forwarding == 0)
			goto e_inval;

		if (res.fi->fib_nhs > 1 && fl.oif == 0)
			dn_fib_select_multipath(&fl, &res);

		/* 
		 * Check for out_dev == in_dev. We use the RTCF_DOREDIRECT
		 * flag as a hint to set the intra-ethernet bit when
		 * forwarding. If we've got NAT in operation, we don't do
		 * this optimisation.
		 */
		if (out_dev == in_dev && !(flags & RTCF_NAT))
			flags |= RTCF_DOREDIRECT;

		local_src = DN_FIB_RES_PREFSRC(res);

	case RTN_BLACKHOLE:
	case RTN_UNREACHABLE:
		break;
	case RTN_LOCAL:
		flags |= RTCF_LOCAL;
		fl.fld_src = cb->dst;
		fl.fld_dst = cb->src;

		/* Routing tables gave us a gateway */
		if (gateway)
			goto make_route;

		/* Packet was intra-ethernet, so we know its on-link */
		if (cb->rt_flags & DN_RT_F_IE) {
			gateway = cb->src;
			flags |= RTCF_DIRECTSRC;
			goto make_route;
		}

		/* Use the default router if there is one */
		neigh = neigh_clone(dn_db->router);
		if (neigh) {
			gateway = ((struct dn_neigh *)neigh)->addr;
			goto make_route;
		}

		/* Close eyes and pray */
		gateway = cb->src;
		flags |= RTCF_DIRECTSRC;
		goto make_route;
	default:
		goto e_inval;
	}

make_route:
	rt = dst_alloc(&dn_dst_ops);
	if (rt == NULL)
		goto e_nobufs;

	rt->rt_saddr      = fl.fld_src;
	rt->rt_daddr      = fl.fld_dst;
	rt->rt_gateway    = fl.fld_dst;
	if (gateway)
		rt->rt_gateway = gateway;
	rt->rt_local_src  = local_src ? local_src : rt->rt_saddr;

	rt->rt_dst_map    = fl.fld_dst;
	rt->rt_src_map    = fl.fld_src;

	rt->fl.fld_src    = cb->src;
	rt->fl.fld_dst    = cb->dst;
	rt->fl.oif        = 0;
	rt->fl.iif        = in_dev->ifindex;
	rt->fl.fld_fwmark = fl.fld_fwmark;

	rt->u.dst.flags = DST_HOST;
	rt->u.dst.neighbour = neigh;
	rt->u.dst.dev = out_dev;
	rt->u.dst.lastuse = jiffies;
	rt->u.dst.output = dn_rt_bug;
	switch(res.type) {
		case RTN_UNICAST:
			rt->u.dst.input = dn_forward;
			break;
		case RTN_LOCAL:
			rt->u.dst.output = dn_output;
			rt->u.dst.input = dn_nsp_rx;
			rt->u.dst.dev = in_dev;
			flags |= RTCF_LOCAL;
			break;
		default:
		case RTN_UNREACHABLE:
		case RTN_BLACKHOLE:
			rt->u.dst.input = dn_blackhole;
	}
	rt->rt_flags = flags;
	if (rt->u.dst.dev)
		dev_hold(rt->u.dst.dev);

	err = dn_rt_set_next_hop(rt, &res);
	if (err)
		goto e_neighbour;

	hash = dn_hash(rt->fl.fld_src, rt->fl.fld_dst);
	dn_insert_route(rt, hash, (struct dn_route **)&skb->dst);

done:
	if (neigh)
		neigh_release(neigh);
	if (free_res)
		dn_fib_res_put(&res);
	dev_put(in_dev);
	if (out_dev)
		dev_put(out_dev);
out:
	return err;

e_inval:
	err = -EINVAL;
	goto done;

e_nobufs:
	err = -ENOBUFS;
	goto done;

e_neighbour:
	dst_free(&rt->u.dst);
	goto done;
}
Example #10
0
static int
__xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
		      struct flowi *fl, struct dst_entry **dst_p)
{
	struct dst_entry *dst, *dst_prev;
	struct rt6_info *rt0 = (struct rt6_info*)(*dst_p);
	struct rt6_info *rt  = rt0;
	struct in6_addr *remote = &fl->fl6_dst;
	struct in6_addr *local  = &fl->fl6_src;
	struct flowi fl_tunnel = {
		.nl_u = {
			.ip6_u = {
				.saddr = *local,
				.daddr = *remote
			}
		}
	};
	int i;
	int err = 0;
	int header_len = 0;
	int nfheader_len = 0;
	int trailer_len = 0;

	dst = dst_prev = NULL;
	dst_hold(&rt->u.dst);

	for (i = 0; i < nx; i++) {
		struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops);
		struct xfrm_dst *xdst;
		int tunnel = 0;

		if (unlikely(dst1 == NULL)) {
			err = -ENOBUFS;
			dst_release(&rt->u.dst);
			goto error;
		}

		if (!dst)
			dst = dst1;
		else {
			dst_prev->child = dst1;
			dst1->flags |= DST_NOHASH;
			dst_clone(dst1);
		}

		xdst = (struct xfrm_dst *)dst1;
		xdst->route = &rt->u.dst;
		xdst->genid = xfrm[i]->genid;
		if (rt->rt6i_node)
			xdst->route_cookie = rt->rt6i_node->fn_sernum;

		dst1->next = dst_prev;
		dst_prev = dst1;
		if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
			remote = __xfrm6_bundle_addr_remote(xfrm[i], remote);
			local  = __xfrm6_bundle_addr_local(xfrm[i], local);
			tunnel = 1;
		}
		__xfrm6_bundle_len_inc(&header_len, &nfheader_len, xfrm[i]);
		trailer_len += xfrm[i]->props.trailer_len;

		if (tunnel) {
			ipv6_addr_copy(&fl_tunnel.fl6_dst, remote);
			ipv6_addr_copy(&fl_tunnel.fl6_src, local);
			err = xfrm_dst_lookup((struct xfrm_dst **) &rt,
					      &fl_tunnel, AF_INET6);
			if (err)
				goto error;
		} else
			dst_hold(&rt->u.dst);
	}

	dst_prev->child = &rt->u.dst;
	dst->path = &rt->u.dst;
	if (rt->rt6i_node)
		((struct xfrm_dst *)dst)->path_cookie = rt->rt6i_node->fn_sernum;

	*dst_p = dst;
	dst = dst_prev;

	dst_prev = *dst_p;
	i = 0;
	for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
		struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;

		dst_prev->xfrm = xfrm[i++];
		dst_prev->dev = rt->u.dst.dev;
		if (rt->u.dst.dev)
			dev_hold(rt->u.dst.dev);
		dst_prev->obsolete	= -1;
		dst_prev->flags	       |= DST_HOST;
		dst_prev->lastuse	= jiffies;
		dst_prev->header_len	= header_len;
		dst_prev->nfheader_len	= nfheader_len;
		dst_prev->trailer_len	= trailer_len;
		memcpy(&dst_prev->metrics, &x->route->metrics, sizeof(dst_prev->metrics));

		/* Copy neighbour for reachability confirmation */
		dst_prev->neighbour	= neigh_clone(rt->u.dst.neighbour);
		dst_prev->input		= rt->u.dst.input;
		dst_prev->output	= xfrm6_output;
		/* Sheit... I remember I did this right. Apparently,
		 * it was magically lost, so this code needs audit */
		x->u.rt6.rt6i_flags    = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
		x->u.rt6.rt6i_metric   = rt0->rt6i_metric;
		x->u.rt6.rt6i_node     = rt0->rt6i_node;
		x->u.rt6.rt6i_gateway  = rt0->rt6i_gateway;
		memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway)); 
		x->u.rt6.rt6i_dst      = rt0->rt6i_dst;
		x->u.rt6.rt6i_src      = rt0->rt6i_src;	
		x->u.rt6.rt6i_idev     = rt0->rt6i_idev;
		in6_dev_hold(rt0->rt6i_idev);
		__xfrm6_bundle_len_dec(&header_len, &nfheader_len, x->u.dst.xfrm);
		trailer_len -= x->u.dst.xfrm->props.trailer_len;
	}

	xfrm_init_pmtu(dst);
	return 0;

error:
	if (dst)
		dst_free(dst);
	return err;
}
Example #11
0
static int
__xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
		      struct flowi *fl, struct dst_entry **dst_p)
{
	struct dst_entry *dst, *dst_prev;
	struct rtable *rt0 = (struct rtable*)(*dst_p);
	struct rtable *rt = rt0;
	u32 remote = fl->fl4_dst;
	u32 local  = fl->fl4_src;
	struct flowi fl_tunnel = {
		.nl_u = {
			.ip4_u = {
				.saddr = local,
				.daddr = remote,
				.tos = fl->fl4_tos
			}
		}
	};
	int i;
	int err;
	int header_len = 0;
	int trailer_len = 0;

	dst = dst_prev = NULL;
	dst_hold(&rt->u.dst);

	for (i = 0; i < nx; i++) {
		struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops);
		struct xfrm_dst *xdst;
		int tunnel = 0;

		if (unlikely(dst1 == NULL)) {
			err = -ENOBUFS;
			dst_release(&rt->u.dst);
			goto error;
		}

		if (!dst)
			dst = dst1;
		else {
			dst_prev->child = dst1;
			dst1->flags |= DST_NOHASH;
			dst_clone(dst1);
		}

		xdst = (struct xfrm_dst *)dst1;
		xdst->route = &rt->u.dst;
		xdst->genid = xfrm[i]->genid;

		dst1->next = dst_prev;
		dst_prev = dst1;
		if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
			remote = xfrm[i]->id.daddr.a4;
			local  = xfrm[i]->props.saddr.a4;
			tunnel = 1;
		}
		header_len += xfrm[i]->props.header_len;
		trailer_len += xfrm[i]->props.trailer_len;

		if (tunnel) {
			fl_tunnel.fl4_src = local;
			fl_tunnel.fl4_dst = remote;
			err = xfrm_dst_lookup((struct xfrm_dst **)&rt,
					      &fl_tunnel, AF_INET);
			if (err)
				goto error;
		} else
			dst_hold(&rt->u.dst);
	}

	dst_prev->child = &rt->u.dst;
	dst->path = &rt->u.dst;

	*dst_p = dst;
	dst = dst_prev;

	dst_prev = *dst_p;
	i = 0;
	for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
		struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;
		x->u.rt.fl = *fl;

		dst_prev->xfrm = xfrm[i++];
		dst_prev->dev = rt->u.dst.dev;
		if (rt->u.dst.dev)
			dev_hold(rt->u.dst.dev);
		dst_prev->obsolete	= -1;
		dst_prev->flags	       |= DST_HOST;
		dst_prev->lastuse	= jiffies;
		dst_prev->header_len	= header_len;
		dst_prev->nfheader_len	= 0;
		dst_prev->trailer_len	= trailer_len;
		memcpy(&dst_prev->metrics, &x->route->metrics, sizeof(dst_prev->metrics));

		/* Copy neighbout for reachability confirmation */
		dst_prev->neighbour	= neigh_clone(rt->u.dst.neighbour);
		dst_prev->input		= rt->u.dst.input;
		dst_prev->output	= xfrm4_output;
		if (rt->peer)
			atomic_inc(&rt->peer->refcnt);
		x->u.rt.peer = rt->peer;
		/* Sheit... I remember I did this right. Apparently,
		 * it was magically lost, so this code needs audit */
		x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
		x->u.rt.rt_type = rt->rt_type;
		x->u.rt.rt_src = rt0->rt_src;
		x->u.rt.rt_dst = rt0->rt_dst;
		x->u.rt.rt_gateway = rt->rt_gateway;
		x->u.rt.rt_spec_dst = rt0->rt_spec_dst;
		x->u.rt.idev = rt0->idev;
		in_dev_hold(rt0->idev);
		header_len -= x->u.dst.xfrm->props.header_len;
		trailer_len -= x->u.dst.xfrm->props.trailer_len;
	}

	xfrm_init_pmtu(dst);
	return 0;

error:
	if (dst)
		dst_free(dst);
	return err;
}
static void CVE_2015_2922_linux3_2_25_ndisc_router_discovery(struct sk_buff *skb)
{
	struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
	struct neighbour *neigh = NULL;
	struct inet6_dev *in6_dev;
	struct rt6_info *rt = NULL;
	int lifetime;
	struct ndisc_options ndopts;
	int optlen;
	unsigned int pref = 0;

	__u8 * opt = (__u8 *)(ra_msg + 1);

	optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);

	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
		ND_PRINTK2(KERN_WARNING
			   "ICMPv6 RA: source address is not link-local.\n");
		return;
	}
	if (optlen < 0) {
		ND_PRINTK2(KERN_WARNING
			   "ICMPv6 RA: packet too short\n");
		return;
	}

#ifdef CONFIG_IPV6_NDISC_NODETYPE
	if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
		ND_PRINTK2(KERN_WARNING
			   "ICMPv6 RA: from host or unauthorized router\n");
		return;
	}
#endif

	/*
	 *	set the RA_RECV flag in the interface
	 */

	in6_dev = __in6_dev_get(skb->dev);
	if (in6_dev == NULL) {
		ND_PRINTK0(KERN_ERR
			   "ICMPv6 RA: can't find inet6 device for %s.\n",
			   skb->dev->name);
		return;
	}

	if (!ndisc_parse_options(opt, optlen, &ndopts)) {
		ND_PRINTK2(KERN_WARNING
			   "ICMP6 RA: invalid ND options\n");
		return;
	}

	if (!accept_ra(in6_dev))
		goto skip_linkparms;

#ifdef CONFIG_IPV6_NDISC_NODETYPE
	/* skip link-specific parameters from interior routers */
	if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
		goto skip_linkparms;
#endif

	if (in6_dev->if_flags & IF_RS_SENT) {
		/*
		 *	flag that an RA was received after an RS was sent
		 *	out on this interface.
		 */
		in6_dev->if_flags |= IF_RA_RCVD;
	}

	/*
	 * Remember the managed/otherconf flags from most recently
	 * received RA message (RFC 2462) -- yoshfuji
	 */
	in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
				IF_RA_OTHERCONF)) |
				(ra_msg->icmph.icmp6_addrconf_managed ?
					IF_RA_MANAGED : 0) |
				(ra_msg->icmph.icmp6_addrconf_other ?
					IF_RA_OTHERCONF : 0);

	if (!in6_dev->cnf.accept_ra_defrtr)
		goto skip_defrtr;

	if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
		goto skip_defrtr;

	lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);

#ifdef CONFIG_IPV6_ROUTER_PREF
	pref = ra_msg->icmph.icmp6_router_pref;
	/* 10b is handled as if it were 00b (medium) */
	if (pref == ICMPV6_ROUTER_PREF_INVALID ||
	    !in6_dev->cnf.accept_ra_rtr_pref)
		pref = ICMPV6_ROUTER_PREF_MEDIUM;
#endif

	rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);

	if (rt)
		neigh = dst_get_neighbour(&rt->dst);

	if (rt && lifetime == 0) {
		neigh_clone(neigh);
		ip6_del_rt(rt);
		rt = NULL;
	}

	if (rt == NULL && lifetime) {
		ND_PRINTK3(KERN_DEBUG
			   "ICMPv6 RA: adding default router.\n");

		rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
		if (rt == NULL) {
			ND_PRINTK0(KERN_ERR
				   "ICMPv6 RA: %s() failed to add default route.\n",
				   __func__);
			return;
		}

		neigh = dst_get_neighbour(&rt->dst);
		if (neigh == NULL) {
			ND_PRINTK0(KERN_ERR
				   "ICMPv6 RA: %s() got default router without neighbour.\n",
				   __func__);
			dst_release(&rt->dst);
			return;
		}
		neigh->flags |= NTF_ROUTER;
	} else if (rt) {
		rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
	}

	if (rt)
		rt->rt6i_expires = jiffies + (HZ * lifetime);

	if (ra_msg->icmph.icmp6_hop_limit) {
		in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
		if (rt)
			dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
				       ra_msg->icmph.icmp6_hop_limit);
	}

skip_defrtr:

	/*
	 *	Update Reachable Time and Retrans Timer
	 */

	if (in6_dev->nd_parms) {
		unsigned long rtime = ntohl(ra_msg->retrans_timer);

		if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
			rtime = (rtime*HZ)/1000;
			if (rtime < HZ/10)
				rtime = HZ/10;
			in6_dev->nd_parms->retrans_time = rtime;
			in6_dev->tstamp = jiffies;
			inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
		}

		rtime = ntohl(ra_msg->reachable_time);
		if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
			rtime = (rtime*HZ)/1000;

			if (rtime < HZ/10)
				rtime = HZ/10;

			if (rtime != in6_dev->nd_parms->base_reachable_time) {
				in6_dev->nd_parms->base_reachable_time = rtime;
				in6_dev->nd_parms->gc_staletime = 3 * rtime;
				in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
				in6_dev->tstamp = jiffies;
				inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
			}
		}
	}

skip_linkparms:

	/*
	 *	Process options.
	 */

	if (!neigh)
		neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
				       skb->dev, 1);
	if (neigh) {
		u8 *lladdr = NULL;
		if (ndopts.nd_opts_src_lladdr) {
			lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
						     skb->dev);
			if (!lladdr) {
				ND_PRINTK2(KERN_WARNING
					   "ICMPv6 RA: invalid link-layer address length\n");
				goto out;
			}
		}
		neigh_update(neigh, lladdr, NUD_STALE,
			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
			     NEIGH_UPDATE_F_OVERRIDE|
			     NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
			     NEIGH_UPDATE_F_ISROUTER);
	}

	if (!accept_ra(in6_dev))
		goto out;

#ifdef CONFIG_IPV6_ROUTE_INFO
	if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
		goto skip_routeinfo;

	if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
		struct nd_opt_hdr *p;
		for (p = ndopts.nd_opts_ri;
		     p;
		     p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
			struct route_info *ri = (struct route_info *)p;
#ifdef CONFIG_IPV6_NDISC_NODETYPE
			if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
			    ri->prefix_len == 0)
				continue;
#endif
			if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
				continue;
			rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
				      &ipv6_hdr(skb)->saddr);
		}
	}

skip_routeinfo:
#endif

#ifdef CONFIG_IPV6_NDISC_NODETYPE
	/* skip link-specific ndopts from interior routers */
	if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
		goto out;
#endif

	if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
		struct nd_opt_hdr *p;
		for (p = ndopts.nd_opts_pi;
		     p;
		     p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
			addrconf_prefix_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3);
		}
	}

	if (ndopts.nd_opts_mtu) {
		__be32 n;
		u32 mtu;

		memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
		mtu = ntohl(n);

		if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
			ND_PRINTK2(KERN_WARNING
				   "ICMPv6 RA: invalid mtu: %d\n",
				   mtu);
		} else if (in6_dev->cnf.mtu6 != mtu) {
			in6_dev->cnf.mtu6 = mtu;

			if (rt)
				dst_metric_set(&rt->dst, RTAX_MTU, mtu);

			rt6_mtu_change(skb->dev, mtu);
		}
	}

	if (ndopts.nd_useropts) {
		struct nd_opt_hdr *p;
		for (p = ndopts.nd_useropts;
		     p;
		     p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
			ndisc_ra_useropt(skb, p);
		}
	}

	if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
		ND_PRINTK2(KERN_WARNING
			   "ICMPv6 RA: invalid RA options");
	}
out:
	if (rt)
		dst_release(&rt->dst);
	else if (neigh)
		neigh_release(neigh);
}
/* Create a child dst_entry with locked interface MTU, and attach it to `dst'. 
   This is needed on newer linux kernels and IP_ONLY_INTERCEPTOR builds,
   where the IP stack fragments packets to path MTU after ssh_interceptor_send.
*/
static struct dst_entry * 
interceptor_route_create_child_dst(struct dst_entry *dst, Boolean ipv6)
{
  struct dst_entry *child;
#ifdef LINUX_HAS_DST_COPY_METRICS
  SshUInt32 set;
  struct rt6_info *rt6;
  struct rtable *rt;
#endif /* LINUX_HAS_DST_COPY_METRICS */

  /* Allocate a dst_entry and copy relevant fields from dst. */
  child = SSH_DST_ALLOC(dst);
  if (child == NULL)
    return NULL;
  
  child->input = dst->input;
  child->output = dst->output;
  
  /* Child is not added to dst hash, and linux native IPsec is disabled. */
  child->flags |= (DST_NOHASH | DST_NOPOLICY | DST_NOXFRM);
  
  /* Copy route metrics and lock MTU to interface MTU. */
#ifdef LINUX_HAS_DST_COPY_METRICS
  if (ipv6 == TRUE)
    {
      rt6 = (struct rt6_info *)child;
      memset(&rt6->rt6i_table, 0, sizeof(*rt6) - sizeof(struct dst_entry));
    }
  else
    {
      rt = (struct rtable *)child;
      memset(&SSH_RTABLE_FIRST_MEMBER(rt), 0, 
	     sizeof(*rt) - sizeof(struct dst_entry));
    }
  
  dst_copy_metrics(child, dst);
  set = dst_metric(child, RTAX_LOCK);
  set |= 1 << RTAX_MTU;
  dst_metric_set(child, RTAX_LOCK, set);
#else  /* LINUX_HAS_DST_COPY_METRICS */
  memcpy(child->metrics, dst->metrics, sizeof(child->metrics));
  child->metrics[RTAX_LOCK-1] |= 1 << RTAX_MTU;
#endif /* LINUX_HAS_DST_COPY_METRICS */
  
#ifdef CONFIG_NET_CLS_ROUTE  
  child->tclassid = dst->tclassid;
#endif /* CONFIG_NET_CLS_ROUTE */
  
#ifdef CONFIG_XFRM 
  child->xfrm = NULL;
#endif /* CONFIG_XFRM */
  
#ifdef LINUX_HAS_HH_CACHE
  if (dst->hh)
    {
      atomic_inc(&dst->hh->hh_refcnt);
      child->hh = dst->hh;
    }
#endif /* LINUX_HAS_HH_CACHE */

#ifdef LINUX_HAS_DST_NEIGHBOUR_FUNCTIONS
  if (dst_get_neighbour(dst) != NULL)
    dst_set_neighbour(child, neigh_clone(dst_get_neighbour(dst)));
#else  /* LINUX_HAS_DST_NEIGHBOUR_FUNCTIONS */
  if (dst->neighbour != NULL)
    child->neighbour = neigh_clone(dst->neighbour);
#endif /* LINUX_HAS_DST_NEIGHBOUR_FUNCTIONS */
    
  if (dst->dev)
    {
      dev_hold(dst->dev);
      child->dev = dst->dev;
    }

  SSH_ASSERT(dst->child == NULL);
  dst->child = dst_clone(child);
  
  SSH_DEBUG(SSH_D_MIDOK, ("Allocated child %p dst_entry for dst %p mtu %d",
			  child, dst, dst_mtu(dst)));

  return child;
}