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 // // Keep module in memory // return modkeep done