--:::::::::::::: --tcp_utilities.ada --:::::::::::::: ----------------------------------------------------------------------- -- -- DoD Protocols NA-00001-200 80-01028-100(-) -- E-Systems, Inc. August 07, 1985 -- -- TCP_UTILITIES.ADA Author : Jim Baldo -- ----------------------------------------------------------------------- with IP_TCP ; use IP_TCP ; with TCP_TO_ULP_COMMUNICATE ; use TCP_TO_ULP_COMMUNICATE ; with SYSTEM; with TEXT_IO; use TEXT_IO; with UNCHECKED_CONVERSION; -- COMMUNICATE AND QUEUE CONTAINS THE NECESSARY FACILITIES FOR MESSAGES TO BE -- QUEUED BETWEEN TASKS. with WITH_TCP_COMMUNICATE; use WITH_TCP_COMMUNICATE; with TCP_GLOBALS; use TCP_GLOBALS; with QUEUES; use QUEUES; with TCP_SEGMENT_ARRIVES_PROCESSING; use TCP_SEGMENT_ARRIVES_PROCESSING; with IP_GLOBALS; use IP_GLOBALS; with TCB_ALLOCATOR; use TCB_ALLOCATOR; package body T_TCP_CONTROLLER_UTILITIES is package INT_IO_16 is new INTEGER_IO ( SIXTEEN_BITS ) ; function USER_ACCESS_CHECK( LCN : in TCB_PTR) return BOOLEAN is RESULT : BOOLEAN := TRUE; I : TCB_PTR := OBTAIN_HEAD_OF_LCN_IN_USE_QUEUE; -- THE BEGINNING INDEX INTO THE LCN LIST! begin while I /= null and RESULT loop if I = LCN then RESULT := FALSE; end if; I := I.NEXT; end loop; return RESULT; exception when OTHERS => PUT("PROBLEM IN USER ACCESS CHECK"); end USER_ACCESS_CHECK; procedure SEND_A_SYN( LCN : in TCB_PTR) is --This subprogram is called by the TCP_OPEN and the TCP_SEND in the --listen state to send out a SYN. The LCN is passed to the subprogram --to calculate the appropiate address. This subprogram will format --and send a SYN segment to the IP for transmission to the remote host. HEADER_LENGTH : SIXTEEN_BITS := 20; BUFPTR : T_TCP_GLOBALS_DATA_STRUCTURES.BUFFER_POINTER; DEST : THIRTYTWO_BITS ; SOURCE : THIRTYTWO_BITS ; PACKED_BUFF : PACKED_BUFFER_PTR; BUFFLEN : SIXTEEN_BITS ; -- TEMPORARY Q_ITEM : STD_Q_ITEM; UMESSAGE : USER_MESSAGE; SOCKET_PARAMS : LCN_PTR_TYPE; MESSAGE_FOR_IP : IP_GLOBALS.IP_MESSAGE ; begin BUFFGET(PACKED_BUFF, BUFFLEN); if PACKED_BUFF = null then -- TELL USER -- ERROR: INSUFFICIENT RESOURCES SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 1, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); else PACKED_BUFF.STATUS := OWNER_TCP; PACKED_BUFF.IN_USE := TRUE; LCN.ISS := MODULAR_CONVERT(ISS);--GET AN INTIAL SEND SEQUENCE NUMBER --(ISS) LCN.SND_NXT := LCN.ISS;-- SET UP THE INITIAL SEND NEXT. TYPE_FLAG := SYN; OPTIONS := CLEAR;--CLEAR THE OPTIONS ARRAY --ADD ANY OPTIONS TO HEADER LENGTH HERE TCP_HEADER_FORMAT( LCN, BUFPTR, TYPE_FLAG, OPTIONS); DEST := LCN.DESTINATION_ADDRESS; -- HERE WE MUST PUT IN THE NECESSARY SECURITY OPTIONS FOR IP. OPTIONS := TCP_SECURITY_OPTIONS; PACK_BUFFER_INTO_BIT_STREAM(BUFPTR, PACKED_BUFF);-- PACK THE BUFFER SOURCE := IP_GLOBALS.WHOIAM; MESSAGE_FOR_IP := ( FROM_TCP, PACKED_BUFF, DEST, TOS, TTL, HEADER_LENGTH, IDENT, DONT_FRAGMENT, OPTIONS, SOURCE ) ; IP_FROM_TCP.Q_ADD( MESSAGE_FOR_IP ) ; -- UPDATE SEND UNACKNOWLEDGED AND SND_NXT. LCN.SND_UNA := LCN.ISS; LCN.SND_NXT := LCN.ISS + MODULAR_CONVERT(SIXTEEN_BITS(1)); -- SAME AS SEND NEXT + 1 -- PUT BUFFER ON RETRANSMIT QUEUE WITH THE LENGTH IN OCTETS. Q_ITEM := (PACKED_BUFF, NULL_UNPACKED_BUFFER, HEADER_LENGTH); -- IT IS QUEUED UP WITH A TIME. QUEUE_ADD(TCP_RETRANSMIT_QUEUE, LCN, Q_ITEM); end if; end SEND_A_SYN; procedure TCP_SEND(LCN : in TCB_PTR; PACKED_BUFF : in out PACKED_BUFFER_PTR; BUFFLEN, PUSH_FLAG,URG_FLAG,TIMEOUT : in SIXTEEN_BITS ) is --This subprogram is called by the TCP controller to process a users send --request. This subprogram will format the header and pass the buffer on to --the IP for transmission. --The following parameters are passed to the subprogram: -- LCN -- Buffer length -- PUSH_FLAG : An indication of whether all data should be p ushed through. -- URG_FLAG : An indication of the urgency of the data. -- (1 URGENT, 0 NORMAL). -- TIMEOUT : The timeout interval for a connection. timeout occurs if -- there is no response for that amount of time. -- PACKED_BUFF : A packed buffer with user data. -- NULL_FLAG, NO_ROOM_ON_QUEUE : BOOLEAN; BUFFSIZE : SIXTEEN_BITS ; BUFPTR : T_TCP_GLOBALS_DATA_STRUCTURES.BUFFER_POINTER; Q_ITEM : STD_Q_ITEM; SEGMENT_LENGTH : SIXTEEN_BITS := 255 - PACKED_BUFF.TCP_PTR; -- CORRECT UNTIL SIZE CHANGES UMESSAGE : USER_MESSAGE; SOCKET_PARAMS : LCN_PTR_TYPE; MESSAGE_FOR_IP : IP_GLOBALS.IP_MESSAGE ; DUMMY : CHARACTER; begin --TEXT_IO.PUT_LINE("TCP_SEND STATE = " & STATES'IMAGE(LCN.STATE)); -- PUT_LINE("HIT ANY CHARACTER TO CONTINUE"); -- GET(DUMMY); T_TCP_GLOBALS_DATA_STRUCTURES.LCN := LCN; -- FOR TEST*** if TIMEOUT /= 0 then -- PUT IN THE NEW CONNECTION TIMEOUT. LCN.CONNECTION_TIMEOUT := TIMEOUT; end if; case LCN.STATE is when CLOSED => if USER_ACCESS_CHECK(LCN) then -- TELL USER -- ERROR: CONNECTION ILLEGAL FOR THIS PROCESS SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 2, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); else -- TELL USER -- ERROR: CONNECTION DOES NOT EXIST SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 3, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); end if; when T_TCP_GLOBALS_DATA_STRUCTURES.LISTEN => if FOREIGN_SOCKET_UNSPECIFIED(LCN) then -- TELL USER -- ERROR: FOREIGN SOCKET UNSPECIFIED SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 4, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); else -- TCB STATE BECOMES ACTIVE LCN.ACTIVE_PASSIVE := ACTIVE; -- PUT ANY TEXT ON THE TEXT TRANSMIT QUEUE FOR LATER TRANSMISSION if BUFFLEN > 0 then -- THERE IS DATA. Q_ITEM := (PACKED_BUFF, NULL_UNPACKED_BUFFER, BUFFLEN); QUEUE_ADD(TRANSMIT_QUEUE, LCN, Q_ITEM, NO_ROOM_ON_QUEUE); if NO_ROOM_ON_QUEUE then -- TELL USER OR ERROR ROUTINE -- ERROR: INSUFFICIENT RESOURCES PUT_LINE("STATE IS LISTEN");--DEBUG PUT_LINE("NO Q RM IN SEND");-- DEBUG SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 5, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); end if; end if; -- SEND OUT A SYN SEND_A_SYN(LCN); -- PUT TCB INTO SYN_SENT STATE. LCN.STATE := SYN_SENT; end if; when SYN_SENT | SYN_RECEIVED => -- PUT THE TEXT ON THE TRANSMIT QUEUE FOR LATER TRANSMISSION if BUFFLEN > 0 then -- THERE IS DATA. Q_ITEM := (PACKED_BUFF, NULL_UNPACKED_BUFFER, BUFFLEN); QUEUE_ADD(TRANSMIT_QUEUE, LCN, Q_ITEM, NO_ROOM_ON_QUEUE); if NO_ROOM_ON_QUEUE then -- TELL USER -- ERROR: INSUFFICIENT RESOURCES PUT_LINE("NO Q RM IN SEND");-- DEBUG PUT_LINE("STATE IS SYN SENT OR SYN RECEIVED");--DEBUG SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 5, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); end if; end if; when ESTABLISHED | CLOSE_WAIT => -- HERE WE WOULD SEGMENTIZE A BUFFER FOR TRANSMISSON, WHICH WE -- ARE CURRENTLY NOT DOING. if LCN.SND_WND + LCN.SND_UNA >= (LCN.SND_NXT + SEGMENT_LENGTH) then -- USE THE SAME BUFFER ALL THE WAY TYPE_FLAG := SEGMENT; -- PUT THE DATA IN THE BUFFER for I in 1..SEGMENT_LENGTH loop BUFPTR.DATA(I) := PACKED_BUFF.BYTE(PACKED_BUFF.TCP_PTR+I); end loop; BUFPTR.DATA_LEN := SEGMENT_LENGTH; -- CLEAR OPTIONS ARRAY OPTIONS := CLEAR; TCP_HEADER_FORMAT(LCN, BUFPTR, TYPE_FLAG, OPTIONS); -- HERE WE MUST PUT IN THE NECESSARY SECURITY OPTIONS FOR IP. OPTIONS := TCP_SECURITY_OPTIONS; -- PACK THE BUFFER -- WE ASSUME THAT THE POINTER IS CORRECTLY SET. PACK_BUFFER_INTO_BIT_STREAM(BUFPTR, PACKED_BUFF); -- THE STANDARD HEADER LENGTH + DATA LENGTH LEN := BUFPTR.DATA_OFFSET * 4 + SEGMENT_LENGTH; PACKED_BUFF.STATUS := OWNER_TCP; MESSAGE_FOR_IP := ( FROM_TCP, PACKED_BUFF, LCN.DESTINATION_ADDRESS, TOS, TTL, LEN, IDENT, DONT_FRAGMENT, OPTIONS, LCN.SOURCE_ADDRESS ) ; IP_FROM_TCP.Q_ADD( MESSAGE_FOR_IP ) ; -- PUT BUFFER IN RETRANSMISSION QUEUE AND SET THE TIMER -- LEN IS THE TOTAL NUM OF BYTES IN THE SEGMENT. Q_ITEM := (PACKED_BUFF, NULL_UNPACKED_BUFFER, LEN); -- TESTING NEW_LINE; -- if QUEUE_FREE_LIST.NEXT = null then -- PUT("QUEUE_FREE_LIST = null "); -- else -- Put("QUEUE_FREE_LIST /= null "); -- end if; -- NEW_LINE; QUEUE_ADD(TCP_RETRANSMIT_QUEUE, LCN, Q_ITEM); -- WECOULD MESS WITH OUR RECEIVE WINDOW HERE IF WE WISHED. -- NEW_LINE; -- PUT_LINE("AFTER QUEUE_ADD TCP_SEND"); -- if QUEUE_FREE_LIST.NEXT = null then -- PUT("QUEUE_FREE_LIST = null "); -- else -- Put("QUEUE_FREE_LIST /= null "); -- end if; -- NEW_LINE; LCN.SND_NXT := LCN.SND_NXT + SEGMENT_LENGTH; if URG_FLAG = BIT_SET then LCN.SND_UP := LCN.SND_NXT - 1; end if; else -- PUT THE TEXT ON THE TRANSMIT QUEUE FOR PROCESSING WHEN AN ACK COMES -- IN AND WE CAN SEND IT. Q_ITEM := (PACKED_BUFF, NULL_UNPACKED_BUFFER, SEGMENT_LENGTH); QUEUE_ADD(TRANSMIT_QUEUE, LCN, Q_ITEM, NO_ROOM_ON_QUEUE); if NO_ROOM_ON_QUEUE then -- USER ERROR: INSUFFICIENT RESOURCES PUT_LINE("NO Q RM IN SEND");-- DEBUG PUT_LINE("STATE IS ESTAB");--DEBUG INT_IO_16.PUT(SEGMENT_LENGTH); SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 5, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); end if; end if; when others => -- TELL USER ERROR: CONNECTION CLOSING SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE :=( 6, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); end case; end TCP_SEND; procedure TCP_RECEIVE( LCN : in TCB_PTR; PACKED_BUFF : in out PACKED_BUFFER_PTR; BYTE_COUNT : in SIXTEEN_BITS ) is --This subprogram is called by the user via the TCP controller. --it will queue the request or if there is data to satisfy it, return --a buffer full of data if one is available. Otherwise it will simply --queue the request until data becomes available. The subprogram is --passed a LCN, BYTE_COUNT which represents the size of the buffer if --one is passed in, and PACKED_BUFF that contains the data to be --returned to the user. RECEIVE_QUEUE_FULL : BOOLEAN; PACKED_BUFFER : PACKED_BUFFER_PTR; BUFFTYPE, PROCESSED_BYTE_COUNT : SIXTEEN_BITS ; Q_ITEM : STD_Q_ITEM; UMESSAGE : USER_MESSAGE; SOCKET_PARAMS : LCN_PTR_TYPE; DUMMY : CHARACTER; begin --TEXT_IO.PUT_LINE("TCP - TCP_RECEIVE"); T_TCP_GLOBALS_DATA_STRUCTURES.LCN := LCN; -- FOR TEST*** -- ALL RECEIVE REQUESTS THAT WE CAN HANDLE WILL BE SATISIFIED OR QUEUED -- ON THE TCP RECEIVE QUEUE FOR LATER PROCESSING WHEN DATA COMES IN. --TEXT_IO.PUT_LINE("TCP_RECEIVE STATE = " & STATES'IMAGE(LCN.STATE)); case LCN.STATE is when T_TCP_GLOBALS_DATA_STRUCTURES.CLOSED => if USER_ACCESS_CHECK(LCN) then -- TELL USER ERROR: CONNECTION ILLEGAL FOR THIS PROCESS SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 2, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); else -- TELL USER ERROR: CONNECTION DOES NOT EXIST SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 3, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); end if; when T_TCP_GLOBALS_DATA_STRUCTURES.LISTEN | SYN_SENT | SYN_RECEIVED => -- PUT REQUEST ON QUEUE FOR LATER PROCESSING Q_ITEM := (PACKED_BUFF, NULL_UNPACKED_BUFFER, BYTE_COUNT); QUEUE_ADD(RECEIVE_QUEUE, LCN, Q_ITEM, RECEIVE_QUEUE_FULL); if RECEIVE_QUEUE_FULL then -- TELL USER ERROR: INSUFFICIENT RESOURCES PUT_LINE("NO Q RM IN RECEIVE");-- DEBUG SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 5, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); end if; when ESTABLISHED | FIN_WAIT_1 | FIN_WAIT_2 => -- NOTE THAT WE CONSIDER ANY DATA TO BE SUFFICIENT DATA FOR A BUFFER. -- WE DO NOTHING WITH THE PUSH FLAG SINCE WE CURRENTLY PUSH EVERYTHING. -- ALSO WE MAKE NO PROVISION FOR URGENT DATA AND DO NOT CHECK FOR IT. -- GET A BUNCH OF DATA IF WE CAN. if QUEUE_FREE_LIST.NEXT = null then NEW_LINE; PUT_LINE("QUEUE_FREE_LIST.NEXT TCP_RECEIVE = null"); PUT_LINE("STRIKE ANY CHARACTER TO CONTINUE"); GET(DUMMY); end if; QUEUE_GET(PROCESSED_SEGMENTS_FOR_USER_QUEUE, LCN, Q_ITEM); -- if QUEUE_FREE_LIST.NEXT = null then -- NEW_LINE; -- PUT_LINE("QUEUE_FREE_LIST.NEXT TCP_RECEIVE = null"); -- PUT_LINE("STRIKE ANY CHARACTER TO CONTINUE"); -- GET(DUMMY); -- end if; PROCESSED_BYTE_COUNT := Q_ITEM.LENGTH; -- if QUEUE_FREE_LIST.NEXT = null then -- NEW_LINE; -- PUT_LINE("QUEUE_FREE_LIST.NEXT TCP_RECEIVE = null"); -- PUT_LINE("STRIKE ANY CHARACTER TO CONTINUE"); -- GET(DUMMY); -- end if; PACKED_BUFFER := Q_ITEM.BUFFER; -- if QUEUE_FREE_LIST.NEXT = null then -- NEW_LINE; -- PUT_LINE("QUEUE_FREE_LIST.NEXT TCP_RECEIVE = null"); -- PUT_LINE("STRIKE ANY CHARACTER TO CONTINUE"); -- GET(DUMMY); -- end if; if PACKED_BUFFER = null then --TEXT_IO.PUT_LINE("TCP_RECEIVE - PACKED_BUFFER = NULL"); -- PUT REQUEST ON QUEUE FOR LATER PROCESSING -- if QUEUE_FREE_LIST.NEXT = null then -- NEW_LINE; -- PUT_LINE("QUEUE_FREE_LIST.NEXT TCP_RECEIVE = null"); -- PUT_LINE("STRIKE ANY CHARACTER TO CONTINUE"); -- GET(DUMMY); -- end if; Q_ITEM := (PACKED_BUFF, NULL_UNPACKED_BUFFER, BYTE_COUNT); -- if QUEUE_FREE_LIST.NEXT = null then -- NEW_LINE; -- PUT_LINE("QUEUE_FREE_LIST.NEXT TCP_RECEIVE = null"); -- PUT_LINE("STRIKE ANY CHARACTER TO CONTINUE"); -- GET(DUMMY); -- end if; QUEUE_ADD(RECEIVE_QUEUE, LCN, Q_ITEM, RECEIVE_QUEUE_FULL); -- if QUEUE_FREE_LIST.NEXT = null then -- NEW_LINE; -- PUT_LINE("QUEUE_FREE_LIST.NEXT TCP_RECEIVE = null"); -- PUT_LINE("STRIKE ANY CHARACTER TO CONTINUE"); -- GET(DUMMY); -- end if; if RECEIVE_QUEUE_FULL then -- TELL USER ERROR: INSUFFICIENT RESOURCES PUT_LINE("NO Q RM IN SEND");-- DEBUG SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 5, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); end if; else -- FILL BUFFER WITH QUEUED INCOMING SEGMENTS INSERT_TEXT_IN_BUFFER(BYTE_COUNT, PACKED_BUFF, PROCESSED_BYTE_COUNT, PACKED_BUFFER); -- WE IGNORE A PUSH; WE IGNORE URGENT POINTER -- GIVE THE BUFFER TO THE USER SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 10, SOCKET_PARAMS, PACKED_BUFF); MESSAGE_FOR_USER(UMESSAGE); -- FREE UP THE PACKED BUFFER PACKED_BUFFER.STATUS := NONE; PACKED_BUFFER.IN_USE := FALSE; BUFFREE(PACKED_BUFFER, BUFFTYPE); end if; when CLOSE_WAIT => QUEUE_GET(PROCESSED_SEGMENTS_FOR_USER_QUEUE, LCN, Q_ITEM); PROCESSED_BYTE_COUNT := Q_ITEM.LENGTH; PACKED_BUFFER := Q_ITEM.BUFFER; if PACKED_BUFFER = null then -- ERROR: CONNECTION CLOSING SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 6, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); else -- FILL BUFFER WITH ANY REMAINING TEXT INSERT_TEXT_IN_BUFFER(BYTE_COUNT, PACKED_BUFF, PROCESSED_BYTE_COUNT, PACKED_BUFFER); -- RETURN BUFFER TO USER SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 10, SOCKET_PARAMS, PACKED_BUFF); MESSAGE_FOR_USER(UMESSAGE); -- FREE UP THE PACKED BUFFER PACKED_BUFFER.STATUS := NONE; PACKED_BUFFER.IN_USE := FALSE; BUFFREE(PACKED_BUFFER, BUFFTYPE); end if; when CLOSING | TIME_WAIT | LAST_ACK => -- TELL USER ERROR: CONNECTION CLOSING SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 6, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); end case; exception when constraint_error => PUT_LINE("CONSTRAINT ERROR IN TCP RECEIVE"); when others => PUT_LINE("ERROR IN TCP RECEIVE"); end TCP_RECEIVE; procedure TCP_ABORT( LCN : in TCB_PTR) is --This subprogram is called by the user via the TCP controller to --abort a connection. It does this by sending a reset to the remote --host and clearing the TCB associated with the particular local --connection name. All queues will have the items from this connection --removed from them. USER_SHOULD_NOT_HAVE_ACCESS : BOOLEAN; BUFFLEN : SIXTEEN_BITS ; BUFPTR : T_TCP_GLOBALS_DATA_STRUCTURES.BUFFER_POINTER; PACKED_BUFF : PACKED_BUFFER_PTR; UMESSAGE : USER_MESSAGE; SOCKET_PARAMS : LCN_PTR_TYPE; MESSAGE_FOR_IP : IP_GLOBALS.IP_MESSAGE ; TEMP_LCN : TCB_PTR ; begin TEMP_LCN := LCN; -- To contain modification locally***** T_TCP_GLOBALS_DATA_STRUCTURES.LCN := LCN; -- FOR TEST*** case LCN.STATE is when T_TCP_GLOBALS_DATA_STRUCTURES.CLOSED => if USER_ACCESS_CHECK(LCN) then -- TELL USER ERROR: CONNECTION ILLEGAL FOR THIS PROCESS SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 2, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); else -- TELL USER ERROR: CONNECTION DOES NOT EXIST SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 3, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); end if; when T_TCP_GLOBALS_DATA_STRUCTURES.LISTEN => QUEUE_CLEAR(RECEIVE_QUEUE, LCN); -- CLEAR THE TCB AND ENTER THE CLOSED STATE LCN.STATE := CLOSED; -- TELL USER OK SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 8, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); TCB_CLEAR(LCN); TCB_FREE( TEMP_LCN ); when SYN_SENT => QUEUE_CLEAR(RECEIVE_QUEUE, LCN); -- NONE ON THIS LEVEL. CLEAR_SEND_QUEUE(LCN); -- CLEAR THE TCB AND ENTER THE CLOSED STATE QUEUE_CLEAR(TCP_RETRANSMIT_QUEUE, LCN); QUEUE_CLEAR(TRANSMIT_QUEUE, LCN); -- TELL USER OK SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 8, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); TCB_CLEAR(LCN); TCB_FREE( TEMP_LCN ); when SYN_RECEIVED | ESTABLISHED | FIN_WAIT_1 | FIN_WAIT_2 | CLOSE_WAIT => -- SEND A RESET SEGMENT BUFFGET(PACKED_BUFF, BUFFLEN); if PACKED_BUFF = null then -- ERROR OUT OF BUFFERS TCP_ERROR(1); else PACKED_BUFF.STATUS := OWNER_TCP; PACKED_BUFF.IN_USE := TRUE; TCP_HEADER_FORMAT( LCN, BUFPTR, RST, OPTIONS); -- HERE WE MUST PUT IN THE NECESSARY SECURITY OPTIONS FOR IP. OPTIONS := TCP_SECURITY_OPTIONS; -- TELL USER OK SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 8, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); -- PACK THE BUFFER PACK_BUFFER_INTO_BIT_STREAM(BUFPTR, PACKED_BUFF); LEN := BUFPTR.DATA_OFFSET * 4;-- SINCE NO DATA SENT MESSAGE_FOR_IP := ( FROM_TCP, PACKED_BUFF, LCN.DESTINATION_ADDRESS, TOS, TTL, LEN, IDENT, DONT_FRAGMENT, OPTIONS, LCN.SOURCE_ADDRESS ) ; IP_FROM_TCP.Q_ADD( MESSAGE_FOR_IP ) ; end if; QUEUE_CLEAR(RECEIVE_QUEUE, LCN); -- NONE ON THIS LEVEL. CLEAR_SEND_QUEUE(LCN); -- TRASH TRANSMIT AND RETRANSMIT QUEUES QUEUE_CLEAR(TRANSMIT_QUEUE, LCN); QUEUE_CLEAR(TCP_RETRANSMIT_QUEUE, LCN); QUEUE_CLEAR(PROCESSED_SEGMENTS_FOR_USER_QUEUE, LCN); -- CLEAR THE TCB AND ENTER THE CLOSED STATE LCN.STATE := CLOSED; TCB_CLEAR(LCN); when CLOSING | LAST_ACK | TIME_WAIT => -- CLEAR THE TCB AND ENTER THE CLOSED STATE QUEUE_CLEAR(RECEIVE_QUEUE, LCN); TCB_CLEAR(LCN); -- PUT IN A KNOWN STATE TCB_FREE ( TEMP_LCN ); end case; end TCP_ABORT; procedure TCP_CLOSE(LCN : in TCB_PTR) is --This subprogram is called by the user via the TCP controller. --It will send a FIN to a remote host. This will cause the connection --to close down upon a FIN and/or an ACK from the remote host. UMESSAGE : USER_MESSAGE; SOCKET_PARAMS : LCN_PTR_TYPE; procedure SEND_A_FIN(LCN : in TCB_PTR) is --This subprogram formats and sends a FIN to the IP for transmission --to the remote host. BUFPTR : T_TCP_GLOBALS_DATA_STRUCTURES.BUFFER_POINTER; BUFFLEN : SIXTEEN_BITS ; PACKED_BUFF : PACKED_BUFFER_PTR; SEGMENT_DATA_LENGTH : CONSTANT SIXTEEN_BITS := 1; TCP_HEAD_AND_DATA_LENGTH : CONSTANT SIXTEEN_BITS := 20; -- THE LENGTH OF A FIN SEGMENT WITHOUT OPTIONS NO_ROOM : BOOLEAN; Q_ITEM : STD_Q_ITEM; UMESSAGE : USER_MESSAGE; MESSAGE_FOR_IP : IP_GLOBALS.IP_MESSAGE ; begin BUFFGET(PACKED_BUFF, BUFFLEN); if PACKED_BUFF = null then -- TELL USE ERROR: INSUFFICIENT RESOURCES SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 20, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 5, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); elsif LCN.SND_WND + LCN.SND_UNA >= (LCN.SND_NXT + SEGMENT_DATA_LENGTH) then -- WE CAN SEND IT. PACKED_BUFF.STATUS := OWNER_TCP; PACKED_BUFF.IN_USE := TRUE; TYPE_FLAG := FIN; -- CLEAR THE OPTIONS ARRAY OPTIONS := CLEAR; TCP_HEADER_FORMAT(LCN, BUFPTR, TYPE_FLAG, OPTIONS);--FIN -- HERE WE MUST PUT IN THE NECESSARY SECURITY OPTIONS FOR IP. OPTIONS := TCP_SECURITY_OPTIONS; -- PACK THE BUFFER PACK_BUFFER_INTO_BIT_STREAM(BUFPTR, PACKED_BUFF); MESSAGE_FOR_IP := ( FROM_TCP, PACKED_BUFF, LCN.DESTINATION_ADDRESS, TOS, TTL, TCP_HEAD_AND_DATA_LENGTH, IDENT, DONT_FRAGMENT, OPTIONS, LCN.SOURCE_ADDRESS ) ; IP_FROM_TCP.Q_ADD( MESSAGE_FOR_IP ) ; -- UPDATE THE SEND NEXT LCN.SND_NXT := LCN.SND_NXT + MODULAR_CONVERT ( SIXTEEN_BITS (1)); -- PUT IT ON THE RETRANSMIT QUEUE IT IS QUEUED UP WITH A TIME. Q_ITEM := (PACKED_BUFF, NULL_UNPACKED_BUFFER,TCP_HEAD_AND_DATA_LENGTH); QUEUE_ADD(TCP_RETRANSMIT_QUEUE, LCN, Q_ITEM); else -- PUT IT ON THE TRANSMIT QUEUE FOR LATER PROCESSING. Q_ITEM := (PACKED_BUFF, NULL_UNPACKED_BUFFER, TCP_HEAD_AND_DATA_LENGTH); QUEUE_ADD(TRANSMIT_QUEUE, LCN, Q_ITEM); end if; end SEND_A_FIN; begin -- TCP_CLOSE T_TCP_GLOBALS_DATA_STRUCTURES.LCN := LCN; -- FOR TEST case LCN.STATE is when T_TCP_GLOBALS_DATA_STRUCTURES.CLOSED => if USER_ACCESS_CHECK(LCN) then -- TELL USER ERROR: CONNECTION ILLEGAL FOR THIS PROCESS SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 2, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); else -- TELL USER ERROR: CONNECTION DOES NOT EXIST SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 3, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); end if; when T_TCP_GLOBALS_DATA_STRUCTURES.LISTEN => QUEUE_CLEAR(RECEIVE_QUEUE, LCN); -- CLEAR TCB AND ENTER THE CLOSED STATE TCB_CLEAR(LCN); LCN.STATE := CLOSED; when SYN_SENT => -- NONE ON THIS LEVEL. CLEAR_SEND_QUEUE(LCN); QUEUE_CLEAR(RECEIVE_QUEUE, LCN); -- CLEAR THE TCB AND ENTER THE CLOSED STATE. TCB_CLEAR(LCN); LCN.STATE := CLOSED; when SYN_RECEIVED | ESTABLISHED => if QUEUE_EMPTY(TRANSMIT_QUEUE, LCN) then SEND_A_FIN(LCN); else -- SET THE CLOSE PENDING FLAG IN THE TCB. LCN.CLOSE_PENDING := TRUE; end if; -- ENTER THE FIN-WAIT-1 STATE LCN.STATE := FIN_WAIT_1; when FIN_WAIT_1 | FIN_WAIT_2 => -- USER ERROR: CONNECTION CLOSING SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 6, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); when CLOSE_WAIT => if QUEUE_EMPTY(TRANSMIT_QUEUE, LCN) then SEND_A_FIN(LCN); -- ENTER THE LAST ACK STATE LCN.STATE := LAST_ACK; QUEUE_CLEAR(PROCESSED_SEGMENTS_FOR_USER_QUEUE,LCN); else -- SET TH CLOSE PENDING FLAG IN THE TCB. LCN.CLOSE_PENDING := TRUE; -- WHEN THE FIN IS SENT DUE TO THE CLOSE PENDING -- FLAG THE STATE MUST BE CHANGED. end if; when CLOSING | LAST_ACK | TIME_WAIT => -- USER ERROR: CONNECTION CLOSING SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 6, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); end case; end TCP_CLOSE; procedure TCP_OPEN( LOCAL_PORT, FOREIGN_PORT : in SIXTEEN_BITS ; FOREIGN_NET_HOST : in THIRTYTWO_BITS ; ACTIVE_PASSIVE : in WITH_TCP_COMMUNICATE.ACKPASS; BUFFER_SIZE, TIMEOUT : in SIXTEEN_BITS ; LOCAL_CONN_NAME : in out LCN_PTR_TYPE ; SECURITY, PRECEDENCE : in SIXTEEN_BITS ; OPTIONS : in TCP_OPTION_TYPE) is --This subprogram will perform the actions necessary to do a passive or --an active OPEN. If a passive OPEN is requested the listen state will --entered. If an active OPEN is requested a SYN will be sent and the --connection actively pursued. The subprogram is called by the user layer --via the TCP controller. The following parameters are passed to the --subprogram : -- LOCAL_PORT : The local port identification number. -- FOREIGN_NET_HOST : The foreign net address of the remote host we wish -- to talk with and the address of the foreign host on the net. -- they are concatenated in a format found in the IP spec. -- FOREIGN_PORT : The port in the foreign host that we wish to send to. -- ACTIVE_PASSIVE : indicates whether an active or passive OPEN is -- desired. -- TIMEOUT : The timeout for transmitting data. if some data does not get -- through in the required time the connection is aborted. -- SECURITY : The request for a level of security. Which must be a -- legal level. -- PRECEDENCE : The precedence of the connection. Used in a multi-level, -- secure environment. -- OPTIONS : This data structure will contain a request for any options -- desired. Currently none will be. -- -- RESTRICTIONS : -- -- CURRENTLY WE ALLOW ONLY ONE CONNECTION PER PORT. -- NO_ROOM_FOR_CONNECTION : BOOLEAN := FALSE; UMESSAGE : USER_MESSAGE; SOCKET_PARAMS : LCN_PTR_TYPE; begin -- if ACTIVE_PASSIVE = WITH_TCP_COMMUNICATE.PASSIVE then LCN := TCB_GET; -- SEND THE LCN BACK TO THE USER. TCB_CLEAR( LCN ) ; -- Clear TCB. SOCKET_PARAMS.LCN := LCN ; LCN.TCP_CHANNEL_NAME := LOCAL_CONN_NAME.CHANNEL_NAME ; LCN.TCP_CHANNEL_PTR := LOCAL_CONN_NAME.CHANNEL_PTR ; SOCKET_PARAMS.CHANNEL_NAME := LOCAL_CONN_NAME.CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LOCAL_CONN_NAME.CHANNEL_PTR ; LOCAL_CONN_NAME.LCN := LCN ; UMESSAGE := (14, SOCKET_PARAMS); --TEXT_IO.PUT_LINE("SENDING LCN BACK TO USER"); MESSAGE_FOR_USER(UMESSAGE); LCN.LOCAL_PORT := LOCAL_PORT; -- else --set lcn -- LCN := LOCAL_CONN_NAME.LCN; -- end if ; if LCN.STATE = T_TCP_GLOBALS_DATA_STRUCTURES.CLOSED then if USER_ACCESS_CHECK(LCN) then -- TELL USER ERROR: CONNECTION ILLEGAL FOR THIS PROCESS. SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 2, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); elsif NO_ROOM_FOR_CONNECTION then -- THERE IS CURRENTLY ALWAYS ROOM -- TELL USER ERROR: INSUFFICIENT RESOURCES PUT_LINE("IN OPEN INSUF RESOURCES");-- DEBUG SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 5, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); elsif ACTIVE_PASSIVE = WITH_TCP_COMMUNICATE.ACTIVE then if PRECEDENCE < 0 or (PRECEDENCE > 7) then -- TELL USER ERROR: PRECEDENCE NOT ALLOWED SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 9, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); elsif SECURITY < 0 or (SECURITY > 7) then -- TELL USER ERROR: SECURITY/COMPARTMENT NOT ALLOWED SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 11, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); elsif FOREIGN_NET_HOST = 0 then -- TELL USER ERROR: FOREIGN SOCKET UNSPECIFIED SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 4, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); else -- START THE TIMEOUT FOR CONNECTION TIMER if TIMEOUT /= 0 then -- PUT IN THE NEW CONNECTION TIMEOUT. LCN.CONNECTION_TIMEOUT := TIMEOUT; end if; START_TIMER(LCN, TIMEOUT_TIMER); -- SET UP THE TCB LCN.DESTINATION_ADDRESS := FOREIGN_NET_HOST; LCN.FOREIGN_PORT := FOREIGN_PORT; -- SET UP THE TCB NET AND HOST ADDRESSES BY -- DECODING THE CONCATENATION OF FOREIGN_NET AND -- FOREIGN HOST. ADDRESS_DECODER(FOREIGN_NET_HOST); SEND_A_SYN(LCN); LCN.STATE := SYN_SENT; -- ENTER THE SYN-SENT STATE end if; else -- THIS IS A PASSIVE OPEN if FOREIGN_NET_HOST /= 0 then -- SET UP THE TCB. LCN.FOREIGN_PORT := FOREIGN_PORT; LCN.DESTINATION_ADDRESS := FOREIGN_NET_HOST ; -- DECODE THE TCB DESTINATION ADDRESS. A CONCATENATION -- OF FOREIGN_NET AND FOREIGN HOST AND PUT IT -- IN THE APPROPRIATE VARIABLES IN THE TCB. ADDRESS_DECODER(FOREIGN_NET_HOST); end if; if TIMEOUT /= 0 then -- PUT IN THE NEW CONNECTION TIMEOUT. LCN.CONNECTION_TIMEOUT := TIMEOUT; end if; LCN.ACTIVE_PASSIVE := T_TCP_GLOBALS_DATA_STRUCTURES.PASSIVE; -- A PASSIVE OPEN LCN.STATE := T_TCP_GLOBALS_DATA_STRUCTURES.LISTEN; -- ENTER THE LISTEN STATE end if; elsif LCN.STATE = T_TCP_GLOBALS_DATA_STRUCTURES.LISTEN then if ACTIVE_PASSIVE = WITH_TCP_COMMUNICATE.ACTIVE then if FOREIGN_NET_HOST = 0 then -- TELL USER ERROR: FOREIGN SOCKET UNSPECIFIED SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 4, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); else -- SET UP THE TCB LCN.ACTIVE_PASSIVE := T_TCP_GLOBALS_DATA_STRUCTURES.ACTIVE; -- CONNECTION NOW ACTIVE. LCN.FOREIGN_PORT := FOREIGN_PORT; LCN.DESTINATION_ADDRESS := FOREIGN_NET_HOST ; -- START THE CONNECTION TIMEOUT TIMER. START_TIMER(LCN, TIMEOUT_TIMER);--not used presently (JB 1/25/85) -- DECODE THE TCB DESTINATION ADDRESS. A CONCATENATION -- OF FOREIGN_NET AND FOREIGN HOST AND PUT IT IN -- THE APPROPRIATE VARIABLES IN THE TCB ADDRESS_DECODER(FOREIGN_NET_HOST); SEND_A_SYN(LCN); LCN.STATE := SYN_SENT; -- ENTER THE SYN_SENT STATE end if; end if; if TIMEOUT /= 0 then -- PUT IN THE NEW CONNECTION TIMEOUT. LCN.CONNECTION_TIMEOUT := TIMEOUT; end if; else -- THERE ALREADY IS A CONNECTION -- TELL USER ERROR: CONNECTION ALREADY EXISTS SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 12, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); end if; end TCP_OPEN; procedure TCP_STATUS( LCN : in TCB_PTR) is --This subprogram returns the status of a connection, specified by the --LCN to the user layer. It also returns a pointer to the TCB for the --connecti on, which will indicate the state of the connection(OPEN --or CLOSED). The subprogram is called by the user interface via the --TCP controller. LCN is passed as a parameter to the subprogram. STATE : WITH_TCP_COMMUNICATE.STATUS_TYPE; STATUS_REC : STATUS_RECORD; UMESSAGE :USER_MESSAGE; SOCKET_PARAMS : LCN_PTR_TYPE; begin T_TCP_GLOBALS_DATA_STRUCTURES.LCN := LCN; -- FOR TEST if USER_ACCESS_CHECK(LCN) then -- TELL USER ERROR: CONNECTION ILLEGAL FOR THIS PROCESS SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 2, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); elsif LCN.STATE = T_TCP_GLOBALS_DATA_STRUCTURES.CLOSED THEN -- TELL USER ERROR CONNECTION DOES NOT EXIST SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 3, SOCKET_PARAMS); MESSAGE_FOR_USER(UMESSAGE); else NEW_LINE; --FOR DEBUG PUT_LINE("ULP just requested a STATUS"); STATUS_REC.SOURCE_PORT := LCN.LOCAL_PORT; STATUS_REC.SOURCE_ADDRESS := LCN.SOURCE_ADDRESS; STATUS_REC.DESTINATION_PORT := LCN.FOREIGN_PORT; STATUS_REC.DESTINATION_ADDRESS := LCN.DESTINATION_ADDRESS; -- THE POSITIONS OF THE ENUMERATED DATA IN EACH RECORD IS THE SAME. -- THEREFORE THE VALUE OF THE CORRESPONDING TYPE GIVES YOU THE CORRECT -- ENUMERATION VALUE. STATUS_REC.CONNECTION_STATE := STATE_TYPE'VAL(STATES'POS(LCN.STATE)); STATUS_REC.STATUS:= WITH_TCP_COMMUNICATE.STATUS_TYPE'VAL( T_TCP_GLOBALS_DATA_STRUCTURES.STATUS_TYPE'POS(LCN.CONNECTION_STATUS)); STATUS_REC.LOCAL_RCV_WINDOW := LCN.RCV_WINDOW; STATUS_REC.REMOTE_RCV_WINDOW := LCN.SND_WND; STATUS_REC.OCTETS_ON_RETRANSMIT_QUEUE := LCN.QHEADS(TCP_RETRANSMIT_QUEUE).ELEMENT_COUNT; -- THIS IS SIMPLE -- UNTIL WE HAVE VARIABLE SIZE PACKETS ON THE QUEUE. STATUS_REC.URGENT_STATE := LCN.USER_NOTIFICATION; -- IF NOTIFIED OF URG. DATA STATUS_REC.PRECEDENCE := LCN.PRECEDENCE; for I in 1..9 loop -- COPY SECURITY PARAMS STATUS_REC.SECURITY(I) := LCN.SECURITY(I); end loop; STATUS_REC.ULP_TIMEOUT := LCN.CONNECTION_TIMEOUT; -- NOW MESSAGE MUST GET BACK TO THE USER INTERFACE. SOCKET_PARAMS.LCN := LCN; SOCKET_PARAMS.CHANNEL_NAME := LCN.TCP_CHANNEL_NAME ; SOCKET_PARAMS.CHANNEL_PTR := LCN.TCP_CHANNEL_PTR ; UMESSAGE := ( 15, SOCKET_PARAMS, STATUS_REC); MESSAGE_FOR_USER(UMESSAGE); end if; exception when others => PUT_LINE(" ERROR IN TCP STATUS"); end TCP_STATUS; procedure RETRANS_TCP( LCN : in TCB_PTR) is --This subprogram will get a segment off the retransmission queue and --send it to the IP for transmission to the remote host. It will --update the window in the segment. This subprogram is called when --a retransmission timeout has occured. It will retransmit a segment --to the remote host. A LCN is passed into the subprogram which pop --a segment off the retransmit to the IP for the remote host. -- ALLOWS US TO ADD TO THE IP ID MOD 2**16. Q_ITEM : STD_Q_ITEM; PACKED_BUFF : PACKED_BUFFER_PTR; BYTE_COUNT : SIXTEEN_BITS ; -- LENGTH OF BUFFER FROM RETRANSMIT QUEUE MESSAGE_FOR_IP : IP_GLOBALS.IP_MESSAGE ; begin -- GET A SEGMENT OFF THE RETRANS QUEUE. QUEUE_GET(TCP_RETRANSMIT_QUEUE, LCN, Q_ITEM); if Q_ITEM.BUFFER /= null then if not Q_ITEM.BUFFER.IN_USE then --its not still waiting --for transmission PACKED_BUFF := Q_ITEM.BUFFER; BYTE_COUNT := Q_ITEM.LENGTH; --Reset the IP pointer in the buffer PACKED_BUFF.IP_PTR := PACKED_BUFF.TCP_PTR - 1; --IT WILL NOT BE OUR RESPONSIBILITY TO DETERMINE THE MAX TIME FOR A --TRANS. THAT WILL BELONG TO THE USER LAYER. THE DATAGRAM LAYER MAY --ALSO HAVE A TIME TO LIVE. WE CAN ALWAYS SEND FROM THE RETRANSMIT --QUEUE WE WILL USE THE PREVIOUSLY PACKED BUFFER FROM THE RETRANS Q --AS IS. -- SET UP ID FOR IP IDENT := LCN.RETRANS_IDENT; -- LCN.IDENT := LCN.IDENT + 1; MESSAGE_FOR_IP := ( FROM_TCP, PACKED_BUFF, LCN.DESTINATION_ADDRESS, TOS, TTL, BYTE_COUNT, IDENT, DONT_FRAGMENT, OPTIONS, LCN.SOURCE_ADDRESS ) ; IP_FROM_TCP.Q_ADD( MESSAGE_FOR_IP ) ; end if; QUEUE_ADD(TCP_RETRANSMIT_QUEUE, LCN, Q_ITEM); else TCP_ERROR(4); end if; end RETRANS_TCP; end T_TCP_CONTROLLER_UTILITIES;