mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-06-17 20:25:19 +00:00
[IPV6]: Support Source Address Selection API (RFC5014).
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
This commit is contained in:
parent
1d5d236d30
commit
7cbca67c07
12 changed files with 146 additions and 13 deletions
|
@ -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 {
|
||||||
__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,
|
||||||
|
|
||||||
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 {
|
||||||
#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 {
|
||||||
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,
|
||||||
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,
|
||||||
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 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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 @@ done:
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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)
|
||||||
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,
|
||||||
|
|
||||||
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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue