Shop OBEX P1 Docs P2 Docs Learn Events
W5200: Possible Gotcha & Fix — Parallax Forums

W5200: Possible Gotcha & Fix

twctwc Posts: 107
edited 2012-12-09 14:37 in Accessories
Hey Mike:

I've been experimenting & stress testing, came across an issue but think I have a fix.

In tens words or less it appears buff can overrun by a byte if the wiz buffer fills up. I discovered the problem when I was moving stuff around and using a variable for a website name. At some point I ended up with...
  buff          byte  $0[BUFFER_2K]
  site          byte  "finance.google.com", $0

...and then I started experiencing intermittent failures with the DNS portion of REPEATed DHCP-DNS-TCP stress test. Looking closer I could see the first byte of the site variable getting clobbered...
Getting network parameters
Requesting IP.....10.0.0.157
Lease Time........86400
DNS...............10.0.0.1
DHCP Server.......10.0.0.1
Router............10.0.0.1
Gateway...........10.0.0.1
DNS Init (bool)...-1
DNS Lookup........finance.google.com
Resolved IP(0)....74.125.224.64
Resolved IPs......11
Initialize Socket
Begin Client Web Request
Connecting to.....74.125.224.64
Sending HTTP Request
Bytes Sent........99
Bytes Received....7066
Disconnect

Getting network parameters
Requesting IP.....10.0.0.157
Lease Time........86400
DNS...............10.0.0.1
DHCP Server.......10.0.0.1
Router............10.0.0.1
Gateway...........10.0.0.1
DNS Init (bool)...-1
DNS Lookup........
Resolved IP(0)....0.0.0.0
00
69 i
6E n
61 a
6E n
63 c
65 e
2E .
67 g

Below is code I think you can use to confirm. It's essentially the TCSDDD demo modified to REPEAT the entire DHCP-DNS-TCP sequence.
CON
  _clkmode = xtal1 + pll16x     
  _xinfreq = 5_000_000
  { Protocol }
  #0, CLOSED, TCP, UDP, IPRAW, MACRAW, PPPOE
  
  { PST Display }
  CR            = $0D
  LF            = $0A
  
  BUFFER_2K     = $800
  ATTEMPTS      = 5
 
  { Serial IO PINs } 
  USB_Rx        = 31
  USB_Tx        = 30
  
  { Run time DHCP and DNS socket paramters }
  DHCP_SOCKET   = 7
  DNS_SOCKET    = 6
      
 
VAR
DAT
  request       byte  "GET /index.htm HTTP/1.1", CR, LF, {
}               byte  "User-Agent: Wiz5200", CR, LF, CR, LF, $0
  request2      byte  "GET /default.aspx HTTP/1.1", CR, LF, {
}               byte  "Host: agaverobotics.com", CR, LF, {
}               byte  "User-Agent: Wiz5200", CR, LF, CR, LF, $0
  google        byte  "GET /finance/historical?q=FB&output=csv HTTP/1.1", CR, LF, {
}               byte  "Host: finance.google.com", CR, LF, {
}               byte  "User-Agent: Wiz5200", CR, LF, CR, LF, $0
  weather       byte  "GET / HTTP/1.1", CR, LF, {
}               byte  "Host: [URL="http://www.weather.gov"]www.weather.gov[/URL]", CR, LF, {
}               byte  "User-Agent: Wiz5200", CR, LF, CR, LF, $0
  buff          byte  $0[BUFFER_2K]
  site          byte  "finance.google.com", $0
  t1            long  $0
  null          long  $00
OBJ
  pst           : "Parallax Serial Terminal"
  wiz           : "W5200"
  sock          : "Socket"
  dhcp          : "Dhcp"
  dns           : "Dns"
   
PUB Main | bytesToRead, buffer, bytesSent, receiving, remoteIP, dnsServer, totalBytes, i, dnsInit
 

  if(ina[USB_Rx] == 0)      '' Check to see if USB port is powered
    outa[USB_Tx] := 0       '' Force Propeller Tx line LOW if USB not connected
  else                      '' Initialize normal serial communication to the PC here                              
    pst.Start(115_200)      '' [URL]http://forums.parallax.com/showthread.php?135067-Serial-Quirk&p=1043169&viewfull=1#post1043169[/URL]
    pause(500)
  pst.str(string("Initialize W5200", CR)) 
  wiz.QS_Init

  'Loop until we get the W5200 version
  'This lets us know if the W5200 is ready to go 
  i := 0
  repeat until wiz.GetVersion > 0
    pause(250)
    if(i++ > ATTEMPTS*5)
      pst.str(string(CR, "SPI communication failed!", CR))
      return   
  pst.str(string("WizNet 5200 Ver: ") )
  pst.dec(wiz.GetVersion)
  pst.char(CR)
  wiz.SetMac($00, $08, $DC, $17, $62, $ca)
REPEAT
  receiving := true
  bytesToRead := 0
  dnsInit := 0
  pst.str(string("Getting network parameters", CR))
  dhcp.Init(@buff, DHCP_SOCKET)
'  pst.str(string("--------------------------------------------------", CR))
  'SetRequestIp allows us to request a specific IP - No guarentee 
  'dhcp.SetRequestIp(192, 168, 1, 110)                    
 ' t1:=0  
  pst.str(string("Requesting IP....."))      
  repeat until dhcp.DoDhcp(true)
    if(++t1 > ATTEMPTS)
      quit
   
  if(t1 > ATTEMPTS)
    pst.char(CR) 
    pst.str(string(CR, "DHCP Attempts: "))
    pst.dec(t1)
    pst.str(string(CR, "Error Code: "))
    pst.dec(dhcp.GetErrorCode)
    pst.char(CR)
    pst.str(dhcp.GetErrorMessage)
    pst.char(CR)
    return
  else
    PrintIp(dhcp.GetIp)
  { Stress test      
  repeat
    pst.str(string("Requesting IP....."))
    t1 := 0
    'wiz.SetIp(0,0,0,0)
    repeat until dhcp.RenewDhcp
      if(++t1 > ATTEMPTS)
        quit
    if(t1 > ATTEMPTS)
      pst.char(CR) 
      pst.str(string(CR, "DHCP Attempts: "))
      pst.dec(t1)
      pst.str(string(CR, "Error Code: "))
      pst.dec(dhcp.GetErrorCode)
      pst.char(CR)
      pst.str(dhcp.GetErrorMessage)
      pst.char(CR)
      return
    else
      PrintIp(dhcp.GetIp)
    'pause(2000)
   }
  
  pst.str(string("Lease Time........"))
  pst.dec(dhcp.GetLeaseTime)
  pst.char(CR)
 
  pst.str(string("DNS..............."))
  dnsServer := wiz.GetDns
  PrintIp(wiz.GetDns)
  pst.str(string("DHCP Server......."))
  printIp(dhcp.GetDhcpServer)
  pst.str(string("Router............"))
  printIp(dhcp.GetRouter)
  pst.str(string("Gateway..........."))                                        
  printIp(wiz.GetGatewayIp)
  
  pst.char(CR) 
  pst.str(string("DNS Init (bool)..."))
  pst.dec(dns.Init(@buff, DNS_SOCKET))
  pst.char(CR)
  pst.str(string("DNS Lookup........"))
  pst.str(@site)
  pst.str(string(CR,"Resolved IP(0)....")) 
  'remoteIP := dns.ResolveDomain(string("[URL="http://www.agaverobotics.com"]www.agaverobotics.com[/URL]"))
  remoteIP := dns.ResolveDomain(@site)
  'remoteIP := dns.ResolveDomain(string("[URL="http://www.weather.gov"]www.weather.gov[/URL]"))
   
  PrintIp(remoteIP)
  if (byte[remoteIP][0] + byte[remoteIP][1] + byte[remoteIP][2] + byte[remoteIP][3]) == 0
    REPEAT i from 0 to 17
      pst.hex(byte[@site][i],2)
      pst.char(" ")
      pst.char(byte[@site][i])
      pst.char(CR)
    return
     
  pst.str(string("Resolved IPs......"))
  pst.dec(dns.GetIpCount)
  pst.char(13)
  pst.char(13)
 
   'remoteIP := dns.GetResolvedIp(1) 
  pst.str(string("Initialize Socket"))
  sock.Init(0, TCP, 8080)
  sock.RemoteIp(byte[remoteIP][0], byte[remoteIP][1], byte[remoteIP][2], byte[remoteIP][3])  
  sock.RemotePort(80) 
  'PrintIp(wiz.GetRemoteIP(0))
  
  pst.str(string(CR, "Begin Client Web Request", CR))
  'wiz.setGateway(0,0,0,0)
  'Client
  'pst.str(string("Open Socket", CR))
  sock.Open
  sock.Connect
  pst.str(string("Connecting to.....")) 
  PrintIp(wiz.GetRemoteIP(0))
  pst.char(CR) 
   
  'Connection?
  repeat until sock.Connected
    pause(100)
  pst.str(string("Sending HTTP Request", CR)) 
  'bytesSent := sock.Send(@request2, strsize(@request2))
  bytesSent := sock.Send(@google, strsize(@google))
  'bytesSent := sock.Send(@weather, strsize(@weather))
  pst.str(string("Bytes Sent........"))
  pst.dec(bytesSent)
  pst.char(13)
  pause(3000)   'wait for wiz buff to fill
  totalBytes := 0
  repeat while receiving 
    'Data in the buffer?
    bytesToRead := sock.Available
    totalBytes += bytesToRead
     
    'Check for a timeout
    if(bytesToRead < 0)
      receiving := false
      pst.str(string("Timeout", CR))
  '    return
    if(bytesToRead == 0)
      receiving := false
      'pst.str(string(CR, "Done Receiving Response", CR))
      next 
    if(bytesToRead > 0) 
      'Get the Rx buffer  
      buffer := sock.Receive(@buff, bytesToRead)
  '    pst.str(buffer)
      
    bytesToRead~
  pst.str(string("Bytes Received...."))
  pst.dec(totalBytes)
  pst.char(CR)
  
  pst.str(string("Disconnect", CR, CR)) 
  sock.Disconnect
  pause(5000)
PUB DisplayMemory(addr, len, isHex) | j
  pst.str(string(13,"-----------------------------------------------------",13))
  pst.str(string(13, "      "))
  repeat j from 0 to $F
    pst.hex(j, 2)
    pst.char($20)
  pst.str(string(13, "      ")) 
  repeat j from 0 to $F
    pst.str(string("-- "))
  pst.char(13) 
  repeat j from 0 to len
    if(j == 0)
      pst.hex(0, 4)
      pst.char($20)
      pst.char($20)
      
    if(isHex)
      pst.hex(byte[addr + j], 2)
    else
      if(byte[addr+j] < $20 OR byte[addr+j] > $7E)
        if(byte[addr+j] == 0)
          pst.char($20)
        else
          pst.hex(byte[addr+j], 2)
      else
        pst.char($20)
        pst.char(byte[addr+j])
    pst.char($20) 
    if((j+1) // $10 == 0) 
      pst.char($0D)
      pst.hex(j+1, 4)
      pst.char($20)
      pst.char($20)  
  pst.char(13)
  
  pst.char(13)
  pst.str(string("Start: "))
  pst.dec(addr)
  pst.str(string(" Len: "))
  pst.dec(len)
  pst.str(string(13,"-----------------------------------------------------",13,13))
  
PUB PrintIp(addr) | i
  i := 0
  repeat i from 0 to 3
    pst.dec(byte[addr][i] & $FF)
    if(i < 3)
      pst.char($2E)
    else
      pst.char($0D)

PRI pause(Duration)  
  waitcnt(((clkfreq / 1_000 * Duration - 3932) #> 381) + cnt)
  return

On my setup the first byte of the site variable gets clobbered as above on the second iteration. If you look down in the code you can see the way I force it fail by inserting a pause after the GET insuring the wiz buffer fills with 2KB...
  pst.str(string("Sending HTTP Request", CR)) 
  'bytesSent := sock.Send(@request2, strsize(@request2))
  bytesSent := sock.Send(@google, strsize(@google))
  'bytesSent := sock.Send(@weather, strsize(@weather))
  pst.str(string("Bytes Sent........"))
  pst.dec(bytesSent)
  pst.char(13)
  pause(3000)  'make sure wiz buf fills
  totalBytes := 0
  repeat while receiving 

...note that, on my setup, taking out the pause will generally allow the loop to work but it eventually fails (I think depending whether the wiz buffer happens to fill). As well, reversing the order of the site and buff variable declaration allows the loop to run - in this case I think buff+1 may have been clobbering the next var in line (t1) but gone unnoticed.

I've tried this fix...
  BUFFER_2K     = $801

...and it seems to be working but don't know if it's as simple as that.

Otherwise software seems rock solid, I've run thousands of DHCP-DNS-TCP test cycles at this point.

Comments

  • Mike GMike G Posts: 2,702
    edited 2012-12-09 08:36
    Thanks for finding and fixing the buffer overflow. I have a few things on my plate to get done today. I'll take a closer look as soon as I can.

    Is the $800+1 byte always the same value?
  • twctwc Posts: 107
    edited 2012-12-09 10:28
    ...yes, the byte is always 0. Maybe 'cause you're adding a 0 to turn it into a string? Seems like only an issue if the wiz buf fills (and/or aligns with buff in a certain way?) Anyway, I let it run with $801 this morning and it's made almost 1000 passes
  • Mike GMike G Posts: 2,702
    edited 2012-12-09 13:25
    Gotcha... I kinda figured it was a zero. I'll fix that as soon as I can.

    1000 passes is not too shabby.
  • twctwc Posts: 107
    edited 2012-12-09 14:37
    This is probably the string issue (i.e. sticking a 0 on the end so pst.str(buffer) works, and a full wiz buffer is $800 so...). Otherwise solid as a rock since the earlier fixes.
Sign In or Register to comment.