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
  * @v address		IPv4 address
79
  * @v address		IPv4 address
80
  * @v netmask		Subnet mask
80
  * @v netmask		Subnet mask
81
  * @v gateway		Gateway address (if any)
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
 	struct ipv4_miniroute *miniroute;
87
 	struct ipv4_miniroute *miniroute;
88
 
88
 
89
 	DBGC ( netdev, "IPv4 add %s", inet_ntoa ( address ) );
89
 	DBGC ( netdev, "IPv4 add %s", inet_ntoa ( address ) );
96
 	miniroute = malloc ( sizeof ( *miniroute ) );
96
 	miniroute = malloc ( sizeof ( *miniroute ) );
97
 	if ( ! miniroute ) {
97
 	if ( ! miniroute ) {
98
 		DBGC ( netdev, "IPv4 could not add miniroute\n" );
98
 		DBGC ( netdev, "IPv4 could not add miniroute\n" );
99
-		return NULL;
99
+		return -ENOMEM;
100
 	}
100
 	}
101
 
101
 
102
 	/* Record routing information */
102
 	/* Record routing information */
114
 		list_add ( &miniroute->list, &ipv4_miniroutes );
114
 		list_add ( &miniroute->list, &ipv4_miniroutes );
115
 	}
115
 	}
116
 
116
 
117
-	return miniroute;
117
+	return 0;
118
 }
118
 }
119
 
119
 
120
 /**
120
 /**
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
  * @ret rc		Return status code
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
 	struct net_device *netdev;
856
 	struct net_device *netdev;
823
 	struct settings *settings;
857
 	struct settings *settings;
824
 	struct in_addr address = { 0 };
858
 	struct in_addr address = { 0 };
825
 	struct in_addr netmask = { 0 };
859
 	struct in_addr netmask = { 0 };
826
 	struct in_addr gateway = { 0 };
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
 	for_each_netdev ( netdev ) {
864
 	for_each_netdev ( netdev ) {
865
+
866
+		/* Get network device settings */
834
 		settings = netdev_settings ( netdev );
867
 		settings = netdev_settings ( netdev );
868
+
835
 		/* Get IPv4 address */
869
 		/* Get IPv4 address */
836
 		address.s_addr = 0;
870
 		address.s_addr = 0;
837
 		fetch_ipv4_setting ( settings, &ip_setting, &address );
871
 		fetch_ipv4_setting ( settings, &ip_setting, &address );
838
 		if ( ! address.s_addr )
872
 		if ( ! address.s_addr )
839
 			continue;
873
 			continue;
874
+
840
 		/* Get subnet mask */
875
 		/* Get subnet mask */
841
 		fetch_ipv4_setting ( settings, &netmask_setting, &netmask );
876
 		fetch_ipv4_setting ( settings, &netmask_setting, &netmask );
877
+
842
 		/* Calculate default netmask, if necessary */
878
 		/* Calculate default netmask, if necessary */
843
 		if ( ! netmask.s_addr ) {
879
 		if ( ! netmask.s_addr ) {
844
 			if ( IN_IS_CLASSA ( address.s_addr ) ) {
880
 			if ( IN_IS_CLASSA ( address.s_addr ) ) {
849
 				netmask.s_addr = INADDR_NET_CLASSC;
885
 				netmask.s_addr = INADDR_NET_CLASSC;
850
 			}
886
 			}
851
 		}
887
 		}
888
+
852
 		/* Get default gateway, if present */
889
 		/* Get default gateway, if present */
853
 		fetch_ipv4_setting ( settings, &gateway_setting, &gateway );
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
 	return 0;
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
 /** IPv4 settings applicator */
924
 /** IPv4 settings applicator */
865
 struct settings_applicator ipv4_settings_applicator __settings_applicator = {
925
 struct settings_applicator ipv4_settings_applicator __settings_applicator = {
866
 	.apply = ipv4_create_routes,
926
 	.apply = ipv4_create_routes,

Loading…
Cancel
Save