! Ether module for (Moose) INet proceess, GDMR, Jan 1988 ! This module contains an external procedure which is called by the main ! dispatcher to send a buffer out to the ether, and an autonomous process ! which takes packets from the ether and fills in the relevant fields in the ! buffer before putting it on the inbound IP queue. ARP processing is also ! performed by an autonomous process %option "-NonStandard-NoCheck-NoDiag-NoLine" %constinteger inbound priority = 7 %constinteger inbound size = 16384 %constinteger out done priority = 7 %constinteger out done size = 16384 %constinteger ARP priority = 7 %constinteger ARP size = 16384 %constinteger ARP table size = 1023; ! 2^^n - 1, used for masking %constinteger IP protocol type = 16_0800 %constinteger ARP protocol type = 16_0806 %include "INet:Control.Inc" %externalintegerspec control %include "INet:Formats.Inc" %include "INet:Utility.Inc" %include "INet:Stats.Inc" %include "GDMR_E:Ether.Inc" %externalintegerfnspec free store %externalroutinespec FS insert(%string(31) what, %integer value) %externalpredicatespec FS lookup(%string(31) what, %integername value) %systemroutinespec phex(%integer x) %systemroutinespec phex2(%integer x) %systemroutinespec phex4(%integer x) %externalrecord(semaphore fm)%spec dispatch semaphore %externalrecord(queue fm)%spec IP inbound queue %externalintegerspec ether IP address %owninteger broadcast 0 = 0 %owninteger broadcast 1 = 0 %owninteger broadcast 2 = -1 %externalrecord(INet statistics fm)%spec stats %ownrecord(mailbox fm)%name ether mailbox %ownrecord(semaphore fm) TX semaphore = 0 %ownrecord(mailbox fm) TX response = 0 %ownrecord(semaphore fm) RX semaphore = 0 %ownrecord(ether request fm) RX request = 0 %ownrecord(mailbox fm) RX response = 0 %ownrecord(semaphore fm) ARP TX semaphore = 0 %ownrecord(ether request fm) ARP TX request = 0 %ownrecord(mailbox fm) ARP TX response = 0 %ownrecord(semaphore fm) ARP RX semaphore = 0 %ownrecord(ether request fm) ARP RX request = 0 %ownrecord(mailbox fm) ARP RX response = 0 %owninteger IP slot = 0 %owninteger ARP slot = 0 %externalroutinespec add local route(%integer network, metric, via) !recordformat ether address fm(%bytearray x(0 : 5)) %ownrecord(ether address fm) our ether address = 0 %ownrecord(ether address fm) broadcast address = 0 %recordformat ether header fm(%record(ether address fm) destination, %record(ether address fm) source, %short type) %recordformat ARP fm(%short format, %short protocol, %byte hardware length, %byte protocol length, %short op, %record(ether address fm) source hardware, %integer source protocol, %record(ether address fm) target hardware, %integer target protocol, %string(47) padding) %constinteger ARP ether protocol = 1 %constinteger ARP IP type = IP protocol type %constinteger ARP request code = 1 %constinteger ARP reply code = 2 %recordformat ARP buffer fm((%record(ether header fm) h, %record(ARP fm) ARP) %c %or %bytearray x(0 : 1499)) ! ARP tables and table manipulation procedures. The tables are hashed into, ! based on the low-order bits of the IP address. %recordformat ARP entry fm(%integer IP address, %record(ether address fm) ether address) ! This version doesn't maintain a timestamp and doesn't time anything out. %ownrecord(ARP entry fm)%array ARP table(0 : ARP table size) = 0(*) %record(ARP entry fm)%map find ARP entry(%integer IP address) %record(ARP entry fm)%name e %integer slot, end !! printstring("Find ARP entry for ") !! print INet address(IP address); newline slot = IP address & ARP table size end = (slot - 1) & ARP table size %cycle e == ARP table(slot) %result == e %if e_IP address = IP address %result == nil %if e_IP address = 0 %or slot = end slot = (slot + 1) & ARP table size %repeat %end %routine add ARP entry(%integer IP address, %record(ether address fm)%name ether address) %record(ARP entry fm)%name e %integer slot, end !! printstring("Add ARP entry: "); print INet address(IP address) !! space; print ether address(ether address); newline %return %if IP address & 16_F0000000 = 0; ! Bogus, probably a Sun booting slot = IP address & ARP table size end = (slot - 1) & ARP table size %cycle e == ARP table(slot) %return %if e_IP address = IP address %or slot = end %if e_IP address = 0 %start ! Found a free slot !! printstring("Adding at "); write(slot, 0); newline e_IP address = IP address e_ether address = ether address %return %finish slot = (slot + 1) & ARP table size %repeat %end ! Tracing !T! %constinteger ether trace slots = 127; ! 2^n - 1 !T! %constinteger ether trace bytes = 96 !T! %recordformat ether trace fm(%integer inout, bytes, !T! %bytearray data(1 : ether trace bytes)) !T! %recordformat ether trace buffer fm(%integer next, !T! %record(ether trace fm)%array t(0 : ether trace slots)) !T! %ownrecord(ether trace buffer fm) etb = 0 !T! !T! %constinteger trace in = 0 !T! %constinteger trace out = 1 !T! !T! %ownrecord(semaphore fm) trace semaphore = 0 !T! !T! %routine trace(%integer direction, bytes, %bytename packet) !T! %record(ether trace fm)%name t !T! %integer i, n !T! n = bytes; n = ether trace bytes %if n > ether trace bytes !T! semaphore wait(trace semaphore) !T! t == etb_t(etb_next) !T! etb_next = (etb_next + 1) & ether trace slots !T! signal semaphore(trace semaphore) !T! t_inout = direction !T! t_bytes = bytes !T! %for i = 1, 1, n %cycle !T! t_data(i) = packet !T! packet == packet [1] !T! %repeat !T! %end ! Outbound packet handler, including ARP requests %routine make ARP request(%integer IP address) %owninteger ARP sequence = 0 %record(ARP buffer fm) ARP = 0 %record(ether request fm)%name r %integer i %if control & trace ARP out # 0 %start printstring("Make ARP request for ") print INet address(IP address); newline %finish !L! lights or B(ARP light) ARP_ARP_format = ARP ether protocol ARP_ARP_protocol = ARP IP type ARP_ARP_hardware length = 6 ARP_ARP_protocol length = 4 ARP_ARP_op = ARP request code ARP_ARP_source hardware = our ether address ARP_ARP_source protocol = ether IP address ARP_ARP_target hardware = 0 ARP_ARP_target protocol = IP address ARP_h_destination_x(i) = 255 %for i = 0, 1, 5 !T! trace(trace out, 60, ARP_x(0)) ARP TX request_code = ether write ARP TX request_slot = ARP slot ARP TX request_tag = ARP sequence ARP TX request_buffer == ARP_x(0) ARP TX request_bytes = 60 send message(ARP TX request, ether mailbox, ARP TX response) r == receive message(ARP TX response) %if r ## ARP TX request %or r_tag # ARP sequence %start printstring("INet ARP: bogus ether response"); newline stats_ARP errors out = stats_ARP errors out + 1 %else %if r_status # ether success printstring("INet ARP: ether status ") write(r_status, 0); newline stats_ARP errors out = stats_ARP errors out + 1 %else stats_ARP out = stats_ARP out + 1 %finish ARP sequence = ARP sequence + 1 !L! lights and B(\ ARP light) %end %externalroutine ether outbound(%record(buffer fm)%name b) %record(ether request fm)%name TX request %record(ARP entry fm)%name ARP entry %record(ether header fm)%name h %if control & trace ether out # 0 %start printstring("Ether outbound: "); phex(addr(b)) write(b_bytes, 1); newline dump(byteinteger(addr(b_IP header)), b_bytes) %finish -> none %if ether mailbox == nil ARP entry == find ARP entry(b_IP target) %if ARP entry == nil %start ! Not there, we'll have to ask !! printstring("Not there..."); newline make ARP request(b_IP target) ! Drop/enqueue the packet. It will be retransmitted ! by the higher layers if required.... none: %if b_next queue == nil %start !! printstring("No next queue"); newline release buffer(b) %else %if b_flags & requeue flag # 0 %start !! printstring("Requeue on "); phex(addr(b_next queue)); newline requeue(b, b_next queue) %else !! printstring("Enqueue on "); phex(addr(b_next queue)); newline enqueue(b, b_next queue) %finish %finish %else ! Address known, send it on its way.... !! printstring("Found: ") !! print ether address(ARP entry_ether address); newline !L! lights or B(ether light) h == record(addr(b_IP header) - 14) h_destination = ARP entry_ether address h_type = IP protocol type TX request == record(addr(b_message(0))) setup message(TX request, size of(TX request)) TX request_code = ether write TX request_tag = addr(b) TX request_slot = IP slot TX request_buffer == h_destination_x(0) TX request_bytes = b_bytes + 14 TX request_bytes = 60 %if TX request_bytes < 60 !T! trace(trace out, TX request_bytes, h_destination_x(0)) send message(TX request, ether mailbox, TX response) ! Response will be handled by autonomous process below... %finish %end %routine outbound done %record(ether request fm)%name r %record(buffer fm)%name b open input(2, ":N"); select input(2) open output(2, ":T"); select output(2) !! printstring("Outbound done: "); write(free store, 0); newline %cycle r == receive message(TX response) %if r_status # ether success %start pdate printstring("INet ether: "); write(r_status, 0) newline stats_fast errors out = stats_fast errors out + 1 %finish ! Finally drop/enqueue the packet. It will be retransmitted ! by the higher layers if required.... b == record(r_tag) stats_fast packets out = stats_fast packets out + 1 stats_fast bytes out = stats_fast bytes out + b_bytes %if addr(b_IP header) - 14 # addr(r_buffer) %start printstring("Dubious buffer address: "); phex(addr(b)) space; phex(addr(b_IP header) - 14); space phex(addr(r_buffer)); newline %finish %if b_next queue == nil %start !! printstring("No next queue"); newline release buffer(b) %else %if b_flags & requeue flag # 0 %start !! printstring("Requeue on "); phex(addr(b_next queue)); newline requeue(b, b_next queue) %else !! printstring("Enqueue on "); phex(addr(b_next queue)); newline enqueue(b, b_next queue) %finish %finish !L! lights and B(\ ether light) %repeat %end %routine inbound process %owninteger RX sequence = 0 %record(buffer fm)%name b %record(ether request fm)%name r open input(2, ":N"); select input(2) open output(2, ":T"); select output(2) !! printstring("Ether inbound handler started"); newline !! printstring("Inbound process: "); write(free store, 0); newline %cycle b == claim buffer %if b == nil %start printstring("No buffers??"); newline %stop %finish !! printstring("Inbound buffer at "); phex(addr(b)); newline RX sequence = RX sequence + 1 RX request_code = ether read RX request_slot = IP slot RX request_tag = RX sequence RX request_buffer == b_data(32 - 14) send message(RX request, ether mailbox, RX response) r == receive message(RX response) !L! lights or A(ether light) %if r_tag # RX sequence %start pdate printstring("INet ether: dud tag ") phex(r_tag); space; phex(RX sequence) newline stats_fast errors in = stats_fast errors in + 1 release buffer(b) %else %if r_status # ether success pdate printstring("INet ether: "); write(r_status, 0) newline stats_fast errors in = stats_fast errors in + 1 release buffer(b) %else !T! trace(trace in, r_bytes, b_data(32 - 14)) b_bytes = r_bytes - 14 b_IP header == record(addr(b_data(32))) b_next queue == nil b_flags = broadcast flag %if b_data(32 - 14) & 1 # 0 b_interface = 2 stats_fast packets in = stats_fast packets in + 1 stats_fast bytes in = stats_fast bytes in + b_bytes %if control & trace ether in # 0 %start printstring("Received "); write(b_bytes, 0) printstring(" into "); phex(addr(b)); newline dump(byteinteger(addr(b_IP header)), b_bytes) %finish enqueue(b, IP inbound queue) signal semaphore(dispatch semaphore) %finish !L! lights and A(\ ether light) %repeat %end ! ARP process. Listen out for ARP packets on the ether. These will either ! be requests, which we may be expected to reply to, or replies to our ! requests. Either way, we use the information to update our tables. ! Note that in this implementation we don't keep a list of pending requests: ! instead we just put out an ARP request and then drop/enqueue the packet ! as appropriate, letting the higher layers worry about retransmitting ! (presumably after sufficient time for our requests to be answered!). ! Beware of non-IP ARP requests. %routine ARP process %owninteger ARP sequence = 0 %record(ARP buffer fm) ARP %record(ether request fm)%name r open input(2, ":N"); select input(2) open output(2, ":T"); select output(2) !! printstring("ARP receiver starting"); newline !! printstring("ARP process: "); write(free store, 0); newline %cycle ARP sequence = ARP sequence + 1 ARP RX request_code = ether read ARP RX request_slot = ARP slot ARP RX request_tag = ARP sequence ARP RX request_buffer == ARP_x(0) send message(ARP RX request, ether mailbox, ARP RX response) r == receive message(ARP RX response) !L! lights or A(ARP light) %if r ## ARP RX request %or r_tag # ARP sequence %start printstring("INet ARP: bogus ether response"); newline stats_ARP errors in = stats_ARP errors in + 1 %else %if r_status # ether success printstring("INet ARP: ether status ") write(r_status, 0); newline stats_ARP errors in = stats_ARP errors in + 1 %else !T! trace(trace in, r_bytes, ARP_x(0)) !! printstring("ARP format "); phex4(ARP_ARP_format) !! printstring(" protocol "); phex4(ARP_ARP_protocol) !! printstring(" from "); print ether address(ARP_h_source) !! printstring(" for "); print ether address(ARP_h_destination) !! newline !! printstring("HW length "); write(ARP_ARP_hardware length, 0) !! printstring(" prot length "); write(ARP_ARP_protocol length, 0) !! printstring(" op "); phex4(ARP_ARP_op); newline !! printstring("Source HW ") !! print ether address(ARP_ARP_source hardware) !! printstring(" source prot ") !! print INet address(ARP_ARP_source protocol); newline !! printstring("Dest HW ") !! print ether address(ARP_ARP_target hardware) !! printstring(" dest prot ") !! print INet address(ARP_ARP_target protocol) !! newline stats_ARP in = stats_ARP in + 1 %if ARP_ARP_format = ARP ether protocol %c %and ARP_ARP_protocol = ARP IP type %c %and ARP_ARP_hardware length = 6 %c %and ARP_ARP_protocol length = 4 %start %if control & trace ARP in # 0 %start printstring("ARP from ") print INet address(ARP_ARP_source protocol) printstring(" for ") print INet address(ARP_ARP_target protocol) newline %finish ! Format is credible. See if we've to respond. ! Either way, we insert the entry in our tables %if ARP_ARP_op = ARP request code %start ! A request. Insert the addresses add ARP entry(ARP_ARP_source protocol, ARP_ARP_source hardware) ! Is it for us? %if ARP_ARP_target protocol = ether IP address %start ! Yes, note it, turn it round and send it back stats_ARP for us = stats_ARP for us + 1 !add ARP entry(ARP_ARP_source protocol, ! ARP_ARP_source hardware) %if control & trace ARP in # 0 %start printstring("Responding to ARP request from ") print ether address(ARP_h_source); newline %finish !L! lights or B(ARP light) ARP_ARP_op = ARP reply code ARP_ARP_target hardware = ARP_ARP_source hardware ARP_ARP_target protocol = ARP_ARP_source protocol ARP_ARP_source hardware = our ether address ARP_ARP_source protocol = ether IP address ARP_h_destination = ARP_h_source ARP sequence = ARP sequence + 1 ARP RX request_code = ether write ARP RX request_slot = ARP slot ARP RX request_tag = ARP sequence ARP RX request_buffer == ARP_x(0) ARP RX request_bytes = 60 send message(ARP RX request, ether mailbox, ARP RX response) r == receive message(ARP RX response) %if r ## ARP RX request %or r_tag # ARP sequence %start printstring("INet ARP: bogus ether response"); newline %else %if r_status # ether success printstring("INet ARP: ether status ") write(r_status, 0); newline %finish !L! lights and B(\ ARP light) %else %if ARP_ARP_target protocol = broadcast 0 %c %or ARP_ARP_target protocol = broadcast 1 %c %or ARP_ARP_target protocol = broadcast 2 !! printstring("ARP for broadcast received from ") !! print INet address(ARP_ARP_source protocol) !! space; print ether address(ARP_h_source); newline stats_ARP for broadcast = stats_ARP for broadcast + 1 %finish %else %if ARP_ARP_op = ARP reply code ! A response, presumably to a request of ours. ! Insert the addresses. stats_ARP responses = stats_ARP responses + 1 add ARP entry(ARP_ARP_source protocol, ARP_ARP_source hardware) %finish %else stats_ARP unknown = stats_ARP unknown + 1 ! Something unrecognised. Ignore it. %finish %finish !L! lights and A(\ARP light) %repeat %end %owninteger slow station = 0 %externalroutine set ether IP address(%integer example) ! Ether IP address is now set based on the first RIP packet received. ! If it's a thin address then the slow station address is used as the ! basis; if it's a fast address then it's table-driven (where the table ! size is currently one!) %return %if ether mailbox == nil; ! No LANCE, so how did we get here...!? %if example & 16_FFFF0000 = 129 << 24 ! 215 << 16 %start ! Thick address: !# 129.215.128.49 to 96 : CS-rib %if slow station = 16_7E %start ether IP address = 129 << 24 ! 215 << 16 ! 128 << 8 ! 96 broadcast 0 = 129 << 24 ! 215 << 16 %else %if slow station = 16_14 ether IP address = 129 << 24 ! 215 << 16 ! 128 << 8 ! 95 broadcast 0 = 129 << 24 ! 215 << 16 %else printstring("INet: currently only 14 and 7E defined for thick ether") newline %return %finish %else %if example & 16_FFFF0000 = 139 << 24 ! 131 << 16 ! A thin address ether IP address = 139 << 24 ! 131 << 16 ! 1 << 8 ! (128 + 64) ! slow station broadcast 0 = 139 << 24 ! 131 << 16 %else printstring("Unknown example IP network: "); phex(example); newline %return %finish broadcast 1 = broadcast 0 ! 16_FFFF {} printstring("Ether IP address is ") {} print INet address(ether IP address); newline add local route(ether IP address, 1, 2) add ARP entry(broadcast 0, broadcast address) add ARP entry(broadcast 1, broadcast address) %end %externalroutine start ether(%integer station) ! Set up mailboxes, etc, register ourselves as protocols 800 and 806, ! and start the receiver process. %record(process fm)%name p %record(ether request fm)%name r %integer x, c %label start inbound, start ARP, start out done %if FS lookup(ether mailbox name, x) %start ether mailbox == record(x) %else !! printstring("No ether mailbox"); newline ether mailbox == nil %return %finish slow station = station FS insert("ETHER_ARP_TABLES", addr(ARP table(0))) broadcast address_x(x) = 255 %for x = 0, 1, 5 ! IP address now set from RIP listener setup semaphore(TX semaphore) setup mailbox(TX response, TX semaphore) setup semaphore(RX semaphore) setup mailbox(RX response, RX semaphore) setup message(RX request, size of(RX request)) setup semaphore(ARP TX semaphore) setup mailbox(ARP TX response, ARP TX semaphore) setup message(ARP TX request, size of(ARP TX request)) setup semaphore(ARP RX semaphore) setup mailbox(ARP RX response, ARP RX semaphore) setup message(ARP RX request, size of(ARP RX request)) !T! setup semaphore(trace semaphore) !T! signal semaphore(trace semaphore) !T! FS insert("ETHER_TRACE_BUFFER", addr(etb)) ! Make ourselves known to the ether driver RX request_code = ether register RX request_type = IP protocol type send message(RX request, ether mailbox, RX response) r == receive message(RX response) %if r_status # ether success %start printstring("Register IP: "); write(r_status, 0) newline %stop %finish IP slot = r_slot RX request_code = ether register RX request_type = ARP protocol type send message(RX request, ether mailbox, RX response) r == receive message(RX response) %if r_status # ether success %start printstring("Register ARP: "); write(r_status, 0) newline %stop %finish ARP slot = r_slot ! Find our station's address RX request_code = ether address RX request_buffer == our ether address_x(0) send message(RX request, ether mailbox, RX response) r == receive message(RX response) %if r_status # ether success %start printstring("Get ether address: "); write(r_status, 0) newline %stop %finish !! printstring("INet ether address is ") !! print ether address(our ether address); newline ! Now start the autonomous processes p == create process(inbound size, addr(start inbound), inbound priority, nil) p == create process(out done size, addr(start out done), out done priority, nil) p == create process(ARP size, addr(start ARP), ARP priority, nil) !! printstring("INet ether handler started"); newline %return start inbound: inbound process start ARP: ARP process start out done: outbound done %end %end %of %file