diff options
author | Christoph Paasch <christoph.paasch@uclouvain.be> | 2014-01-22 07:58:44 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-01-23 15:48:28 -0500 |
commit | 3ad88cf70af79e6f19c4f89dd85453ba4fdf425e (patch) | |
tree | 3717f53d734cecb7945638846f19477dad3ea8d0 /net | |
parent | 5ae5e991ee6274cc269dabd536399146be038a0f (diff) |
tcp: metrics: Handle v6/v4-mapped sockets in tcp-metrics
A socket may be v6/v4-mapped. In that case sk->sk_family is AF_INET6,
but the IP being used is actually an IPv4-address.
Current's tcp-metrics will thus represent it as an IPv6-address:
root@server:~# ip tcp_metrics
::ffff:10.1.1.2 age 22.920sec rtt 18750us rttvar 15000us cwnd 10
10.1.1.2 age 47.970sec rtt 16250us rttvar 10000us cwnd 10
This patch modifies the tcp-metrics so that they are able to handle the
v6/v4-mapped sockets correctly.
Signed-off-by: Christoph Paasch <christoph.paasch@uclouvain.be>
Acked-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/tcp_metrics.c | 64 |
1 files changed, 40 insertions, 24 deletions
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 9ae48b4a37d1..d547075d8300 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c | |||
@@ -274,24 +274,32 @@ static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock | |||
274 | unsigned int hash; | 274 | unsigned int hash; |
275 | struct net *net; | 275 | struct net *net; |
276 | 276 | ||
277 | saddr.family = tw->tw_family; | 277 | if (tw->tw_family == AF_INET) { |
278 | daddr.family = tw->tw_family; | 278 | saddr.family = AF_INET; |
279 | switch (daddr.family) { | ||
280 | case AF_INET: | ||
281 | saddr.addr.a4 = tw->tw_rcv_saddr; | 279 | saddr.addr.a4 = tw->tw_rcv_saddr; |
280 | daddr.family = AF_INET; | ||
282 | daddr.addr.a4 = tw->tw_daddr; | 281 | daddr.addr.a4 = tw->tw_daddr; |
283 | hash = (__force unsigned int) daddr.addr.a4; | 282 | hash = (__force unsigned int) daddr.addr.a4; |
284 | break; | 283 | } |
285 | #if IS_ENABLED(CONFIG_IPV6) | 284 | #if IS_ENABLED(CONFIG_IPV6) |
286 | case AF_INET6: | 285 | else if (tw->tw_family == AF_INET6) { |
287 | *(struct in6_addr *)saddr.addr.a6 = tw->tw_v6_rcv_saddr; | 286 | if (ipv6_addr_v4mapped(&tw->tw_v6_daddr)) { |
288 | *(struct in6_addr *)daddr.addr.a6 = tw->tw_v6_daddr; | 287 | saddr.family = AF_INET; |
289 | hash = ipv6_addr_hash(&tw->tw_v6_daddr); | 288 | saddr.addr.a4 = tw->tw_rcv_saddr; |
290 | break; | 289 | daddr.family = AF_INET; |
290 | daddr.addr.a4 = tw->tw_daddr; | ||
291 | hash = (__force unsigned int) daddr.addr.a4; | ||
292 | } else { | ||
293 | saddr.family = AF_INET6; | ||
294 | *(struct in6_addr *)saddr.addr.a6 = tw->tw_v6_rcv_saddr; | ||
295 | daddr.family = AF_INET6; | ||
296 | *(struct in6_addr *)daddr.addr.a6 = tw->tw_v6_daddr; | ||
297 | hash = ipv6_addr_hash(&tw->tw_v6_daddr); | ||
298 | } | ||
299 | } | ||
291 | #endif | 300 | #endif |
292 | default: | 301 | else |
293 | return NULL; | 302 | return NULL; |
294 | } | ||
295 | 303 | ||
296 | net = twsk_net(tw); | 304 | net = twsk_net(tw); |
297 | hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); | 305 | hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); |
@@ -314,24 +322,32 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, | |||
314 | unsigned int hash; | 322 | unsigned int hash; |
315 | struct net *net; | 323 | struct net *net; |
316 | 324 | ||
317 | saddr.family = sk->sk_family; | 325 | if (sk->sk_family == AF_INET) { |
318 | daddr.family = sk->sk_family; | 326 | saddr.family = AF_INET; |
319 | switch (daddr.family) { | ||
320 | case AF_INET: | ||
321 | saddr.addr.a4 = inet_sk(sk)->inet_saddr; | 327 | saddr.addr.a4 = inet_sk(sk)->inet_saddr; |
328 | daddr.family = AF_INET; | ||
322 | daddr.addr.a4 = inet_sk(sk)->inet_daddr; | 329 | daddr.addr.a4 = inet_sk(sk)->inet_daddr; |
323 | hash = (__force unsigned int) daddr.addr.a4; | 330 | hash = (__force unsigned int) daddr.addr.a4; |
324 | break; | 331 | } |
325 | #if IS_ENABLED(CONFIG_IPV6) | 332 | #if IS_ENABLED(CONFIG_IPV6) |
326 | case AF_INET6: | 333 | else if (sk->sk_family == AF_INET6) { |
327 | *(struct in6_addr *)saddr.addr.a6 = sk->sk_v6_rcv_saddr; | 334 | if (ipv6_addr_v4mapped(&sk->sk_v6_daddr)) { |
328 | *(struct in6_addr *)daddr.addr.a6 = sk->sk_v6_daddr; | 335 | saddr.family = AF_INET; |
329 | hash = ipv6_addr_hash(&sk->sk_v6_daddr); | 336 | saddr.addr.a4 = inet_sk(sk)->inet_saddr; |
330 | break; | 337 | daddr.family = AF_INET; |
338 | daddr.addr.a4 = inet_sk(sk)->inet_daddr; | ||
339 | hash = (__force unsigned int) daddr.addr.a4; | ||
340 | } else { | ||
341 | saddr.family = AF_INET6; | ||
342 | *(struct in6_addr *)saddr.addr.a6 = sk->sk_v6_rcv_saddr; | ||
343 | daddr.family = AF_INET6; | ||
344 | *(struct in6_addr *)daddr.addr.a6 = sk->sk_v6_daddr; | ||
345 | hash = ipv6_addr_hash(&sk->sk_v6_daddr); | ||
346 | } | ||
347 | } | ||
331 | #endif | 348 | #endif |
332 | default: | 349 | else |
333 | return NULL; | 350 | return NULL; |
334 | } | ||
335 | 351 | ||
336 | net = dev_net(dst->dev); | 352 | net = dev_net(dst->dev); |
337 | hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); | 353 | hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); |