1
0
mirror of https://github.com/dschmenk/PLASMA.git synced 2026-03-12 01:41:40 +00:00
Files
PLASMA/src/libsrc/etherip.pla
2018-04-24 18:56:35 -07:00

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