emailler/ip65/arp.s

424 lines
9.8 KiB
ArmAsm

; ARP address resolution
.include "zeropage.inc"
.include "../inc/common.inc"
.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
ap = ptr1
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 proto addr
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
beq :+
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 ; set packet length
sta eth_outp_len
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 ; set timeout to now + ARP_TIMEOUT_MS
sta arptimeout
txa
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 ; set packet length
sta eth_outp_len
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 --