NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

bin/49868: tftpd(8) doesn't play well with clients that return acknowledgements to the broadcast address



>Number:         49868
>Category:       bin
>Synopsis:       Clients that return acks to the brodcast address can't talk to our tftpd(8) server
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Apr 30 17:10:00 +0000 2015
>Originator:     Brian Buhrow
>Release:        NetBSd-current and all prior releases
>Organization:
NFB of California
	
>Environment:
	
	
System: NetBSD lothlorien.nfbcal.org 5.2_STABLE NetBSD 5.2_STABLE (RBL) #0: Thu Mar 27 10:15:56 PDT 2014 buhrow%lothlorien.nfbcal.org@localhost:/usr/src/sys/arch/i386/compile/RBL i386
Architecture: i386
Machine: i386
>Description:
	

Some Cisco equipment have firmware recovery software in them that includes
a tftp client which can be used to reflash the device with fresh firmware
in the event that the original firmware became unusable.
This tftp client is broken in the sense that when it returns
acknowledgements to the tftp server as its receiving data, rather than
sending those acks to the unicast address representing the server's IP
address, it sends them to the broadcast address.  To illustrate, here is a
tcpdump showing the problem in action.

	The tftp client is IP address 10.0.0.1 and the tftp server is IP
address 10.0.0.2.  Notice how the 10.0.0.1 client returns its
acknowledgements to the correct port number for the  tftp session, but
sends those acks to 255.255.255.255, the broadcast address.  In this trace,
you can see both sides retrying to talk to each other, and missing each
other.  

03:25:36.200156 IP 10.0.0.1.1024 > 255.255.255.255.69:  31 RRQ "c1240-k9w7-tar.default" octet 
03:25:36.243033 arp who-has 10.0.0.1 tell 10.0.0.2
03:25:36.243448 arp reply 10.0.0.1 is-at 00:1a:6d:3e:1a:aa
03:25:36.243455 IP 10.0.0.2.63484 > 10.0.0.1.1024: UDP, length 516
03:25:36.315783 IP 10.0.0.1.1024 > 255.255.255.255.63484: UDP, length 4
03:25:40.121059 IP 10.0.0.1.1024 > 255.255.255.255.63484: UDP, length 4
03:25:41.254158 IP 10.0.0.2.63484 > 10.0.0.1.1024: UDP, length 516
03:25:45.060073 IP 10.0.0.1.1024 > 255.255.255.255.63484: UDP, length 4
03:25:46.266457 IP 10.0.0.2.63484 > 10.0.0.1.1024: UDP, length 516
03:25:50.072412 IP 10.0.0.1.1024 > 255.255.255.255.63484: UDP, length 4
03:25:51.278758 IP 10.0.0.2.63484 > 10.0.0.1.1024: UDP, length 516
03:25:55.084701 IP 10.0.0.1.1024 > 255.255.255.255.63484: UDP, length 4
03:25:56.291059 IP 10.0.0.2.63484 > 10.0.0.1.1024: UDP, length 516
03:26:00.096881 IP 10.0.0.1.1024 > 255.255.255.255.63484: UDP, length 4
03:26:03.902150 IP 10.0.0.1.1024 > 255.255.255.255.63484: UDP, length 4


>How-To-Repeat:
	

	The Cisco Aironet 1240 boot loader, as well as other Cisco Aironet
boot loaders demonstrates this behavior.  I believe the boot loader on
older Cisco 1900 switches exhibits this behavior as well.  If you have such
a device, you can:

1.  addan alias to your tftp serving  network interface:
i.e. ifconfig nfe0 inet 10.0.0.2 netmask 255.255.255.224 alias

2.  Perform the required steps to put your device into recovery mode and
get it to try and retrieve an image via tftp from your server.

3.  Watch it fail.

>Fix:
	

Here is a patch that allows our tftpd(8) utility to work with such broken
tftp clients.  This patch does not break any existing functionality that I
can detect.  Essentially it enables the reception of traffic to the
broadcast address, but only the currently active ephemeral port number, in
addition to receiving the usual unicast traffic during a session.


Here is the patch:

Index: tftpd.c
===================================================================
RCS file: /cvsroot/src/libexec/tftpd/tftpd.c,v
retrieving revision 1.43
diff -u -r1.43 tftpd.c
--- tftpd.c	4 Oct 2013 07:51:48 -0000	1.43
+++ tftpd.c	30 Apr 2015 16:35:36 -0000
@@ -1,4 +1,4 @@
-/*	$NetBSD$	*/
+/*	$NetBSD: tftpd.c,v 1.31.4.1 2011/11/02 19:58:23 riz Exp $	*/
 
 /*
  * Copyright (c) 1983, 1993
@@ -36,7 +36,7 @@
 #if 0
 static char sccsid[] = "@(#)tftpd.c	8.1 (Berkeley) 6/4/93";
 #else
-__RCSID("$NetBSD$");
+__RCSID("$NetBSD: tftpd.c,v 1.43 2013/10/04 07:51:48 jnemeth Exp $");
 #endif
 #endif /* not lint */
 
@@ -393,12 +393,13 @@
 		syslog(LOG_ERR, "socket: %m");
 		exit(1);
 	}
-	if (bind(peer, (struct sockaddr *)&me, me.ss_len) < 0) {
-		syslog(LOG_ERR, "bind: %m");
+	soopt = 1;
+	if (setsockopt(peer, SOL_SOCKET, SO_BROADCAST, (void *) &soopt, sizeof(soopt)) < 0) {
+		syslog(LOG_ERR, "set SO_BROADCAST: %m");
 		exit(1);
 	}
-	if (connect(peer, (struct sockaddr *)&from, from.ss_len) < 0) {
-		syslog(LOG_ERR, "connect: %m");
+	if (bind(peer, (struct sockaddr *)&me, me.ss_len) < 0) {
+		syslog(LOG_ERR, "bind: %m");
 		exit(1);
 	}
 	soopt = 65536;	/* larger than we'll ever need */
@@ -955,7 +956,7 @@
 send_data:
 		if (!etftp && debug)
 			syslog(LOG_DEBUG, "Send DATA %u", block);
-		if ((n = send(peer, dp, size + 4, 0)) != size + 4) {
+		if ((n = sendto(peer, dp, size + 4, 0, (struct sockaddr *)&from, fromlen)) != size + 4) {
 			syslog(LOG_ERR, "tftpd: write: %m");
 			goto abort;
 		}
@@ -963,7 +964,8 @@
 			read_ahead(file, tftp_blksize, pf->f_convert);
 		for ( ; ; ) {
 			alarm(rexmtval);        /* read the ack */
-			n = recv(peer, ackbuf, tftp_blksize, 0);
+			n = recvfrom(peer, ackbuf, tftp_blksize, 0,(struct sockaddr
+			*)&from, &fromlen );
 			alarm(0);
 			if (n < 0) {
 				syslog(LOG_ERR, "tftpd: read: %m");
@@ -1049,14 +1051,15 @@
 		(void) setjmp(timeoutbuf);
 send_ack:
 		ap = (struct tftphdr *) (etftp ? oackbuf : ackbuf);
-		if (send(peer, ap, acklength, 0) != acklength) {
+		if (sendto(peer, ap, acklength, 0, (struct sockaddr *)&from, fromlen) != acklength) {
 			syslog(LOG_ERR, "tftpd: write: %m");
 			goto abort;
 		}
 		write_behind(file, pf->f_convert);
 		for ( ; ; ) {
 			alarm(rexmtval);
-			n = recv(peer, dp, tftp_blksize + 4, 0);
+			n = recvfrom(peer, dp, tftp_blksize + 4, 0, (struct sockaddr
+			*)&from, &fromlen);
 			alarm(0);
 			if (n < 0) {            /* really? */
 				syslog(LOG_ERR, "tftpd: read: %m");
@@ -1108,16 +1111,16 @@
 	ap->th_block = htons((u_short)(block));
 	if (debug)
 		syslog(LOG_DEBUG, "Send final ACK %u", block);
-	(void) send(peer, ackbuf, 4, 0);
+	(void) sendto(peer, ackbuf, 4, 0, (struct sockaddr *)&from, fromlen);
 
 	signal(SIGALRM, justquit);      /* just quit on timeout */
 	alarm(rexmtval);
-	n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
+	n = recvfrom(peer, buf, sizeof (buf), 0, (struct sockaddr *)&from, &fromlen); /* normally times out and quits */
 	alarm(0);
 	if (n >= 4 &&                   /* if read some data */
 	    dp->th_opcode == DATA &&    /* and got a data block */
 	    block == dp->th_block) {	/* then my last ack was lost */
-		(void) send(peer, ackbuf, 4, 0);     /* resend final ack */
+		(void) sendto(peer, ackbuf, 4, 0, (struct sockaddr *)&from, fromlen);     /* resend final ack */
 	}
 abort:
 	return;
@@ -1185,7 +1188,7 @@
 		syslog(LOG_DEBUG, "Send NACK %s", tp->th_msg);
 	length = strlen(tp->th_msg);
 	msglen = &tp->th_msg[length + 1] - buf;
-	if (send(peer, buf, msglen, 0) != (ssize_t)msglen)
+	if (sendto(peer, buf, msglen, 0, (struct sockaddr *)&from, fromlen) != (ssize_t)msglen)
 		syslog(LOG_ERR, "nak: %m");
 }
 

>Unformatted:
 	
 	


Home | Main Index | Thread Index | Old Index