⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dn_route.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	/*	 * If we have padding, remove it.	 */	if (flags & DN_RT_F_PF) {		padlen = flags & ~DN_RT_F_PF;		if (!pskb_may_pull(skb, padlen + 1))			goto dump_it;		skb_pull(skb, padlen);		flags = *skb->data;	}	skb_reset_network_header(skb);	/*	 * Weed out future version DECnet	 */	if (flags & DN_RT_F_VER)		goto dump_it;	cb->rt_flags = flags;	if (decnet_debug_level & 1)		printk(KERN_DEBUG			"dn_route_rcv: got 0x%02x from %s [%d %d %d]\n",			(int)flags, (dev) ? dev->name : "???", len, skb->len,			padlen);	if (flags & DN_RT_PKT_CNTL) {		if (unlikely(skb_linearize(skb)))			goto dump_it;		switch(flags & DN_RT_CNTL_MSK) {			case DN_RT_PKT_INIT:				dn_dev_init_pkt(skb);				break;			case DN_RT_PKT_VERI:				dn_dev_veri_pkt(skb);				break;		}		if (dn->parms.state != DN_DEV_S_RU)			goto dump_it;		switch(flags & DN_RT_CNTL_MSK) {			case DN_RT_PKT_HELO:				return NF_HOOK(PF_DECnet, NF_DN_HELLO, skb, skb->dev, NULL, dn_route_ptp_hello);			case DN_RT_PKT_L1RT:			case DN_RT_PKT_L2RT:				return NF_HOOK(PF_DECnet, NF_DN_ROUTE, skb, skb->dev, NULL, dn_route_discard);			case DN_RT_PKT_ERTH:				return NF_HOOK(PF_DECnet, NF_DN_HELLO, skb, skb->dev, NULL, dn_neigh_router_hello);			case DN_RT_PKT_EEDH:				return NF_HOOK(PF_DECnet, NF_DN_HELLO, skb, skb->dev, NULL, dn_neigh_endnode_hello);		}	} else {		if (dn->parms.state != DN_DEV_S_RU)			goto dump_it;		skb_pull(skb, 1); /* Pull flags */		switch(flags & DN_RT_PKT_MSK) {			case DN_RT_PKT_LONG:				return dn_route_rx_long(skb);			case DN_RT_PKT_SHORT:				return dn_route_rx_short(skb);		}	}dump_it:	kfree_skb(skb);out:	return NET_RX_DROP;}static int dn_output(struct sk_buff *skb){	struct dst_entry *dst = skb->dst;	struct dn_route *rt = (struct dn_route *)dst;	struct net_device *dev = dst->dev;	struct dn_skb_cb *cb = DN_SKB_CB(skb);	struct neighbour *neigh;	int err = -EINVAL;	if ((neigh = dst->neighbour) == NULL)		goto error;	skb->dev = dev;	cb->src = rt->rt_saddr;	cb->dst = rt->rt_daddr;	/*	 * Always set the Intra-Ethernet bit on all outgoing packets	 * originated on this node. Only valid flag from upper layers	 * is return-to-sender-requested. Set hop count to 0 too.	 */	cb->rt_flags &= ~DN_RT_F_RQR;	cb->rt_flags |= DN_RT_F_IE;	cb->hops = 0;	return NF_HOOK(PF_DECnet, NF_DN_LOCAL_OUT, skb, NULL, dev, neigh->output);error:	if (net_ratelimit())		printk(KERN_DEBUG "dn_output: This should not happen\n");	kfree_skb(skb);	return err;}static int dn_forward(struct sk_buff *skb){	struct dn_skb_cb *cb = DN_SKB_CB(skb);	struct dst_entry *dst = skb->dst;	struct dn_dev *dn_db = dst->dev->dn_ptr;	struct dn_route *rt;	struct neighbour *neigh = dst->neighbour;	int header_len;#ifdef CONFIG_NETFILTER	struct net_device *dev = skb->dev;#endif	if (skb->pkt_type != PACKET_HOST)		goto drop;	/* Ensure that we have enough space for headers */	rt = (struct dn_route *)skb->dst;	header_len = dn_db->use_long ? 21 : 6;	if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+header_len))		goto drop;	/*	 * Hop count exceeded.	 */	if (++cb->hops > 30)		goto drop;	skb->dev = rt->u.dst.dev;	/*	 * If packet goes out same interface it came in on, then set	 * the Intra-Ethernet bit. This has no effect for short	 * packets, so we don't need to test for them here.	 */	cb->rt_flags &= ~DN_RT_F_IE;	if (rt->rt_flags & RTCF_DOREDIRECT)		cb->rt_flags |= DN_RT_F_IE;	return NF_HOOK(PF_DECnet, NF_DN_FORWARD, skb, dev, skb->dev, neigh->output);drop:	kfree_skb(skb);	return NET_RX_DROP;}/* * Drop packet. This is used for endnodes and for * when we should not be forwarding packets from * this dest. */static int dn_blackhole(struct sk_buff *skb){	kfree_skb(skb);	return NET_RX_DROP;}/* * Used to catch bugs. This should never normally get * called. */static int dn_rt_bug(struct sk_buff *skb){	if (net_ratelimit()) {		struct dn_skb_cb *cb = DN_SKB_CB(skb);		printk(KERN_DEBUG "dn_rt_bug: skb from:%04x to:%04x\n",				dn_ntohs(cb->src), dn_ntohs(cb->dst));	}	kfree_skb(skb);	return NET_RX_BAD;}static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res){	struct dn_fib_info *fi = res->fi;	struct net_device *dev = rt->u.dst.dev;	struct neighbour *n;	unsigned mss;	if (fi) {		if (DN_FIB_RES_GW(*res) &&		    DN_FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK)			rt->rt_gateway = DN_FIB_RES_GW(*res);		memcpy(rt->u.dst.metrics, fi->fib_metrics,		       sizeof(rt->u.dst.metrics));	}	rt->rt_type = res->type;	if (dev != NULL && rt->u.dst.neighbour == NULL) {		n = __neigh_lookup_errno(&dn_neigh_table, &rt->rt_gateway, dev);		if (IS_ERR(n))			return PTR_ERR(n);		rt->u.dst.neighbour = n;	}	if (rt->u.dst.metrics[RTAX_MTU-1] == 0 ||	    rt->u.dst.metrics[RTAX_MTU-1] > rt->u.dst.dev->mtu)		rt->u.dst.metrics[RTAX_MTU-1] = rt->u.dst.dev->mtu;	mss = dn_mss_from_pmtu(dev, dst_mtu(&rt->u.dst));	if (rt->u.dst.metrics[RTAX_ADVMSS-1] == 0 ||	    rt->u.dst.metrics[RTAX_ADVMSS-1] > mss)		rt->u.dst.metrics[RTAX_ADVMSS-1] = mss;	return 0;}static inline int dn_match_addr(__le16 addr1, __le16 addr2){	__u16 tmp = dn_ntohs(addr1) ^ dn_ntohs(addr2);	int match = 16;	while(tmp) {		tmp >>= 1;		match--;	}	return match;}static __le16 dnet_select_source(const struct net_device *dev, __le16 daddr, int scope){	__le16 saddr = 0;	struct dn_dev *dn_db = dev->dn_ptr;	struct dn_ifaddr *ifa;	int best_match = 0;	int ret;	read_lock(&dev_base_lock);	for(ifa = dn_db->ifa_list; ifa; ifa = ifa->ifa_next) {		if (ifa->ifa_scope > scope)			continue;		if (!daddr) {			saddr = ifa->ifa_local;			break;		}		ret = dn_match_addr(daddr, ifa->ifa_local);		if (ret > best_match)			saddr = ifa->ifa_local;		if (best_match == 0)			saddr = ifa->ifa_local;	}	read_unlock(&dev_base_lock);	return saddr;}static inline __le16 __dn_fib_res_prefsrc(struct dn_fib_res *res){	return dnet_select_source(DN_FIB_RES_DEV(*res), DN_FIB_RES_GW(*res), res->scope);}static inline __le16 dn_fib_rules_map_destination(__le16 daddr, struct dn_fib_res *res){	__le16 mask = dnet_make_mask(res->prefixlen);	return (daddr&~mask)|res->fi->fib_nh->nh_gw;}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,				     } },			    .mark = oldflp->mark,			    .iif = init_net.loopback_dev->ifindex,			    .oif = oldflp->oif };	struct dn_route *rt = NULL;	struct net_device *dev_out = NULL, *dev;	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->mark, init_net.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(&init_net, 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_each_netdev(&init_net, dev) {			if (!dev->dn_ptr)				continue;			if (!dn_dev_islocal(dev, oldflp->fld_src))				continue;			if ((dev->flags & IFF_LOOPBACK) &&			    oldflp->fld_dst &&			    !dn_dev_islocal(dev, oldflp->fld_dst))				continue;			dev_out = dev;			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 = init_net.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 = init_net.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 = init_net.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 = init_net.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 = init_net.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;	rt->fl.mark       = oldflp->mark;	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;}/* * N.B. The flags may be moved into the flowi at some future stage. */static int __dn_route_output_key(struct dst_entry **pprt, const struct flowi *flp, int flags){	unsigned hash = dn_hash(flp->fld_src, flp->fld_dst);	struct dn_route *rt = NULL;	if (!(flags & MSG_TRYHARD)) {		rcu_read_lock_bh();		for(rt = rcu_dereference(dn_rt_hash_table[hash].chain); rt;			rt = rcu_dereference(rt->u.dst.dn_next)) {			if ((flp->fld_dst == rt->fl.fld_dst) &&			    (flp->fld_src == rt->fl.fld_src) &&			    (flp->mark == rt->fl.mark) &&			    (rt->fl.iif == 0) &&			    (rt->fl.oif == flp->oif)) {				dst_use(&rt->u.dst, jiffies);				rcu_read_unlock_bh();				*pprt = &rt->u.dst;				return 0;			}		}		rcu_read_unlock_bh();	}	return dn_route_output_slow(pprt, flp, flags);}static int dn_route_output_key(struct dst_entry **pprt, struct flowi *flp, int flags){	int err;	err = __dn_route_output_key(pprt, flp, flags);	if (err == 0 && flp->proto) {		err = xfrm_lookup(pprt, flp, NULL, 0);	}	return err;}int dn_route_output_sock(struct dst_entry **pprt, struct flowi *fl, struct sock *sk, int flags){	int err;	err = __dn_route_output_key(pprt, fl, flags & MSG_TRYHARD);	if (err == 0 && fl->proto) {		err = xfrm_lookup(pprt, fl, sk, !(flags & MSG_DONTWAIT));	}	return err;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -