Browse Source

[ipv4] Send gratuitous ARPs whenever a new IPv4 address is applied

In a busy network (such as a public cloud), IPv4 addresses may be
recycled rapidly.  When this happens, unidirectional traffic (such as
UDP syslog) will succeed, but bidirectional traffic (such as TCP
connections) may fail due to stale ARP cache entries on other nodes.
The remote ARP cache expiry timeout is likely to exceed iPXE's
connection timeout, meaning that boot attempts can fail before the
problem is automatically resolved.

Fix by sending gratuitous ARPs whenever an IPv4 address is changed, to
attempt to update stale remote ARP cache entries.  Note that this is
not a guaranteed fix, since ARP is an unreliable protocol.

We avoid sending gratuitous ARPs unconditionally, since otherwise any
unrelated settings change (e.g. "set dns 192.168.0.1") would cause
unexpected gratuitous ARPs to be sent.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 8 years ago
parent
commit
45dd627689
1 changed files with 80 additions and 20 deletions
  1. 80
    20
      src/net/ipv4.c

+ 80
- 20
src/net/ipv4.c View File

@@ -79,11 +79,11 @@ static struct profiler ipv4_rx_profiler __profiler = { .name = "ipv4.rx" };
79 79
  * @v address		IPv4 address
80 80
  * @v netmask		Subnet mask
81 81
  * @v gateway		Gateway address (if any)
82
- * @ret miniroute	Routing table entry, or NULL
82
+ * @ret rc		Return status code
83 83
  */
84
-static struct ipv4_miniroute * __malloc
85
-add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address,
86
-		     struct in_addr netmask, struct in_addr gateway ) {
84
+static int add_ipv4_miniroute ( struct net_device *netdev,
85
+				struct in_addr address, struct in_addr netmask,
86
+				struct in_addr gateway ) {
87 87
 	struct ipv4_miniroute *miniroute;
88 88
 
89 89
 	DBGC ( netdev, "IPv4 add %s", inet_ntoa ( address ) );
@@ -96,7 +96,7 @@ add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address,
96 96
 	miniroute = malloc ( sizeof ( *miniroute ) );
97 97
 	if ( ! miniroute ) {
98 98
 		DBGC ( netdev, "IPv4 could not add miniroute\n" );
99
-		return NULL;
99
+		return -ENOMEM;
100 100
 	}
101 101
 
102 102
 	/* Record routing information */
@@ -114,7 +114,7 @@ add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address,
114 114
 		list_add ( &miniroute->list, &ipv4_miniroutes );
115 115
 	}
116 116
 
117
-	return miniroute;
117
+	return 0;
118 118
 }
119 119
 
120 120
 /**
@@ -812,33 +812,69 @@ const struct setting gateway_setting __setting ( SETTING_IP, gateway ) = {
812 812
 };
813 813
 
814 814
 /**
815
- * Create IPv4 routing table based on configured settings
815
+ * Send gratuitous ARP, if applicable
816 816
  *
817
+ * @v netdev		Network device
818
+ * @v address		IPv4 address
819
+ * @v netmask		Subnet mask
820
+ * @v gateway		Gateway address (if any)
817 821
  * @ret rc		Return status code
818 822
  */
819
-static int ipv4_create_routes ( void ) {
820
-	struct ipv4_miniroute *miniroute;
821
-	struct ipv4_miniroute *tmp;
823
+static int ipv4_gratuitous_arp ( struct net_device *netdev,
824
+				 struct in_addr address,
825
+				 struct in_addr netmask __unused,
826
+				 struct in_addr gateway __unused ) {
827
+	int rc;
828
+
829
+	/* Do nothing if network device already has this IPv4 address */
830
+	if ( ipv4_has_addr ( netdev, address ) )
831
+		return 0;
832
+
833
+	/* Transmit gratuitous ARP */
834
+	DBGC ( netdev, "IPv4 sending gratuitous ARP for %s via %s\n",
835
+	       inet_ntoa ( address ), netdev->name );
836
+	if ( ( rc = arp_tx_request ( netdev, &ipv4_protocol, &address,
837
+				     &address ) ) != 0 ) {
838
+		DBGC ( netdev, "IPv4 could not transmit gratuitous ARP: %s\n",
839
+		       strerror ( rc ) );
840
+		/* Treat failures as non-fatal */
841
+	}
842
+
843
+	return 0;
844
+}
845
+
846
+/**
847
+ * Process IPv4 network device settings
848
+ *
849
+ * @v apply		Application method
850
+ * @ret rc		Return status code
851
+ */
852
+static int ipv4_settings ( int ( * apply ) ( struct net_device *netdev,
853
+					     struct in_addr address,
854
+					     struct in_addr netmask,
855
+					     struct in_addr gateway ) ) {
822 856
 	struct net_device *netdev;
823 857
 	struct settings *settings;
824 858
 	struct in_addr address = { 0 };
825 859
 	struct in_addr netmask = { 0 };
826 860
 	struct in_addr gateway = { 0 };
861
+	int rc;
827 862
 
828
-	/* Delete all existing routes */
829
-	list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list )
830
-		del_ipv4_miniroute ( miniroute );
831
-
832
-	/* Create a route for each configured network device */
863
+	/* Process settings for each network device */
833 864
 	for_each_netdev ( netdev ) {
865
+
866
+		/* Get network device settings */
834 867
 		settings = netdev_settings ( netdev );
868
+
835 869
 		/* Get IPv4 address */
836 870
 		address.s_addr = 0;
837 871
 		fetch_ipv4_setting ( settings, &ip_setting, &address );
838 872
 		if ( ! address.s_addr )
839 873
 			continue;
874
+
840 875
 		/* Get subnet mask */
841 876
 		fetch_ipv4_setting ( settings, &netmask_setting, &netmask );
877
+
842 878
 		/* Calculate default netmask, if necessary */
843 879
 		if ( ! netmask.s_addr ) {
844 880
 			if ( IN_IS_CLASSA ( address.s_addr ) ) {
@@ -849,18 +885,42 @@ static int ipv4_create_routes ( void ) {
849 885
 				netmask.s_addr = INADDR_NET_CLASSC;
850 886
 			}
851 887
 		}
888
+
852 889
 		/* Get default gateway, if present */
853 890
 		fetch_ipv4_setting ( settings, &gateway_setting, &gateway );
854
-		/* Configure route */
855
-		miniroute = add_ipv4_miniroute ( netdev, address,
856
-						 netmask, gateway );
857
-		if ( ! miniroute )
858
-			return -ENOMEM;
891
+
892
+		/* Apply settings */
893
+		if ( ( rc = apply ( netdev, address, netmask, gateway ) ) != 0 )
894
+			return rc;
859 895
 	}
860 896
 
861 897
 	return 0;
862 898
 }
863 899
 
900
+/**
901
+ * Create IPv4 routing table based on configured settings
902
+ *
903
+ * @ret rc		Return status code
904
+ */
905
+static int ipv4_create_routes ( void ) {
906
+	struct ipv4_miniroute *miniroute;
907
+	struct ipv4_miniroute *tmp;
908
+	int rc;
909
+
910
+	/* Send gratuitous ARPs for any new IPv4 addresses */
911
+	ipv4_settings ( ipv4_gratuitous_arp );
912
+
913
+	/* Delete all existing routes */
914
+	list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list )
915
+		del_ipv4_miniroute ( miniroute );
916
+
917
+	/* Create a route for each configured network device */
918
+	if ( ( rc = ipv4_settings ( add_ipv4_miniroute ) ) != 0 )
919
+		return rc;
920
+
921
+	return 0;
922
+}
923
+
864 924
 /** IPv4 settings applicator */
865 925
 struct settings_applicator ipv4_settings_applicator __settings_applicator = {
866 926
 	.apply = ipv4_create_routes,

Loading…
Cancel
Save