Commit 7cbca67c authored by YOSHIFUJI Hideaki's avatar YOSHIFUJI Hideaki

[IPV6]: Support Source Address Selection API (RFC5014).

Signed-off-by: default avatarYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
parent 1d5d236d
...@@ -249,4 +249,15 @@ struct in6_flowlabel_req ...@@ -249,4 +249,15 @@ struct in6_flowlabel_req
* IP6T_SO_GET_REVISION_TARGET 69 * IP6T_SO_GET_REVISION_TARGET 69
*/ */
/* RFC5014: Source address selection */
#define IPV6_ADDR_PREFERENCES 72
#define IPV6_PREFER_SRC_TMP 0x0001
#define IPV6_PREFER_SRC_PUBLIC 0x0002
#define IPV6_PREFER_SRC_PUBTMP_DEFAULT 0x0100
#define IPV6_PREFER_SRC_COA 0x0004
#define IPV6_PREFER_SRC_HOME 0x0400
#define IPV6_PREFER_SRC_CGA 0x0008
#define IPV6_PREFER_SRC_NONCGA 0x0800
#endif #endif
...@@ -322,7 +322,11 @@ struct ipv6_pinfo { ...@@ -322,7 +322,11 @@ struct ipv6_pinfo {
__u8 recverr:1, __u8 recverr:1,
sndflow:1, sndflow:1,
pmtudisc:2, pmtudisc:2,
ipv6only:1; ipv6only:1,
srcprefs:3; /* 001: prefer temporary address
* 010: prefer public address
* 100: prefer care-of address
*/
__u8 tclass; __u8 tclass;
__u32 dst_cookie; __u32 dst_cookie;
......
...@@ -78,6 +78,7 @@ extern struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, ...@@ -78,6 +78,7 @@ extern struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net,
extern int ipv6_dev_get_saddr(struct net_device *dev, extern int ipv6_dev_get_saddr(struct net_device *dev,
struct in6_addr *daddr, struct in6_addr *daddr,
unsigned int srcprefs,
struct in6_addr *saddr); struct in6_addr *saddr);
extern int ipv6_get_lladdr(struct net_device *dev, extern int ipv6_get_lladdr(struct net_device *dev,
struct in6_addr *addr, struct in6_addr *addr,
......
...@@ -30,9 +30,12 @@ struct route_info { ...@@ -30,9 +30,12 @@ struct route_info {
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h> #include <linux/ipv6.h>
#define RT6_LOOKUP_F_IFACE 0x1 #define RT6_LOOKUP_F_IFACE 0x00000001
#define RT6_LOOKUP_F_REACHABLE 0x2 #define RT6_LOOKUP_F_REACHABLE 0x00000002
#define RT6_LOOKUP_F_HAS_SADDR 0x4 #define RT6_LOOKUP_F_HAS_SADDR 0x00000004
#define RT6_LOOKUP_F_SRCPREF_TMP 0x00000008
#define RT6_LOOKUP_F_SRCPREF_PUBLIC 0x00000010
#define RT6_LOOKUP_F_SRCPREF_COA 0x00000020
extern struct rt6_info *ip6_null_entry; extern struct rt6_info *ip6_null_entry;
......
...@@ -909,6 +909,7 @@ struct ipv6_saddr_dst { ...@@ -909,6 +909,7 @@ struct ipv6_saddr_dst {
int ifindex; int ifindex;
int scope; int scope;
int label; int label;
unsigned int prefs;
}; };
static inline int ipv6_saddr_preferred(int type) static inline int ipv6_saddr_preferred(int type)
...@@ -984,9 +985,12 @@ static int ipv6_get_saddr_eval(struct ipv6_saddr_score *score, ...@@ -984,9 +985,12 @@ static int ipv6_get_saddr_eval(struct ipv6_saddr_score *score,
break; break;
#ifdef CONFIG_IPV6_MIP6 #ifdef CONFIG_IPV6_MIP6
case IPV6_SADDR_RULE_HOA: case IPV6_SADDR_RULE_HOA:
{
/* Rule 4: Prefer home address */ /* Rule 4: Prefer home address */
ret = !!(score->ifa->flags & IFA_F_HOMEADDRESS); int prefhome = !(dst->prefs & IPV6_PREFER_SRC_COA);
ret = !(score->ifa->flags & IFA_F_HOMEADDRESS) ^ prefhome;
break; break;
}
#endif #endif
case IPV6_SADDR_RULE_OIF: case IPV6_SADDR_RULE_OIF:
/* Rule 5: Prefer outgoing interface */ /* Rule 5: Prefer outgoing interface */
...@@ -1000,11 +1004,16 @@ static int ipv6_get_saddr_eval(struct ipv6_saddr_score *score, ...@@ -1000,11 +1004,16 @@ static int ipv6_get_saddr_eval(struct ipv6_saddr_score *score,
break; break;
#ifdef CONFIG_IPV6_PRIVACY #ifdef CONFIG_IPV6_PRIVACY
case IPV6_SADDR_RULE_PRIVACY: case IPV6_SADDR_RULE_PRIVACY:
{
/* Rule 7: Prefer public address /* Rule 7: Prefer public address
* Note: prefer temprary address if use_tempaddr >= 2 * Note: prefer temprary address if use_tempaddr >= 2
*/ */
ret = (!(score->ifa->flags & IFA_F_TEMPORARY)) ^ (score->ifa->idev->cnf.use_tempaddr >= 2); int preftmp = dst->prefs & (IPV6_PREFER_SRC_PUBLIC|IPV6_PREFER_SRC_TMP) ?
!!(dst->prefs & IPV6_PREFER_SRC_TMP) :
score->ifa->idev->cnf.use_tempaddr >= 2;
ret = (!(score->ifa->flags & IFA_F_TEMPORARY)) ^ preftmp;
break; break;
}
#endif #endif
case IPV6_SADDR_RULE_ORCHID: case IPV6_SADDR_RULE_ORCHID:
/* Rule 8-: Prefer ORCHID vs ORCHID or /* Rule 8-: Prefer ORCHID vs ORCHID or
...@@ -1030,7 +1039,8 @@ static int ipv6_get_saddr_eval(struct ipv6_saddr_score *score, ...@@ -1030,7 +1039,8 @@ static int ipv6_get_saddr_eval(struct ipv6_saddr_score *score,
} }
int ipv6_dev_get_saddr(struct net_device *dst_dev, int ipv6_dev_get_saddr(struct net_device *dst_dev,
struct in6_addr *daddr, struct in6_addr *saddr) struct in6_addr *daddr, unsigned int prefs,
struct in6_addr *saddr)
{ {
struct ipv6_saddr_score scores[2], struct ipv6_saddr_score scores[2],
*score = &scores[0], *hiscore = &scores[1]; *score = &scores[0], *hiscore = &scores[1];
...@@ -1044,6 +1054,7 @@ int ipv6_dev_get_saddr(struct net_device *dst_dev, ...@@ -1044,6 +1054,7 @@ int ipv6_dev_get_saddr(struct net_device *dst_dev,
dst.ifindex = dst_dev ? dst_dev->ifindex : 0; dst.ifindex = dst_dev ? dst_dev->ifindex : 0;
dst.scope = __ipv6_addr_src_scope(dst_type); dst.scope = __ipv6_addr_src_scope(dst_type);
dst.label = ipv6_addr_label(daddr, dst_type, dst.ifindex); dst.label = ipv6_addr_label(daddr, dst_type, dst.ifindex);
dst.prefs = prefs;
hiscore->rule = -1; hiscore->rule = -1;
hiscore->ifa = NULL; hiscore->ifa = NULL;
......
...@@ -84,8 +84,18 @@ static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp, ...@@ -84,8 +84,18 @@ static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp,
if ((rule->flags & FIB_RULE_FIND_SADDR) && if ((rule->flags & FIB_RULE_FIND_SADDR) &&
r->src.plen && !(flags & RT6_LOOKUP_F_HAS_SADDR)) { r->src.plen && !(flags & RT6_LOOKUP_F_HAS_SADDR)) {
struct in6_addr saddr; struct in6_addr saddr;
unsigned int srcprefs = 0;
if (flags & RT6_LOOKUP_F_SRCPREF_TMP)
srcprefs |= IPV6_PREFER_SRC_TMP;
if (flags & RT6_LOOKUP_F_SRCPREF_PUBLIC)
srcprefs |= IPV6_PREFER_SRC_PUBLIC;
if (flags & RT6_LOOKUP_F_SRCPREF_COA)
srcprefs |= IPV6_PREFER_SRC_COA;
if (ipv6_dev_get_saddr(ip6_dst_idev(&rt->u.dst)->dev, if (ipv6_dev_get_saddr(ip6_dst_idev(&rt->u.dst)->dev,
&flp->fl6_dst, &saddr)) &flp->fl6_dst, srcprefs,
&saddr))
goto again; goto again;
if (!ipv6_prefix_equal(&saddr, &r->src.addr, if (!ipv6_prefix_equal(&saddr, &r->src.addr,
r->src.plen)) r->src.plen))
......
...@@ -920,7 +920,9 @@ static int ip6_dst_lookup_tail(struct sock *sk, ...@@ -920,7 +920,9 @@ static int ip6_dst_lookup_tail(struct sock *sk,
if (ipv6_addr_any(&fl->fl6_src)) { if (ipv6_addr_any(&fl->fl6_src)) {
err = ipv6_dev_get_saddr(ip6_dst_idev(*dst)->dev, err = ipv6_dev_get_saddr(ip6_dst_idev(*dst)->dev,
&fl->fl6_dst, &fl->fl6_src); &fl->fl6_dst,
sk ? inet6_sk(sk)->srcprefs : 0,
&fl->fl6_src);
if (err) if (err)
goto out_err_release; goto out_err_release;
} }
......
...@@ -617,7 +617,67 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -617,7 +617,67 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
retv = xfrm_user_policy(sk, optname, optval, optlen); retv = xfrm_user_policy(sk, optname, optval, optlen);
break; break;
case IPV6_ADDR_PREFERENCES:
{
unsigned int pref = 0;
unsigned int prefmask = ~0;
retv = -EINVAL;
/* check PUBLIC/TMP/PUBTMP_DEFAULT conflicts */
switch (val & (IPV6_PREFER_SRC_PUBLIC|
IPV6_PREFER_SRC_TMP|
IPV6_PREFER_SRC_PUBTMP_DEFAULT)) {
case IPV6_PREFER_SRC_PUBLIC:
pref |= IPV6_PREFER_SRC_PUBLIC;
break;
case IPV6_PREFER_SRC_TMP:
pref |= IPV6_PREFER_SRC_TMP;
break;
case IPV6_PREFER_SRC_PUBTMP_DEFAULT:
break;
case 0:
goto pref_skip_pubtmp;
default:
goto e_inval;
}
prefmask &= ~(IPV6_PREFER_SRC_PUBLIC|
IPV6_PREFER_SRC_TMP);
pref_skip_pubtmp:
/* check HOME/COA conflicts */
switch (val & (IPV6_PREFER_SRC_HOME|IPV6_PREFER_SRC_COA)) {
case IPV6_PREFER_SRC_HOME:
break;
case IPV6_PREFER_SRC_COA:
pref |= IPV6_PREFER_SRC_COA;
case 0:
goto pref_skip_coa;
default:
goto e_inval;
}
prefmask &= ~IPV6_PREFER_SRC_COA;
pref_skip_coa:
/* check CGA/NONCGA conflicts */
switch (val & (IPV6_PREFER_SRC_CGA|IPV6_PREFER_SRC_NONCGA)) {
case IPV6_PREFER_SRC_CGA:
case IPV6_PREFER_SRC_NONCGA:
case 0:
break;
default:
goto e_inval;
}
np->srcprefs = (np->srcprefs & prefmask) | pref;
retv = 0;
break;
}
} }
release_sock(sk); release_sock(sk);
return retv; return retv;
...@@ -932,6 +992,24 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, ...@@ -932,6 +992,24 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
val = np->sndflow; val = np->sndflow;
break; break;
case IPV6_ADDR_PREFERENCES:
val = 0;
if (np->srcprefs & IPV6_PREFER_SRC_TMP)
val |= IPV6_PREFER_SRC_TMP;
else if (np->srcprefs & IPV6_PREFER_SRC_PUBLIC)
val |= IPV6_PREFER_SRC_PUBLIC;
else {
/* XXX: should we return system default? */
val |= IPV6_PREFER_SRC_PUBTMP_DEFAULT;
}
if (np->srcprefs & IPV6_PREFER_SRC_COA)
val |= IPV6_PREFER_SRC_COA;
else
val |= IPV6_PREFER_SRC_HOME;
break;
default: default:
return -ENOPROTOOPT; return -ENOPROTOOPT;
} }
......
...@@ -546,7 +546,9 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, ...@@ -546,7 +546,9 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
override = 0; override = 0;
in6_ifa_put(ifp); in6_ifa_put(ifp);
} else { } else {
if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr)) if (ipv6_dev_get_saddr(dev, daddr,
inet6_sk(dev->nd_net->ipv6.ndisc_sk)->srcprefs,
&tmpaddr))
return; return;
src_addr = &tmpaddr; src_addr = &tmpaddr;
} }
......
...@@ -782,6 +782,15 @@ struct dst_entry * ip6_route_output(struct net *net, struct sock *sk, ...@@ -782,6 +782,15 @@ struct dst_entry * ip6_route_output(struct net *net, struct sock *sk,
if (!ipv6_addr_any(&fl->fl6_src)) if (!ipv6_addr_any(&fl->fl6_src))
flags |= RT6_LOOKUP_F_HAS_SADDR; flags |= RT6_LOOKUP_F_HAS_SADDR;
else if (sk) {
unsigned int prefs = inet6_sk(sk)->srcprefs;
if (prefs & IPV6_PREFER_SRC_TMP)
flags |= RT6_LOOKUP_F_SRCPREF_TMP;
if (prefs & IPV6_PREFER_SRC_PUBLIC)
flags |= RT6_LOOKUP_F_SRCPREF_PUBLIC;
if (prefs & IPV6_PREFER_SRC_COA)
flags |= RT6_LOOKUP_F_SRCPREF_COA;
}
return fib6_rule_lookup(net, fl, flags, ip6_pol_route_output); return fib6_rule_lookup(net, fl, flags, ip6_pol_route_output);
} }
...@@ -2162,7 +2171,7 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt, ...@@ -2162,7 +2171,7 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
else if (dst) { else if (dst) {
struct in6_addr saddr_buf; struct in6_addr saddr_buf;
if (ipv6_dev_get_saddr(ip6_dst_idev(&rt->u.dst)->dev, if (ipv6_dev_get_saddr(ip6_dst_idev(&rt->u.dst)->dev,
dst, &saddr_buf) == 0) dst, 0, &saddr_buf) == 0)
NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf); NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
} }
......
...@@ -58,7 +58,7 @@ static int xfrm6_get_saddr(xfrm_address_t *saddr, xfrm_address_t *daddr) ...@@ -58,7 +58,7 @@ static int xfrm6_get_saddr(xfrm_address_t *saddr, xfrm_address_t *daddr)
return -EHOSTUNREACH; return -EHOSTUNREACH;
ipv6_dev_get_saddr(ip6_dst_idev(dst)->dev, ipv6_dev_get_saddr(ip6_dst_idev(dst)->dev,
(struct in6_addr *)&daddr->a6, (struct in6_addr *)&daddr->a6, 0,
(struct in6_addr *)&saddr->a6); (struct in6_addr *)&saddr->a6);
dst_release(dst); dst_release(dst);
return 0; return 0;
......
...@@ -316,7 +316,9 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc, ...@@ -316,7 +316,9 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc,
if (!asoc) { if (!asoc) {
ipv6_dev_get_saddr(dst ? ip6_dst_idev(dst)->dev : NULL, ipv6_dev_get_saddr(dst ? ip6_dst_idev(dst)->dev : NULL,
&daddr->v6.sin6_addr, &saddr->v6.sin6_addr); &daddr->v6.sin6_addr,
inet6_sk(asoc->base.sk)->srcprefs,
&saddr->v6.sin6_addr);
SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: " NIP6_FMT "\n", SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: " NIP6_FMT "\n",
NIP6(saddr->v6.sin6_addr)); NIP6(saddr->v6.sin6_addr));
return; return;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment