mirror of
https://github.com/bobbimanners/emailler.git
synced 2024-11-15 02:04:28 +00:00
476 lines
11 KiB
ArmAsm
476 lines
11 KiB
ArmAsm
; minimal dhcp implementation - ip addresses are requested from a dhcp server
|
|
; (aka 'leased') but are not renewed or released. although this is not correct
|
|
; behaviour according to the DHCP RFC, this works fine in practice in a typical
|
|
; home network environment.
|
|
;
|
|
; cfg_ip,cfg_netmask,cfg_gateway and cfg_dns variables are all overwritten,
|
|
; therefore, these values must be stored in RAM not ROM
|
|
;
|
|
|
|
MAX_DHCP_MESSAGES_SENT=12 ;timeout after sending 12 messages will be about 15 seconds (1+2+3...)/4
|
|
|
|
.include "../inc/common.i"
|
|
.ifndef KPR_API_VERSION_NUMBER
|
|
.define EQU =
|
|
.include "../inc/kipper_constants.i"
|
|
.endif
|
|
|
|
.export dhcp_init
|
|
.import dhcp_server
|
|
.export dhcp_state
|
|
|
|
.import ip65_error
|
|
.import cfg_mac
|
|
.import cfg_ip
|
|
.import cfg_netmask
|
|
.import cfg_gateway
|
|
.import cfg_dns
|
|
|
|
.import arp_calculate_gateway_mask
|
|
|
|
.import ip65_process
|
|
|
|
.import udp_add_listener
|
|
.import udp_remove_listener
|
|
|
|
.import udp_callback
|
|
.import udp_send
|
|
|
|
.import udp_inp
|
|
|
|
.importzp udp_data
|
|
|
|
.import output_buffer
|
|
|
|
.import udp_send_dest
|
|
.import udp_send_src_port
|
|
.import udp_send_dest_port
|
|
.import udp_send_len
|
|
.import check_for_abort_key
|
|
.import timer_read
|
|
|
|
.bss
|
|
|
|
; dhcp packet offsets
|
|
dhcp_inp = udp_inp + udp_data
|
|
|
|
dhcp_op = 0
|
|
dhcp_htype = 1
|
|
dhcp_hlen = 2
|
|
dhcp_hops = 3
|
|
dhcp_xid = 4
|
|
dhcp_secs = 8
|
|
dhcp_flags = 10
|
|
dhcp_ciaddr = 12
|
|
dhcp_yiaddr = 16
|
|
dhcp_siaddr = 20
|
|
dhcp_giaddr = 24
|
|
dhcp_chaddr =28
|
|
dhcp_sname = 44
|
|
dhcp_file=108
|
|
dhcp_cookie=236
|
|
dhcp_options=240
|
|
|
|
dhcp_server_port=67
|
|
dhcp_client_port=68
|
|
|
|
|
|
; dhcp state machine
|
|
dhcp_initializing = 1 ; initial state
|
|
dhcp_selecting = 2 ; sent a DHCPDISCOVER, waiting for a DHCPOFFER
|
|
dhcp_ready_to_request = 3 ; got a DHCPOFFER, ready to send a DHCPREQUEST
|
|
dhcp_requesting = 4 ; sent a DHCPREQUEST, waiting for a DHCPACK
|
|
dhcp_bound = 5 ; we have been allocated an IP address
|
|
|
|
;flag indicating current state of dhcp initialization.
|
|
dhcp_state: .res 1
|
|
|
|
dhcp_message_sent_count: .res 1
|
|
dhcp_timer: .res 1
|
|
dhcp_loop_count: .res 1
|
|
dhcp_break_polling_loop: .res 1
|
|
|
|
|
|
|
|
;DHCP constants
|
|
BOOTREQUEST =1
|
|
BOOTREPLY =2
|
|
|
|
DHCPDISCOVER =1
|
|
DHCPOFFER =2
|
|
DHCPREQUEST =3
|
|
DHCPDECLINE =4
|
|
DHCPACK =5
|
|
DHCPNAK =6
|
|
DHCPRELEASE =7
|
|
DHCPINFORM =8
|
|
|
|
|
|
.code
|
|
;
|
|
;inputs: none (although ip65_init should be called first)
|
|
;outputs:
|
|
; carry flag clear means IP config has been sucesfully obtained and
|
|
; cfg_ip, cfg_netmask, cfg_gateway and cfg_dns will be set per response from dhcp server.
|
|
; dhcp_server will be set to address of server that provided configuration
|
|
; if carry flag is set there was an error.
|
|
; in either case, dhcp_state will indicate where dhcp initialization ended (to help debug)
|
|
; possible values for dhcp_state are:
|
|
; 1 - initial state
|
|
; 2 - sent a DHCPDISCOVER, waiting for a DHCPOFFER
|
|
; 3 - got a DHCPOFFER, ready to send a DHCPREQUEST
|
|
; 4 - sent a DHCPREQUEST, waiting for a DHCPACK
|
|
; 5 - we have been allocated an IP address
|
|
dhcp_init:
|
|
|
|
ldx #3 ; rewrite ip address
|
|
lda #0
|
|
: sta cfg_ip,x
|
|
dex
|
|
bpl :-
|
|
|
|
lda #dhcp_initializing
|
|
sta dhcp_state
|
|
|
|
ldax #dhcp_in
|
|
stax udp_callback
|
|
ldax #dhcp_client_port
|
|
jsr udp_add_listener
|
|
bcc :+
|
|
rts
|
|
:
|
|
lda #0 ;reset the "message sent" counter
|
|
sta dhcp_message_sent_count
|
|
jsr send_dhcpdiscover
|
|
|
|
@dhcp_polling_loop:
|
|
|
|
lda dhcp_message_sent_count
|
|
adc #1
|
|
sta dhcp_loop_count ;we wait a bit longer between each resend
|
|
|
|
@outer_delay_loop:
|
|
lda #0
|
|
sta dhcp_break_polling_loop
|
|
jsr timer_read
|
|
stx dhcp_timer ;we only care about the high byte
|
|
|
|
@inner_delay_loop:
|
|
jsr ip65_process
|
|
jsr check_for_abort_key
|
|
bcc @no_abort
|
|
lda #KPR_ERROR_ABORTED_BY_USER
|
|
sta ip65_error
|
|
rts
|
|
@no_abort:
|
|
lda #0
|
|
cmp dhcp_break_polling_loop
|
|
bne @break_polling_loop
|
|
jsr timer_read
|
|
cpx dhcp_timer ;this will tick over after about 1/4 of a second
|
|
beq @inner_delay_loop
|
|
|
|
dec dhcp_loop_count
|
|
bne @outer_delay_loop
|
|
|
|
@break_polling_loop:
|
|
|
|
inc dhcp_message_sent_count
|
|
lda dhcp_message_sent_count
|
|
cmp #MAX_DHCP_MESSAGES_SENT-1
|
|
bpl @too_many_messages_sent
|
|
lda dhcp_state
|
|
cmp #dhcp_initializing
|
|
beq @initializing
|
|
cmp #dhcp_selecting
|
|
beq @selecting
|
|
cmp #dhcp_ready_to_request
|
|
beq @ready_to_request
|
|
cmp #dhcp_bound
|
|
beq @bound
|
|
jmp @dhcp_polling_loop
|
|
@initializing:
|
|
@selecting:
|
|
jsr send_dhcpdiscover
|
|
jmp @dhcp_polling_loop
|
|
|
|
@ready_to_request:
|
|
jsr send_dhcprequest
|
|
jmp @dhcp_polling_loop
|
|
|
|
@bound:
|
|
ldax #dhcp_client_port
|
|
jsr udp_remove_listener
|
|
rts
|
|
|
|
@too_many_messages_sent:
|
|
lda #KPR_ERROR_TIMEOUT_ON_RECEIVE
|
|
sta ip65_error
|
|
jsr @bound ;to remove the listener ( thanks to ShadowM for bug report)
|
|
sec ;signal an error
|
|
rts
|
|
|
|
dhcp_create_request_msg:
|
|
lda #BOOTREQUEST
|
|
sta output_buffer+dhcp_op
|
|
lda #1 ;htype 1 = "10 MB ethernet"
|
|
sta output_buffer+dhcp_htype
|
|
lda #6 ;ethernet MACs are 6 bytes
|
|
sta output_buffer+dhcp_hlen
|
|
lda #0 ;hops = 0
|
|
sta output_buffer+dhcp_hops
|
|
ldx #3 ;set xid to be "1234"
|
|
clc
|
|
: txa
|
|
adc #01
|
|
sta output_buffer+dhcp_xid,x
|
|
dex
|
|
bpl :-
|
|
|
|
lda #0 ;secs =00
|
|
sta output_buffer+dhcp_secs
|
|
sta output_buffer+dhcp_secs+1
|
|
;initially turn off all flags
|
|
sta output_buffer+dhcp_flags
|
|
sta output_buffer+dhcp_flags+1
|
|
|
|
ldx #$0F ;set ciaddr to 0.0.0.0
|
|
;set yiaddr to 0.0.0.0
|
|
;set siaddr to 0.0.0.0
|
|
;set giaddr to 0.0.0.0
|
|
: sta output_buffer+dhcp_ciaddr,x
|
|
dex
|
|
bpl :-
|
|
|
|
ldx #5 ;set chaddr to mac
|
|
: lda cfg_mac,x
|
|
sta output_buffer+dhcp_chaddr,x
|
|
dex
|
|
bpl :-
|
|
|
|
ldx #192 ;set sname & file both to null
|
|
lda #0
|
|
: sta output_buffer+dhcp_sname-1,x
|
|
dex
|
|
bne :-
|
|
|
|
|
|
lda #$63 ;copy the magic cookie
|
|
sta output_buffer+dhcp_cookie+0
|
|
lda #$82
|
|
sta output_buffer+dhcp_cookie+1
|
|
lda #$53
|
|
sta output_buffer+dhcp_cookie+2
|
|
lda #$63
|
|
sta output_buffer+dhcp_cookie+3
|
|
|
|
ldax #dhcp_client_port ; set source port
|
|
stax udp_send_src_port
|
|
|
|
ldax #dhcp_server_port ; set destination port
|
|
stax udp_send_dest_port
|
|
|
|
rts
|
|
|
|
send_dhcpdiscover:
|
|
lda #dhcp_initializing
|
|
sta dhcp_state
|
|
|
|
jsr dhcp_create_request_msg
|
|
|
|
lda #$80 ;broadcast flag =1, all other bits 0
|
|
sta output_buffer+dhcp_flags
|
|
|
|
lda #53 ;option 53 - DHCP message type
|
|
sta output_buffer+dhcp_options+0
|
|
lda #1 ;option length is 1
|
|
sta output_buffer+dhcp_options+1
|
|
lda #DHCPDISCOVER
|
|
sta output_buffer+dhcp_options+2
|
|
lda #$FF ;option FF = end of options
|
|
sta output_buffer+dhcp_options+3
|
|
|
|
ldx #3 ; set destination address
|
|
lda #$FF ; des = 255.255.255.255 (broadcast)
|
|
: sta udp_send_dest,x
|
|
dex
|
|
bpl :-
|
|
|
|
ldax #dhcp_options+4
|
|
stax udp_send_len
|
|
ldax #output_buffer
|
|
jsr udp_send
|
|
bcc :+
|
|
rts
|
|
|
|
: lda #dhcp_selecting
|
|
sta dhcp_state
|
|
rts
|
|
|
|
;got a message on port 68
|
|
dhcp_in:
|
|
|
|
lda dhcp_inp+dhcp_op
|
|
cmp #BOOTREPLY
|
|
beq :+
|
|
rts ;it's not what we were expecting
|
|
:
|
|
|
|
lda #0
|
|
cmp dhcp_inp+dhcp_yiaddr ;is the first byte in the assigned address 0?
|
|
bne :+
|
|
rts ;if so, it's a bogus response - ignore
|
|
:
|
|
ldx #4 ;copy the our new IP address
|
|
:
|
|
lda dhcp_inp+dhcp_yiaddr,x
|
|
sta cfg_ip,x
|
|
dex
|
|
bpl :-
|
|
|
|
ldx #0
|
|
@unpack_dhcp_options:
|
|
lda dhcp_inp+dhcp_options,x
|
|
cmp #$ff
|
|
bne :+
|
|
jmp @finished_unpacking_dhcp_options
|
|
:
|
|
cmp #53 ;is this field DHCP message type?
|
|
bne @not_dhcp_message_type
|
|
jmp @get_next_option
|
|
lda dhcp_inp+dhcp_options+2,x
|
|
cmp #DHCPOFFER ;if it's not a DHCP OFFER message, then stop processing
|
|
beq :+
|
|
rts
|
|
: jmp @get_next_option
|
|
|
|
@not_dhcp_message_type:
|
|
|
|
cmp #1 ;option 1 is netmask
|
|
bne @not_netmask
|
|
lda dhcp_inp+dhcp_options+2,x
|
|
sta cfg_netmask
|
|
lda dhcp_inp+dhcp_options+3,x
|
|
sta cfg_netmask+1
|
|
lda dhcp_inp+dhcp_options+4,x
|
|
sta cfg_netmask+2
|
|
lda dhcp_inp+dhcp_options+5,x
|
|
sta cfg_netmask+3
|
|
jmp @get_next_option
|
|
|
|
@not_netmask:
|
|
|
|
cmp #3 ;option 3 is gateway
|
|
bne @not_gateway
|
|
lda dhcp_inp+dhcp_options+2,x
|
|
sta cfg_gateway
|
|
lda dhcp_inp+dhcp_options+3,x
|
|
sta cfg_gateway+1
|
|
lda dhcp_inp+dhcp_options+4,x
|
|
sta cfg_gateway+2
|
|
lda dhcp_inp+dhcp_options+5,x
|
|
sta cfg_gateway+3
|
|
jmp @get_next_option
|
|
|
|
@not_gateway:
|
|
|
|
cmp #6 ;option 3 6 is dns server
|
|
bne @not_dns_server
|
|
lda dhcp_inp+dhcp_options+2,x
|
|
sta cfg_dns
|
|
lda dhcp_inp+dhcp_options+3,x
|
|
sta cfg_dns+1
|
|
lda dhcp_inp+dhcp_options+4,x
|
|
sta cfg_dns+2
|
|
lda dhcp_inp+dhcp_options+5,x
|
|
sta cfg_dns+3
|
|
jmp @get_next_option
|
|
|
|
@not_dns_server:
|
|
|
|
cmp #54 ;option 54 is DHCP server
|
|
bne @not_server
|
|
lda dhcp_inp+dhcp_options+2,x
|
|
sta dhcp_server
|
|
lda dhcp_inp+dhcp_options+3,x
|
|
sta dhcp_server+1
|
|
lda dhcp_inp+dhcp_options+4,x
|
|
sta dhcp_server+2
|
|
lda dhcp_inp+dhcp_options+5,x
|
|
sta dhcp_server+3
|
|
jmp @get_next_option
|
|
|
|
@not_server:
|
|
|
|
|
|
@get_next_option:
|
|
txa
|
|
clc
|
|
adc #02
|
|
adc dhcp_inp+dhcp_options+1,x
|
|
bcs @finished_unpacking_dhcp_options ; if we overflow, then we're done
|
|
tax
|
|
jmp @unpack_dhcp_options
|
|
|
|
|
|
@finished_unpacking_dhcp_options:
|
|
jsr arp_calculate_gateway_mask ;we have modified our netmask, so we need to recalculate gw_test
|
|
lda dhcp_state
|
|
cmp #dhcp_bound
|
|
beq :+
|
|
lda #dhcp_ready_to_request
|
|
sta dhcp_state
|
|
:
|
|
lda #1
|
|
sta dhcp_break_polling_loop
|
|
|
|
rts
|
|
|
|
send_dhcprequest:
|
|
jsr dhcp_create_request_msg
|
|
lda #53 ;option 53 - DHCP message type
|
|
sta output_buffer+dhcp_options+0
|
|
lda #1 ;option length is 1
|
|
sta output_buffer+dhcp_options+1
|
|
lda #DHCPREQUEST
|
|
sta output_buffer+dhcp_options+2
|
|
|
|
lda #50 ;option 50 - requested IP address
|
|
sta output_buffer+dhcp_options+3
|
|
ldx #4 ;option length is 4
|
|
stx output_buffer+dhcp_options+4
|
|
dex
|
|
: lda cfg_ip,x
|
|
sta output_buffer+dhcp_options+5,x
|
|
dex
|
|
bpl :-
|
|
|
|
lda #54 ;option 54 - DHCP server
|
|
sta output_buffer+dhcp_options+9
|
|
ldx #4 ;option length is 4
|
|
stx output_buffer+dhcp_options+10
|
|
dex
|
|
|
|
: lda dhcp_server,x
|
|
sta output_buffer+dhcp_options+11,x
|
|
lda #$ff ;bugfix by ShadowM - DHCP request should be broadcast
|
|
sta udp_send_dest,x
|
|
dex
|
|
bpl :-
|
|
|
|
;A still = option FF = end of options
|
|
|
|
sta output_buffer+dhcp_options+17
|
|
|
|
ldax #dhcp_options+18
|
|
stax udp_send_len
|
|
|
|
ldax #output_buffer
|
|
jsr udp_send
|
|
bcs :+ ;if we didn't send the message we probably need to wait for an ARP reply to come back.
|
|
lda #dhcp_bound ;technically, we should wait till we get a DHCPACK message. but we'll assume success
|
|
sta dhcp_state
|
|
rts
|
|
:
|
|
rts
|