diff options
Diffstat (limited to 'net/decnet/dn_fib.c')
| -rw-r--r-- | net/decnet/dn_fib.c | 307 | 
1 files changed, 164 insertions, 143 deletions
diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c index 0ef0a81bcd7..d332aefb084 100644 --- a/net/decnet/dn_fib.c +++ b/net/decnet/dn_fib.c @@ -30,7 +30,7 @@  #include <linux/netdevice.h>  #include <linux/timer.h>  #include <linux/spinlock.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include <asm/uaccess.h>  #include <net/neighbour.h>  #include <net/dst.h> @@ -145,22 +145,10 @@ static inline struct dn_fib_info *dn_fib_find_info(const struct dn_fib_info *nfi  	return NULL;  } -__le16 dn_fib_get_attr16(struct rtattr *attr, int attrlen, int type) +static int dn_fib_count_nhs(const struct nlattr *attr)  { -	while(RTA_OK(attr,attrlen)) { -		if (attr->rta_type == type) -			return *(__le16*)RTA_DATA(attr); -		attr = RTA_NEXT(attr, attrlen); -	} - -	return 0; -} - -static int dn_fib_count_nhs(struct rtattr *rta) -{ -	int nhs = 0; -	struct rtnexthop *nhp = RTA_DATA(rta); -	int nhlen = RTA_PAYLOAD(rta); +	struct rtnexthop *nhp = nla_data(attr); +	int nhs = 0, nhlen = nla_len(attr);  	while(nhlen >= (int)sizeof(struct rtnexthop)) {  		if ((nhlen -= nhp->rtnh_len) < 0) @@ -172,10 +160,11 @@ static int dn_fib_count_nhs(struct rtattr *rta)  	return nhs;  } -static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct rtattr *rta, const struct rtmsg *r) +static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct nlattr *attr, +			  const struct rtmsg *r)  { -	struct rtnexthop *nhp = RTA_DATA(rta); -	int nhlen = RTA_PAYLOAD(rta); +	struct rtnexthop *nhp = nla_data(attr); +	int nhlen = nla_len(attr);  	change_nexthops(fi) {  		int attrlen = nhlen - sizeof(struct rtnexthop); @@ -187,7 +176,10 @@ static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct rtattr *rta, cons  		nh->nh_weight = nhp->rtnh_hops + 1;  		if (attrlen) { -			nh->nh_gw = dn_fib_get_attr16(RTNH_DATA(nhp), attrlen, RTA_GATEWAY); +			struct nlattr *gw_attr; + +			gw_attr = nla_find((struct nlattr *) (nhp + 1), attrlen, RTA_GATEWAY); +			nh->nh_gw = gw_attr ? nla_get_le16(gw_attr) : 0;  		}  		nhp = RTNH_NEXT(nhp);  	} endfor_nexthops(fi); @@ -201,7 +193,7 @@ static int dn_fib_check_nh(const struct rtmsg *r, struct dn_fib_info *fi, struct  	int err;  	if (nh->nh_gw) { -		struct flowi fl; +		struct flowidn fld;  		struct dn_fib_res res;  		if (nh->nh_flags&RTNH_F_ONLINK) { @@ -221,15 +213,15 @@ static int dn_fib_check_nh(const struct rtmsg *r, struct dn_fib_info *fi, struct  			return 0;  		} -		memset(&fl, 0, sizeof(fl)); -		fl.fld_dst = nh->nh_gw; -		fl.oif = nh->nh_oif; -		fl.fld_scope = r->rtm_scope + 1; +		memset(&fld, 0, sizeof(fld)); +		fld.daddr = nh->nh_gw; +		fld.flowidn_oif = nh->nh_oif; +		fld.flowidn_scope = r->rtm_scope + 1; -		if (fl.fld_scope < RT_SCOPE_LINK) -			fl.fld_scope = RT_SCOPE_LINK; +		if (fld.flowidn_scope < RT_SCOPE_LINK) +			fld.flowidn_scope = RT_SCOPE_LINK; -		if ((err = dn_fib_lookup(&fl, &res)) != 0) +		if ((err = dn_fib_lookup(&fld, &res)) != 0)  			return err;  		err = -EINVAL; @@ -268,7 +260,8 @@ out:  } -struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta *rta, const struct nlmsghdr *nlh, int *errp) +struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct nlattr *attrs[], +				       const struct nlmsghdr *nlh, int *errp)  {  	int err;  	struct dn_fib_info *fi = NULL; @@ -281,11 +274,9 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta  	if (dn_fib_props[r->rtm_type].scope > r->rtm_scope)  		goto err_inval; -	if (rta->rta_mp) { -		nhs = dn_fib_count_nhs(rta->rta_mp); -		if (nhs == 0) -			goto err_inval; -	} +	if (attrs[RTA_MULTIPATH] && +	    (nhs = dn_fib_count_nhs(attrs[RTA_MULTIPATH])) == 0) +		goto err_inval;  	fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct dn_fib_nh), GFP_KERNEL);  	err = -ENOBUFS; @@ -295,52 +286,65 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta  	fi->fib_protocol = r->rtm_protocol;  	fi->fib_nhs = nhs;  	fi->fib_flags = r->rtm_flags; -	if (rta->rta_priority) -		fi->fib_priority = *rta->rta_priority; -	if (rta->rta_mx) { -		int attrlen = RTA_PAYLOAD(rta->rta_mx); -		struct rtattr *attr = RTA_DATA(rta->rta_mx); - -		while(RTA_OK(attr, attrlen)) { -			unsigned flavour = attr->rta_type; -			if (flavour) { -				if (flavour > RTAX_MAX) + +	if (attrs[RTA_PRIORITY]) +		fi->fib_priority = nla_get_u32(attrs[RTA_PRIORITY]); + +	if (attrs[RTA_METRICS]) { +		struct nlattr *attr; +		int rem; + +		nla_for_each_nested(attr, attrs[RTA_METRICS], rem) { +			int type = nla_type(attr); + +			if (type) { +				if (type > RTAX_MAX || nla_len(attr) < 4)  					goto err_inval; -				fi->fib_metrics[flavour-1] = *(unsigned*)RTA_DATA(attr); + +				fi->fib_metrics[type-1] = nla_get_u32(attr);  			} -			attr = RTA_NEXT(attr, attrlen);  		}  	} -	if (rta->rta_prefsrc) -		memcpy(&fi->fib_prefsrc, rta->rta_prefsrc, 2); -	if (rta->rta_mp) { -		if ((err = dn_fib_get_nhs(fi, rta->rta_mp, r)) != 0) +	if (attrs[RTA_PREFSRC]) +		fi->fib_prefsrc = nla_get_le16(attrs[RTA_PREFSRC]); + +	if (attrs[RTA_MULTIPATH]) { +		if ((err = dn_fib_get_nhs(fi, attrs[RTA_MULTIPATH], r)) != 0)  			goto failure; -		if (rta->rta_oif && fi->fib_nh->nh_oif != *rta->rta_oif) + +		if (attrs[RTA_OIF] && +		    fi->fib_nh->nh_oif != nla_get_u32(attrs[RTA_OIF]))  			goto err_inval; -		if (rta->rta_gw && memcmp(&fi->fib_nh->nh_gw, rta->rta_gw, 2)) + +		if (attrs[RTA_GATEWAY] && +		    fi->fib_nh->nh_gw != nla_get_le16(attrs[RTA_GATEWAY]))  			goto err_inval;  	} else {  		struct dn_fib_nh *nh = fi->fib_nh; -		if (rta->rta_oif) -			nh->nh_oif = *rta->rta_oif; -		if (rta->rta_gw) -			memcpy(&nh->nh_gw, rta->rta_gw, 2); + +		if (attrs[RTA_OIF]) +			nh->nh_oif = nla_get_u32(attrs[RTA_OIF]); + +		if (attrs[RTA_GATEWAY]) +			nh->nh_gw = nla_get_le16(attrs[RTA_GATEWAY]); +  		nh->nh_flags = r->rtm_flags;  		nh->nh_weight = 1;  	}  	if (r->rtm_type == RTN_NAT) { -		if (rta->rta_gw == NULL || nhs != 1 || rta->rta_oif) +		if (!attrs[RTA_GATEWAY] || nhs != 1 || attrs[RTA_OIF])  			goto err_inval; -		memcpy(&fi->fib_nh->nh_gw, rta->rta_gw, 2); + +		fi->fib_nh->nh_gw = nla_get_le16(attrs[RTA_GATEWAY]);  		goto link_it;  	}  	if (dn_fib_props[r->rtm_type].error) { -		if (rta->rta_gw || rta->rta_oif || rta->rta_mp) +		if (attrs[RTA_GATEWAY] || attrs[RTA_OIF] || attrs[RTA_MULTIPATH])  			goto err_inval; +  		goto link_it;  	} @@ -366,8 +370,8 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta  	}  	if (fi->fib_prefsrc) { -		if (r->rtm_type != RTN_LOCAL || rta->rta_dst == NULL || -		    memcmp(&fi->fib_prefsrc, rta->rta_dst, 2)) +		if (r->rtm_type != RTN_LOCAL || !attrs[RTA_DST] || +		    fi->fib_prefsrc != nla_get_le16(attrs[RTA_DST]))  			if (dnet_addr_type(fi->fib_prefsrc) != RTN_LOCAL)  				goto err_inval;  	} @@ -404,7 +408,7 @@ failure:  	return NULL;  } -int dn_fib_semantic_match(int type, struct dn_fib_info *fi, const struct flowi *fl, struct dn_fib_res *res) +int dn_fib_semantic_match(int type, struct dn_fib_info *fi, const struct flowidn *fld, struct dn_fib_res *res)  {  	int err = dn_fib_props[type].error; @@ -414,38 +418,39 @@ int dn_fib_semantic_match(int type, struct dn_fib_info *fi, const struct flowi *  		res->fi = fi; -		switch(type) { -			case RTN_NAT: -				DN_FIB_RES_RESET(*res); +		switch (type) { +		case RTN_NAT: +			DN_FIB_RES_RESET(*res); +			atomic_inc(&fi->fib_clntref); +			return 0; +		case RTN_UNICAST: +		case RTN_LOCAL: +			for_nexthops(fi) { +				if (nh->nh_flags & RTNH_F_DEAD) +					continue; +				if (!fld->flowidn_oif || +				    fld->flowidn_oif == nh->nh_oif) +					break; +			} +			if (nhsel < fi->fib_nhs) { +				res->nh_sel = nhsel;  				atomic_inc(&fi->fib_clntref);  				return 0; -			case RTN_UNICAST: -			case RTN_LOCAL: -				for_nexthops(fi) { -					if (nh->nh_flags & RTNH_F_DEAD) -						continue; -					if (!fl->oif || fl->oif == nh->nh_oif) -						break; -				} -				if (nhsel < fi->fib_nhs) { -					res->nh_sel = nhsel; -					atomic_inc(&fi->fib_clntref); -					return 0; -				} -				endfor_nexthops(fi); -				res->fi = NULL; -				return 1; -			default: -				if (net_ratelimit()) -					 printk("DECnet: impossible routing event : dn_fib_semantic_match type=%d\n", type); -				res->fi = NULL; -				return -EINVAL; +			} +			endfor_nexthops(fi); +			res->fi = NULL; +			return 1; +		default: +			net_err_ratelimited("DECnet: impossible routing event : dn_fib_semantic_match type=%d\n", +					    type); +			res->fi = NULL; +			return -EINVAL;  		}  	}  	return err;  } -void dn_fib_select_multipath(const struct flowi *fl, struct dn_fib_res *res) +void dn_fib_select_multipath(const struct flowidn *fld, struct dn_fib_res *res)  {  	struct dn_fib_info *fi = res->fi;  	int w; @@ -484,63 +489,62 @@ void dn_fib_select_multipath(const struct flowi *fl, struct dn_fib_res *res)  	spin_unlock_bh(&dn_fib_multipath_lock);  } - -static int dn_fib_check_attr(struct rtmsg *r, struct rtattr **rta) +static inline u32 rtm_get_table(struct nlattr *attrs[], u8 table)  { -	int i; +	if (attrs[RTA_TABLE]) +		table = nla_get_u32(attrs[RTA_TABLE]); -	for(i = 1; i <= RTA_MAX; i++) { -		struct rtattr *attr = rta[i-1]; -		if (attr) { -			if (RTA_PAYLOAD(attr) < 4 && RTA_PAYLOAD(attr) != 2) -				return -EINVAL; -			if (i != RTA_MULTIPATH && i != RTA_METRICS && -			    i != RTA_TABLE) -				rta[i-1] = (struct rtattr *)RTA_DATA(attr); -		} -	} - -	return 0; +	return table;  } -static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)  {  	struct net *net = sock_net(skb->sk);  	struct dn_fib_table *tb; -	struct rtattr **rta = arg; -	struct rtmsg *r = NLMSG_DATA(nlh); +	struct rtmsg *r = nlmsg_data(nlh); +	struct nlattr *attrs[RTA_MAX+1]; +	int err; + +	if (!netlink_capable(skb, CAP_NET_ADMIN)) +		return -EPERM;  	if (!net_eq(net, &init_net))  		return -EINVAL; -	if (dn_fib_check_attr(r, rta)) -		return -EINVAL; +	err = nlmsg_parse(nlh, sizeof(*r), attrs, RTA_MAX, rtm_dn_policy); +	if (err < 0) +		return err; -	tb = dn_fib_get_table(rtm_get_table(rta, r->rtm_table), 0); -	if (tb) -		return tb->delete(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb)); +	tb = dn_fib_get_table(rtm_get_table(attrs, r->rtm_table), 0); +	if (!tb) +		return -ESRCH; -	return -ESRCH; +	return tb->delete(tb, r, attrs, nlh, &NETLINK_CB(skb));  } -static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)  {  	struct net *net = sock_net(skb->sk);  	struct dn_fib_table *tb; -	struct rtattr **rta = arg; -	struct rtmsg *r = NLMSG_DATA(nlh); +	struct rtmsg *r = nlmsg_data(nlh); +	struct nlattr *attrs[RTA_MAX+1]; +	int err; + +	if (!netlink_capable(skb, CAP_NET_ADMIN)) +		return -EPERM;  	if (!net_eq(net, &init_net))  		return -EINVAL; -	if (dn_fib_check_attr(r, rta)) -		return -EINVAL; +	err = nlmsg_parse(nlh, sizeof(*r), attrs, RTA_MAX, rtm_dn_policy); +	if (err < 0) +		return err; -	tb = dn_fib_get_table(rtm_get_table(rta, r->rtm_table), 1); -	if (tb) -		return tb->insert(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb)); +	tb = dn_fib_get_table(rtm_get_table(attrs, r->rtm_table), 1); +	if (!tb) +		return -ENOBUFS; -	return -ENOBUFS; +	return tb->insert(tb, r, attrs, nlh, &NETLINK_CB(skb));  }  static void fib_magic(int cmd, int type, __le16 dst, int dst_len, struct dn_ifaddr *ifa) @@ -550,10 +554,31 @@ static void fib_magic(int cmd, int type, __le16 dst, int dst_len, struct dn_ifad  		struct nlmsghdr nlh;  		struct rtmsg rtm;  	} req; -	struct dn_kern_rta rta; +	struct { +		struct nlattr hdr; +		__le16 dst; +	} dst_attr = { +		.dst = dst, +	}; +	struct { +		struct nlattr hdr; +		__le16 prefsrc; +	} prefsrc_attr = { +		.prefsrc = ifa->ifa_local, +	}; +	struct { +		struct nlattr hdr; +		u32 oif; +	} oif_attr = { +		.oif = ifa->ifa_dev->dev->ifindex, +	}; +	struct nlattr *attrs[RTA_MAX+1] = { +		[RTA_DST] = (struct nlattr *) &dst_attr, +		[RTA_PREFSRC] = (struct nlattr * ) &prefsrc_attr, +		[RTA_OIF] = (struct nlattr *) &oif_attr, +	};  	memset(&req.rtm, 0, sizeof(req.rtm)); -	memset(&rta, 0, sizeof(rta));  	if (type == RTN_UNICAST)  		tb = dn_fib_get_table(RT_MIN_TABLE, 1); @@ -575,14 +600,10 @@ static void fib_magic(int cmd, int type, __le16 dst, int dst_len, struct dn_ifad  	req.rtm.rtm_scope = (type != RTN_LOCAL ? RT_SCOPE_LINK : RT_SCOPE_HOST);  	req.rtm.rtm_type = type; -	rta.rta_dst = &dst; -	rta.rta_prefsrc = &ifa->ifa_local; -	rta.rta_oif = &ifa->ifa_dev->dev->ifindex; -  	if (cmd == RTM_NEWROUTE) -		tb->insert(tb, &req.rtm, &rta, &req.nlh, NULL); +		tb->insert(tb, &req.rtm, attrs, &req.nlh, NULL);  	else -		tb->delete(tb, &req.rtm, &rta, &req.nlh, NULL); +		tb->delete(tb, &req.rtm, attrs, &req.nlh, NULL);  }  static void dn_fib_add_ifaddr(struct dn_ifaddr *ifa) @@ -646,20 +667,20 @@ static int dn_fib_dnaddr_event(struct notifier_block *this, unsigned long event,  {  	struct dn_ifaddr *ifa = (struct dn_ifaddr *)ptr; -	switch(event) { -		case NETDEV_UP: -			dn_fib_add_ifaddr(ifa); -			dn_fib_sync_up(ifa->ifa_dev->dev); +	switch (event) { +	case NETDEV_UP: +		dn_fib_add_ifaddr(ifa); +		dn_fib_sync_up(ifa->ifa_dev->dev); +		dn_rt_cache_flush(-1); +		break; +	case NETDEV_DOWN: +		dn_fib_del_ifaddr(ifa); +		if (ifa->ifa_dev && ifa->ifa_dev->ifa_list == NULL) { +			dn_fib_disable_addr(ifa->ifa_dev->dev, 1); +		} else {  			dn_rt_cache_flush(-1); -			break; -		case NETDEV_DOWN: -			dn_fib_del_ifaddr(ifa); -			if (ifa->ifa_dev && ifa->ifa_dev->ifa_list == NULL) { -				dn_fib_disable_addr(ifa->ifa_dev->dev, 1); -			} else { -				dn_rt_cache_flush(-1); -			} -			break; +		} +		break;  	}  	return NOTIFY_DONE;  } @@ -762,8 +783,8 @@ void __init dn_fib_init(void)  	register_dnaddr_notifier(&dn_fib_dnaddr_notifier); -	rtnl_register(PF_DECnet, RTM_NEWROUTE, dn_fib_rtm_newroute, NULL); -	rtnl_register(PF_DECnet, RTM_DELROUTE, dn_fib_rtm_delroute, NULL); +	rtnl_register(PF_DECnet, RTM_NEWROUTE, dn_fib_rtm_newroute, NULL, NULL); +	rtnl_register(PF_DECnet, RTM_DELROUTE, dn_fib_rtm_delroute, NULL, NULL);  }  | 
