mirror of
https://github.com/dschmenk/PLASMA.git
synced 2026-03-12 01:41:40 +00:00
606 lines
15 KiB
Plaintext
606 lines
15 KiB
Plaintext
include "inc/cmdsys.plh"
|
|
include "inc/inet.plh"
|
|
//
|
|
// Predefine service routine
|
|
//
|
|
predef etherServiceIP
|
|
//
|
|
// Segment list element
|
|
//
|
|
struc t_segment
|
|
word seg_buf
|
|
word seg_len
|
|
end
|
|
//
|
|
// Max Ethernet frame size
|
|
//
|
|
const MAX_FRAME_SIZE = 1518
|
|
const MAC_BROADCAST = $FFFF
|
|
const MAC_SIZE = 6
|
|
//
|
|
// Ethernet header
|
|
//
|
|
struc t_ethrhdr
|
|
byte[MAC_SIZE] ethr_dst
|
|
byte[MAC_SIZE] ethr_src
|
|
word ethr_payload
|
|
end
|
|
const PAYLOAD_IP = $0008 // BE format
|
|
const PAYLOAD_ARP = $0608 // BE format
|
|
//
|
|
// IP datagram header
|
|
//
|
|
const IP4ADR_SIZE = 4
|
|
struc t_iphdr
|
|
byte ip_vers_hlen
|
|
byte ip_service
|
|
word ip_length
|
|
word ip_id
|
|
word ip_flags_fragofst
|
|
byte ip_ttl
|
|
byte ip_proto
|
|
word ip_chksm
|
|
byte[IP4ADR_SIZE] ip_src
|
|
byte[IP4ADR_SIZE] ip_dst
|
|
byte[] ip_options
|
|
end
|
|
const t_ethriphdr = t_ethrhdr + t_iphdr
|
|
const IP_BROADCAST = $FFFF
|
|
const IP_PROTO_ICMP = $01
|
|
const IP_PROTO_UDP = $11
|
|
const IP_PROTO_TCP = $06
|
|
//
|
|
// ICMP type/codes
|
|
//
|
|
const ICMP_ECHO_REQST = 8
|
|
const ICMP_ECHO_REPLY = 0
|
|
//
|
|
// ICMP message format
|
|
//
|
|
struc t_icmp
|
|
byte icmp_type
|
|
byte icmp_code
|
|
word icmp_chksm
|
|
word[2] icmp_header
|
|
end
|
|
//
|
|
// UDP IPv4 pseudo header
|
|
//
|
|
struc t_piphdr
|
|
byte[IP4ADR_SIZE] pip_src
|
|
byte[IP4ADR_SIZE] pip_dst
|
|
byte pip_zero
|
|
byte pip_proto
|
|
word pip_len
|
|
end
|
|
//
|
|
// UDP header
|
|
//
|
|
struc t_udphdr
|
|
word udp_src
|
|
word udp_dst
|
|
word udp_len
|
|
word udp_chksm
|
|
end
|
|
//
|
|
// TCP header
|
|
//
|
|
struc t_tcphdr
|
|
word tcp_src
|
|
word tcp_dst
|
|
word tcp_len
|
|
word tcp_chksm
|
|
end
|
|
//
|
|
// ARP packet
|
|
//
|
|
const HW_ETHER = $0100 // BE format
|
|
const ARP_PROTO = $0008 // BE format
|
|
const ARP_REQST = $0100 // BE format
|
|
const ARP_REPLY = $0200 // BE format
|
|
struc t_arp
|
|
word arp_hw
|
|
word arp_prot
|
|
byte arp_hlen
|
|
byte arp_plen
|
|
word arp_op
|
|
byte[MAC_SIZE] arp_senderha
|
|
byte[IP4ADR_SIZE] arp_senderip
|
|
byte[MAC_SIZE] arp_targha
|
|
byte[IP4ADR_SIZE] arp_targip
|
|
end
|
|
const t_earp = t_ethrhdr+t_arp
|
|
//
|
|
// Pre-configured Ethernet header
|
|
//
|
|
byte[] eFrame
|
|
//
|
|
// Destination MAC address
|
|
//
|
|
byte[MAC_SIZE] dstMAC
|
|
//
|
|
// My MAC address
|
|
//
|
|
byte[MAC_SIZE] myMAC
|
|
//
|
|
// Ethernet payload
|
|
//
|
|
word ePayload = PAYLOAD_ARP
|
|
//
|
|
// Pre-configured ARP packet - MUST follow ethernet header!
|
|
//
|
|
word ARP = HW_ETHER // HW TYPE
|
|
word = ARP_PROTO // PROTO TYPE
|
|
byte = MAC_SIZE // HLEN
|
|
byte = IP4ADR_SIZE // PLEN
|
|
word opARP // OP
|
|
export byte[MAC_SIZE] localha
|
|
export byte[IP4ADR_SIZE] localip
|
|
byte[MAC_SIZE] remoteha
|
|
byte[IP4ADR_SIZE] remoteip
|
|
//
|
|
// Local network parameters
|
|
//
|
|
byte[IP4ADR_SIZE] netmask
|
|
byte[IP4ADR_SIZE] subnet
|
|
byte[IP4ADR_SIZE] gateway
|
|
const MAX_UDP_NOTIFIES = 4
|
|
const MAX_TCP_NOTIFIES = 4
|
|
//
|
|
// Notify callbacks
|
|
//
|
|
struc t_notify
|
|
word notify_port
|
|
word notify_func
|
|
word notify_parm
|
|
end
|
|
byte[t_notify] portsUDP[MAX_UDP_NOTIFIES]
|
|
byte[t_notify] portsTCP[MAX_TCP_NOTIFIES]
|
|
//
|
|
// Service ICMP hook
|
|
//
|
|
export word hookICMP
|
|
//
|
|
// Ethernet driver entrypoints
|
|
//
|
|
word setFrameLen, writeFrame, getFrameLen, readFrame
|
|
//
|
|
// Defines for ASM routines
|
|
//
|
|
asm equates
|
|
!SOURCE "vmsrc/plvmzp.inc"
|
|
end
|
|
//
|
|
// Swap bytes in word
|
|
//
|
|
asm swab(val)
|
|
LDA ESTKL,X
|
|
LDY ESTKH,X
|
|
STA ESTKH,X
|
|
STY ESTKL,X
|
|
RTS
|
|
end
|
|
//
|
|
// 1'S COMPLIMENT SUM BE format
|
|
// sum1(PREVSUM, BUF, LEN)
|
|
//
|
|
asm sum1(prevsum, buf, len)
|
|
LDY #$00
|
|
LDA ESTKL+1,X
|
|
STA SRCL
|
|
LDA ESTKH+1,X
|
|
STA SRCH
|
|
LSR ESTKH,X ; CONVERT BYTE LEN TO WORD LEN
|
|
LDA ESTKL,X
|
|
ROR
|
|
ADC #$00
|
|
STA ESTKL,X
|
|
BEQ +
|
|
!BYTE $A9
|
|
- CLC
|
|
INC ESTKH,X
|
|
+ BCS -
|
|
CHKLP LDA (SRC),Y
|
|
PHA
|
|
INY
|
|
BNE +
|
|
INC SRCH
|
|
+ LDA (SRC),Y
|
|
ADC ESTKH+2,X
|
|
STA ESTKH+2,X
|
|
PLA
|
|
ADC ESTKL+2,X
|
|
STA ESTKL+2,X
|
|
INY
|
|
BNE +
|
|
INC SRCH
|
|
+ DEC ESTKL,X
|
|
BNE CHKLP
|
|
DEC ESTKH,X
|
|
BNE CHKLP
|
|
BCC +
|
|
- INC ESTKH+2,X
|
|
BNE +
|
|
INC ESTKL+2,X
|
|
BEQ -
|
|
+ INX
|
|
INX
|
|
RTS
|
|
end
|
|
//
|
|
// Send IP datagram
|
|
//
|
|
def etherSendIP(ipdst, proto, seglist, size)
|
|
byte[t_iphdr] hdr
|
|
byte retry
|
|
word timeout
|
|
|
|
hdr.ip_vers_hlen = $45
|
|
hdr.ip_service = 0
|
|
hdr:ip_length = swab(t_iphdr + size)
|
|
hdr:ip_id = 0
|
|
hdr:ip_flags_fragofst = 0 //$40 // Don't fragment
|
|
hdr.ip_ttl = 10 // Is this reasonable?
|
|
hdr.ip_proto = proto
|
|
hdr:ip_chksm = 0
|
|
memcpy(@hdr.ip_src, @localip, IP4ADR_SIZE)
|
|
if !ipdst // IP_BROADCAST
|
|
memset(@hdr.ip_dst, IP_BROADCAST, IP4ADR_SIZE)
|
|
memset(@dstMAC, MAC_BROADCAST, MAC_SIZE)
|
|
else
|
|
if ipdst=>0 & netmask:0 <> subnet:0 or ipdst=>2 & netmask:2 <> subnet:2
|
|
ipdst = @gateway // External net destination
|
|
fin
|
|
memcpy(@hdr.ip_dst, ipdst, IP4ADR_SIZE)
|
|
retry = 0
|
|
while hdr:ip_dst:0 <> remoteip:0 and hdr:ip_dst:2 <> remoteip:2
|
|
if retry >= 3
|
|
return -1 // ARP failed
|
|
fin
|
|
retry++
|
|
memset(@dstMAC, MAC_BROADCAST, MAC_SIZE)
|
|
memset(@remoteha, 0, MAC_SIZE)
|
|
memcpy(@remoteip, @hdr.ip_dst, IP4ADR_SIZE)
|
|
ePayload = PAYLOAD_ARP
|
|
opARP = ARP_REQST
|
|
setFrameLen(t_earp)
|
|
writeFrame(@eFrame, t_earp)
|
|
for timeout = 1000 downto 0
|
|
etherServiceIP
|
|
if remoteha:0 | remoteha:2 | remoteha:4
|
|
break
|
|
fin
|
|
next
|
|
loop
|
|
memcpy(@dstMAC, @remoteha, MAC_SIZE)
|
|
fin
|
|
//
|
|
// Calculate checksum
|
|
//
|
|
hdr:ip_chksm = sum1(0, @hdr, t_iphdr) ^ $FFFF
|
|
//
|
|
// set the ethernet payload and frame length
|
|
//
|
|
ePayload = PAYLOAD_IP
|
|
setFrameLen(t_ethriphdr + size)
|
|
//
|
|
// Write the ethernet header
|
|
//
|
|
writeFrame(@eFrame, t_ethrhdr)
|
|
//
|
|
// Write the IP header
|
|
//
|
|
writeFrame(@hdr, t_iphdr)
|
|
//
|
|
// Write the remaining segments
|
|
//
|
|
while size > 0
|
|
writeFrame(seglist=>seg_buf, seglist=>seg_len)
|
|
size = size - seglist=>seg_len
|
|
seglist = seglist + t_segment
|
|
loop
|
|
return 0
|
|
end
|
|
//
|
|
// Send UDP datagram
|
|
//
|
|
def etherSendUDP(port, ipdst, portdst, data, len)
|
|
word[8] seglist // list of data and header segments
|
|
byte[t_udphdr] hdr
|
|
|
|
hdr:udp_src = swab(port=>notify_port)
|
|
hdr:udp_dst = swab(portdst)
|
|
hdr:udp_len = swab(t_udphdr + len)
|
|
hdr:udp_chksm = 0
|
|
seglist:0:seg_buf = @hdr
|
|
seglist:0:seg_len = t_udphdr
|
|
seglist:4:seg_buf = data
|
|
seglist:4:seg_len = len
|
|
return etherSendIP(ipdst, IP_PROTO_UDP, @seglist, t_udphdr + len)
|
|
end
|
|
//
|
|
// Notify on UDP datagram received
|
|
//
|
|
def etherOpenUDP(localport, callback, param)
|
|
word port
|
|
byte i
|
|
|
|
if !localport; return -1; fin // invalid port
|
|
//
|
|
// Look for an existing notification on localport
|
|
//
|
|
port = @portsUDP
|
|
for i = 1 to MAX_UDP_NOTIFIES
|
|
if port=>notify_port == localport
|
|
port=>notify_func = callback
|
|
port=>notify_parm = param
|
|
return port
|
|
fin
|
|
port = port + t_notify
|
|
next
|
|
//
|
|
// Add notification on localport if room
|
|
//
|
|
port = @portsUDP
|
|
for i = 1 to MAX_UDP_NOTIFIES
|
|
if !port=>notify_port
|
|
port=>notify_port = localport
|
|
port=>notify_func = callback
|
|
port=>notify_parm = param
|
|
return port
|
|
fin
|
|
port = port + t_notify
|
|
next
|
|
return 0
|
|
end
|
|
//
|
|
// Clear notify on UDP port
|
|
//
|
|
def etherCloseUDP(port)
|
|
word port
|
|
byte i
|
|
|
|
if isuge(port, @portsUDP) and isult(port, @portsUDP + MAX_UDP_NOTIFIES * t_notify)
|
|
//
|
|
// Clear notiications on this port
|
|
//
|
|
if port=>notify_port
|
|
port=>notify_port = 0
|
|
return 0
|
|
fin
|
|
fin
|
|
//
|
|
// Invalid port
|
|
//
|
|
return -1
|
|
end
|
|
//
|
|
// Open TCP socket in SERVER mode
|
|
//
|
|
def etherListenTCP(lclport, callback, param)
|
|
puts("TCP/IP not yet implented for this hardware.\n")
|
|
return 0
|
|
end
|
|
//
|
|
// Open TCP socket in CLIENT mode
|
|
//
|
|
def etherConnectTCP(remip, remport, lclport, callback, param)
|
|
puts("TCP/IP not yet implented for this hardware.\n")
|
|
return 0
|
|
end
|
|
//
|
|
// Write to TCP socket
|
|
//
|
|
def etherSendTCP(wiz, data, len)
|
|
return 0
|
|
end
|
|
//
|
|
// Close TCP socket
|
|
//
|
|
def etherCloseTCP(wiz)
|
|
return 0
|
|
end
|
|
//
|
|
// Update notify callback
|
|
//
|
|
def etherSetCallback(port, callback)
|
|
if isuge(port, @portsUDP) and isult(port, @portsUDP + MAX_UDP_NOTIFIES * t_notify)
|
|
//
|
|
// Update callback on this port
|
|
//
|
|
if port=>notify_port
|
|
port=>notify_func = callback
|
|
return 0
|
|
fin
|
|
fin
|
|
//
|
|
// Invalid port
|
|
//
|
|
return -1
|
|
end
|
|
//
|
|
// Update notify param
|
|
//
|
|
def etherSetParam(port, param)
|
|
if isuge(port, @portsUDP) and isult(port, @portsUDP + MAX_UDP_NOTIFIES * t_notify)
|
|
//
|
|
// Update callback on this port
|
|
//
|
|
if port=>notify_port
|
|
port=>notify_parm = param
|
|
return 0
|
|
fin
|
|
fin
|
|
//
|
|
// Invalid port
|
|
//
|
|
return -1
|
|
end
|
|
//
|
|
// Service incoming packets
|
|
//
|
|
def etherServiceIP
|
|
word rxsize, rxpacket, rxptr
|
|
word iphdr, opt, optlen, port, lclport, remport
|
|
byte i, echolen
|
|
word[4] seglist
|
|
byte[128] echo_reply
|
|
|
|
//
|
|
// Check for received packet
|
|
//
|
|
rxsize = getFrameLen()
|
|
if rxsize
|
|
//
|
|
// Read the entire packet into memory
|
|
//
|
|
rxpacket = heapalloc(rxsize)
|
|
readFrame(rxpacket, rxsize)
|
|
rxptr = rxpacket + t_ethrhdr
|
|
rxsize = rxsize - t_ethrhdr
|
|
//
|
|
// What kind of packet is it?
|
|
//
|
|
when rxpacket=>ethr_payload
|
|
is PAYLOAD_IP
|
|
iphdr = rxptr
|
|
rxptr = rxptr + t_iphdr
|
|
rxsize = rxsize - t_iphdr
|
|
if iphdr->ip_vers_hlen <> $45
|
|
optlen = iphdr=>ip_vers_hlen
|
|
if optlen & $F0 <> $40
|
|
//
|
|
// Not IPv4, ignore
|
|
//
|
|
break
|
|
fin
|
|
optlen = (optlen & $0F) << 2
|
|
if optlen > t_iphdr
|
|
//
|
|
// Read the options and throw them on the ground!
|
|
//
|
|
rxptr = rxptr + (optlen - t_iphdr)
|
|
rxsize = rxsize - (optlen - t_iphdr)
|
|
fin
|
|
fin
|
|
//
|
|
// Filter valid destination address
|
|
//
|
|
if iphdr=>ip_dst:2 <> localip:2 // Yes, this is a little lazy
|
|
if (iphdr=>ip_dst:0|netmask:0) & (iphdr=>ip_dst:2|netmask:2) <> IP_BROADCAST
|
|
break
|
|
fin
|
|
fin
|
|
//
|
|
// What kind of IP protocol is it?
|
|
//
|
|
when iphdr->ip_proto
|
|
is IP_PROTO_UDP
|
|
port = @portsUDP
|
|
if port
|
|
lclport = swab(rxptr=>udp_dst)
|
|
for i = 1 to MAX_UDP_NOTIFIES
|
|
if port=>notify_port == lclport
|
|
port=>notify_func(@iphdr=>ip_src,swab(rxptr=>udp_src),rxptr+t_udphdr,swab(rxptr=>udp_len)-t_udphdr,port=>notify_parm)
|
|
break
|
|
fin
|
|
port = port + t_notify
|
|
next
|
|
fin
|
|
break
|
|
is IP_PROTO_TCP
|
|
break
|
|
is IP_PROTO_ICMP
|
|
//
|
|
// Service ICMP packets
|
|
//
|
|
if rxptr->icmp_type == ICMP_ECHO_REQST
|
|
if rxsize > 128
|
|
echolen = 128
|
|
else
|
|
echolen = rxsize
|
|
fin
|
|
memcpy(@echo_reply, rxptr, echolen)
|
|
echo_reply.icmp_type = ICMP_ECHO_REPLY
|
|
echo_reply:icmp_chksm = 0
|
|
seglist:seg_buf = @echo_reply
|
|
seglist:seg_len = echolen
|
|
etherSendIP(@iphdr=>ip_src, IP_PROTO_ICMP, @seglist, echolen)
|
|
fin
|
|
if hookICMP
|
|
hookICMP(@iphdr=>ip_src, rxptr, rxsize)
|
|
fin
|
|
wend
|
|
break
|
|
is PAYLOAD_ARP
|
|
when rxptr=>arp_op
|
|
is ARP_REPLY
|
|
//
|
|
// Fill in ARP cache
|
|
//
|
|
memcpy(@remoteha, @rxptr=>arp_senderha, 10) // copy ha and ip
|
|
break
|
|
is ARP_REQST
|
|
//
|
|
// Is this a request for me?
|
|
//
|
|
if rxptr=>arp_targip:0 == localip:0 and rxptr=>arp_targip:2 == localip:2
|
|
memcpy(@dstMAC, @rxptr=>arp_senderha, MAC_SIZE)
|
|
memcpy(@remoteha, @rxptr=>arp_senderha, 10) // copy ha and ip
|
|
ePayload = PAYLOAD_ARP
|
|
opARP = ARP_REPLY
|
|
setFrameLen(t_earp)
|
|
writeFrame(@eFrame, t_earp)
|
|
fin
|
|
wend
|
|
wend
|
|
heaprelease(rxpacket)
|
|
fin
|
|
return 0
|
|
end
|
|
//
|
|
// Initialize the driver interface
|
|
//
|
|
export def setEtherDriver(MAC, getlen, readfrm, setlen, writefrm)#0
|
|
memcpy(@myMAC, MAC, MAC_SIZE)
|
|
memcpy(@localha, MAC, MAC_SIZE)
|
|
getFrameLen = getlen
|
|
readFrame = readfrm
|
|
setFrameLen = setlen
|
|
writeFrame = writefrm
|
|
end
|
|
//
|
|
// Set the local IP addresses
|
|
//
|
|
def setEtherIP(newIP, newNetmask, newGateway)
|
|
if newIP; memcpy(@localip, newIP, IP4ADR_SIZE); fin
|
|
if newNetmask; memcpy(@netmask, newNetmask, IP4ADR_SIZE); fin
|
|
if newGateway; memcpy(@gateway, newGateway, IP4ADR_SIZE); fin
|
|
subnet:0 = netmask:0 & gateway:0
|
|
subnet:2 = netmask:2 & gateway:2
|
|
return 0
|
|
end
|
|
//
|
|
// Get the interface hardware address
|
|
//
|
|
def getEtherHA(ha)
|
|
if ha; memcpy(ha, @myMAC, MAC_SIZE); fin
|
|
return MAC_SIZE
|
|
end
|
|
//
|
|
// Fill in iNet class
|
|
//
|
|
iNet:serviceIP = @etherServiceIP
|
|
iNet:openUDP = @etherOpenUDP
|
|
iNet:sendUDP = @etherSendUDP
|
|
iNet:closeUDP = @etherCloseUDP
|
|
iNet:listenTCP = @etherListenTCP
|
|
iNet:connectTCP = @etherConnectTCP
|
|
iNet:sendTCP = @etherSendTCP
|
|
iNet:closeTCP = @etherCloseTCP
|
|
iNet:setInterfaceIP = @setEtherIP
|
|
iNet:getInterfaceHA = @getEtherHA
|
|
iNet:setCallback = @etherSetCallback
|
|
iNet:setParam = @etherSetParam
|
|
done
|