ip65 technical reference
File : ip65/dhcp.s
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
functions
function | description |
---|
dhcp_init |
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
|
variables
variable | description | size (bytes) |
---|
dhcp_state | flag indicating current state of dhcp initialization.
| 1 |
implementation
; 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
ldx #dhcp_discover_options_length ; set destination address
:
lda dhcp_discover_options,x
sta output_buffer+dhcp_options,x
dex
bpl :-
ldx #3 ; set destination address
lda #$FF ; des = 255.255.255.255 (broadcast)
: sta udp_send_dest,x
dex
bpl :-
ldax #dhcp_options+dhcp_discover_options_length
stax udp_send_len
ldax #output_buffer
jsr udp_send
bcc :+
rts
: lda #dhcp_selecting
sta dhcp_state
rts
dhcp_discover_options:
.byte 53 ;option 53 - DHCP message type
.byte 1 ;option length =1
.byte DHCPDISCOVER ; message type
.byte 55 ; option 55 - Parameter Request List
.byte 3 ;option length
.byte 1 ; subnet mask
.byte 3 ; router (gateway)
.byte 6 ; DNS server
.byte $FF ; end of options
dhcp_discover_options_length=*-dhcp_discover_options
;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 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+15
ldax #dhcp_options+16
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
;-- LICENSE FOR dhcp.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 Jonno Downes,
; jonno@jamtronix.com.
; Portions created by the Initial Developer are Copyright (C) 2009
; Jonno Downes. All Rights Reserved.
; -- LICENSE END --