diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/ila/ila.h | 79 | ||||
-rw-r--r-- | net/ipv6/ila/ila_common.c | 81 | ||||
-rw-r--r-- | net/ipv6/ila/ila_lwt.c | 49 | ||||
-rw-r--r-- | net/ipv6/ila/ila_xlat.c | 161 |
4 files changed, 252 insertions, 118 deletions
diff --git a/net/ipv6/ila/ila.h b/net/ipv6/ila/ila.h index 28542cb2b387..d08fd2d48a78 100644 --- a/net/ipv6/ila/ila.h +++ b/net/ipv6/ila/ila.h | |||
@@ -23,10 +23,76 @@ | |||
23 | #include <net/protocol.h> | 23 | #include <net/protocol.h> |
24 | #include <uapi/linux/ila.h> | 24 | #include <uapi/linux/ila.h> |
25 | 25 | ||
26 | struct ila_locator { | ||
27 | union { | ||
28 | __u8 v8[8]; | ||
29 | __be16 v16[4]; | ||
30 | __be32 v32[2]; | ||
31 | __be64 v64; | ||
32 | }; | ||
33 | }; | ||
34 | |||
35 | struct ila_identifier { | ||
36 | union { | ||
37 | struct { | ||
38 | #if defined(__LITTLE_ENDIAN_BITFIELD) | ||
39 | u8 __space:4; | ||
40 | u8 csum_neutral:1; | ||
41 | u8 type:3; | ||
42 | #elif defined(__BIG_ENDIAN_BITFIELD) | ||
43 | u8 type:3; | ||
44 | u8 csum_neutral:1; | ||
45 | u8 __space:4; | ||
46 | #else | ||
47 | #error "Adjust your <asm/byteorder.h> defines" | ||
48 | #endif | ||
49 | u8 __space2[7]; | ||
50 | }; | ||
51 | __u8 v8[8]; | ||
52 | __be16 v16[4]; | ||
53 | __be32 v32[2]; | ||
54 | __be64 v64; | ||
55 | }; | ||
56 | }; | ||
57 | |||
58 | enum { | ||
59 | ILA_ATYPE_IID = 0, | ||
60 | ILA_ATYPE_LUID, | ||
61 | ILA_ATYPE_VIRT_V4, | ||
62 | ILA_ATYPE_VIRT_UNI_V6, | ||
63 | ILA_ATYPE_VIRT_MULTI_V6, | ||
64 | ILA_ATYPE_RSVD_1, | ||
65 | ILA_ATYPE_RSVD_2, | ||
66 | ILA_ATYPE_RSVD_3, | ||
67 | }; | ||
68 | |||
69 | #define CSUM_NEUTRAL_FLAG htonl(0x10000000) | ||
70 | |||
71 | struct ila_addr { | ||
72 | union { | ||
73 | struct in6_addr addr; | ||
74 | struct { | ||
75 | struct ila_locator loc; | ||
76 | struct ila_identifier ident; | ||
77 | }; | ||
78 | }; | ||
79 | }; | ||
80 | |||
81 | static inline struct ila_addr *ila_a2i(struct in6_addr *addr) | ||
82 | { | ||
83 | return (struct ila_addr *)addr; | ||
84 | } | ||
85 | |||
86 | static inline bool ila_addr_is_ila(struct ila_addr *iaddr) | ||
87 | { | ||
88 | return (iaddr->ident.type != ILA_ATYPE_IID); | ||
89 | } | ||
90 | |||
26 | struct ila_params { | 91 | struct ila_params { |
27 | __be64 locator; | 92 | struct ila_locator locator; |
28 | __be64 locator_match; | 93 | struct ila_locator locator_match; |
29 | __wsum csum_diff; | 94 | __wsum csum_diff; |
95 | u8 csum_mode; | ||
30 | }; | 96 | }; |
31 | 97 | ||
32 | static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to) | 98 | static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to) |
@@ -38,7 +104,14 @@ static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to) | |||
38 | return csum_partial(diff, sizeof(diff), 0); | 104 | return csum_partial(diff, sizeof(diff), 0); |
39 | } | 105 | } |
40 | 106 | ||
41 | void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p); | 107 | static inline bool ila_csum_neutral_set(struct ila_identifier ident) |
108 | { | ||
109 | return !!(ident.csum_neutral); | ||
110 | } | ||
111 | |||
112 | void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p); | ||
113 | |||
114 | void ila_init_saved_csum(struct ila_params *p); | ||
42 | 115 | ||
43 | int ila_lwt_init(void); | 116 | int ila_lwt_init(void); |
44 | void ila_lwt_fini(void); | 117 | void ila_lwt_fini(void); |
diff --git a/net/ipv6/ila/ila_common.c b/net/ipv6/ila/ila_common.c index 30613050e4ca..0e94042d1289 100644 --- a/net/ipv6/ila/ila_common.c +++ b/net/ipv6/ila/ila_common.c | |||
@@ -15,20 +15,52 @@ | |||
15 | 15 | ||
16 | static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p) | 16 | static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p) |
17 | { | 17 | { |
18 | if (*(__be64 *)&ip6h->daddr == p->locator_match) | 18 | struct ila_addr *iaddr = ila_a2i(&ip6h->daddr); |
19 | |||
20 | if (p->locator_match.v64) | ||
19 | return p->csum_diff; | 21 | return p->csum_diff; |
20 | else | 22 | else |
21 | return compute_csum_diff8((__be32 *)&ip6h->daddr, | 23 | return compute_csum_diff8((__be32 *)&iaddr->loc, |
24 | (__be32 *)&p->locator); | ||
25 | } | ||
26 | |||
27 | static void ila_csum_do_neutral(struct ila_addr *iaddr, | ||
28 | struct ila_params *p) | ||
29 | { | ||
30 | __sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3]; | ||
31 | __wsum diff, fval; | ||
32 | |||
33 | /* Check if checksum adjust value has been cached */ | ||
34 | if (p->locator_match.v64) { | ||
35 | diff = p->csum_diff; | ||
36 | } else { | ||
37 | diff = compute_csum_diff8((__be32 *)iaddr, | ||
22 | (__be32 *)&p->locator); | 38 | (__be32 *)&p->locator); |
39 | } | ||
40 | |||
41 | fval = (__force __wsum)(ila_csum_neutral_set(iaddr->ident) ? | ||
42 | ~CSUM_NEUTRAL_FLAG : CSUM_NEUTRAL_FLAG); | ||
43 | |||
44 | diff = csum_add(diff, fval); | ||
45 | |||
46 | *adjust = ~csum_fold(csum_add(diff, csum_unfold(*adjust))); | ||
47 | |||
48 | /* Flip the csum-neutral bit. Either we are doing a SIR->ILA | ||
49 | * translation with ILA_CSUM_NEUTRAL_MAP as the csum_method | ||
50 | * and the C-bit is not set, or we are doing an ILA-SIR | ||
51 | * tranlsation and the C-bit is set. | ||
52 | */ | ||
53 | iaddr->ident.csum_neutral ^= 1; | ||
23 | } | 54 | } |
24 | 55 | ||
25 | void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p) | 56 | static void ila_csum_adjust_transport(struct sk_buff *skb, |
57 | struct ila_params *p) | ||
26 | { | 58 | { |
27 | __wsum diff; | 59 | __wsum diff; |
28 | struct ipv6hdr *ip6h = ipv6_hdr(skb); | 60 | struct ipv6hdr *ip6h = ipv6_hdr(skb); |
61 | struct ila_addr *iaddr = ila_a2i(&ip6h->daddr); | ||
29 | size_t nhoff = sizeof(struct ipv6hdr); | 62 | size_t nhoff = sizeof(struct ipv6hdr); |
30 | 63 | ||
31 | /* First update checksum */ | ||
32 | switch (ip6h->nexthdr) { | 64 | switch (ip6h->nexthdr) { |
33 | case NEXTHDR_TCP: | 65 | case NEXTHDR_TCP: |
34 | if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) { | 66 | if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) { |
@@ -68,7 +100,46 @@ void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p) | |||
68 | } | 100 | } |
69 | 101 | ||
70 | /* Now change destination address */ | 102 | /* Now change destination address */ |
71 | *(__be64 *)&ip6h->daddr = p->locator; | 103 | iaddr->loc = p->locator; |
104 | } | ||
105 | |||
106 | void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p) | ||
107 | { | ||
108 | struct ipv6hdr *ip6h = ipv6_hdr(skb); | ||
109 | struct ila_addr *iaddr = ila_a2i(&ip6h->daddr); | ||
110 | |||
111 | /* First deal with the transport checksum */ | ||
112 | if (ila_csum_neutral_set(iaddr->ident)) { | ||
113 | /* C-bit is set in the locator indicating that this | ||
114 | * is a locator being translated to a SIR address. | ||
115 | * Perform (receiver) checksum-neutral translation. | ||
116 | */ | ||
117 | ila_csum_do_neutral(iaddr, p); | ||
118 | } else { | ||
119 | switch (p->csum_mode) { | ||
120 | case ILA_CSUM_ADJUST_TRANSPORT: | ||
121 | ila_csum_adjust_transport(skb, p); | ||
122 | break; | ||
123 | case ILA_CSUM_NEUTRAL_MAP: | ||
124 | ila_csum_do_neutral(iaddr, p); | ||
125 | break; | ||
126 | case ILA_CSUM_NO_ACTION: | ||
127 | break; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | /* Now change destination address */ | ||
132 | iaddr->loc = p->locator; | ||
133 | } | ||
134 | |||
135 | void ila_init_saved_csum(struct ila_params *p) | ||
136 | { | ||
137 | if (!p->locator_match.v64) | ||
138 | return; | ||
139 | |||
140 | p->csum_diff = compute_csum_diff8( | ||
141 | (__be32 *)&p->locator_match, | ||
142 | (__be32 *)&p->locator); | ||
72 | } | 143 | } |
73 | 144 | ||
74 | static int __init ila_init(void) | 145 | static int __init ila_init(void) |
diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c index 9db3621b2126..4985e1a735a6 100644 --- a/net/ipv6/ila/ila_lwt.c +++ b/net/ipv6/ila/ila_lwt.c | |||
@@ -26,7 +26,7 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) | |||
26 | if (skb->protocol != htons(ETH_P_IPV6)) | 26 | if (skb->protocol != htons(ETH_P_IPV6)) |
27 | goto drop; | 27 | goto drop; |
28 | 28 | ||
29 | update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); | 29 | ila_update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); |
30 | 30 | ||
31 | return dst->lwtstate->orig_output(net, sk, skb); | 31 | return dst->lwtstate->orig_output(net, sk, skb); |
32 | 32 | ||
@@ -42,7 +42,7 @@ static int ila_input(struct sk_buff *skb) | |||
42 | if (skb->protocol != htons(ETH_P_IPV6)) | 42 | if (skb->protocol != htons(ETH_P_IPV6)) |
43 | goto drop; | 43 | goto drop; |
44 | 44 | ||
45 | update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); | 45 | ila_update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); |
46 | 46 | ||
47 | return dst->lwtstate->orig_input(skb); | 47 | return dst->lwtstate->orig_input(skb); |
48 | 48 | ||
@@ -53,6 +53,7 @@ drop: | |||
53 | 53 | ||
54 | static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = { | 54 | static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = { |
55 | [ILA_ATTR_LOCATOR] = { .type = NLA_U64, }, | 55 | [ILA_ATTR_LOCATOR] = { .type = NLA_U64, }, |
56 | [ILA_ATTR_CSUM_MODE] = { .type = NLA_U8, }, | ||
56 | }; | 57 | }; |
57 | 58 | ||
58 | static int ila_build_state(struct net_device *dev, struct nlattr *nla, | 59 | static int ila_build_state(struct net_device *dev, struct nlattr *nla, |
@@ -64,11 +65,28 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla, | |||
64 | size_t encap_len = sizeof(*p); | 65 | size_t encap_len = sizeof(*p); |
65 | struct lwtunnel_state *newts; | 66 | struct lwtunnel_state *newts; |
66 | const struct fib6_config *cfg6 = cfg; | 67 | const struct fib6_config *cfg6 = cfg; |
68 | struct ila_addr *iaddr; | ||
67 | int ret; | 69 | int ret; |
68 | 70 | ||
69 | if (family != AF_INET6) | 71 | if (family != AF_INET6) |
70 | return -EINVAL; | 72 | return -EINVAL; |
71 | 73 | ||
74 | if (cfg6->fc_dst_len < sizeof(struct ila_locator) + 1) { | ||
75 | /* Need to have full locator and at least type field | ||
76 | * included in destination | ||
77 | */ | ||
78 | return -EINVAL; | ||
79 | } | ||
80 | |||
81 | iaddr = (struct ila_addr *)&cfg6->fc_dst; | ||
82 | |||
83 | if (!ila_addr_is_ila(iaddr) || ila_csum_neutral_set(iaddr->ident)) { | ||
84 | /* Don't allow translation for a non-ILA address or checksum | ||
85 | * neutral flag to be set. | ||
86 | */ | ||
87 | return -EINVAL; | ||
88 | } | ||
89 | |||
72 | ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla, | 90 | ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla, |
73 | ila_nl_policy); | 91 | ila_nl_policy); |
74 | if (ret < 0) | 92 | if (ret < 0) |
@@ -84,16 +102,19 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla, | |||
84 | newts->len = encap_len; | 102 | newts->len = encap_len; |
85 | p = ila_params_lwtunnel(newts); | 103 | p = ila_params_lwtunnel(newts); |
86 | 104 | ||
87 | p->locator = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]); | 105 | p->locator.v64 = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]); |
88 | 106 | ||
89 | if (cfg6->fc_dst_len > sizeof(__be64)) { | 107 | /* Precompute checksum difference for translation since we |
90 | /* Precompute checksum difference for translation since we | 108 | * know both the old locator and the new one. |
91 | * know both the old locator and the new one. | 109 | */ |
92 | */ | 110 | p->locator_match = iaddr->loc; |
93 | p->locator_match = *(__be64 *)&cfg6->fc_dst; | 111 | p->csum_diff = compute_csum_diff8( |
94 | p->csum_diff = compute_csum_diff8( | 112 | (__be32 *)&p->locator_match, (__be32 *)&p->locator); |
95 | (__be32 *)&p->locator_match, (__be32 *)&p->locator); | 113 | |
96 | } | 114 | if (tb[ILA_ATTR_CSUM_MODE]) |
115 | p->csum_mode = nla_get_u8(tb[ILA_ATTR_CSUM_MODE]); | ||
116 | |||
117 | ila_init_saved_csum(p); | ||
97 | 118 | ||
98 | newts->type = LWTUNNEL_ENCAP_ILA; | 119 | newts->type = LWTUNNEL_ENCAP_ILA; |
99 | newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT | | 120 | newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT | |
@@ -109,9 +130,11 @@ static int ila_fill_encap_info(struct sk_buff *skb, | |||
109 | { | 130 | { |
110 | struct ila_params *p = ila_params_lwtunnel(lwtstate); | 131 | struct ila_params *p = ila_params_lwtunnel(lwtstate); |
111 | 132 | ||
112 | if (nla_put_u64_64bit(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator, | 133 | if (nla_put_u64_64bit(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator.v64, |
113 | ILA_ATTR_PAD)) | 134 | ILA_ATTR_PAD)) |
114 | goto nla_put_failure; | 135 | goto nla_put_failure; |
136 | if (nla_put_u64(skb, ILA_ATTR_CSUM_MODE, (__force u8)p->csum_mode)) | ||
137 | goto nla_put_failure; | ||
115 | 138 | ||
116 | return 0; | 139 | return 0; |
117 | 140 | ||
@@ -130,7 +153,7 @@ static int ila_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) | |||
130 | struct ila_params *a_p = ila_params_lwtunnel(a); | 153 | struct ila_params *a_p = ila_params_lwtunnel(a); |
131 | struct ila_params *b_p = ila_params_lwtunnel(b); | 154 | struct ila_params *b_p = ila_params_lwtunnel(b); |
132 | 155 | ||
133 | return (a_p->locator != b_p->locator); | 156 | return (a_p->locator.v64 != b_p->locator.v64); |
134 | } | 157 | } |
135 | 158 | ||
136 | static const struct lwtunnel_encap_ops ila_encap_ops = { | 159 | static const struct lwtunnel_encap_ops ila_encap_ops = { |
diff --git a/net/ipv6/ila/ila_xlat.c b/net/ipv6/ila/ila_xlat.c index 0e9e579410da..a90e57229c6c 100644 --- a/net/ipv6/ila/ila_xlat.c +++ b/net/ipv6/ila/ila_xlat.c | |||
@@ -11,13 +11,11 @@ | |||
11 | 11 | ||
12 | struct ila_xlat_params { | 12 | struct ila_xlat_params { |
13 | struct ila_params ip; | 13 | struct ila_params ip; |
14 | __be64 identifier; | ||
15 | int ifindex; | 14 | int ifindex; |
16 | unsigned int dir; | ||
17 | }; | 15 | }; |
18 | 16 | ||
19 | struct ila_map { | 17 | struct ila_map { |
20 | struct ila_xlat_params p; | 18 | struct ila_xlat_params xp; |
21 | struct rhash_head node; | 19 | struct rhash_head node; |
22 | struct ila_map __rcu *next; | 20 | struct ila_map __rcu *next; |
23 | struct rcu_head rcu; | 21 | struct rcu_head rcu; |
@@ -66,31 +64,29 @@ static __always_inline void __ila_hash_secret_init(void) | |||
66 | net_get_random_once(&hashrnd, sizeof(hashrnd)); | 64 | net_get_random_once(&hashrnd, sizeof(hashrnd)); |
67 | } | 65 | } |
68 | 66 | ||
69 | static inline u32 ila_identifier_hash(__be64 identifier) | 67 | static inline u32 ila_locator_hash(struct ila_locator loc) |
70 | { | 68 | { |
71 | u32 *v = (u32 *)&identifier; | 69 | u32 *v = (u32 *)loc.v32; |
72 | 70 | ||
73 | return jhash_2words(v[0], v[1], hashrnd); | 71 | return jhash_2words(v[0], v[1], hashrnd); |
74 | } | 72 | } |
75 | 73 | ||
76 | static inline spinlock_t *ila_get_lock(struct ila_net *ilan, __be64 identifier) | 74 | static inline spinlock_t *ila_get_lock(struct ila_net *ilan, |
75 | struct ila_locator loc) | ||
77 | { | 76 | { |
78 | return &ilan->locks[ila_identifier_hash(identifier) & ilan->locks_mask]; | 77 | return &ilan->locks[ila_locator_hash(loc) & ilan->locks_mask]; |
79 | } | 78 | } |
80 | 79 | ||
81 | static inline int ila_cmp_wildcards(struct ila_map *ila, __be64 loc, | 80 | static inline int ila_cmp_wildcards(struct ila_map *ila, |
82 | int ifindex, unsigned int dir) | 81 | struct ila_addr *iaddr, int ifindex) |
83 | { | 82 | { |
84 | return (ila->p.ip.locator_match && ila->p.ip.locator_match != loc) || | 83 | return (ila->xp.ifindex && ila->xp.ifindex != ifindex); |
85 | (ila->p.ifindex && ila->p.ifindex != ifindex) || | ||
86 | !(ila->p.dir & dir); | ||
87 | } | 84 | } |
88 | 85 | ||
89 | static inline int ila_cmp_params(struct ila_map *ila, struct ila_xlat_params *p) | 86 | static inline int ila_cmp_params(struct ila_map *ila, |
87 | struct ila_xlat_params *xp) | ||
90 | { | 88 | { |
91 | return (ila->p.ip.locator_match != p->ip.locator_match) || | 89 | return (ila->xp.ifindex != xp->ifindex); |
92 | (ila->p.ifindex != p->ifindex) || | ||
93 | (ila->p.dir != p->dir); | ||
94 | } | 90 | } |
95 | 91 | ||
96 | static int ila_cmpfn(struct rhashtable_compare_arg *arg, | 92 | static int ila_cmpfn(struct rhashtable_compare_arg *arg, |
@@ -98,17 +94,14 @@ static int ila_cmpfn(struct rhashtable_compare_arg *arg, | |||
98 | { | 94 | { |
99 | const struct ila_map *ila = obj; | 95 | const struct ila_map *ila = obj; |
100 | 96 | ||
101 | return (ila->p.identifier != *(__be64 *)arg->key); | 97 | return (ila->xp.ip.locator_match.v64 != *(__be64 *)arg->key); |
102 | } | 98 | } |
103 | 99 | ||
104 | static inline int ila_order(struct ila_map *ila) | 100 | static inline int ila_order(struct ila_map *ila) |
105 | { | 101 | { |
106 | int score = 0; | 102 | int score = 0; |
107 | 103 | ||
108 | if (ila->p.ip.locator_match) | 104 | if (ila->xp.ifindex) |
109 | score += 1 << 0; | ||
110 | |||
111 | if (ila->p.ifindex) | ||
112 | score += 1 << 1; | 105 | score += 1 << 1; |
113 | 106 | ||
114 | return score; | 107 | return score; |
@@ -117,7 +110,7 @@ static inline int ila_order(struct ila_map *ila) | |||
117 | static const struct rhashtable_params rht_params = { | 110 | static const struct rhashtable_params rht_params = { |
118 | .nelem_hint = 1024, | 111 | .nelem_hint = 1024, |
119 | .head_offset = offsetof(struct ila_map, node), | 112 | .head_offset = offsetof(struct ila_map, node), |
120 | .key_offset = offsetof(struct ila_map, p.identifier), | 113 | .key_offset = offsetof(struct ila_map, xp.ip.locator_match), |
121 | .key_len = sizeof(u64), /* identifier */ | 114 | .key_len = sizeof(u64), /* identifier */ |
122 | .max_size = 1048576, | 115 | .max_size = 1048576, |
123 | .min_size = 256, | 116 | .min_size = 256, |
@@ -136,50 +129,45 @@ static struct genl_family ila_nl_family = { | |||
136 | }; | 129 | }; |
137 | 130 | ||
138 | static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = { | 131 | static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = { |
139 | [ILA_ATTR_IDENTIFIER] = { .type = NLA_U64, }, | ||
140 | [ILA_ATTR_LOCATOR] = { .type = NLA_U64, }, | 132 | [ILA_ATTR_LOCATOR] = { .type = NLA_U64, }, |
141 | [ILA_ATTR_LOCATOR_MATCH] = { .type = NLA_U64, }, | 133 | [ILA_ATTR_LOCATOR_MATCH] = { .type = NLA_U64, }, |
142 | [ILA_ATTR_IFINDEX] = { .type = NLA_U32, }, | 134 | [ILA_ATTR_IFINDEX] = { .type = NLA_U32, }, |
143 | [ILA_ATTR_DIR] = { .type = NLA_U32, }, | 135 | [ILA_ATTR_CSUM_MODE] = { .type = NLA_U8, }, |
144 | }; | 136 | }; |
145 | 137 | ||
146 | static int parse_nl_config(struct genl_info *info, | 138 | static int parse_nl_config(struct genl_info *info, |
147 | struct ila_xlat_params *p) | 139 | struct ila_xlat_params *xp) |
148 | { | 140 | { |
149 | memset(p, 0, sizeof(*p)); | 141 | memset(xp, 0, sizeof(*xp)); |
150 | |||
151 | if (info->attrs[ILA_ATTR_IDENTIFIER]) | ||
152 | p->identifier = (__force __be64)nla_get_u64( | ||
153 | info->attrs[ILA_ATTR_IDENTIFIER]); | ||
154 | 142 | ||
155 | if (info->attrs[ILA_ATTR_LOCATOR]) | 143 | if (info->attrs[ILA_ATTR_LOCATOR]) |
156 | p->ip.locator = (__force __be64)nla_get_u64( | 144 | xp->ip.locator.v64 = (__force __be64)nla_get_u64( |
157 | info->attrs[ILA_ATTR_LOCATOR]); | 145 | info->attrs[ILA_ATTR_LOCATOR]); |
158 | 146 | ||
159 | if (info->attrs[ILA_ATTR_LOCATOR_MATCH]) | 147 | if (info->attrs[ILA_ATTR_LOCATOR_MATCH]) |
160 | p->ip.locator_match = (__force __be64)nla_get_u64( | 148 | xp->ip.locator_match.v64 = (__force __be64)nla_get_u64( |
161 | info->attrs[ILA_ATTR_LOCATOR_MATCH]); | 149 | info->attrs[ILA_ATTR_LOCATOR_MATCH]); |
162 | 150 | ||
163 | if (info->attrs[ILA_ATTR_IFINDEX]) | 151 | if (info->attrs[ILA_ATTR_CSUM_MODE]) |
164 | p->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]); | 152 | xp->ip.csum_mode = nla_get_u8(info->attrs[ILA_ATTR_CSUM_MODE]); |
165 | 153 | ||
166 | if (info->attrs[ILA_ATTR_DIR]) | 154 | if (info->attrs[ILA_ATTR_IFINDEX]) |
167 | p->dir = nla_get_u32(info->attrs[ILA_ATTR_DIR]); | 155 | xp->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]); |
168 | 156 | ||
169 | return 0; | 157 | return 0; |
170 | } | 158 | } |
171 | 159 | ||
172 | /* Must be called with rcu readlock */ | 160 | /* Must be called with rcu readlock */ |
173 | static inline struct ila_map *ila_lookup_wildcards(__be64 id, __be64 loc, | 161 | static inline struct ila_map *ila_lookup_wildcards(struct ila_addr *iaddr, |
174 | int ifindex, | 162 | int ifindex, |
175 | unsigned int dir, | ||
176 | struct ila_net *ilan) | 163 | struct ila_net *ilan) |
177 | { | 164 | { |
178 | struct ila_map *ila; | 165 | struct ila_map *ila; |
179 | 166 | ||
180 | ila = rhashtable_lookup_fast(&ilan->rhash_table, &id, rht_params); | 167 | ila = rhashtable_lookup_fast(&ilan->rhash_table, &iaddr->loc, |
168 | rht_params); | ||
181 | while (ila) { | 169 | while (ila) { |
182 | if (!ila_cmp_wildcards(ila, loc, ifindex, dir)) | 170 | if (!ila_cmp_wildcards(ila, iaddr, ifindex)) |
183 | return ila; | 171 | return ila; |
184 | ila = rcu_access_pointer(ila->next); | 172 | ila = rcu_access_pointer(ila->next); |
185 | } | 173 | } |
@@ -188,15 +176,16 @@ static inline struct ila_map *ila_lookup_wildcards(__be64 id, __be64 loc, | |||
188 | } | 176 | } |
189 | 177 | ||
190 | /* Must be called with rcu readlock */ | 178 | /* Must be called with rcu readlock */ |
191 | static inline struct ila_map *ila_lookup_by_params(struct ila_xlat_params *p, | 179 | static inline struct ila_map *ila_lookup_by_params(struct ila_xlat_params *xp, |
192 | struct ila_net *ilan) | 180 | struct ila_net *ilan) |
193 | { | 181 | { |
194 | struct ila_map *ila; | 182 | struct ila_map *ila; |
195 | 183 | ||
196 | ila = rhashtable_lookup_fast(&ilan->rhash_table, &p->identifier, | 184 | ila = rhashtable_lookup_fast(&ilan->rhash_table, |
185 | &xp->ip.locator_match, | ||
197 | rht_params); | 186 | rht_params); |
198 | while (ila) { | 187 | while (ila) { |
199 | if (!ila_cmp_params(ila, p)) | 188 | if (!ila_cmp_params(ila, xp)) |
200 | return ila; | 189 | return ila; |
201 | ila = rcu_access_pointer(ila->next); | 190 | ila = rcu_access_pointer(ila->next); |
202 | } | 191 | } |
@@ -221,14 +210,14 @@ static void ila_free_cb(void *ptr, void *arg) | |||
221 | } | 210 | } |
222 | } | 211 | } |
223 | 212 | ||
224 | static int ila_xlat_addr(struct sk_buff *skb, int dir); | 213 | static int ila_xlat_addr(struct sk_buff *skb); |
225 | 214 | ||
226 | static unsigned int | 215 | static unsigned int |
227 | ila_nf_input(void *priv, | 216 | ila_nf_input(void *priv, |
228 | struct sk_buff *skb, | 217 | struct sk_buff *skb, |
229 | const struct nf_hook_state *state) | 218 | const struct nf_hook_state *state) |
230 | { | 219 | { |
231 | ila_xlat_addr(skb, ILA_DIR_IN); | 220 | ila_xlat_addr(skb); |
232 | return NF_ACCEPT; | 221 | return NF_ACCEPT; |
233 | } | 222 | } |
234 | 223 | ||
@@ -241,11 +230,11 @@ static struct nf_hook_ops ila_nf_hook_ops[] __read_mostly = { | |||
241 | }, | 230 | }, |
242 | }; | 231 | }; |
243 | 232 | ||
244 | static int ila_add_mapping(struct net *net, struct ila_xlat_params *p) | 233 | static int ila_add_mapping(struct net *net, struct ila_xlat_params *xp) |
245 | { | 234 | { |
246 | struct ila_net *ilan = net_generic(net, ila_net_id); | 235 | struct ila_net *ilan = net_generic(net, ila_net_id); |
247 | struct ila_map *ila, *head; | 236 | struct ila_map *ila, *head; |
248 | spinlock_t *lock = ila_get_lock(ilan, p->identifier); | 237 | spinlock_t *lock = ila_get_lock(ilan, xp->ip.locator_match); |
249 | int err = 0, order; | 238 | int err = 0, order; |
250 | 239 | ||
251 | if (!ilan->hooks_registered) { | 240 | if (!ilan->hooks_registered) { |
@@ -264,22 +253,16 @@ static int ila_add_mapping(struct net *net, struct ila_xlat_params *p) | |||
264 | if (!ila) | 253 | if (!ila) |
265 | return -ENOMEM; | 254 | return -ENOMEM; |
266 | 255 | ||
267 | ila->p = *p; | 256 | ila_init_saved_csum(&xp->ip); |
268 | 257 | ||
269 | if (p->ip.locator_match) { | 258 | ila->xp = *xp; |
270 | /* Precompute checksum difference for translation since we | ||
271 | * know both the old identifier and the new one. | ||
272 | */ | ||
273 | ila->p.ip.csum_diff = compute_csum_diff8( | ||
274 | (__be32 *)&p->ip.locator_match, | ||
275 | (__be32 *)&p->ip.locator); | ||
276 | } | ||
277 | 259 | ||
278 | order = ila_order(ila); | 260 | order = ila_order(ila); |
279 | 261 | ||
280 | spin_lock(lock); | 262 | spin_lock(lock); |
281 | 263 | ||
282 | head = rhashtable_lookup_fast(&ilan->rhash_table, &p->identifier, | 264 | head = rhashtable_lookup_fast(&ilan->rhash_table, |
265 | &xp->ip.locator_match, | ||
283 | rht_params); | 266 | rht_params); |
284 | if (!head) { | 267 | if (!head) { |
285 | /* New entry for the rhash_table */ | 268 | /* New entry for the rhash_table */ |
@@ -289,7 +272,7 @@ static int ila_add_mapping(struct net *net, struct ila_xlat_params *p) | |||
289 | struct ila_map *tila = head, *prev = NULL; | 272 | struct ila_map *tila = head, *prev = NULL; |
290 | 273 | ||
291 | do { | 274 | do { |
292 | if (!ila_cmp_params(tila, p)) { | 275 | if (!ila_cmp_params(tila, xp)) { |
293 | err = -EEXIST; | 276 | err = -EEXIST; |
294 | goto out; | 277 | goto out; |
295 | } | 278 | } |
@@ -326,23 +309,23 @@ out: | |||
326 | return err; | 309 | return err; |
327 | } | 310 | } |
328 | 311 | ||
329 | static int ila_del_mapping(struct net *net, struct ila_xlat_params *p) | 312 | static int ila_del_mapping(struct net *net, struct ila_xlat_params *xp) |
330 | { | 313 | { |
331 | struct ila_net *ilan = net_generic(net, ila_net_id); | 314 | struct ila_net *ilan = net_generic(net, ila_net_id); |
332 | struct ila_map *ila, *head, *prev; | 315 | struct ila_map *ila, *head, *prev; |
333 | spinlock_t *lock = ila_get_lock(ilan, p->identifier); | 316 | spinlock_t *lock = ila_get_lock(ilan, xp->ip.locator_match); |
334 | int err = -ENOENT; | 317 | int err = -ENOENT; |
335 | 318 | ||
336 | spin_lock(lock); | 319 | spin_lock(lock); |
337 | 320 | ||
338 | head = rhashtable_lookup_fast(&ilan->rhash_table, | 321 | head = rhashtable_lookup_fast(&ilan->rhash_table, |
339 | &p->identifier, rht_params); | 322 | &xp->ip.locator_match, rht_params); |
340 | ila = head; | 323 | ila = head; |
341 | 324 | ||
342 | prev = NULL; | 325 | prev = NULL; |
343 | 326 | ||
344 | while (ila) { | 327 | while (ila) { |
345 | if (ila_cmp_params(ila, p)) { | 328 | if (ila_cmp_params(ila, xp)) { |
346 | prev = ila; | 329 | prev = ila; |
347 | ila = rcu_dereference_protected(ila->next, | 330 | ila = rcu_dereference_protected(ila->next, |
348 | lockdep_is_held(lock)); | 331 | lockdep_is_held(lock)); |
@@ -404,31 +387,28 @@ static int ila_nl_cmd_add_mapping(struct sk_buff *skb, struct genl_info *info) | |||
404 | static int ila_nl_cmd_del_mapping(struct sk_buff *skb, struct genl_info *info) | 387 | static int ila_nl_cmd_del_mapping(struct sk_buff *skb, struct genl_info *info) |
405 | { | 388 | { |
406 | struct net *net = genl_info_net(info); | 389 | struct net *net = genl_info_net(info); |
407 | struct ila_xlat_params p; | 390 | struct ila_xlat_params xp; |
408 | int err; | 391 | int err; |
409 | 392 | ||
410 | err = parse_nl_config(info, &p); | 393 | err = parse_nl_config(info, &xp); |
411 | if (err) | 394 | if (err) |
412 | return err; | 395 | return err; |
413 | 396 | ||
414 | ila_del_mapping(net, &p); | 397 | ila_del_mapping(net, &xp); |
415 | 398 | ||
416 | return 0; | 399 | return 0; |
417 | } | 400 | } |
418 | 401 | ||
419 | static int ila_fill_info(struct ila_map *ila, struct sk_buff *msg) | 402 | static int ila_fill_info(struct ila_map *ila, struct sk_buff *msg) |
420 | { | 403 | { |
421 | if (nla_put_u64_64bit(msg, ILA_ATTR_IDENTIFIER, | 404 | if (nla_put_u64_64bit(msg, ILA_ATTR_LOCATOR, |
422 | (__force u64)ila->p.identifier, | 405 | (__force u64)ila->xp.ip.locator.v64, |
423 | ILA_ATTR_PAD) || | ||
424 | nla_put_u64_64bit(msg, ILA_ATTR_LOCATOR, | ||
425 | (__force u64)ila->p.ip.locator, | ||
426 | ILA_ATTR_PAD) || | 406 | ILA_ATTR_PAD) || |
427 | nla_put_u64_64bit(msg, ILA_ATTR_LOCATOR_MATCH, | 407 | nla_put_u64_64bit(msg, ILA_ATTR_LOCATOR_MATCH, |
428 | (__force u64)ila->p.ip.locator_match, | 408 | (__force u64)ila->xp.ip.locator_match.v64, |
429 | ILA_ATTR_PAD) || | 409 | ILA_ATTR_PAD) || |
430 | nla_put_s32(msg, ILA_ATTR_IFINDEX, ila->p.ifindex) || | 410 | nla_put_s32(msg, ILA_ATTR_IFINDEX, ila->xp.ifindex) || |
431 | nla_put_u32(msg, ILA_ATTR_DIR, ila->p.dir)) | 411 | nla_put_u32(msg, ILA_ATTR_CSUM_MODE, ila->xp.ip.csum_mode)) |
432 | return -1; | 412 | return -1; |
433 | 413 | ||
434 | return 0; | 414 | return 0; |
@@ -460,11 +440,11 @@ static int ila_nl_cmd_get_mapping(struct sk_buff *skb, struct genl_info *info) | |||
460 | struct net *net = genl_info_net(info); | 440 | struct net *net = genl_info_net(info); |
461 | struct ila_net *ilan = net_generic(net, ila_net_id); | 441 | struct ila_net *ilan = net_generic(net, ila_net_id); |
462 | struct sk_buff *msg; | 442 | struct sk_buff *msg; |
463 | struct ila_xlat_params p; | 443 | struct ila_xlat_params xp; |
464 | struct ila_map *ila; | 444 | struct ila_map *ila; |
465 | int ret; | 445 | int ret; |
466 | 446 | ||
467 | ret = parse_nl_config(info, &p); | 447 | ret = parse_nl_config(info, &xp); |
468 | if (ret) | 448 | if (ret) |
469 | return ret; | 449 | return ret; |
470 | 450 | ||
@@ -474,7 +454,7 @@ static int ila_nl_cmd_get_mapping(struct sk_buff *skb, struct genl_info *info) | |||
474 | 454 | ||
475 | rcu_read_lock(); | 455 | rcu_read_lock(); |
476 | 456 | ||
477 | ila = ila_lookup_by_params(&p, ilan); | 457 | ila = ila_lookup_by_params(&xp, ilan); |
478 | if (ila) { | 458 | if (ila) { |
479 | ret = ila_dump_info(ila, | 459 | ret = ila_dump_info(ila, |
480 | info->snd_portid, | 460 | info->snd_portid, |
@@ -617,45 +597,32 @@ static struct pernet_operations ila_net_ops = { | |||
617 | .size = sizeof(struct ila_net), | 597 | .size = sizeof(struct ila_net), |
618 | }; | 598 | }; |
619 | 599 | ||
620 | static int ila_xlat_addr(struct sk_buff *skb, int dir) | 600 | static int ila_xlat_addr(struct sk_buff *skb) |
621 | { | 601 | { |
622 | struct ila_map *ila; | 602 | struct ila_map *ila; |
623 | struct ipv6hdr *ip6h = ipv6_hdr(skb); | 603 | struct ipv6hdr *ip6h = ipv6_hdr(skb); |
624 | struct net *net = dev_net(skb->dev); | 604 | struct net *net = dev_net(skb->dev); |
625 | struct ila_net *ilan = net_generic(net, ila_net_id); | 605 | struct ila_net *ilan = net_generic(net, ila_net_id); |
626 | __be64 identifier, locator_match; | 606 | struct ila_addr *iaddr = ila_a2i(&ip6h->daddr); |
627 | size_t nhoff; | ||
628 | 607 | ||
629 | /* Assumes skb contains a valid IPv6 header that is pulled */ | 608 | /* Assumes skb contains a valid IPv6 header that is pulled */ |
630 | 609 | ||
631 | identifier = *(__be64 *)&ip6h->daddr.in6_u.u6_addr8[8]; | 610 | if (!ila_addr_is_ila(iaddr)) { |
632 | locator_match = *(__be64 *)&ip6h->daddr.in6_u.u6_addr8[0]; | 611 | /* Type indicates this is not an ILA address */ |
633 | nhoff = sizeof(struct ipv6hdr); | 612 | return 0; |
613 | } | ||
634 | 614 | ||
635 | rcu_read_lock(); | 615 | rcu_read_lock(); |
636 | 616 | ||
637 | ila = ila_lookup_wildcards(identifier, locator_match, | 617 | ila = ila_lookup_wildcards(iaddr, skb->dev->ifindex, ilan); |
638 | skb->dev->ifindex, dir, ilan); | ||
639 | if (ila) | 618 | if (ila) |
640 | update_ipv6_locator(skb, &ila->p.ip); | 619 | ila_update_ipv6_locator(skb, &ila->xp.ip); |
641 | 620 | ||
642 | rcu_read_unlock(); | 621 | rcu_read_unlock(); |
643 | 622 | ||
644 | return 0; | 623 | return 0; |
645 | } | 624 | } |
646 | 625 | ||
647 | int ila_xlat_incoming(struct sk_buff *skb) | ||
648 | { | ||
649 | return ila_xlat_addr(skb, ILA_DIR_IN); | ||
650 | } | ||
651 | EXPORT_SYMBOL(ila_xlat_incoming); | ||
652 | |||
653 | int ila_xlat_outgoing(struct sk_buff *skb) | ||
654 | { | ||
655 | return ila_xlat_addr(skb, ILA_DIR_OUT); | ||
656 | } | ||
657 | EXPORT_SYMBOL(ila_xlat_outgoing); | ||
658 | |||
659 | int ila_xlat_init(void) | 626 | int ila_xlat_init(void) |
660 | { | 627 | { |
661 | int ret; | 628 | int ret; |