rxrpc: Fix IPv6 support
authorDavid Howells <dhowells@redhat.com>
Tue, 29 Aug 2017 09:18:37 +0000 (10:18 +0100)
committerDavid Howells <dhowells@redhat.com>
Tue, 29 Aug 2017 09:55:20 +0000 (10:55 +0100)
Fix IPv6 support in AF_RXRPC in the following ways:

 (1) When extracting the address from a received IPv4 packet, if the local
     transport socket is open for IPv6 then fill out the sockaddr_rxrpc
     struct for an IPv4-mapped-to-IPv6 AF_INET6 transport address instead
     of an AF_INET one.

 (2) When sending CHALLENGE or RESPONSE packets, the transport length needs
     to be set from the sockaddr_rxrpc::transport_len field rather than
     sizeof() on the IPv4 transport address.

 (3) When processing an IPv4 ICMP packet received by an IPv6 socket, set up
     the address correctly before searching for the affected peer.

Signed-off-by: David Howells <dhowells@redhat.com>
net/rxrpc/ar-internal.h
net/rxrpc/call_accept.c
net/rxrpc/conn_object.c
net/rxrpc/local_event.c
net/rxrpc/output.c
net/rxrpc/peer_event.c
net/rxrpc/rxkad.c
net/rxrpc/utils.c

index 8cac66774de1bd5378883126f0c2c588ba035fa7..227e2479405534e4d3236a6fa50801d96248e5d9 100644 (file)
@@ -830,7 +830,6 @@ void rxrpc_process_connection(struct work_struct *);
  */
 extern unsigned int rxrpc_connection_expiry;
 
-int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *, struct sk_buff *);
 struct rxrpc_connection *rxrpc_alloc_connection(gfp_t);
 struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *,
                                                   struct sk_buff *);
@@ -1060,7 +1059,8 @@ static inline void rxrpc_sysctl_exit(void) {}
 /*
  * utils.c
  */
-int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *, struct sk_buff *);
+int rxrpc_extract_addr_from_skb(struct rxrpc_local *, struct sockaddr_rxrpc *,
+                               struct sk_buff *);
 
 static inline bool before(u32 seq1, u32 seq2)
 {
index ec3383f97d4c3e863c7fe77af8d3ed3ba5ea039f..cbd1701e813a76864da2f4cc294476cce008d878 100644 (file)
@@ -277,7 +277,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx,
                 * anticipation - and to save on stack space.
                 */
                xpeer = b->peer_backlog[peer_tail];
-               if (rxrpc_extract_addr_from_skb(&xpeer->srx, skb) < 0)
+               if (rxrpc_extract_addr_from_skb(local, &xpeer->srx, skb) < 0)
                        return NULL;
 
                peer = rxrpc_lookup_incoming_peer(local, xpeer);
index 929b50d5afe843fd6e9e5822b483fd4e451c8013..fe575798592fec5945148694b54de90a3d78c3f6 100644 (file)
@@ -72,7 +72,7 @@ struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local,
 
        _enter(",%x", sp->hdr.cid & RXRPC_CIDMASK);
 
-       if (rxrpc_extract_addr_from_skb(&srx, skb) < 0)
+       if (rxrpc_extract_addr_from_skb(local, &srx, skb) < 0)
                goto not_found;
 
        k.epoch = sp->hdr.epoch;
index 540d3955c1bcc11652879c7e80630978513cc16e..93b5d910b4a130a8f2641f1a20f0d2e83b60ed56 100644 (file)
@@ -39,7 +39,7 @@ static void rxrpc_send_version_request(struct rxrpc_local *local,
 
        _enter("");
 
-       if (rxrpc_extract_addr_from_skb(&srx, skb) < 0)
+       if (rxrpc_extract_addr_from_skb(local, &srx, skb) < 0)
                return;
 
        msg.msg_name    = &srx.transport;
index 5bd2d0fa4a039235d179cb41bf7ee25aef13b594..71e6f713fbe79044d5dd35e7601bc6b6da70dba3 100644 (file)
@@ -444,7 +444,7 @@ void rxrpc_reject_packets(struct rxrpc_local *local)
                rxrpc_see_skb(skb, rxrpc_skb_rx_seen);
                sp = rxrpc_skb(skb);
 
-               if (rxrpc_extract_addr_from_skb(&srx, skb) == 0) {
+               if (rxrpc_extract_addr_from_skb(local, &srx, skb) == 0) {
                        msg.msg_namelen = srx.transport_len;
 
                        code = htonl(skb->priority);
index 1ed9c0c2e94f1c70ab0ca61fa16e87f76530f78a..7f749505e699a0ac5d8e04f832f21629643a55d0 100644 (file)
@@ -37,6 +37,7 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local,
 
        memset(&srx, 0, sizeof(srx));
        srx.transport_type = local->srx.transport_type;
+       srx.transport_len = local->srx.transport_len;
        srx.transport.family = local->srx.transport.family;
 
        /* Can we see an ICMP4 packet on an ICMP6 listening socket?  and vice
@@ -45,7 +46,6 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local,
        switch (srx.transport.family) {
        case AF_INET:
                srx.transport.sin.sin_port = serr->port;
-               srx.transport_len = sizeof(struct sockaddr_in);
                switch (serr->ee.ee_origin) {
                case SO_EE_ORIGIN_ICMP:
                        _net("Rx ICMP");
@@ -69,7 +69,6 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local,
 #ifdef CONFIG_AF_RXRPC_IPV6
        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");
@@ -79,6 +78,9 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local,
                        break;
                case SO_EE_ORIGIN_ICMP:
                        _net("Rx ICMP on v6 sock");
+                       srx.transport.sin6.sin6_addr.s6_addr32[0] = 0;
+                       srx.transport.sin6.sin6_addr.s6_addr32[1] = 0;
+                       srx.transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
                        memcpy(srx.transport.sin6.sin6_addr.s6_addr + 12,
                               skb_network_header(skb) + serr->addr_offset,
                               sizeof(struct in_addr));
index 34c86d2bcae5ae03e073ea004d34863fc441d484..c38b3a1de56c136f3e99096151635c226c5e02df 100644 (file)
@@ -634,8 +634,8 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn)
        challenge.min_level     = htonl(0);
        challenge.__padding     = 0;
 
-       msg.msg_name    = &conn->params.peer->srx.transport.sin;
-       msg.msg_namelen = sizeof(conn->params.peer->srx.transport.sin);
+       msg.msg_name    = &conn->params.peer->srx.transport;
+       msg.msg_namelen = conn->params.peer->srx.transport_len;
        msg.msg_control = NULL;
        msg.msg_controllen = 0;
        msg.msg_flags   = 0;
@@ -689,8 +689,8 @@ static int rxkad_send_response(struct rxrpc_connection *conn,
 
        _enter("");
 
-       msg.msg_name    = &conn->params.peer->srx.transport.sin;
-       msg.msg_namelen = sizeof(conn->params.peer->srx.transport.sin);
+       msg.msg_name    = &conn->params.peer->srx.transport;
+       msg.msg_namelen = conn->params.peer->srx.transport_len;
        msg.msg_control = NULL;
        msg.msg_controllen = 0;
        msg.msg_flags   = 0;
index ff7af71c4b49815b9406061c02ad453275df026c..e801171fa351410025addaf0519b5c3844ea5b4f 100644 (file)
 /*
  * Fill out a peer address from a socket buffer containing a packet.
  */
-int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *srx, struct sk_buff *skb)
+int rxrpc_extract_addr_from_skb(struct rxrpc_local *local,
+                               struct sockaddr_rxrpc *srx,
+                               struct sk_buff *skb)
 {
        memset(srx, 0, sizeof(*srx));
 
        switch (ntohs(skb->protocol)) {
        case ETH_P_IP:
-               srx->transport_type = SOCK_DGRAM;
-               srx->transport_len = sizeof(srx->transport.sin);
-               srx->transport.sin.sin_family = AF_INET;
-               srx->transport.sin.sin_port = udp_hdr(skb)->source;
-               srx->transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr;
+               if (local->srx.transport.family == AF_INET6) {
+                       srx->transport_type = SOCK_DGRAM;
+                       srx->transport_len = sizeof(srx->transport.sin6);
+                       srx->transport.sin6.sin6_family = AF_INET6;
+                       srx->transport.sin6.sin6_port = udp_hdr(skb)->source;
+                       srx->transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
+                       srx->transport.sin6.sin6_addr.s6_addr32[3] = ip_hdr(skb)->saddr;
+               } else {
+                       srx->transport_type = SOCK_DGRAM;
+                       srx->transport_len = sizeof(srx->transport.sin);
+                       srx->transport.sin.sin_family = AF_INET;
+                       srx->transport.sin.sin_port = udp_hdr(skb)->source;
+                       srx->transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr;
+               }
                return 0;
 
 #ifdef CONFIG_AF_RXRPC_IPV6