Index: src/wp-includes/functions.php
===================================================================
--- src/wp-includes/functions.php	(revision 43602)
+++ src/wp-includes/functions.php	(working copy)
@@ -6442,3 +6442,69 @@
 		}
 	}
 }
+
+/**
+ * Determines the user's actual IP address and attempts to partially
+ * anonymize an IP address by converting it to a network ID.
+ *
+ * Geolocating the network ID usually returns a similar location as the
+ * actual IP, but provides some privacy for the user.
+ *
+ * $_SERVER['REMOTE_ADDR'] cannot be used in all cases, such as when the user
+ * is making their request through a proxy, or when the web server is behind
+ * a proxy. In those cases, $_SERVER['REMOTE_ADDR'] is set to the proxy address rather
+ * than the user's actual address.
+ *
+ * Modified from https://stackoverflow.com/a/2031935/450127, MIT license.
+ * Modified from https://github.com/geertw/php-ip-anonymizer, MIT license.
+ *
+ * SECURITY WARNING: This function is _NOT_ intended to be used in
+ * circumstances where the authenticity of the IP address matters. This does
+ * _NOT_ guarantee that the returned address is valid or accurate, and it can
+ * be easily spoofed.
+ *
+ * @since 4.8.0
+ *
+ * @return false|string The anonymized address on success; the given address
+ *                      or false on failure.
+ */
+function wp_get_unsafe_client_ip() {
+	$client_ip = false;
+
+	// In order of preference, with the best ones for this purpose first.
+	$address_headers = array(
+		'HTTP_CLIENT_IP',
+		'HTTP_X_FORWARDED_FOR',
+		'HTTP_X_FORWARDED',
+		'HTTP_X_CLUSTER_CLIENT_IP',
+		'HTTP_FORWARDED_FOR',
+		'HTTP_FORWARDED',
+		'REMOTE_ADDR',
+	);
+
+	foreach ( $address_headers as $header ) {
+		if ( array_key_exists( $header, $_SERVER ) ) {
+			/*
+			 * HTTP_X_FORWARDED_FOR can contain a chain of comma-separated
+			 * addresses. The first one is the original client. It can't be
+			 * trusted for authenticity, but we don't need to for this purpose.
+			 */
+			$address_chain = explode( ',', $_SERVER[ $header ] );
+			$client_ip     = trim( $address_chain[0] );
+
+			break;
+		}
+	}
+
+	if ( ! $client_ip ) {
+		return false;
+	}
+
+	$anon_ip = wp_privacy_anonymize_ip( $client_ip, true );
+
+	if ( '0.0.0.0' === $anon_ip || '::' === $anon_ip ) {
+		return false;
+	}
+
+	return $anon_ip;
+}
Index: src/wp-login.php
===================================================================
--- src/wp-login.php	(revision 43602)
+++ src/wp-login.php	(working copy)
@@ -377,7 +377,8 @@
 	$message .= sprintf( __( 'Username: %s' ), $user_login ) . "\r\n\r\n";
 	$message .= __( 'If this was a mistake, just ignore this email and nothing will happen.' ) . "\r\n\r\n";
 	$message .= __( 'To reset your password, visit the following address:' ) . "\r\n\r\n";
-	$message .= '<' . network_site_url( "wp-login.php?action=rp&key=$key&login=" . rawurlencode( $user_login ), 'login' ) . ">\r\n";
+	$message .= '<' . network_site_url( "wp-login.php?action=rp&key=$key&login=" . rawurlencode( $user_login ), 'login' ) . ">\r\n\r\n";
+	$message .= sprintf( __( 'IP address: %s' ), wp_get_unsafe_client_ip() ) . "\r\n";
 
 	/* translators: Password reset email subject. %s: Site name */
 	$title = sprintf( __( '[%s] Password Reset' ), $site_name );
