From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S936660AbcKKTaE (ORCPT ); Fri, 11 Nov 2016 14:30:04 -0500 Received: from frisell.zx2c4.com ([192.95.5.64]:53505 "EHLO frisell.zx2c4.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932697AbcKKTaC (ORCPT ); Fri, 11 Nov 2016 14:30:02 -0500 MIME-Version: 1.0 From: "Jason A. Donenfeld" Date: Fri, 11 Nov 2016 20:29:55 +0100 X-Gmail-Original-Message-ID: Message-ID: Subject: Source address fib invalidation on IPv6 To: Netdev Cc: WireGuard mailing list , LKML Content-Type: text/plain; charset=UTF-8 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi folks, If I'm replying to a UDP packet, I generally want to use a source address that's the same as the destination address of the packet to which I'm replying. For example: Peer A sends packet: src = 10.0.0.1, dst = 10.0.0.3 Peer B replies with: src = 10.0.0.3, dst = 10.0.0.1 But let's complicate things. Let's say Peer B has multiple IPs on an interface: 10.0.0.2, 10.0.0.3. The default route uses 10.0.0.2. In this case what do you think should happen? Case 1: Peer A sends packet: src = 10.0.0.1, dst = 10.0.0.3 Peer B replies with: src = 10.0.0.2, dst = 10.0.0.1 Case 2: Peer A sends packet: src = 10.0.0.1, dst = 10.0.0.3 Peer B replies with: src = 10.0.0.3, dst = 10.0.0.1 Intuition tells me the answer is "Case 2". If you agree, keep reading. If you disagree, stop reading here, and instead correct my poor intuition. So, assuming "Case 2", when Peer B receives the first packet, he notes that packet's destination address, so that he can use it as a source address next. When replying, Peer B sets the stored source address and calls the routing function: struct flowi4 fl = { .saddr = from_daddr_of_previous_packet, .daddr = from_saddr_of_previous_packet, }; rt = ip_route_output_flow(sock_net(sock), &fl, sock); What if, however, by the time Peer B chooses to reply, his interface no longer has that source address? No problem, because ip_route_output_flow will return -EINVAL in that case. So, we can do this: struct flowi4 fl = { .saddr = from_daddr_of_previous_packet, .daddr = from_saddr_of_previous_packet, }; rt = ip_route_output_flow(sock_net(sock), &fl, sock); if (unlikely(IS_ERR(rt))) { fl.saddr = 0; rt = ip_route_output_flow(sock_net(sock), &fl, sock); } And then all is good in the neighborhood. This solution works. Done. But what about IPv6? That's where we get into trouble: struct flowi6 fl = { .saddr = from_daddr_of_previous_packet, .daddr = from_saddr_of_previous_packet, }; ret = ipv6_stub->ipv6_dst_lookup(sock_net(sock), sock, &dst, &fl); In this case, IPv6 returns a valid dst, when no interface has the source address anymore! So, there's no way to know whether or not the source address for replying has gone stale. We don't have a means of falling back to inaddr_any for the source address. Primary question: is this behavior a bug? Or is this some consequence of a fundamental IPv6 difference with v4? Or is something else happening here? Thanks, Jason From mboxrd@z Thu Jan 1 00:00:00 1970 From: "Jason A. Donenfeld" Subject: Source address fib invalidation on IPv6 Date: Fri, 11 Nov 2016 20:29:55 +0100 Message-ID: Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Cc: LKML , WireGuard mailing list To: Netdev Return-path: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: wireguard-bounces@lists.zx2c4.com Sender: "WireGuard" List-Id: netdev.vger.kernel.org Hi folks, If I'm replying to a UDP packet, I generally want to use a source address that's the same as the destination address of the packet to which I'm replying. For example: Peer A sends packet: src = 10.0.0.1, dst = 10.0.0.3 Peer B replies with: src = 10.0.0.3, dst = 10.0.0.1 But let's complicate things. Let's say Peer B has multiple IPs on an interface: 10.0.0.2, 10.0.0.3. The default route uses 10.0.0.2. In this case what do you think should happen? Case 1: Peer A sends packet: src = 10.0.0.1, dst = 10.0.0.3 Peer B replies with: src = 10.0.0.2, dst = 10.0.0.1 Case 2: Peer A sends packet: src = 10.0.0.1, dst = 10.0.0.3 Peer B replies with: src = 10.0.0.3, dst = 10.0.0.1 Intuition tells me the answer is "Case 2". If you agree, keep reading. If you disagree, stop reading here, and instead correct my poor intuition. So, assuming "Case 2", when Peer B receives the first packet, he notes that packet's destination address, so that he can use it as a source address next. When replying, Peer B sets the stored source address and calls the routing function: struct flowi4 fl = { .saddr = from_daddr_of_previous_packet, .daddr = from_saddr_of_previous_packet, }; rt = ip_route_output_flow(sock_net(sock), &fl, sock); What if, however, by the time Peer B chooses to reply, his interface no longer has that source address? No problem, because ip_route_output_flow will return -EINVAL in that case. So, we can do this: struct flowi4 fl = { .saddr = from_daddr_of_previous_packet, .daddr = from_saddr_of_previous_packet, }; rt = ip_route_output_flow(sock_net(sock), &fl, sock); if (unlikely(IS_ERR(rt))) { fl.saddr = 0; rt = ip_route_output_flow(sock_net(sock), &fl, sock); } And then all is good in the neighborhood. This solution works. Done. But what about IPv6? That's where we get into trouble: struct flowi6 fl = { .saddr = from_daddr_of_previous_packet, .daddr = from_saddr_of_previous_packet, }; ret = ipv6_stub->ipv6_dst_lookup(sock_net(sock), sock, &dst, &fl); In this case, IPv6 returns a valid dst, when no interface has the source address anymore! So, there's no way to know whether or not the source address for replying has gone stale. We don't have a means of falling back to inaddr_any for the source address. Primary question: is this behavior a bug? Or is this some consequence of a fundamental IPv6 difference with v4? Or is something else happening here? Thanks, Jason From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Jason@zx2c4.com Received: from frisell.zx2c4.com (frisell.zx2c4.com [192.95.5.64]) by krantz.zx2c4.com (ZX2C4 Mail Server) with ESMTP id c3ab24fe for ; Fri, 11 Nov 2016 19:27:43 +0000 (UTC) Received: by frisell.zx2c4.com (ZX2C4 Mail Server) with ESMTP id 84a9901e for ; Fri, 11 Nov 2016 19:27:43 +0000 (UTC) Received: by frisell.zx2c4.com (ZX2C4 Mail Server) with ESMTPSA id 476bde4b (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128:NO) for ; Fri, 11 Nov 2016 19:27:42 +0000 (UTC) Received: by mail-lf0-f48.google.com with SMTP id c13so19359587lfg.0 for ; Fri, 11 Nov 2016 11:29:57 -0800 (PST) MIME-Version: 1.0 From: "Jason A. Donenfeld" Date: Fri, 11 Nov 2016 20:29:55 +0100 Message-ID: To: Netdev Content-Type: text/plain; charset=UTF-8 Cc: LKML , WireGuard mailing list Subject: [WireGuard] Source address fib invalidation on IPv6 List-Id: Development discussion of WireGuard List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Hi folks, If I'm replying to a UDP packet, I generally want to use a source address that's the same as the destination address of the packet to which I'm replying. For example: Peer A sends packet: src = 10.0.0.1, dst = 10.0.0.3 Peer B replies with: src = 10.0.0.3, dst = 10.0.0.1 But let's complicate things. Let's say Peer B has multiple IPs on an interface: 10.0.0.2, 10.0.0.3. The default route uses 10.0.0.2. In this case what do you think should happen? Case 1: Peer A sends packet: src = 10.0.0.1, dst = 10.0.0.3 Peer B replies with: src = 10.0.0.2, dst = 10.0.0.1 Case 2: Peer A sends packet: src = 10.0.0.1, dst = 10.0.0.3 Peer B replies with: src = 10.0.0.3, dst = 10.0.0.1 Intuition tells me the answer is "Case 2". If you agree, keep reading. If you disagree, stop reading here, and instead correct my poor intuition. So, assuming "Case 2", when Peer B receives the first packet, he notes that packet's destination address, so that he can use it as a source address next. When replying, Peer B sets the stored source address and calls the routing function: struct flowi4 fl = { .saddr = from_daddr_of_previous_packet, .daddr = from_saddr_of_previous_packet, }; rt = ip_route_output_flow(sock_net(sock), &fl, sock); What if, however, by the time Peer B chooses to reply, his interface no longer has that source address? No problem, because ip_route_output_flow will return -EINVAL in that case. So, we can do this: struct flowi4 fl = { .saddr = from_daddr_of_previous_packet, .daddr = from_saddr_of_previous_packet, }; rt = ip_route_output_flow(sock_net(sock), &fl, sock); if (unlikely(IS_ERR(rt))) { fl.saddr = 0; rt = ip_route_output_flow(sock_net(sock), &fl, sock); } And then all is good in the neighborhood. This solution works. Done. But what about IPv6? That's where we get into trouble: struct flowi6 fl = { .saddr = from_daddr_of_previous_packet, .daddr = from_saddr_of_previous_packet, }; ret = ipv6_stub->ipv6_dst_lookup(sock_net(sock), sock, &dst, &fl); In this case, IPv6 returns a valid dst, when no interface has the source address anymore! So, there's no way to know whether or not the source address for replying has gone stale. We don't have a means of falling back to inaddr_any for the source address. Primary question: is this behavior a bug? Or is this some consequence of a fundamental IPv6 difference with v4? Or is something else happening here? Thanks, Jason