ip65 technical reference

File : ip65/arp.s

 ARP address resolution

functions

functiondescription
arp_add
 add arp_mac and arp_ip to the cache
inputs: 
  arp_ip is ip address to add to cache
  arp_mac is corresponding mac address of specified ip  
outputs: 
  arp_cache is updated
arp_calculate_gateway_mask
arp_init
initialize arp (including clearing the arp cache)
inputs: none
outputs: none
arp_lookup
lookup the mac address for an ip
inputs: arp_ip should be set to ip address to be resolved
outputs: 
   if carry flag is clear, then arp_mac will be set to correct mac address
   if carry flag is set, then the correct mac address could not be found in
   the arp cache, so an arp request was sent. so the caller should wait a while
   (to allow time for an arp response message to arrive) and then call arp_lookup again.
arp_process
handle incoming arp packets
inputs: eth_inp should contain an arp packet
outputs: 
   carry flag is set if there was an error processing the arp packet, clear otherwise
   the arp_cache will be updated with the mac & ip address (whether the inbound 
   message was a request or a response). if the incoming packet was an arp 
   request for this machine, then an arp response will be  created (overwriting
   eth_outp) and sent out

variables

variabledescriptionsize (bytes)
arp_cachecache of IP addresses and corresponding MAC addresses (6+4)*ac_size
arp_ip set arp_ip before calling arp_lookup 4
arp_mac result is delivered here when arp_lookup returns with carry flag clear 6

constants

constantsdescriptionvalue
ac_size lookup cache 8

implementation

; ARP address resolution

.include "../inc/common.i"

  .export arp_init
  .export arp_lookup
  .export arp_process
  .export arp_add 
  .export arp_calculate_gateway_mask
  .export arp_ip
  .export arp_mac
  .export arp_cache
  .exportzp ac_size
  
  .import eth_inp
  .import eth_inp_len
  .import eth_outp
  .import eth_outp_len
  .import eth_tx
  .import eth_set_broadcast_dest
  .import eth_set_my_mac_src
  .import eth_set_proto
  .importzp eth_proto_arp
  
  .import cfg_mac
  .import cfg_ip
  .import cfg_netmask
  .import cfg_gateway

  .import timer_read
  .import timer_timeout
  
  .segment "IP65ZP" : zeropage

ap:    .res 2

ARP_TIMEOUT_MS=100

  .bss

; arp state machine
arp_idle  = 1    ; idling
arp_wait  = 2    ; waiting for reply
arp_state:  .res 1    ; current activity

; arguments for lookup and add 
arp:        ; ptr to mac/ip pair
arp_mac:  .res 6    ; result is delivered here when arp_lookup returns with carry flag clear  
arp_ip:    .res 4    ; set arp_ip before calling arp_lookup

; arp cache
ac_size    = 8    ; lookup cache
ac_ip    = 6      ; offset for ip
ac_mac    = 0    ; offset for mac
arp_cache:  .res (6+4)*ac_size ;cache of IP addresses and corresponding MAC addresses

; offsets for arp packet generation
ap_hw    = 14    ; hw type (eth = 0001)
ap_proto  = 16    ; protocol (ip = 0800)
ap_hwlen  = 18    ; hw addr len (eth = 06)
ap_protolen  = 19    ; proto addr len (ip = 04)
ap_op    = 20    ; request = 0001, reply = 0002
ap_shw    = 22    ; sender hw addr
ap_sp    = 28    ; sender proto addr
ap_thw    = 32    ; target hw addr
ap_tp    = 38    ; target protoaddr
ap_packlen  = 42    ; total length of packet

; gateway handling
gw_mask:  .res 4    ; inverted netmask
gw_test:  .res 4    ; gateway ip or:d with inverted netmask
gw_last:  .res 1    ; netmask length - 1

; timeout
arptimeout:  .res 2    ; time when we will have timed out


  .code
;initialize arp (including clearing the arp cache)
;inputs: none
;outputs: none
arp_init:
  lda #0

  ldx #(6+4)*ac_size - 1  ; clear cache
:  
  sta arp_cache,x
  dex
  bpl :-

arp_calculate_gateway_mask:

  lda #$ff    ; counter for netmask length - 1
  sta gw_last

  ldx #3
@gw:
  lda cfg_netmask,x
  eor #$ff
  cmp #$ff
  bne :+
  inc gw_last
:  sta gw_mask,x
  ora cfg_gateway,x
  sta gw_test,x
  dex
  bpl @gw

  lda #arp_idle    ; start out idle
  sta arp_state

  rts


;lookup the mac address for an ip
;inputs: arp_ip should be set to ip address to be resolved
;outputs: 
;   if carry flag is clear, then arp_mac will be set to correct mac address
;   if carry flag is set, then the correct mac address could not be found in
;   the arp cache, so an arp request was sent. so the caller should wait a while
;   (to allow time for an arp response message to arrive) and then call arp_lookup again.
arp_lookup:

  lda arp_ip    ; check for broadcast IP (255.255.255.255)
  and arp_ip + 1
  and arp_ip + 2
  and arp_ip + 3
  cmp #$ff
  bne @notbroadcast
  ldx #6        ;copy ff:ff:ff:ff:ff:ff to ap_mac
: sta arp_mac,x
  dex
  bpl :-
  clc
  rts
  
@notbroadcast:

  ldx gw_last    ; check if address is on our subnet
:  lda arp_ip,x
  ora gw_mask,x
  cmp gw_test,x
  bne @notlocal
  dex
  bpl :-
  bmi @local

@notlocal:

  ldx #3      ; copy gateway's ip address
:  lda cfg_gateway,x
  sta arp_ip,x
  dex
  bpl :-

@local:
  jsr findip
  bcs @cachemiss

  ldy #ac_ip - 1    ; copy mac
:  lda (ap),y
  sta arp,y
  dey
  bpl :-
  rts

@cachemiss:
  lda arp_state    ; are we already waiting for a reply?
  cmp #arp_idle
  beq @sendrequest  ; yes, send request

  ldax arptimeout    ; check if we've timed out
  jsr timer_timeout
  bcs @notimeout    ; no, don't send

@sendrequest:      ; send out arp request
  jsr eth_set_broadcast_dest
  jsr eth_set_my_mac_src

  jsr makearppacket  ; add arp, eth, ip, hwlen, protolen

  lda #0      ; set opcode (request = 0001)
  sta eth_outp + ap_op
  lda #1
  sta eth_outp + ap_op + 1

  ldx #5
:  lda cfg_mac,x    ; set source mac addr
  sta eth_outp + ap_shw,x
  lda #0      ; set target mac addr
  sta eth_outp + ap_thw,x
  dex
  bpl :-

  ldx #3
:  lda cfg_ip,x    ; set source ip addr
  sta eth_outp + ap_sp,x
  lda arp_ip,x    ; set target ip addr
  sta eth_outp + ap_tp,x
  dex
  bpl :-

  lda #ap_packlen
  sta eth_outp_len + 1

  jsr eth_tx    ; send packet

  lda #arp_wait    ; waiting for reply
  sta arp_state

  jsr timer_read    ; read current timer value
  clc
  adc #ARP_TIMEOUT_MS
  sta arptimeout + 1

@notimeout:
  sec       ; set carry to indicate that
  rts      ; no result is availble


; find arp_ip in the cache
; clc returns pointer to entry in (ap)
findip:

  ldax #arp_cache
  stax ap  
  ldx #ac_size
@compare:      ; compare cache entry
  ldy #ac_ip
  lda (ap),y
  beq @notfound
:  lda (ap),y
  cmp arp,y
  bne @next
  iny
  cpy #ac_ip + 4
  bne :-
    
  clc      ; return  
  rts

@next:        ; next entry
  lda ap
  clc
  adc #10
  sta ap
  bcc :+
  inc ap + 1
:  dex
  bne @compare

@notfound:
  sec
  rts


;handle incoming arp packets
;inputs: eth_inp should contain an arp packet
;outputs: 
;   carry flag is set if there was an error processing the arp packet, clear otherwise
;   the arp_cache will be updated with the mac & ip address (whether the inbound 
;   message was a request or a response). if the incoming packet was an arp 
;   request for this machine, then an arp response will be  created (overwriting
;   eth_outp) and sent out
arp_process:

  lda eth_inp + ap_op  ; should be 0
  bne @badpacket
  lda eth_inp + ap_op + 1  ; check opcode
  cmp #1      ; request?
  beq @request
  cmp #2      ; reply?
  beq @reply

@badpacket:
  sec
  rts

@request:
  ldx #3
:  lda eth_inp + ap_tp,x  ; check if they're asking for
  cmp cfg_ip,x    ; my address
  bne @done
  dex
  bpl :-

  ldax #eth_inp + ap_shw
  jsr ac_add_source  ; add them to arp cache

  ldx #5      ; send reply
:  lda eth_inp + ap_shw,x
  sta eth_outp,x    ; set sender packet dest
  sta eth_outp + ap_thw,x  ; and as target
  lda cfg_mac,x    ; me as source
  sta eth_outp + ap_shw,x
  dex
  bpl :-

  jsr eth_set_my_mac_src  ; me as packet source

  jsr makearppacket  ; add arp, eth, ip, hwlen, protolen

  lda #0      ; set opcode (reply = 0002)
  sta eth_outp + ap_op
  lda #2
  sta eth_outp + ap_op + 1

  ldx #3
:  lda eth_inp + ap_sp,x  ; sender as target addr
  sta eth_outp + ap_tp,x
  lda cfg_ip,x    ; my ip as source addr
  sta eth_outp + ap_sp,x
  dex
  bpl :-

  lda #ap_packlen
  sta eth_outp_len + 1

  jsr eth_tx    ; send packet

@done:
  clc
  rts

@reply:
  lda arp_state
  cmp #arp_wait    ; are we waiting for a reply?
  bne @badpacket

  ldax #eth_inp + ap_shw
  jsr ac_add_source  ; add to cache

  lda #arp_idle
  sta arp_state
  
  rts


; add arp_mac and arp_ip to the cache
;inputs: 
;  arp_ip is ip address to add to cache
;  arp_mac is corresponding mac address of specified ip  
;outputs: 
;  arp_cache is updated
arp_add:
  jsr findip    ; check if ip is already in cache
  bcs @add

  ldy #9      ; update old entry
:  lda arp,y    ; move to top as well?
  sta (ap),y
  dey
  bpl :-
  
  rts

@add:
  ldax #arp    ; add


;add source to cache
ac_add_source:
  stax ap

  ldx #9      ; make space in the arp cache
:

  lda arp_cache + 60,x
  sta arp_cache + 70,x
  lda arp_cache + 50,x
  sta arp_cache + 60,x
  lda arp_cache + 40,x
  sta arp_cache + 50,x
  lda arp_cache + 30,x
  sta arp_cache + 40,x
  lda arp_cache + 20,x
  sta arp_cache + 30,x
  lda arp_cache + 10,x
  sta arp_cache + 20,x
  lda arp_cache,x
  sta arp_cache + 10,x

  dex
  bpl :-

  ldy #9
:  lda (ap),y    ; copy source
  sta arp_cache,y
  dey
  bpl :-

  rts


; adds proto = arp, hw = eth, and proto = ip to outgoing packet
makearppacket:
  lda #eth_proto_arp
  jsr eth_set_proto

  lda #0      ; set hw type (eth = 0001)
  sta eth_outp + ap_hw
  lda #1
  sta eth_outp + ap_hw + 1

  lda #8      ; set protcol (ip = 0800)
  sta eth_outp + ap_proto
  lda #0
  sta eth_outp + ap_proto + 1

  lda #6      ; set hw addr len (eth = 06)
  sta eth_outp + ap_hwlen
  lda #4      ; set proto addr len (eth = 04)
  sta eth_outp + ap_protolen

  rts



;-- LICENSE FOR arp.s --
; The contents of this file are subject to the Mozilla Public License
; Version 1.1 (the "License"); you may not use this file except in
; compliance with the License. You may obtain a copy of the License at
; http://www.mozilla.org/MPL/
; 
; Software distributed under the License is distributed on an "AS IS"
; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
; License for the specific language governing rights and limitations
; under the License.
; 
; The Original Code is ip65.
; 
; The Initial Developer of the Original Code is Per Olofsson,
; MagerValp@gmail.com.
; Portions created by the Initial Developer are Copyright (C) 2009
; Per Olofsson. All Rights Reserved.  
; -- LICENSE END --