Category : Network Files
Archive   : PDCLK145.ZIP
Filename : IP.ASM

 
Output of file : IP.ASM contained in archive : PDCLK145.ZIP
; ip.asm
;========================================================================

; Copyright (C) 1991 by [email protected], see file COPYING.

;************************************************************************
;************************************************************************
;*
;* UDP/IP library
;*
;* This library is in a transition phase from a PDCLKSET specific library
;* into a multiprocess, reentrant, any hardware type, general UDP/IP
;* library. The buffers used have a descriptor part and a packet part.
;* The descriptor part is used to allow reentrancy, different physical
;* address lengths and varying number of IP options.
;*
;* In this library DS:BX always points to the descriptor buffer, DS:DI
;* usually points to the physical header/IP header/UDP header part of
;* the IP packet buffer and DX,AX often contains an IP number in network
;* byte order (DL,DH,AL,AH). For efficiency reasons, IP numbers are
;* searched last half first (first halves are often equal).
;*
;* As this library is still evolving, you can not assume that it looks
;* the same in the next release. In particular, if you are using any of
;* of the subroutines, check if their calls or results have been changed.
;*
;* The current implementation is RFC791 (IP) and RFC1122 (host requirements)
;* compliant, except for a few cases: I havn't found a reasonable solution
;* on how to report back to the application ICMP errors like parameter
;* problem, protocol and port unreachables, and fragment reassembly time
;* exceeded. Also, IP options are allowed, but the interpretation and handling
;* must be done by the application. Apart from the above, it is a de luxe
;* implementation that includes things like multiple default gateways,
;* sending protocol and port unreachables, IP type of service handling (not
;* tested), fragment reassembly and source quench introduced delay.
;*
;************************************************************************
;************************************************************************

; send errors:
SERRUNREACH equ 0 ; host or net unreachable
SERRNOBUF equ 2 ; temporary out of buffers
SERRNOARP equ 4 ; got no ARP reply
SERRTIMOUT equ 6 ; general timeout
SERRNOTRAF equ 8 ; received no traffic from dst
SERRNOIP equ 10 ; no IP # to send to

ICMP_PROT equ 1 ; Internet Control Msg Prot
UDP_PROT equ 17 ; IP protocol type UDP

Descriptor struc
dLink LinkStruc <> ; next & prev links
; dIOCB iocb <> ; pkt drvr hi perf struc
dPtrPhys dw 0 ; ptr to physical hdr
dPtrIp dw 0 ; ptr to IP hdr
dPtrUdp dw 0 ; ptr to UDP hdr
dPktLen dw 0 ; packet length (bytes)
dPktEnd dw 0 ; end of packet
dTimOut2Msg equ dPktEnd ; Timeout msg for findsends
dSqDelay dw 0 ; source quench delay (ms)
dWaitEvent dw 0 ; event to wait for
dPtrFrag equ dWaitEvent ; ptr to defargment listhead
dTimOutMsg dw 0 ; to be displayed at timeout
dTickResend dw 1*18 ; resend start interval (ticks)
dTickTimeout dw 4*18 ; timeout ticks (in 1/18 s)
dTick2Timeout dw 14*18 ; second timeout
dIdxHwDst equ dSqDelay ; arp table index for hw dst
dIdxIpDst equ dTick2Timeout ; arp table index for IP dst
dAdjust equ (4-(($-dLink) and 3)) and 3
if dAdjust
db dAdjust dup (0)
endif
dGwys2Chk db 0 ; # of untried gateways
dSnap db 0 ; 802.3 snaps used
dPtrDes dw 0 ; ptr to start of descriptor
dHwDst equ $-dLink ; (part of pd packet)
DESCRLEN equ $-dLink
Descriptor ends

HwStruc struc
hEthDst db 6 dup (0) ; Ethernet destination address
hEthSrc db 6 dup (0) ; my Ether addr
hProtType dw 0008 ; 0800 = IP
HWHDRLEN equ $-hEthDst
HwStruc ends

SnapStruc struc
sDSAP db 170
sSSAP db 170
sContr db 3
sOrgCode db 0,0,0
sProt db 08, 00
SNAPLEN equ $-sDSAP
SnapStruc ends

IpStruc struc
iIpVerHlen db 45h ; ver 4, 5 32-bit hdr words
iIpTos db 0 ; type of service
iIpLen dw 0 ; IP packet length
iIpId dw 0100h ; id 0001
iIpFlFrag dw 0 ; no flags or fragments
iIpTtl db 90 ; time to live
iIpProt db 0 ; 17 = udp
iIpXsum dw 0 ; header checksum
iIpSrc dw 0, 0 ; Don't know my IP nmbr
iIpDst dw 0ffffh, 0ffffh ; local net broadcast IP
iIpOptions equ $-iIpVerHlen
IPHDRLEN equ $-iIpVerHlen
IpStruc ends

UdpStruc struc
uUdpSrc dw 4400h ; source port 44h = 68
uUdpDst dw 4300h ; dest port 43h = 67
uUdpLen dw 0 ; length in bytes
uUdpXsum dw 0 ; udp checksum
uUdpData equ $-uUdpSrc
UDPHDRLEN equ $-uUdpSrc
UdpStruc ends

BootpStruc struc
uUdpStruc UdpStruc <>
uBotOp db 1 ; bootp request
uBotHtype db 0 ; hardware type
uBotHlen db 0 ; hardware addr lenght
uBotHops db 0 ; hops
uBotXid dw 0, 0 ; transaction id
uBotSecs dw 0001 ; 0100h seconds since boot
uBotUnused dw 0 ; not defined
uBotCliIp dw 0, 0 ; Client IP unknown
uBotYourIp dw 0, 0 ; my IP yet unknown
uBotServIp dw 0, 0 ; Server IP
uBotGwyIp dw 0, 0 ; Gateway IP
uBotCliHwAd db 16 dup (0) ; My Hardware address
uBotSname db 64 dup (0) ; server name
uBotFilNam db 128 dup (0) ; file name
uBotMagNum dw 8263h, 6353h ; Magic number
uBotVend db 64-4 dup (0) ; Vendor specific area
BpDataLen = $-uBotOp
BootpStruc ends

IcmpStruc struc
uIcmpTypecode dw 0
uIcmpXsum dw 0
uIcmpData dw 0, 0
uIcmpIpHdr dw iIpOptions+4 dup (0)
ICMPHDRLEN equ uIcmpIpHdr-uIcmpTypecode
IcmpStruc ends

BootpFrame struc
fDescriptor Descriptor <>
fHwStruc HwStruc <>
fIpStruc IpStruc <>
fBotStruc BootpStruc <>
BpLen = $-fHwStruc
BootpFrame ends

even
Events dw 1 ; =1 so DONT_WAIT always true
DONT_WAIT equ 1
GOT_BOOTP equ 2
GOT_ARPREPLY equ 4
GOT_TIMEREPLY equ 8
GOT_ARPREQ equ 16
GOT_ICMPMSG equ 32
GOT_NSREPLY equ 64

GenFlags dw 0
ARGZONE equ 2
ARGZONESPEC equ 4
DSTNOW equ 8
IS_386 equ 16
TBL_READY equ 32
UDP_ECHO equ 64
DBGINTERR equ 08000h

MySegm dw 0
Hlen dw 0
H2Len dw 0
MyMask dw 2 dup (0h)
MyNet dw 2 dup (0h)
IpHandle dw 0
IpIdCounter dw 0
InSendAndW dw 0
LongerTimOut dw 0

ifndef MAXDEFGWYS
MAXDEFGWYS equ 4
endif

DefGwyIndex dw 0
DefGwyNum dw 0
DefGwys dw MAXDEFGWYS*2 dup (0)

if TBLBUILD or PINGCLIENT
DefNSnum dw 0
DefNS dw MAXDEFNS*2 dup (0)
endif ; TBLBUILD or PINGCLIENT

ifndef ROUTESLOTS
ROUTESLOTS equ 8
endif

; The route table contains information on hosts behind gateways.
;
; If we don't receive anything from an IP number within one minute after we
; last sent to it, we return SERRNOTRAF on the next send. This is cleared
; after another minute.

RouteTabIpD2 dw ROUTESLOTS dup (0) ; destination IP #
RouteTabIpD1 dw ROUTESLOTS dup (0)
RouteTabTos dw ROUTESLOTS dup (0) ; type of service
RouteTabIpG2 dw ROUTESLOTS dup (0) ; gateway IP #
RouteTabIpG1 dw ROUTESLOTS dup (0)
RouteTabTrTx dw ROUTESLOTS dup (0) ; transmitt timer
RouteTabTrRx dw ROUTESLOTS dup (0) ; receive timer
RouteTabFlags dw ROUTESLOTS dup (0) ; flags (must follow TrRx)
RouteTabTrSq dw ROUTESLOTS dup (0) ; source quench timer
RouteTabSqDelay dw ROUTESLOTS dup (0) ; source quench delay
RouteTabUnreach dw ROUTESLOTS dup (0) ; destination unreachable

RoutePutSlot dw 0

IpDesBuf BootpFrame <>

SnapHdr SnapStruc <>

IpHdr equ IpDesBuf.fIpStruc
MyIpNr equ IpDesBuf.fIpStruc.iIpSrc



;************************************************************************
;* SendUdpFind
;*
;* Input: BX = IP description buffer ptr (BX saved)
;* DX = addr of length prepended IP list to send to until reply
;* Output: Zero and CX = 0 if OK
;* non-zero and CX = errorcode if error
;* Destroys: AX, CX, DX, SI, DI, ES and flags
;************************************************************************

SendUdpFind proc near
mov [bx].dTimoutMsg,0 ; continue even if no reply
mov ah,2 ; do 2 turns over all IP #s
Send2ndTurn:
mov si,dx ; get IP list addr
lodsb ; # of IP numbers to al
mov cx,SERRNOIP
or al,al
jz SendUdpFindRet

inc si
SendNextIP:
mov di,[bx].dPtrIp
lea di,[di].iIpDst
movsw ; put next IP number
movsw

cmp ax,0101h ; last IP last turn?
jne Send2orMore
mov di,[bx].dTimOut2Msg
mov [bx].dTimoutMsg,di ; -yes, terminate if no reply
Send2orMore:
push ax
push dx
push si
mov ax,[bx].dPktLen
call SendUdpPkt ; send udp packet
pop si
pop dx
pop ax
jz SendUdpFindRet ; done if reply

dec al
jnz SendNextIP ; more IP numbers this turn?

mov di,[bx].dTick2Timeout ; -no, use longer timeout
mov [bx].dTickTimeout,di ; for 2nd turn
dec ah
jnz Send2ndTurn ; both turns done?

SendUdpFindRet:
or cx,cx
ret
SendUdpFind endp



;************************************************************************
;* SendUdpPkt
;*
;* Input: AX = IP data byte length (AX destroyed)
;* BX = IP description buffer ptr (BX saved)
;* Output: Zero and CX = 0 if OK
;* non-zero and CX = errorcode if error
;* Destroys: AX, CX, DX, SI, DI, ES and flags
;************************************************************************

SendUdpPkt proc near
push ax
mov si,offset MyIpNr ; fill in my IP # (used
mov di,[bx].dPtrIp ; in udp chksum!)
add di,iIpSrc
movsw
movsw
xchg ah,al
mov di,[bx].dPtrUdp
mov [di].uUdpLen,ax ; fill in UDP total length

mov [di].uUdpXsum,ax ; ensure chksum is calculated

mov di,[bx].dPtrIp
mov [di].iIpProt,UDP_PROT ; set UDP protocol

call UdpChkSum ; calculate UDP checksum

pop ax
call SendIpPkt ; send UDP packet

ret
SendUdpPkt endp



;************************************************************************
;* SendIcmpPkt
;*
;* Input: BX = IP description buffer ptr (BX saved)
;* CX = IP data length
;* Output: Zero and CX = 0 if OK
;* non-zero and CX = errorcode if error
;* Destroys: AX, CX, DX, SI, DI, ES and flags
;************************************************************************

SendIcmpPkt proc near
push cx ; save length

call IcmpChkSum ; calculate icmp checksum

mov [bx].dWaitEvent,0 ; don't wait for answer
mov di,[bx].dPtrIp
mov [di].iIpProt,ICMP_PROT

pop ax ; restore length
; call SendIpPkt
; ret
SendIcmpPkt endp



;************************************************************************
;* SendIpPkt
;*
;* Input: AX = IP data byte length (AX destroyed)
;* BX = IP description buffer ptr (BX saved)
;* Output: Zero and CX = 0 if OK
;* non-zero and CX = errorcode if error
;* Destroys: AX, CX, DX, SI, DI, ES and flags
;************************************************************************

SendIpPkt proc near
if PINGCLIENT
mov cx,ax
add cx,IPHDRLEN+14+8+4+12 ; preamble, crc, intergap ...
cli
add EchoLoad+2,cx
adc EchoLoad,0
sti
endif ; PINGCLIENT
call IPHdrLength ; calculate IP hdr length

mov dx,MyIpNr ; my IP # to IP src
mov [si].iIpSrc,dx
mov dx,MyIpNr+2
mov [si].iIpSrc+2,dx

add ax,cx ; add hdr length to data length
mov [bx].dPktLen,ax ; and save it

xchg ah,al
mov [si].iIpLen,ax ; fill in IP length

mov dx,IpIdCounter
mov [si].iIpId,dx ; unique id for frag reassembly
inc dx
mov IpIdCounter,dx

call IpChkSum ; calculate checksum
if RFCC
call Ageing ; clear unused table slots
endif ; RFCC
call PutPhysDst ; do we have HW dest addr?
jnz SendIpRet ; -no, ARP timed out

call PutPhysSrc ; put my HW src addr

add [bx].dPktLen,cx ; pkt length (HW addr part)

jmp short SendAndWait ; -yes, send packet
SendIpRet:
ret
SendIpPkt endp



;************************************************************************
;* SendAndWait
;* Input: BX = description buffer ptr (saved)
;* Output: Zero and CX = 0 if OK
;* non-zero and CX = errorcode if error
;* Destroys: AX, CX, DX, SI, DI, ES, flags
;************************************************************************

SendAndWait proc near
inc InSendAndW
mov si,[bx].dWaitEvent ; event to wait for

or si,si ; no event to wait for?
jz SendDontWait

not si
and Events,si ; clear this event bit
SendDontWait:
not si
if RFCC
; Do RFC1016 Source Quench Introduced Delay

mov ax,[bx].dSqDelay ; SQ delay (ms)
add ax,32 ; round up
mov cl,6
shr ax,cl ; convert ms to ticks
call CurrentTicks
add ax,cx ; time to send at
SendSqLoop:
call CurrentTicks
cmp cx,ax ; can we send it now?
jns SendNoSqDelay
call Something2Do ; - no, wait a while
jmp short SendSqLoop
SendNoSqDelay:
else
call CurrentTicks
endif ; RFCC
call SendRawPkt ; send packet part of buffer

mov di,[bx].dTickResend ; first resend time
mov ax,cx
add ax,di
add ax,LongerTimOut

mov dx,cx
add dx,LongerTimOut
add dx,[bx].dTickTimeout ; time out time
WaitLoop:
test Events,si ; got what we want?
jz SendChk
SendWaitOK:
xor cx,cx
SendWaitRet:
dec InSendAndW
or cx,cx
ret

SendChk:
call Something2Do ; anything else to do?
ChkTimeout:
call CurrentTicks
cmp cx,dx ; time out?
jns SendTimedout

cmp cx,ax ; time to resend?
js WaitLoop

shl di,1 ; double resend time

cmp di,30*18
jbe SendDouble
mov di,30*18 ; max 30 seconds
SendDouble:
add ax,di ; next resend time

call SendRawPkt ; send same packet again
jmp short WaitLoop
SendTimedout:
mov dx,[bx].dTimOutMsg
mov cx,SERRTIMOUT
or dx,dx ; if no timeout msg
jz SendWaitRet ; just return

mov ah,9 ; print error msg in dx
int 21h
mov al,04 ; error code 4
call Terminate
SendAndWait endp



;************************************************************************
;* SendRawPkt
;* Input: BX = description buffer ptr (saved)
;* Destroys: flags
;*
;* (The saving of all registers below is not needed for the Crynwr
;* collection of packet drivers, but as far as I understand the packet
;* driver specification other drivers may need it.)
;************************************************************************

SendRawPkt proc near
push dx
push ax
mov ax,3000
SendRawAgain:
push cx
push bx
push ax
push si
push di
push bp
push es

mov si,[bx].dPtrPhys ; get start of packet
mov cx,[bx].dPktLen ; and its length

mov ah,4 ; send packet to
push ds ; packet driver
int_pkt
pop ds

pop es
pop bp
pop di
pop si
pop ax
pop bx
pop cx

jc SendBadPkt ; any errors?

pop ax
pop dx
ret

SendBadPkt:
cmp dh,CANT_SEND ; can't send?
jne SendPermBad
SendTempBad:
dec ax ; have we tried enough times?
jnz SendRawAgain
SendPermBad:
stc
call print_error ; display explanation

mov al,03 ; error code 3
call Terminate
SendRawPkt endp



;************************************************************************
;* PutPhysSrc
;*
;* Input: BX = IP description buffer ptr (saved)
;* Output: HW src addr and IP prot type put into pkt
;* CX = phys header length
;* Destroys: CX, SI, DI, ES, flags
;************************************************************************

PutPhysSrc proc near
mov si,offset MyHwAd ; get my HW addr from ARP buf
mov di,[bx].dPtrPhys
mov cx,Hlen
add di,cx
push cs
pop es
rep movsb ; put my HW addr as src HW addr

test [bx].dSnap,1
jz PutPhysNotSnap

mov cx,[bx].dPktLen
add cx,8
xchg ch,cl
mov [di],cx
add di,2

mov cx,sProt-sDSAP
mov si,offset SnapHdr
rep movsb
PutPhysNotSnap:
mov cx,di
sub cx,[bx].dPtrPhys
add cx,2 ; physical header length

mov word ptr [di],0008h ; protocol type IP (0800)

ret
PutPhysSrc endp



;************************************************************************
;* PutPhysDst
;*
;* Input: BX = IP description buffer ptr (saved)
;* Output: Zero and CX = 0 if OK
;* non-zero and CX = errorcode if error
;* Destroys: AX, CX, DX, SI, DI, ES, flags
;************************************************************************

PutPhysDst proc near
mov ax,DefGwyNum ; # of default gwys we have
mov [bx].dGwys2Chk,al ; gwys still left to check
PutPhysAgain:
mov di,[bx].dPtrIp
mov al,[di].iIpTos ; get type of service
xor ah,ah
mov si,ax

mov dx,[di].iIpDst ; set dx,ax = dst IP #
mov ax,[di].iIpDst+2

call ArpPutHwDst ; do we know dst phys addr?
jz FoundArpEntry ; - yes, copied hw addr

call MyNetChk ; - no. Is dst on my net?
jz PutMynetArp ; - yes, don't use gwy
; and ARP dst
call UseGwy ; - no, use a gateway
jcxz PutArp ; destination unreachable?
jmp short PutPhysRet

PutMynetArp:
mov [bx].dGwys2Chk,1
PutArp:
call ArpPutHwDst ; have hw addr for this IP # ?
jz FoundArpEntry ; - yes, copied hw addr to pkt

call SendArpReq ; - no, arp for it
jz PutPhysAgain ; if reply, put in hw addr

call SwitchGwy ; else try next gwy

mov cx,SERRNOARP
dec [bx].dGwys2Chk ; any more default gwys?
jnz PutPhysAgain

PutPhysRet:
or cx,cx
FoundArpEntry:
ret
PutPhysDst endp



;************************************************************************
;* RouteFind
;*
;* Input: DX = first word of dst IP # (saved if not found)
;* AX = second word of dst IP # (saved if not found)
;* SI = IP type of service
;* Output: if found: zero and DI = route table index
;* DX = first word of gwy IP #
;* AX = second word of gwy IP #
;* CX = net/host unreachable code
;* Destroys: CX, DI, ES, flags
;************************************************************************

RouteFind proc near
mov di,offset RouteTabIpD2
mov cx,ROUTESLOTS
or cx,cx ; ensure non-zero flag
PushfDI
RouteFindNext: ; look for matching slot
repne scasw
jnz RouteFindRet

cmp dx,2*RouteSLOTS-2[di] ; does dst IP first part match?
jne RouteFindNext ; - no, look further

cmp si,4*RouteSLOTS-2[di] ; does dst tos match?
jne RouteFindNext ; - no, look further

sub di,offset RouteTabIpD2+2 ;- yes, compute slot index

mov dx,RouteTabIpG1[di] ; get gwy IP #
mov ax,RouteTabIpG2[di] ;

mov cx,RouteTabUnreach[di] ; get unreachable code

PopfEI
cmp di,di ; set zero flag
ret ; zero (found) return

RouteFindRet:
PopfEI
ret ; non zero (not found) return
RouteFind endp



;************************************************************************
;* MyNetChk
;*
;* Input: DX = first word of IP # (saved)
;* AX = second word of IP # (saved)
;* Output: zero if same net else non-zero
;* Destroys: flags
;************************************************************************

MyNetChk proc near
push ax
push dx

and dx,MyMask ; check if my net
cmp dx,MyNet
jne MyNetRet
and ax,MyMask+2
cmp ax,MyNet+2
MyNetRet:
pop dx
pop ax
ret
MyNetChk endp



;************************************************************************
;* UseGwy
;*
;* Input: DX = first word of destination IP #
;* AX = second word of destination IP #
;* SI = IP type of service
;* Output: DX = first word of gateway IP #
;* AX = second part of gateway IP #
;* CX = net/host unreachable code
;* Destroys: CX, SI, DI, ES, flags
;************************************************************************

UseGwy proc near
PushfDI

call RouteFind ; is dst in route tbl?
jz UseGwyKnown ; - yes, have gwy IP #
; - no, use default gwy
dec cx
cmp dl,127 ; don't send to 127.x.x.x
je UseGwyRet

mov di,RoutePutSlot
add di,2 ; advance index two bytes
cmp di,ROUTESLOTS*2 ; at end of table?
jb UseRouteSlot

xor di,di ; - yes, wrap around
UseRouteSlot:
mov RoutePutSlot,di
mov RouteTabIpD1[di],dx ; put destination IP #
mov RouteTabIpD2[di],ax
mov RouteTabTos[di],si

mov si,DefGwyIndex
shl si,1
shl si,1
mov dx,DefGwys[si]
mov ax,DefGwys+2[si]
mov RouteTabIpG1[di],dx ; and default gwy IP #
mov RouteTabIpG2[di],ax ; into route table

call CurrentTicks
mov RouteTabTrRx[di],cx ; put current time

xor cx,cx ; initialize other fields
mov RouteTabUnreach[di],cx ; clear unreachable
mov RouteTabFlags[di],cx
mov RouteTabSqDelay[di],cx

or si,dx
or si,ax ; any non-zero gwy IP #?
jz UseGwyNone ; - yes
UseGwyKnown:
if RFCC
push cx
push ax
call CurrentTicks
mov RouteTabTrTx[di],cx ; transmit time

mov ax,RouteTabSqDelay[di] ; move sq delay to descriptor
mov [bx].dSqDelay,ax
dec ax ; any delay?
js UseNoSqDelay
mov ax,RouteTabTrSq[di] ; - yes
add ax,18 ; has one second passed
cmp ax,cx ; since we last decremented
jns UseNoSqDelay ; the delay value?
mov RouteTabTrSq[di],cx
dec RouteTabSqDelay[di] ; - yes, one millisecond off
UseNoSqDelay:
pop ax
pop cx
endif ; RFCC
UsegwyRet:
PopfEI
ret

UseGwyNone:
PopfEI
mov dx,offset NoGwyMsg
mov ah,9 ; - no, print error msg in dx
int 21h
mov al,08 ; error code 8
call Terminate
UseGwy endp



;************************************************************************
;* SwitchGwy
;*
;* Input: DX = first word of gateway IP # (saved)
;* AX = second word of gateway IP # (saved)
;* Destroys: CX, DI, ES, flags
;************************************************************************

SwitchGwy proc near
PushfDI
mov di,DefGwyIndex ; if current default gateway
shl di,1
shl di,1

cmp dx,DefGwys[di] ; is the one
jne SwitchNotThis
cmp ax,DefGwys+2[di] ; that failed
jne SwitchNotThis

shr di,1
shr di,1
inc di ; then switch to
cmp di,DefGwyNum
jb SwitchNextGwy
xor di,di
SwitchNextGwy:
mov DefGwyIndex,di ; next default gateway
SwitchNotThis:
PopfEI

mov di,offset RouteTabIpG2 ; remove failing gateway
mov cx,ROUTESLOTS ; from all route slots
PushfDI
SwitchRouteNext: ; look for matching slot
repne scasw
jnz SwitchGwyRet ; all entries searched

cmp dx,2*ROUTESLOTS-2[di] ; does gwy IP 2nd part match?
jne SwitchRouteNext ; - no, look further

sub di,offset RouteTabIpG2+2 ;- yes, clear this route
mov RouteTabIpD1[di],0
mov RouteTabIpD2[di],0
mov RouteTabIpG1[di],0
mov RouteTabIpG2[di],0
add di,offset RouteTabIpG2+2
jmp short SwitchRouteNext ; more routes using this gwy?

SwitchGwyRet:
PopfEI
ret
SwitchGwy endp



;************************************************************************
;* ChkSum
;* Input: SI = start addr
;* CX = # of bytes
;* Output: AX = 1-complement checksum
;* CX = partial checksum
;* SI = addr of next byte
;* Destroys: Flags
;************************************************************************

ChkSum proc near
if PINGCLIENT
test GenFlags,IS_386
jz ChkSum8086

.386
push edx ; save 386 registers
push eax
shr cx,1 ; divide by two, round down
pushf ; keep odd bit in carry
shr cx,1
pushf
xor edx,edx ; clear edx and carry
ChkLoop386:
lodsd
adc edx,eax ; add to previous running sum
loopw ChkLoop386

adc edx,0 ; add the last carry in again
mov eax,edx
shr eax,16
add dx,ax
adc dx,0

popf ; odd # of words?
jnc NotOddWords

lodsw
add dx,ax
adc dx,0
NotOddWords:
popf ; odd # of bytes?
jnc NotOddBytes

lodsb ; get that last byte
xor ah,ah ; clear carry and the high portion
add dx,ax ; add the last one in
adc dx,0 ; add the carry in, too
NotOddBytes:
pop eax
mov ax,dx
mov cx,dx
pop edx

not ax ; take one more 1-complement
ret

.8086
endif ; PINGCLIENT

ChkSum8086:
push dx
shr cx,1 ; divide by two, round down
pushf ; keep odd bit in carry
xor dx,dx ; clear dx and carry
ChkLoop:
lodsw
adc dx,ax ; add to previous running sum
loop ChkLoop

adc dx,0 ; add the last carry in again

popf ; odd # of bytes?
jnc NotOdd

lodsb ; get that last byte
xor ah,ah ; clear carry and the high portion
add dx,ax ; add the last one in
adc dx,0 ; add the carry in, too
NotOdd:
mov ax,dx
mov cx,dx
pop dx

not ax ; take one more 1-complement
ret
ChkSum endp



;************************************************************************
;* UdpChkSum (both for generate and check!)
;*
;* Input: BX = IP description buffer ptr (saved)
;* Output: Zero if OK; checksum filled in
;* SI = addr of next byte
;* DI = IP Ptr
;* Destroys: AX, CX, DX, SI, DI, flags
;************************************************************************

UdpChkSum proc near
mov si,[bx].dPtrIp
mov di,[bx].dPtrUdp

push word ptr [si].iIpTtl ; save ttl for pseudo hdr
mov [si].iIpTtl,0 ; usage

xor dx,dx
xchg dx,[di].uUdpXsum ; save and clear checksum

mov cx,[di].uUdpLen ; save UDP length
mov [si].iIpXsum,cx
xchg ch,cl

mov ax,di
add ax,cx
or dx,dx ; is there a checksum?
mov si,ax ; - no, not available/wanted
jz UdpNoSum ; but set si to end of data

push cx ; - yes
mov cx,12 ; checksum pseudo hdr
mov si,[bx].dPtrIp
add si,iIpTtl
call ChkSum

mov [di].uUdpXsum,cx ; move partial checksum
; into udp hdr
pop cx ; checksum udp hdr+data
mov si,di
call ChkSum
jnz UdpNotZero ; checksum is zero?
not ax ; - yes, make it FFFFh
UdpNotZero:
mov [di].uUdpXsum,ax

cmp ax,dx ; does checksums match?
UdpNoSum:
mov di,[bx].dPtrIp
pop word ptr [di].iIpTtl ; restore ttl

ret
UdpChkSum endp



;************************************************************************
;* IpHdrLength
;*
;* Input: BX = IP description buffer ptr (saved)
;* Output: CX = IP header byte length
;* SI = pointer to IP header
;* Destroys: SI, flags
;************************************************************************

IpHdrLength proc near
mov si,[bx].dPtrIp
mov cx,[si] ; get version+length byte
and cx,0fh ; length (32 bit words)
shl cx,1 ; convert to
shl cx,1 ; byte length
ret
IpHdrLength endp



;************************************************************************
;* IpChkSum (both for generate and check!)
;*
;* Input: BX = IP description buffer ptr (saved)
;* Output: Zero if OK; checksum filled in
;* Destroys: AX, CX, DX, SI, DI, flags
;************************************************************************

IpChkSum proc near
xor dx,dx
mov di,[bx].dPtrIp
xchg dx,[di].iIpXsum ; save and clear checksum

call IpHdrLength
call ChkSum ; calculate new checksum
mov [di].iIpXsum,ax

cmp ax,dx ; does checksums match?
ret
IpChkSum endp



;************************************************************************
;* IcmpChkSum (both for generate and check!)
;*
;* Input: BX = IP description buffer ptr (saved)
;* CX = IP data length
;* Output: Zero if OK; checksum filled in
;* Destroys: AX, CX, DX, SI, DI, flags
;************************************************************************

IcmpChkSum proc near
xor dx,dx
mov di,[bx].dPtrUdp
xchg dx,[di].uIcmpXsum ; save and clear checksum

mov si,di
call ChkSum ; calculate new checksum
mov [di].uIcmpXsum,ax

cmp ax,dx ; does checksums match?
ret
IcmpChkSum endp



;************************************************************************
;* CurrentTicks
;* Output: CX = low word of ticks (1/18 second) counter
;* Destroys: CX, ES
;************************************************************************

CurrentTicks proc near
mov cx,040h ; DOS data segment
mov es,cx
mov cx,es:6ch ; get low word of ticks cntr
push cs
pop es
ret
CurrentTicks endp


if RFCC
;************************************************************************
;* Ageing of ARP and Route tables
;*
;* Destroys: AX, CX, DX, SI, DI, ES, flags
;************************************************************************

AgeNext dw 0 ; time to check slots

Ageing proc near
PushfDI
call CurrentTicks ; get current ticks value
cmp cx,AgeNext
jns AgeNow ; time to do ageing?

PopfEI
ret

AgeNow:
mov di,cx
mov dx,cx
add cx,2*18 ; chk next 2 seconds from now
mov AgeNext,cx

sub dx,65*18 ; clear what is older than 1 min

mov cx,ARPSLOTS-3
mov si,offset ArpTabTr+6
AgeArpLoop:
and word ptr [si+2*ARPSLOTS],not SQ_UPDATED ; clr every 2 s

lodsw
cmp ax,dx
jns AgeArpYoung ; not touched in a minute?

sub si,offset ArpTabTr+2
push cx
push di
push dx
mov dx,ArpTabIp1[si]
mov ax,ArpTabIp2[si]
call SwitchGwy ; in case it's a gwy: switch
pop dx
pop di
pop cx

xor ax,ax
mov ArpTabTr[si],di ; set current time
mov ArpTabIp1[si],ax ; clear old entries
mov ArpTabIp2[si],ax
mov ArpTabHw1[si],ax
mov ArpTabHw2[si],ax
mov ArpTabHw3[si],ax
add si,offset ArpTabTr+2
AgeArpYoung:
loop AgeArpLoop
PopfEI

mov cx,ROUTESLOTS
mov si,offset RouteTabTrRx
PushfDI
AgeRouteLoop:
and word ptr [si+2*ROUTESLOTS],not SQ_UPDATED ; clr every 2 s

lodsw
cmp ax,dx ; old slot?
jns AgeRouteYoung

sub si,offset RouteTabTrRx+2
mov RouteTabTrRx[si],di
xor ax,ax
cmp RouteTabUnreach[si],ax ; any error code?
jne AgeRouteUnr

cmp dx,RouteTabTrTx[si] ; - no. Sent anything lately?
jns AgeRouteEnd ; - no, that's why

mov RouteTabUnreach[si],SERRNOTRAF ;- yes, somebody is dead
jmp short AgeRouteEnd
AgeRouteUnr: ; - yes, remove slot
mov RouteTabIpD1[si],ax
mov RouteTabIpD2[si],ax
mov RouteTabIpG1[si],ax
mov RouteTabIpG2[si],ax
AgeRouteEnd:
add si,offset RouteTabTrRx+2
AgeRouteYoung:
loop AgeRouteLoop

PopfEI
call AgeFrags ; release old fragments

ret
Ageing endp



;************************************************************************
;* VerifyIpHdr (RFC1122 requirements)
;*
;* verify IP dst = me (or bcast), process IP options, verify IPver=4,
;* verify IP src not bcast, set timer value in ArpTab and RouteTab.
;* Process IP fragments.
;************************************************************************

holeStruc struc
holeNext dw 0
holeFirst dw 0
holeLast dw 0
HOLEDESCRLEN equ $-holeNext
holeStruc ends

k18 db 18

VerifyIpHdr proc near
push si
push di
mov al,[di].iIpVerHlen
and al,0f0h
cmp al,040h ; IP version 4?
jne VerifyErr1

mov di,[bx].dPtrPhys
mov si,[di]
mov dx,[di+2]
mov ax,[di+4]
call ArpFindHw ; find Hw Dst arp index
mov [bx].dIdxHwDst,di ; 0 = bcast, 4 = me

cmp MyIpNr,0
je VerifyMeNotInArp

cmp di,ARPMYIDX ; is Hw addr to me or bcast?
ja VerifyErr1
VerifyMeNotInArp:
mov di,[bx].dPtrPhys
add di,Hlen ; get physical src addr
mov si,[di]
mov dx,[di+2]
mov ax,[di+4]
call ArpFindHw ; is Hw Src in arp table?
jz VerifyChkBcast ; - yes

cmp [bx].dIdxHwDst,ARPMYIDX ; - no. Addressed to me?
jne VerifyIpDst ;

pop di ; - yes
push di
mov dx,[di].iIpSrc
mov ax,[di].iIpSrc+2

call MyNetChk ; if he is on my net
jnz VerifyIpDst

mov si,[bx].dPtrPhys
add si,Hlen
call ArpPutNew ; add him to arp table
jmp short VerifyIpDst

VerifyErr1: jmp short VerifyErr

VerifyChkBcast:
cmp di,ARPMYIDX ; from me or broadcast?
jbe VerifyErr ; - yes, ignore packet

call CurrentTicks ; get current ticks value
mov ArpTabTr[di],cx
VerifyIpDst:
cmp MyIpNr,0
je VerifyIpUnknown

pop di
push di
mov dx,[di].iIpDst ; check IP dst
mov ax,[di].iIpDst+2
call ArpFindIp
mov [bx].dIdxIpDst,di
cmp di,ARPMYIDX ; is it to me or broadcast?
ja VerifyErr ; - no, ignore packet
VerifyIpUnknown:
pop di
push di
mov dx,[di].iIpSrc
mov ax,[di].iIpSrc+2
call ArpFindIp
jnz VerifyNoArp

cmp di,ARPMYIDX ; from IP broadcast?
jb VerifyErr ; -yes, ignore packet
VerifyOK:
call AgeFrags ; release old reassembly bufs

pop di
push di
test [di].iIpFlFrag,0ff3fh ; is this a fragment?
jnz VerifyFrag
clc ; - no, use as is
VerifyRet:
pop di
pop si
ret

VerifyNoArp:
pop di
push di
mov cl,[di].iIpTos
xor ch,ch
mov si,cx
call RouteFind ; is he in route table?
jnz VerifyNoRoute

call CurrentTicks
mov RouteTabTrRx[di],cx ; note we've got pkts from him
VerifyNoRoute:
jmp short VerifyOK

FragAddAndErr:
mov si,offset FragList
call AddToList
VerifyFragErr:
mov bx,bp
VerifyErr:
call BufRelease
VerifyKeep:
stc
jmp short VerifyRet

VerifyFrag: ; RFC 815 fragment reassembly:
mov bp,bx ; addr of fragment buffer
mov dx,[di].iIpSrc ; get IP # / IP id / IP prot
mov ax,[di].iIpSrc+2
mov si,[di].iIpId
mov cx,Fraglist.lBufsAvail
mov ch,[di].iIpProt
FragLook4Buf:
mov di,offset FragList
call GetFromList
jz FragEarliest ; any reassembly buffers?

mov di,[bx].dPtrIp ; match IP#/Id/Prot
cmp dx,[di].iIpSrc
jne FragNextBuf
cmp ax,[di].iIpSrc+2
jne FragNextBuf
cmp si,[di].iIpId
jne FragNextBuf
cmp ch,[di].iIpProt
je FragBufFound
FragNextBuf:
push si ; irrelevant buf, put it back
mov si,offset FragList
call AddToList
pop si
dec cl
jnz FragLook4Buf
FragEarliest:
call FragFirstLast ; compute addr of fragment
or ax,ax ; first part earliest?
jnz FragOffset

mov bx,bp
mov di,[bx].dPtrUdp
add di,dx
cmp [bp].dHomeList,offset FreeBufs ; and a big buffer?
je FragFirstHole ; - yes, no extra buffer needed
FragOffset:
call BufAlloc ; - no, create empty buffer
jz VerifyFragErr

add di,2+2*EADDR_LEN
mov [bx].dPtrIp,di

mov si,[bp].dPtrIP
mov cx,[bp].dPtrUdp ; copy IP header
sub cx,si
call movemem
mov [bx].dPtrUdp,di
FragFirstHole:
mov [di].holeNext,0 ; create first hole descriptor
mov [di].holeFirst,0
mov [di].holeLast,GIANT
mov [bx].dPtrFrag,di

add di,HOLEDESCRLEN ; mark end of info
mov [bx].dPktEnd,di
FragBufFound:
call FragFirstLast ; compute addr of fragment
lea di,[bx].dPtrFrag
FragNextHole: ; 1. select next hole descr
mov si,di
mov di,[si].holeNext
or di,di ; any more descriptors?
jz FragEndHoles

cmp ax,[di].holeLast ; 2. frag.first > hole.last?
jae FragNextHole

cmp dx,[di].holeFirst ; 3. frag.last < hole.first?
jbe FragNextHole

mov cx,[di].holeNext ; 4. delete current hole descr
mov [si].holeNext,cx

mov cx,[di].holeLast
cmp ax,[di].holeFirst ; 5. frag.first > hole.first?
jbe FragNoNew1
mov [si].holeNext,di ; create new hole descr
mov [di].holeLast,ax
FragNoNew1:
mov si,[bp].dPtrIp ; 6. if frag.more
test [si].iIpFlFrag,0020h
jz FragLastFrag

cmp dx,cx ; and frag.last < hole.last?
jae FragNextHole

mov si,[bx].dPtrUdp
add si,dx
mov [si].holeFirst,dx ; create new hole descr
mov [si].holeLast,cx
mov cx,[bx].dPtrFrag
mov [si].holeNext,cx
mov [bx].dPtrFrag,si

add si,HOLEDESCRLEN
cmp si,[bx].dPktEnd
jbe FragNextHole
FragPktEnd:
mov [bx].dPktEnd,si
jmp short FragNextHole

FragLastFrag: ; when last frag
mov si,[bx].dPtrUdp ; prepare IP length
add si,dx
jmp short FragPktEnd

FragEndHoles: ; 8. copy data to assembly buf
cmp bx,bp ; do we need to copy?
je FragTimeUpd

or ax,ax ; if first frag
jnz FragNotFirst

mov si,[bx].dPtrUdp ; retain this IP header
mov di,[bp].dPtrUdp ; and copy assembled data
mov cx,[bx].dPktEnd ; to frag buffer
sub cx,si
add si,dx ; skip over first fragment
add di,dx
sub cx,dx
call movemem
mov [bp].dPktEnd,di

mov cx,[bx].dPtrFrag
mov [bp].dPtrFrag,cx

xchg bx,bp ; and switch buffers

mov cx,di
sub cx,si ; hole ptr adjustment
lea di,[bx].dPtrFrag
FragUpdLoop:
mov si,di
mov di,[si].holeNext
or di,di
jz FragRelBuf ; end of hole list

add di,cx
mov [si].holeNext,di ; new pointer address
jmp short FragUpdLoop

FragNotFirst:
mov si,[bp].dPtrUdp ; copy this frag to asm buf
mov cx,dx
sub cx,ax
mov di,[bx].dPtrUdp
add di,ax
call movemem
FragRelBuf:
push bx ; release frag buf
mov bx,bp
call BufRelease
pop bx
FragTimeUpd:
call CurrentTicks ; update timer entry
mov di,[bx].dPtrIp
mov al,[di].iIpTtl
mul k18
add cx,ax
mov [bx].dTickTimeout,cx

mov di,[bx].dPtrIp
mov si,[bx].dPtrUdp
cmp [bx].dPtrFrag,0 ; any holes?
jne FragKeep ; - yes, wait for more pkts

mov cx,[bx].dPktEnd ; - no
mov ax,cx ; compute IP length
sub cx,[bx].dPtrIp
xchg ch,cl
mov [di].iIpLen,cx

add sp,4 ; pop si and pop di
ret

FragKeep:
mov si,offset FragList ; keep buf a while
call AddToList
jmp VerifyKeep
VerifyIpHdr endp



;************************************************************************
;* FragFirstLast
;************************************************************************

FragFirstLast proc near
mov di,[bp].dPtrIp ; compute frag.first
mov ax,[di].iIpFlFrag
xchg ah,al
and ax,1fffh
shl ax,1
shl ax,1
shl ax,1
mov dx,[di].iIpLen ; compute frag.last (+1)
xchg dh,dl
add dx,[bp].dPtrIp
sub dx,[bp].dPtrUdp
add dx,ax
cmp dx,GIANT-IPHDRLEN-HWHDRLEN ; fits in buffer?
ja FragTooBig

ret

FragTooBig:
add sp,2 ; remove return addr from stack
jmp FragAddAndErr
FragFirstLast endp



;************************************************************************
;* AgeFrags
;************************************************************************

AgeFragsNext dw 0

AgeFrags proc near
call CurrentTicks
cmp cx,AgeFragsNext
jns AgeFragsNow ; time to age yet?

ret

AgeFragsNow:
mov dx,cx
add cx,2*18 ; check next in 2 seconds
mov AgeFragsNext,cx

push bx
PushfDI
mov cx,Fraglist.lBufsAvail
AgeLook4Buf:
mov di,offset FragList
call GetFromList
jz AgeNoMoreFrags ; any reassembly buffers?

mov si,offset FragList
cmp dx,[bx].dTickTimeout
js AgenextBuf ; too old?
; - no, put it back
mov si,[bx].dHomeList ; - yes, release it
AgeNextBuf:
call AddToList

dec cl
jnz AgeLook4Buf ; any more reassembly bufs?
AgeNoMoreFrags:
PopfEI
pop bx
ret
AgeFrags endp

endif ; RFCC

;************************************************************************
;* SwitchIpDst
;************************************************************************


SwitchIpDst proc near
mov si,[bx].dPtrIp
SwitchIpDstB:
mov dx,[si].iIpSrc ; mov his IP # to IP dst
mov di,[bx].dPtrIp
mov [di].iIpDst,dx
mov dx,[si].iIpSrc+2
mov [di].iIpDst+2,dx
ret
SwitchIpDst endp



;************************************************************************
;* InitIp
;************************************************************************

InitIp proc near

push sp
pop ax
cmp ax,sp ; 286 or better?
jne IsNot386

pushf
pop ax
or ax,7000h ;the 386 lets us set these bits
push ax
popf
pushf
pop ax
test ax,7000h ;did the bits get set?
jz IsNot386 ;no.
or GenFlags,IS_386 ;yes, use a 386-optimized code
IsNot386:
push ds
mov IpDesBuf.dPtrPhys,offset IpDesBuf.fHwStruc ; *test*
mov IpDesBuf.dPtrIp,offset IpDesBuf.fIpStruc
mov IpDesBuf.dPtrUdp,offset IpDesBuf.fBotStruc

mov ax,1ffh ;driver_info
int_pkt
pop ds
call fatal_error

mov ah,2 ;access all packets.
mov al,ch ;their class from driver_info().
mov bx,dx ;their type from driver_info().
mov dl,cl ;their number from driver_info().
mov cx,2 ;type length of two.
mov si,offset IpDesBuf.fHwStruc.hProtType
push cs ;es:di -> our receiver.
pop es
mov di,offset IpRecv

push ds
int_pkt
pop ds

call fatal_error

mov IpHandle,ax

ret
InitIp endp



;************************************************************************
;* EndProtocol
;************************************************************************

EndProtocol proc near
mov ah,3 ; release handle in bx

push ds
int_pkt
pop ds

call print_error

ret
EndProtocol endp



;************************************************************************
;* Terminate
;************************************************************************

Terminate proc near
pop bx ; remove return addr fr stack
push ax

cld
push cs
push cs
pop ds
pop es
sti

mov bx,IpHandle ; release ARP and IP handles
call EndProtocol
mov bx,ArpHandle
call EndProtocol
if PINGCLIENT
cmp EchoTarget,0
jz TermNoPing
call RestoreTimer
TermNoPing:
endif ; PINGCLIENT
pop ax
push ax
or al,al ; any errors?
jz termnorm

cmp EnoughWord,HAVE_MYIPNR ; clock errors?
je termnorm

mov dx,offset MsgNotSet ; display "clock not set"
mov ah,9
int 21h
termnorm:
pop ax
push ax
add al,'0'
mov MsgTermNr,al ; show error # in end line

mov si,offset MyHwAd
mov di,offset MsgTermHw
call PutHwNum ; put my HW addr

mov si,offset MyIpNr
mov di,offset MsgMyIp
call PutIpNum ; put my IP #

mov dx,offset MsgTerm ; End of PDCLKSET msg
mov ah,9
int 21h

pop ax ; error code
mov ah,4ch
int 21h ; terminate program
Terminate endp




;************************************************************************
;* IP receiver *
;************************************************************************

IpRecv:
pushf

push ds ; set segment registers
mov dx,cs
mov ds,dx
mov es,dx
cld

or ax,ax ; first or second call?
jne IpRecv_1 ; - second, we've got data
; - first, they want a buf
cmp cx,GIANT ; packet too long?
ja IpRecTooBig
if PINGCLIENT
cmp cx,BUFBODYSML
ja IpRecBig

call BufAlSml
jz IpRecBig
pop ds
popf
retf

IpRecBig:
endif ; PINGCLIENT
call BufAlloc ; get a receive buffer
jz IpRecNoBuf
IpRecRet0:
pop ds
popf
retf

if DEBUG
DbgIpRecv:
or GenFlags,DBGINTERR
endif ; DEBUG
IpRecNoBuf:
if PINGCLIENT
inc EchoDrop
endif ; PINGCLIENT
IpRecTooBig:
xor di,di ; no buffer available
mov es,di
jmp short IpRecRet0


IpRecv_1:

; If I understand PC interrupt handling correctly, the following stackswitch
; and interrupt enabling should not kill multiprocess reentrancy.

cli ; switch to our
mov word ptr SaveSP,sp ; interrupt stack
mov word ptr SaveSS,ss
mov sp,offset StackEnd
mov ss,MySegm
sti ; allow interrupts
if DEBUG
dec DbgIntCnt ; double interrupt?
jnz DbgIpRecv
endif ; DEBUG
call BuildRecDescr ; calculate pointers etc

call IpChkSum ; only accept a correct
jne IpRecRet ; IP checksum

mov [bx].dPtrUdp,si
if RFCC
call VerifyIpHdr ; verify and process IP hdr
jc IpRecKeepBuf
endif ; RFCC
mov cx,[di].iIpLen ; calculate IP data length
xchg ch,cl
add cx,di
sub cx,si
mov [bx].dPktLen,cx ; save IP data length
if PINGCLIENT
mov ax,cx
add ax,IPHDRLEN+14+8+4+12 ; preamble, crc, intergap
add EchoLoad+2,ax
adc EchoLoad,0
endif ; PINGCLIENT
cmp [di].iIpProt,ICMP_PROT ; ICMP protocol?
jne IpNotIcmp
jmp IpRecIcmp
IpNotIcmp:
cmp [di].iIpProt,UDP_PROT ; UDP protocol?
jne IpProtUnr

call UdpChkSum ; if available, test
jne IpRecRet ; UDP checksum

cmp si,[bx].dPktEnd ; still within packet limit?
jbe IpRecUdp
IpRecRet:
call BufRelease ; release receive buffer
IPRecKeepBuf:
if DEBUG
inc DbgIntCnt
endif ; DEBUG
cli ; restore previous stack
mov sp, word ptr SaveSP
mov ss, word ptr SaveSS
pop ds
popf
retf


IpProtUnr:
mov ah,2 ; protocol unreachable
jmp short IpUnr
IpPortUnr:
mov ah,3 ; port unreachable
IpUnr:
if RFCC
mov al,3 ; destination unreachable

cmp [bx].dIdxHwDst,ARPMYIDX ; was it specifically to me?
jne IpRecRet

mov bp,bx
call BufAlloc
jz UnrEnd
call MakeSendDescr
mov si,[bp].dPtrIp ; copy IP header
mov cx,[bp].dPtrUdp
add cx,8 ; + 8 bytes
sub cx,si

call SwitchIpDstB ; return to sender

mov di,[bx].dPtrUdp
mov [di].uIcmpTypecode,ax ; set type and code

add di,8
call movemem

sub di,[bx].dPtrUdp
mov [bx].dPktLen,di ; set IP data length

mov si,offset IcmpToDo ; put buf on the ICMP send list
call AddToList
UnrEnd:
mov bx,bp
endif ; RFCC
jmp IpRecRet


IpRecUdp:
mov [bx].dPktEnd,si ; end of logical pkt

mov di,[bx].dPtrUdp
cmp word ptr [di].uUdpSrc,2500h ; Src port 0025 = 37 ?
je IpRecvTime ; - must be time reply
if TBLBUILD or PINGCLIENT
cmp word ptr [di].uUdpSrc,3500h ; Name server (53)?
jne $+5
jmp IpRecvName
cmp word ptr [di].uUdpDst,0703h ; Udp Echo?
jne $+5
jmp IpEchoReply
cmp word ptr [di].uUdpDst,0700h ; Udp Echo request?
jne $+5
jmp IpEchoRequest
cmp word ptr [di].uUdpDst,0900h ; Udp Discard request?
jne $+5
jmp IpDiscard
endif ; TBLBUILD or PINGCLIENT
cmp word ptr [di].uUdpSrc,4300h ; Src port 0043 = 67
jne IpPortUnr

cmp word ptr [di].uUdpDst,4400h ; Dst port 0044 = 68
jne IpPortUnr

cmp byte ptr [di].uBotOp,2 ; bootp reply?
jne IpRecRet2
IpRecBootp:
push di ; - must be bootp reply
lea si,[di].uBotCliHwAd
mov di,offset MyHwAd
mov cx,Hlen
repe cmpsb ; our Ethernet addr?
pop di
jne IpRecRet2

mov ax,word ptr IpDesBuf.fBotStruc.uBotXid
cmp ax,word ptr [di].uBotXid ; our random id?
jne IpRecRet2
mov ax,word ptr IpDesBuf.fBotStruc.uBotXid+2
cmp ax,word ptr [di].uBotXid+2
jne IpRecRet2
; yes, it's about me!
mov dx,[di].uBotGwyIp ; saves an ARP request
mov ax,[di].uBotGwyIp+2 ; in many cases
mov si,[bx].dPtrPhys
add si,Hlen
call ArpPutNew

test Events,GOT_BOOTP ; if first answer
jnz IpRecRet2 ; save bootp answer

mov AdrBootpReply,bx ; for further study
or Events,GOT_BOOTP
jmp IpRecKeepBuf

IpRecvTime:
test Events,GOT_TIMEREPLY ; already got reply?
jnz IpRecRet2

mov ax,[di].uUdpData ; save seconds
mov cTime,ax ; since year 1900
mov ax,[di].uUdpData+2
mov cTime+2,ax

mov di,[bx].dPtrIp ; save who was answering
mov dx,[di].iIpSrc
mov ax,[di].iIpSrc+2
mov RespondingIpNr,dx
mov RespondingIpNr+2,ax
or Events,GOT_TIMEREPLY
IpRecRet2:
jmp IpRecRet

if TBLBUILD or PINGCLIENT
IpRecvName:
mov si,offset NameToDo ; process nameserver reply
call AddToList
jmp IpRecKeepBuf
endif ; TBLBUILD or PINGCLIENT


IpRecIcmp:
call SwitchIpDst ; in case we want to return it

call IcmpChkSum ; valid ICMP checksum?
jne IpRecRet2

mov si,[bx].dPtrUdp ; point to ICMP header
mov cx,[si].uIcmpTypecode ; get type and code
mov dx,[si].uIcmpIpHdr.iIpDst ; get original IP hdr
mov ax,[si].uIcmpIpHdr.iIpDst+2 ; dst IP #

if PINGCLIENT
cmp cl,0 ; echo reply?
jne IpNotEchoReply
IpEchoReply:
call EchoCalc
jmp IpRecRet
endif ; PINGCLIENT

if RFCC
IpNotEchoReply:
cmp cl,8 ; echo request?
jne IpNotEchoReq

mov byte ptr [si].uIcmpTypecode,0 ; put echo reply value
IpQueReply:
cmp [bx].dIdxHwDst,ARPMYIDX ; broadcast?
jb IpRecRet2 ; - yes, don't echo

mov si,offset IcmpToDo ; put buf on the ICMP send list
call AddToList
jmp IpRecKeepBuf

IpNotEchoReq:
endif ; RFCC

cmp cx,11 ; TTL exceeded?
je IpIcmpUnreach

cmp cl,3 ; unreachable
jne IpNotUnreach

cmp ch,12 ; net or host unreachable?
ja IpIcmpRet2
cmp ch,6
ja IpIcmpUnreach
cmp ch,1
ja IpIcmpRet2
IpIcmpUnreach: ; codes 0 - 1, 6 - 12 go here
PushfDI
push si
mov cl,[si].uIcmpIpHdr.iIpTos
xor ch,ch
mov si,cx
call RouteFind ; this host in route table?
pop si
jnz IpIcmpUnfound

mov ah,byte ptr [si].uIcmpTypeCode+1 ; icmp code
xor al,al ;*test
inc ah ; ensure non zero error code
mov RouteTabUnreach[di],ax ; mark it unreachable

call CurrentTicks ; get current ticks value
mov RouteTabTrRx[di],cx ; set timer
IpIcmpUnfound:
PopfEI
IpIcmpRet2:
jmp IpIcmpRet

IpNotUnreach:
if RFCC
cmp cl,4 ; source quench
jne IpIcmpNotQuench

PushfDI
push si
mov cl,[si].uIcmpIpHdr.iIpTos
xor ch,ch
mov si,cx
call RouteFind ; this host in route table?
pop si
jnz IpIcmpNotRoute

mov dx,RouteTabFlags[di]
or RouteTabFlags[di],SQ_UPDATED
lea si,RouteTabTrSq[di]
lea di,RouteTabSqDelay[di]
jmp short IpIcmpSq
IpIcmpNotRoute:
call ArpFindIp
jnz IpIcmpSqEnd

mov dx,ArpTabFlags[di]
or ArpTabFlags[di],SQ_UPDATED
lea si,ArpTabTrSq[di]
lea di,ArpTabSqDelay[di]
IpIcmpSq:
mov ax,[di] ; get current sq delay
or ax,ax
jnz IpIcmpAddDelay
call CurrentTicks
mov [si],cx ; initialize sq timer
mov ax,150*64/55 ; initial sq delay 178 ms
IpIcmpAddDelay:
add ax,28*64/55 ; increase delay by 28 ms
test dx,SQ_UPDATED ; unless already done in
jnz IpIcmpSqEnd ; the last two seconds
mov [di],ax
IpIcmpSqEnd:
PopfEI
jmp short IpIcmpRet

IpIcmpNotQuench:
endif ; RFCC
cmp cl,5 ; redirect
jne IpNotRedirect

PushfDI
push si
mov cl,[si].uIcmpIpHdr.iIpTos
xor ch,ch
mov si,cx
call RouteFind ; this host in route table?
pop si
jnz IpIcmpDisfound

mov dx,RouteTabIpG1[di] ; icmp from the gwy used?
mov ax,RouteTabIpG2[di]
mov cx,di
mov di,[bx].dPtrIp
cmp dx,[di].iIpSrc
jne IpIcmpDisfound
cmp ax,[di].iIpSrc+2
jne IpIcmpDisfound
mov di,cx

mov dx,[si].uIcmpData ; new gateway IP #
mov ax,[si].uIcmpData+2

call MyNetChk ; is he on my net?
jnz IpIcmpDisfound

mov RouteTabIpG1[di],dx ; put it into route table
mov RouteTabIpG2[di],ax
IpIcmpDisfound:
PopfEI
jmp short IpIcmpRet

IpNotRedirect:
IpIcmpRet:
jmp IpRecRet

if RFCC
IpEchoRequest:
call SwitchIpDst

mov si,[bx].dPtrUdp

mov dx,[si].uUdpDst ; swap udp src and dst ports
mov ax,[si].uUdpSrc
mov [si].uUdpDst,ax
mov [si].uUdpSrc,dx
add EchoRx32+2,1 ; increment receive counters
adc EchoRx32,0
add EchoTx32+2,1 ; increment tramsmit counters
adc EchoTx32,0
jmp IpQueReply

IpDiscard:
add EchoRx32+2,1 ; increment receive counters
adc EchoRx32,0
jmp IpRecRet
endif ; RFCC


;************************************************************************
;* MakeMynet
;************************************************************************

MakeMynet proc near
mov dx,MyIpNr
mov ax,MyIpNr+2

mov ArpTabIp1+4,dx ; third arp slot = me
mov ArpTabIp2+4,ax
mov si,offset MyHwAd
call ArpPutNew

mov si,MyMask
mov di,MyMask+2
and dx,si ; calculate network #
and ax,di
mov MyNet,dx
mov MyNet+2,ax

not si ; second arp slot =
not di ; subnet broadcast
or dx,si
or ax,di
mov ArpTabIp1+2,dx
mov ArpTabIp2+2,ax
ret
MakeMynet endp



;************************************************************************
;* ValidateIpNr
;************************************************************************

ValidateIpNr proc near
call BufAlloc

mov [bx].dTimOutMsg,0 ; we SHOULD get no answer
mov [bx].dTickTimeout,10 ; give them half a second
mov dx,MyIpNr
mov ax,MyIpNr+2
call SendArpReq ; send arp to our Ip #
call BufRelease
jz ValConflict ; someone else has our IP # ?

ret ; - no, return

ValConflict: ; - yes, error termination
call ArpFindIp
mov cl,3
shl di,cl
lea si,ArpTabHwAdr[di] ; get HW addr of arp reply

mov di,offset OccupiedHw
call PutHwNum ; put offending HW addr

mov dx,offset OccupiedMsg ; display IP occupied msg
mov ah,9
int 21h

mov al,09 ; error code 9
call terminate
ValidateIpNr endp



;************************************************************************
;* DoBootpPkt
;************************************************************************

DoBootpPkt proc near
call CurrentTicks ; get current ticks value
mov IpDesBuf.fBotStruc.uBotXid,cx ; as transaction id
if RFCC
add cx,2*18 ; initialize ageing
mov AgeNext,cx
mov AgeFragsNext,cx
endif ; RFCC
mov al,byte ptr ArpBuf.iArpHtype+1
mov IpDesBuf.fBotStruc.uBotHtype,al ; put my HW type

mov si,offset MyHwAd
mov di,offset IpDesBuf.fBotStruc.uBotCliHwAd
push cs
pop es
mov cx,Hlen
mov IpDesBuf.fBotStruc.uBotHlen,cl
rep movsb ; copy my HW addr

mov bx,offset IpDesBuf ; frame to send
call MakeSendDescr
mov [bx].dWaitEvent,GOT_BOOTP ; event to wait for
mov [bx].dTimOutMsg,offset NoBotReplyMsg ; error msg
mov ax,BpDataLen+UDPHDRLEN ; bootp pkt length
call SendUdpPkt ; send bootp request

ret
DoBootpPkt endp



;************************************************************************
;* InterpBootp according to RFC 1084 and RFC1048
;************************************************************************

InterpBootp proc near
mov bx,AdrBootpReply
mov di,[bx].dPtrUdp
mov dx,[di].uBotYourIp ; mov my IP #
mov MyIpNr,dx ; to IP buf
mov ax,[di].uBotYourIp+2
mov MyIpNr+2,ax

or Flagword,HAVE_MYIPNR ; note what we've got

cmp word ptr [di].uBotMagNum,8263h ; RFC 1084 type of
jne VendEnd ; vendor area?
cmp word ptr [di].uBotMagNum+2,6353h
jne VendEnd

lea si,[di].uBotVend ; start of "vendor area"
lea bp,[si]+64-4 ; end of "vendor area"
push cs
pop es
VendLoop:
cmp si,bp ; beyond vendor area?
jb VendNext

VendEnd:
call BufRelease ; release bootp reply buf
ret

VendNext:
lodsb ; get field type:

cmp al,0 ; filler
je VendLoop

cmp al,1 ; netmask
jne NotMask
mov di,offset MyMask
call Xtract4Bytes
jmp short VendLoop
NotMask:
cmp al,2 ; zone/time (?) offset
jne NotTimeOffs
test Flagword,HAVE_TIMEOFFSET
jnz VendDefault
mov di,offset tzoffset
call Xtract4Bytes
or Flagword,HAVE_TIMEOFFSET
jmp short VendLoop
NotTimeOffs:
cmp al,3 ; default gateways
jne NotDefGwys
mov di,offset DefGwys
mov cx,MAXDEFGWYS
mov ax,DefGwyNum
call XtractIpNums
add DefGwyNum,cx
jmp short VendLoop
NotDefGwys:
cmp al,4 ; time servers
jne NotTimeserv
or Flagword,HAVE_TIMESERVER
mov di,offset TimeServIpNr
mov cx,MAXTSERVS
mov ax,TservNum
call XtractIpNums
add TservNum,cx
jmp short VendLoop
NotTimeserv:
if TBLBUILD or PINGCLIENT
cmp al,6 ; default nameservers
jne NotDefNS
mov di,offset DefNS
mov cx,MAXDEFNS
mov ax,DefNSnum
call XtractIpNums
add DefNSnum,cx
jmp short VendLoop
NotDefNS:
endif ; TBLBUILD or PINGCLIENT
cmp al,0ffh ; end
je VendEnd
VendDefault: ; everything else
lodsb
xor ah,ah
add si,ax ; skip field
jmp Vendloop

InterpBootp endp



;************************************************************************
;* XtractIpNums
;************************************************************************

XtractIpNums proc near
sub cx,ax ; wanted - already got

shl ax,1
shl ax,1
add di,ax ; append to what we got
shl cx,1
shl cx,1
jmp short XtractNBytes
Xtract4Bytes:
mov cl,4
XtractNBytes:
lodsb
cmp cl,al ; as many as we want?
jbe XtractOkLen

mov cl,al ; - no, take what there is
XtractOkLen:
xor ch,ch
xor ah,ah
push cx
push ax
push si
rep movsb ; copy field
pop si

pop ax
add si,ax ; advance field ptr

pop cx
shr cx,1
shr cx,1 ; cx = IP #'s read

ret
XtractIpNums endp

;========================================================================
; endinclude


  3 Responses to “Category : Network Files
Archive   : PDCLK145.ZIP
Filename : IP.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/