Commit 75b54cb5 authored by David Howells's avatar David Howells

rxrpc: Add IPv6 support

Add IPv6 support to AF_RXRPC.  With this, AF_RXRPC sockets can be created:

	service = socket(AF_RXRPC, SOCK_DGRAM, PF_INET6);

instead of:

	service = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);

The AFS filesystem doesn't support IPv6 at the moment, though, since that
requires upgrades to some of the RPC calls.

Note that a good portion of this patch is replacing "%pI4:%u" in print
statements with "%pISpc" which is able to handle both protocols and print
the port.
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent 1c2bc7b9
...@@ -106,19 +106,23 @@ static int rxrpc_validate_address(struct rxrpc_sock *rx, ...@@ -106,19 +106,23 @@ static int rxrpc_validate_address(struct rxrpc_sock *rx,
case AF_INET: case AF_INET:
if (srx->transport_len < sizeof(struct sockaddr_in)) if (srx->transport_len < sizeof(struct sockaddr_in))
return -EINVAL; return -EINVAL;
_debug("INET: %x @ %pI4",
ntohs(srx->transport.sin.sin_port),
&srx->transport.sin.sin_addr);
tail = offsetof(struct sockaddr_rxrpc, transport.sin.__pad); tail = offsetof(struct sockaddr_rxrpc, transport.sin.__pad);
break; break;
case AF_INET6: case AF_INET6:
if (srx->transport_len < sizeof(struct sockaddr_in6))
return -EINVAL;
tail = offsetof(struct sockaddr_rxrpc, transport) +
sizeof(struct sockaddr_in6);
break;
default: default:
return -EAFNOSUPPORT; return -EAFNOSUPPORT;
} }
if (tail < len) if (tail < len)
memset((void *)srx + tail, 0, len - tail); memset((void *)srx + tail, 0, len - tail);
_debug("INET: %pISp", &srx->transport);
return 0; return 0;
} }
...@@ -409,6 +413,9 @@ static int rxrpc_sendmsg(struct socket *sock, struct msghdr *m, size_t len) ...@@ -409,6 +413,9 @@ static int rxrpc_sendmsg(struct socket *sock, struct msghdr *m, size_t len)
case AF_INET: case AF_INET:
rx->srx.transport_len = sizeof(struct sockaddr_in); rx->srx.transport_len = sizeof(struct sockaddr_in);
break; break;
case AF_INET6:
rx->srx.transport_len = sizeof(struct sockaddr_in6);
break;
default: default:
ret = -EAFNOSUPPORT; ret = -EAFNOSUPPORT;
goto error_unlock; goto error_unlock;
...@@ -563,7 +570,7 @@ static int rxrpc_create(struct net *net, struct socket *sock, int protocol, ...@@ -563,7 +570,7 @@ static int rxrpc_create(struct net *net, struct socket *sock, int protocol,
return -EAFNOSUPPORT; return -EAFNOSUPPORT;
/* we support transport protocol UDP/UDP6 only */ /* we support transport protocol UDP/UDP6 only */
if (protocol != PF_INET) if (protocol != PF_INET && protocol != PF_INET6)
return -EPROTONOSUPPORT; return -EPROTONOSUPPORT;
if (sock->type != SOCK_DGRAM) if (sock->type != SOCK_DGRAM)
......
...@@ -134,6 +134,14 @@ struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local, ...@@ -134,6 +134,14 @@ struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local,
srx.transport.sin.sin_addr.s_addr) srx.transport.sin.sin_addr.s_addr)
goto not_found; goto not_found;
break; break;
case AF_INET6:
if (peer->srx.transport.sin6.sin6_port !=
srx.transport.sin6.sin6_port ||
memcmp(&peer->srx.transport.sin6.sin6_addr,
&srx.transport.sin6.sin6_addr,
sizeof(struct in6_addr)) != 0)
goto not_found;
break;
default: default:
BUG(); BUG();
} }
......
...@@ -58,6 +58,15 @@ static long rxrpc_local_cmp_key(const struct rxrpc_local *local, ...@@ -58,6 +58,15 @@ static long rxrpc_local_cmp_key(const struct rxrpc_local *local,
memcmp(&local->srx.transport.sin.sin_addr, memcmp(&local->srx.transport.sin.sin_addr,
&srx->transport.sin.sin_addr, &srx->transport.sin.sin_addr,
sizeof(struct in_addr)); sizeof(struct in_addr));
case AF_INET6:
/* If the choice of UDP6 port is left up to the transport, then
* the endpoint record doesn't match.
*/
return ((u16 __force)local->srx.transport.sin6.sin6_port -
(u16 __force)srx->transport.sin6.sin6_port) ?:
memcmp(&local->srx.transport.sin6.sin6_addr,
&srx->transport.sin6.sin6_addr,
sizeof(struct in6_addr));
default: default:
BUG(); BUG();
} }
...@@ -100,7 +109,8 @@ static int rxrpc_open_socket(struct rxrpc_local *local) ...@@ -100,7 +109,8 @@ static int rxrpc_open_socket(struct rxrpc_local *local)
struct sock *sock; struct sock *sock;
int ret, opt; int ret, opt;
_enter("%p{%d}", local, local->srx.transport_type); _enter("%p{%d,%d}",
local, local->srx.transport_type, local->srx.transport.family);
/* create a socket to represent the local endpoint */ /* create a socket to represent the local endpoint */
ret = sock_create_kern(&init_net, local->srx.transport.family, ret = sock_create_kern(&init_net, local->srx.transport.family,
...@@ -169,18 +179,8 @@ struct rxrpc_local *rxrpc_lookup_local(const struct sockaddr_rxrpc *srx) ...@@ -169,18 +179,8 @@ struct rxrpc_local *rxrpc_lookup_local(const struct sockaddr_rxrpc *srx)
long diff; long diff;
int ret; int ret;
if (srx->transport.family == AF_INET) { _enter("{%d,%d,%pISp}",
_enter("{%d,%u,%pI4+%hu}", srx->transport_type, srx->transport.family, &srx->transport);
srx->transport_type,
srx->transport.family,
&srx->transport.sin.sin_addr,
ntohs(srx->transport.sin.sin_port));
} else {
_enter("{%d,%u}",
srx->transport_type,
srx->transport.family);
return ERR_PTR(-EAFNOSUPPORT);
}
mutex_lock(&rxrpc_local_mutex); mutex_lock(&rxrpc_local_mutex);
...@@ -233,13 +233,8 @@ struct rxrpc_local *rxrpc_lookup_local(const struct sockaddr_rxrpc *srx) ...@@ -233,13 +233,8 @@ struct rxrpc_local *rxrpc_lookup_local(const struct sockaddr_rxrpc *srx)
found: found:
mutex_unlock(&rxrpc_local_mutex); mutex_unlock(&rxrpc_local_mutex);
_net("LOCAL %s %d {%d,%u,%pI4+%hu}", _net("LOCAL %s %d {%pISp}",
age, age, local->debug_id, &local->srx.transport);
local->debug_id,
local->srx.transport_type,
local->srx.transport.family,
&local->srx.transport.sin.sin_addr,
ntohs(local->srx.transport.sin.sin_port));
_leave(" = %p", local); _leave(" = %p", local);
return local; return local;
......
...@@ -258,6 +258,22 @@ int rxrpc_send_data_packet(struct rxrpc_connection *conn, struct sk_buff *skb) ...@@ -258,6 +258,22 @@ int rxrpc_send_data_packet(struct rxrpc_connection *conn, struct sk_buff *skb)
(char *)&opt, sizeof(opt)); (char *)&opt, sizeof(opt));
} }
break; break;
case AF_INET6:
opt = IPV6_PMTUDISC_DONT;
ret = kernel_setsockopt(conn->params.local->socket,
SOL_IPV6, IPV6_MTU_DISCOVER,
(char *)&opt, sizeof(opt));
if (ret == 0) {
ret = kernel_sendmsg(conn->params.local->socket, &msg,
iov, 1, iov[0].iov_len);
opt = IPV6_PMTUDISC_DO;
kernel_setsockopt(conn->params.local->socket,
SOL_IPV6, IPV6_MTU_DISCOVER,
(char *)&opt, sizeof(opt));
}
break;
} }
up_write(&conn->params.local->defrag_sem); up_write(&conn->params.local->defrag_sem);
......
...@@ -66,6 +66,30 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local, ...@@ -66,6 +66,30 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local,
} }
break; break;
case AF_INET6:
srx.transport.sin6.sin6_port = serr->port;
srx.transport_len = sizeof(struct sockaddr_in6);
switch (serr->ee.ee_origin) {
case SO_EE_ORIGIN_ICMP6:
_net("Rx ICMP6");
memcpy(&srx.transport.sin6.sin6_addr,
skb_network_header(skb) + serr->addr_offset,
sizeof(struct in6_addr));
break;
case SO_EE_ORIGIN_ICMP:
_net("Rx ICMP on v6 sock");
memcpy(&srx.transport.sin6.sin6_addr.s6_addr + 12,
skb_network_header(skb) + serr->addr_offset,
sizeof(struct in_addr));
break;
default:
memcpy(&srx.transport.sin6.sin6_addr,
&ipv6_hdr(skb)->saddr,
sizeof(struct in6_addr));
break;
}
break;
default: default:
BUG(); BUG();
} }
......
...@@ -16,12 +16,14 @@ ...@@ -16,12 +16,14 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/udp.h> #include <linux/udp.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/in6.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/hashtable.h> #include <linux/hashtable.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/af_rxrpc.h> #include <net/af_rxrpc.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/route.h> #include <net/route.h>
#include <net/ip6_route.h>
#include "ar-internal.h" #include "ar-internal.h"
static DEFINE_HASHTABLE(rxrpc_peer_hash, 10); static DEFINE_HASHTABLE(rxrpc_peer_hash, 10);
...@@ -50,6 +52,11 @@ static unsigned long rxrpc_peer_hash_key(struct rxrpc_local *local, ...@@ -50,6 +52,11 @@ static unsigned long rxrpc_peer_hash_key(struct rxrpc_local *local,
size = sizeof(srx->transport.sin.sin_addr); size = sizeof(srx->transport.sin.sin_addr);
p = (u16 *)&srx->transport.sin.sin_addr; p = (u16 *)&srx->transport.sin.sin_addr;
break; break;
case AF_INET6:
hash_key += (u16 __force)srx->transport.sin.sin_port;
size = sizeof(srx->transport.sin6.sin6_addr);
p = (u16 *)&srx->transport.sin6.sin6_addr;
break;
default: default:
WARN(1, "AF_RXRPC: Unsupported transport address family\n"); WARN(1, "AF_RXRPC: Unsupported transport address family\n");
return 0; return 0;
...@@ -93,6 +100,12 @@ static long rxrpc_peer_cmp_key(const struct rxrpc_peer *peer, ...@@ -93,6 +100,12 @@ static long rxrpc_peer_cmp_key(const struct rxrpc_peer *peer,
memcmp(&peer->srx.transport.sin.sin_addr, memcmp(&peer->srx.transport.sin.sin_addr,
&srx->transport.sin.sin_addr, &srx->transport.sin.sin_addr,
sizeof(struct in_addr)); sizeof(struct in_addr));
case AF_INET6:
return ((u16 __force)peer->srx.transport.sin6.sin6_port -
(u16 __force)srx->transport.sin6.sin6_port) ?:
memcmp(&peer->srx.transport.sin6.sin6_addr,
&srx->transport.sin6.sin6_addr,
sizeof(struct in6_addr));
default: default:
BUG(); BUG();
} }
...@@ -130,17 +143,7 @@ struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *local, ...@@ -130,17 +143,7 @@ struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *local,
peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key); peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key);
if (peer) { if (peer) {
switch (srx->transport.family) { _net("PEER %d {%pISp}", peer->debug_id, &peer->srx.transport);
case AF_INET:
_net("PEER %d {%d,%u,%pI4+%hu}",
peer->debug_id,
peer->srx.transport_type,
peer->srx.transport.family,
&peer->srx.transport.sin.sin_addr,
ntohs(peer->srx.transport.sin.sin_port));
break;
}
_leave(" = %p {u=%d}", peer, atomic_read(&peer->usage)); _leave(" = %p {u=%d}", peer, atomic_read(&peer->usage));
} }
return peer; return peer;
...@@ -152,22 +155,49 @@ struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *local, ...@@ -152,22 +155,49 @@ struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *local,
*/ */
static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer) static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer)
{ {
struct dst_entry *dst;
struct rtable *rt; struct rtable *rt;
struct flowi4 fl4; struct flowi fl;
struct flowi4 *fl4 = &fl.u.ip4;
struct flowi6 *fl6 = &fl.u.ip6;
peer->if_mtu = 1500; peer->if_mtu = 1500;
rt = ip_route_output_ports(&init_net, &fl4, NULL, memset(&fl, 0, sizeof(fl));
peer->srx.transport.sin.sin_addr.s_addr, 0, switch (peer->srx.transport.family) {
htons(7000), htons(7001), case AF_INET:
IPPROTO_UDP, 0, 0); rt = ip_route_output_ports(
if (IS_ERR(rt)) { &init_net, fl4, NULL,
_leave(" [route err %ld]", PTR_ERR(rt)); peer->srx.transport.sin.sin_addr.s_addr, 0,
return; htons(7000), htons(7001), IPPROTO_UDP, 0, 0);
if (IS_ERR(rt)) {
_leave(" [route err %ld]", PTR_ERR(rt));
return;
}
dst = &rt->dst;
break;
case AF_INET6:
fl6->flowi6_iif = LOOPBACK_IFINDEX;
fl6->flowi6_scope = RT_SCOPE_UNIVERSE;
fl6->flowi6_proto = IPPROTO_UDP;
memcpy(&fl6->daddr, &peer->srx.transport.sin6.sin6_addr,
sizeof(struct in6_addr));
fl6->fl6_dport = htons(7001);
fl6->fl6_sport = htons(7000);
dst = ip6_route_output(&init_net, NULL, fl6);
if (IS_ERR(dst)) {
_leave(" [route err %ld]", PTR_ERR(dst));
return;
}
break;
default:
BUG();
} }
peer->if_mtu = dst_mtu(&rt->dst); peer->if_mtu = dst_mtu(dst);
dst_release(&rt->dst); dst_release(dst);
_leave(" [if_mtu %u]", peer->if_mtu); _leave(" [if_mtu %u]", peer->if_mtu);
} }
...@@ -207,17 +237,22 @@ static void rxrpc_init_peer(struct rxrpc_peer *peer, unsigned long hash_key) ...@@ -207,17 +237,22 @@ static void rxrpc_init_peer(struct rxrpc_peer *peer, unsigned long hash_key)
rxrpc_assess_MTU_size(peer); rxrpc_assess_MTU_size(peer);
peer->mtu = peer->if_mtu; peer->mtu = peer->if_mtu;
if (peer->srx.transport.family == AF_INET) { switch (peer->srx.transport.family) {
case AF_INET:
peer->hdrsize = sizeof(struct iphdr); peer->hdrsize = sizeof(struct iphdr);
switch (peer->srx.transport_type) { break;
case SOCK_DGRAM: case AF_INET6:
peer->hdrsize += sizeof(struct udphdr); peer->hdrsize = sizeof(struct ipv6hdr);
break; break;
default: default:
BUG(); BUG();
break; }
}
} else { switch (peer->srx.transport_type) {
case SOCK_DGRAM:
peer->hdrsize += sizeof(struct udphdr);
break;
default:
BUG(); BUG();
} }
...@@ -285,11 +320,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local, ...@@ -285,11 +320,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local,
struct rxrpc_peer *peer, *candidate; struct rxrpc_peer *peer, *candidate;
unsigned long hash_key = rxrpc_peer_hash_key(local, srx); unsigned long hash_key = rxrpc_peer_hash_key(local, srx);
_enter("{%d,%d,%pI4+%hu}", _enter("{%pISp}", &srx->transport);
srx->transport_type,
srx->transport_len,
&srx->transport.sin.sin_addr,
ntohs(srx->transport.sin.sin_port));
/* search the peer list first */ /* search the peer list first */
rcu_read_lock(); rcu_read_lock();
...@@ -326,11 +357,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local, ...@@ -326,11 +357,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local,
peer = candidate; peer = candidate;
} }
_net("PEER %d {%d,%pI4+%hu}", _net("PEER %d {%pISp}", peer->debug_id, &peer->srx.transport);
peer->debug_id,
peer->srx.transport_type,
&peer->srx.transport.sin.sin_addr,
ntohs(peer->srx.transport.sin.sin_port));
_leave(" = %p {u=%d}", peer, atomic_read(&peer->usage)); _leave(" = %p {u=%d}", peer, atomic_read(&peer->usage));
return peer; return peer;
......
...@@ -52,11 +52,12 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) ...@@ -52,11 +52,12 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
struct rxrpc_sock *rx; struct rxrpc_sock *rx;
struct rxrpc_peer *peer; struct rxrpc_peer *peer;
struct rxrpc_call *call; struct rxrpc_call *call;
char lbuff[4 + 4 + 4 + 4 + 5 + 1], rbuff[4 + 4 + 4 + 4 + 5 + 1]; char lbuff[50], rbuff[50];
if (v == &rxrpc_calls) { if (v == &rxrpc_calls) {
seq_puts(seq, seq_puts(seq,
"Proto Local Remote " "Proto Local "
" Remote "
" SvID ConnID CallID End Use State Abort " " SvID ConnID CallID End Use State Abort "
" UserID\n"); " UserID\n");
return 0; return 0;
...@@ -68,9 +69,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) ...@@ -68,9 +69,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
if (rx) { if (rx) {
local = READ_ONCE(rx->local); local = READ_ONCE(rx->local);
if (local) if (local)
sprintf(lbuff, "%pI4:%u", sprintf(lbuff, "%pISpc", &local->srx.transport);
&local->srx.transport.sin.sin_addr,
ntohs(local->srx.transport.sin.sin_port));
else else
strcpy(lbuff, "no_local"); strcpy(lbuff, "no_local");
} else { } else {
...@@ -79,14 +78,12 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) ...@@ -79,14 +78,12 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
peer = call->peer; peer = call->peer;
if (peer) if (peer)
sprintf(rbuff, "%pI4:%u", sprintf(rbuff, "%pISpc", &peer->srx.transport);
&peer->srx.transport.sin.sin_addr,
ntohs(peer->srx.transport.sin.sin_port));
else else
strcpy(rbuff, "no_connection"); strcpy(rbuff, "no_connection");
seq_printf(seq, seq_printf(seq,
"UDP %-22.22s %-22.22s %4x %08x %08x %s %3u" "UDP %-47.47s %-47.47s %4x %08x %08x %s %3u"
" %-8.8s %08x %lx\n", " %-8.8s %08x %lx\n",
lbuff, lbuff,
rbuff, rbuff,
...@@ -145,11 +142,12 @@ static void rxrpc_connection_seq_stop(struct seq_file *seq, void *v) ...@@ -145,11 +142,12 @@ static void rxrpc_connection_seq_stop(struct seq_file *seq, void *v)
static int rxrpc_connection_seq_show(struct seq_file *seq, void *v) static int rxrpc_connection_seq_show(struct seq_file *seq, void *v)
{ {
struct rxrpc_connection *conn; struct rxrpc_connection *conn;
char lbuff[4 + 4 + 4 + 4 + 5 + 1], rbuff[4 + 4 + 4 + 4 + 5 + 1]; char lbuff[50], rbuff[50];
if (v == &rxrpc_connection_proc_list) { if (v == &rxrpc_connection_proc_list) {
seq_puts(seq, seq_puts(seq,
"Proto Local Remote " "Proto Local "
" Remote "
" SvID ConnID End Use State Key " " SvID ConnID End Use State Key "
" Serial ISerial\n" " Serial ISerial\n"
); );
...@@ -163,16 +161,12 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v) ...@@ -163,16 +161,12 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v)
goto print; goto print;
} }
sprintf(lbuff, "%pI4:%u", sprintf(lbuff, "%pISpc", &conn->params.local->srx.transport);
&conn->params.local->srx.transport.sin.sin_addr,
ntohs(conn->params.local->srx.transport.sin.sin_port));
sprintf(rbuff, "%pI4:%u", sprintf(rbuff, "%pISpc", &conn->params.peer->srx.transport);
&conn->params.peer->srx.transport.sin.sin_addr,
ntohs(conn->params.peer->srx.transport.sin.sin_port));
print: print:
seq_printf(seq, seq_printf(seq,
"UDP %-22.22s %-22.22s %4x %08x %s %3u" "UDP %-47.47s %-47.47s %4x %08x %s %3u"
" %s %08x %08x %08x\n", " %s %08x %08x %08x\n",
lbuff, lbuff,
rbuff, rbuff,
......
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