emailler/client/ip65/ip.s

429 lines
8.1 KiB
ArmAsm

.include "../inc/common.i"
.export ip_init
.export ip_process
.export ip_calc_cksum
.export ip_create_packet
.export ip_send
.export ip_inp
.export ip_outp
.export ip_broadcast
.exportzp ip_cksum_ptr
.exportzp ip_ver_ihl
.exportzp ip_tos
.exportzp ip_len
.exportzp ip_id
.exportzp ip_frag
.exportzp ip_ttl
.exportzp ip_proto
.exportzp ip_header_cksum
.exportzp ip_src
.exportzp ip_dest
.exportzp ip_data
.exportzp ip_proto_icmp
.exportzp ip_proto_tcp
.exportzp ip_proto_udp
.import cfg_mac
.import cfg_ip
.import eth_tx
.import eth_set_proto
.import eth_inp
.import eth_inp_len
.import eth_outp
.import eth_outp_len
.importzp eth_dest
.importzp eth_src
.importzp eth_type
.importzp eth_data
.importzp eth_proto_ip
.importzp eth_proto_arp
.import arp_lookup
.import arp_mac
.import arp_ip
.import icmp_init
.import icmp_process
.import udp_init
.import udp_process
.importzp copy_src
.segment "IP65ZP" : zeropage
; checksum
ip_cksum_ptr: .res 2 ; pointer to data to be checksummed
.bss
ip_cksum_len: .res 2 ; length of data to be checksummed
; ip packets start at ethernet packet + 14
ip_inp = eth_inp + eth_data ;pointer to start of IP packet in input ethernet frame
ip_outp = eth_outp + eth_data ;pointer to start of IP packet in output ethernet frame
; temp storage for size calculation
len: .res 2
; flag for incoming broadcast packets
ip_broadcast: .res 1 ;flag set when an incoming IP packet was sent to a broadcast address
; ip packet offsets
ip_ver_ihl = 0 ;offset of 4 bit "version" field and 4 bit "header length" field in an IP packet header
ip_tos = 1 ;offset of "type of service" field in an IP packet header
ip_len = 2 ;offset of "length" field in an IP packet header
ip_id = 4 ;offset of "identification" field in an IP packet header
ip_frag = 6 ;offset of "fragmentation offset" field in an IP packet header
ip_ttl = 8 ;offset of "time to live" field in an IP packet header
ip_proto = 9 ;offset of "protocol number" field in an IP packet header
ip_header_cksum = 10 ;offset of "ip header checksum" field in an IP packet header
ip_src = 12 ;offset of "source address" field in an IP packet header
ip_dest = 16 ;offset of "destination address" field in an IP packet header
ip_data = 20 ;offset of data payload in an IP packet
; ip protocols
ip_proto_icmp = 1
ip_proto_tcp = 6
ip_proto_udp = 17
; temp for calculating checksum
cksum: .res 3
; bad packet counters
bad_header: .res 2
bad_addr: .res 2
.code
; initialize ip routines
; inputs: none
; outputs: none
ip_init:
lda #0
sta bad_header
sta bad_header + 1
sta bad_addr
sta bad_addr + 1
jsr icmp_init
jsr udp_init
; jsr tcp_init
rts
;process an incoming packet & call the appropriate protocol handler
;inputs:
; eth_inp: should point to the received ethernet packet
;outputs:
; carry flag - set on any error, clear if OK
; depending on the packet contents and the protocol handler, a response
; message may be generated and sent out (overwriting eth_outp buffer)
ip_process:
jsr verifyheader ; ver, ihl, len, frag, checksum
bcc @ok
@badpacket:
sec
rts
@ok:
jsr checkaddr ; make sure it's meant for us
bcs @badpacket
lda ip_inp + ip_proto
cmp #ip_proto_icmp
bne :+
jmp icmp_process ; jump to icmp handler
: cmp #ip_proto_tcp
bne :+
jmp tcp_process ; jump to tcp handler
: cmp #ip_proto_udp
bne :+
jmp udp_process ; jump to udp handler
:
tcp_process:
sec ; unknown protocol
rts
; verify that header contains what we expect
verifyheader:
lda ip_inp + ip_ver_ihl ; IPv4 and no IP options
cmp #$45
bne @badpacket
; lda ip_inp + ip_tos ; ignore ToS
lda ip_inp + ip_len + 1 ; ip + 14 bytes ethernet header
clc
adc #14
sta len
lda ip_inp + ip_len
adc #0
sta len + 1
lda eth_inp_len ; check if advertised length is shorter
sec ; than actual length
sbc len
lda eth_inp_len + 1
sbc len + 1
bmi @badpacket
lda ip_inp + ip_frag ; check for fragmentation
beq :+
cmp #$40
bne @badpacket
: lda ip_inp + ip_frag + 1
bne @badpacket
ldax #ip_inp ; verify checksum
stax ip_cksum_ptr
ldax #20
jsr ip_calc_cksum
cmp #0
bne @badpacket
cpx #0
bne @badpacket
clc
rts
@badpacket:
inc bad_header
bne :+
inc bad_header + 1
: sec
rts
; check that this packet was addressed to us
checkaddr:
lda #0
sta ip_broadcast
lda ip_inp + ip_dest ; compare ip address
cmp cfg_ip
bne @broadcast
lda ip_inp + ip_dest + 1
cmp cfg_ip + 1
bne @broadcast
lda ip_inp + ip_dest + 2
cmp cfg_ip + 2
bne @broadcast
lda ip_inp + ip_dest + 3
cmp cfg_ip + 3
bne @broadcast
@ok: clc
rts
@broadcast:
inc ip_broadcast
lda ip_inp + ip_dest ; check for broadcast
and ip_inp + ip_dest + 1
and ip_inp + ip_dest + 2
and ip_inp + ip_dest + 3
cmp #$ff
beq @ok
inc bad_addr
bne :+
inc bad_addr + 1
: sec
rts
; create an IP header (with all the appropriate flags and common fields set) inside an
; ethernet frame
;inputs:
; eth_outp: should point to a buffer in which the ethernet frame is being built
;outputs:
; eth_outp: contains an IP header with version, TTL, flags, src address & IP header
; checksum fields set.
ip_create_packet:
lda #$45 ; set IP version and header length
sta ip_outp + ip_ver_ihl
lda #0 ; set type of service
sta ip_outp + ip_tos
; skip length
; skip ID
lda #$40 ; don't fragment - or should we not care?
sta ip_outp + ip_frag
lda #0
sta ip_outp + ip_frag + 1
lda #$40 ; set time to live
sta ip_outp + ip_ttl
; skip protocol
lda #0 ; clear checksum
sta ip_outp + ip_header_cksum
sta ip_outp + ip_header_cksum + 1
ldx #3 ; copy source address
: lda cfg_ip,x
sta ip_outp + ip_src,x
dex
bpl :-
; skip destination address
rts
; send an IP packet
;inputs
; eth_outp: should point to an ethernet frame that has an IP header created (by
; calling ip_create_packet)
; ip_len: should contain length of IP packet (header + data)
; ip_id: should contain an ID that is unique for each packet
; ip_protocol: should contain protocol ID
; ip_dest: should contain the destination IP address
;outputs:
; eth_outp: ethernet frame updated with correct IP header, then sent out over
; the wire
; carry flag - set on any error, clear if OK
ip_send:
ldx #3 ; get mac addr from ip
: lda ip_outp + ip_dest,x
sta arp_ip,x
dex
bpl :-
jsr arp_lookup
bcc :+
rts ; packet buffer nuked, fail
:
ldax #ip_outp ; calculate ip header checksum
stax ip_cksum_ptr
ldax #20
jsr ip_calc_cksum
stax ip_outp + ip_header_cksum
ldx #5
: lda arp_mac,x ; copy destination mac address
sta eth_outp + eth_dest,x
lda cfg_mac,x ; copy my mac address
sta eth_outp + eth_src,x
dex
bpl :-
lda #eth_proto_ip ; set type to IP
jsr eth_set_proto
lda ip_outp + ip_len + 1 ; set packet length
lsr
bcc @dontpad
rol ; pad with 0
;clc
adc #<ip_outp
sta copy_src ; borrow copymem zp...
lda ip_outp + ip_len
adc #>ip_outp
sta copy_src + 1
ldy #0
tya
sta (copy_src),y
sec ; round up to even number
@dontpad:
lda ip_outp + ip_len + 1
adc #eth_data
sta eth_outp_len
lda ip_outp + ip_len
adc #0
sta eth_outp_len + 1
;jsr dbg_dump_ip_header
jmp eth_tx ; send packet and return status
; calculate checksum for a buffer according to the standard IP checksum algorithm
;inputs:
; ip_cksum_ptr: points at buffer to be checksummed
; AX: length of buffer to be checksumed
;outputs:
; AX: checkum of buffer
ip_calc_cksum:
sta ip_cksum_len ; save length
stx ip_cksum_len + 1
lda #0
sta cksum
sta cksum + 1
sta cksum + 2
cpx #0
beq @tail
: ldx #$80 ; number of bytes / 2
jsr @calc
inc ip_cksum_ptr + 1
dec ip_cksum_len + 1
bne :-
@tail:
lda ip_cksum_len ; divide length by 2
lsr
php ; save carry for odd size
tax
jsr @calc
plp
bcc @done
clc
lda (ip_cksum_ptr),y
adc cksum
sta cksum
bcc @done
inc cksum + 1
bne @done
inc cksum + 2
@done:
lda cksum + 2 ; add carries back in
clc
adc cksum
pha
lda cksum + 1
adc #0
eor #$ff ; return inverted result
tax
pla
eor #$ff
rts
@calc:
ldy #0 ; 1's complement 16-bit sum
@next:
clc
lda (ip_cksum_ptr),y
adc cksum
sta cksum
iny
lda (ip_cksum_ptr),y
adc cksum + 1
sta cksum + 1
bcc :+
inc cksum + 2
: iny
dex
bne @next
rts