* Copyright (c) University of British Columbia, 1984
* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* the Laboratory for Computation Vision and the Computer Science Department
* of the University of British Columbia.
* %sccs.include.redist.c%
* @(#)pk_input.c 7.5 (Berkeley) %G%
* This procedure is called by the link level whenever the link
* becomes operational, is reset, or when the link goes down.
register struct pkcb
*pkp
;
if (pkp
-> pk_state
== DTE_WAITING
)
pk_restart (pkp
, X25_RESTART_NETWORK_CONGESTION
);
pk_restart (pkp
, -1); /* Clear all active circuits */
pkp
-> pk_state
= DTE_WAITING
;
pk_restart (pkp
, X25_RESTART_NETWORK_CONGESTION
);
* This procedure is called by a link level procedure whenever
* an information frame is received. It decodes the packet and
* demultiplexes based on the logical channel number.
register struct x25_packet
*xp
;
register struct pklcd
*lcp
;
register struct socket
*so
= 0;
register struct pkcb
*pkp
;
int ptype
, lcn
, lcdstate
= LISTEN
;
static struct x25config
*lastxcp
;
static struct pkcb
*lastpkp
;
for (pkp
= pkcbhead
; ; pkp
= pkp
-> pk_next
) {
pk_message (0, xcp
, "pk_input: unknown network");
if (pkp
-> pk_xcp
== xcp
)
xp
= mtod (m
, struct x25_packet
*);
lcn
= xp
-> logical_channel_number
;
lcp
= pkp
-> pk_chan
[lcn
];
* If the DTE is in Restart state, then it will ignore data,
* interrupt, call setup and clearing, flow control and reset
if (lcn
< 0 || lcn
> pkp
-> pk_maxlcn
) {
pk_message (lcn
, pkp
-> pk_xcp
, "illegal lcn");
pk_trace (pkp
-> pk_xcp
, xp
, "P-In");
if (pkp
-> pk_state
!= DTE_READY
&& ptype
!= RESTART
&& ptype
!= RESTART_CONF
) {
lcdstate
= lcp
-> lcd_state
;
if (ptype
== CLEAR
) { /* idle line probe (Datapac specific) */
/* send response on lcd 0's output queue */
lcp
-> lcd_template
= pk_template (lcn
, X25_CLEAR_CONFIRM
);
if (lcn
== 0 && ptype
!= RESTART
&& ptype
!= RESTART_CONF
) {
pk_message (0, pkp
-> pk_xcp
, "illegal ptype (%s) on lcn 0",
pk_name
[ptype
/ MAXSTATES
]);
switch (ptype
+ lcdstate
) {
* Incoming Call packet received.
incoming_call (pkp
, xp
, m
-> m_len
);
* Call collision: Just throw this "incoming call" away since
* the DCE will ignore it anyway.
pk_message ((int)xp
-> logical_channel_number
, pkp
-> pk_xcp
,
"incoming call collision");
* Call confirmation packet received. This usually means our
* previous connect request is now complete.
case CALL_ACCEPTED
+ SENT_CALL
:
call_accepted (lcp
, xp
, m
-> m_len
);
* This condition can only happen if the previous state was
* SENT_CALL. Just ignore the packet, eventually a clear
* confirmation should arrive.
case CALL_ACCEPTED
+ SENT_CLEAR
:
* Clear packet received. This requires a complete tear down
* of the virtual circuit. Free buffers and control blocks.
* and send a clear confirmation.
case CLEAR
+ RECEIVED_CALL
:
case CLEAR
+ DATA_TRANSFER
:
lcp
-> lcd_state
= RECEIVED_CLEAR
;
lcp
-> lcd_template
= pk_template (lcp
-> lcd_lcn
, X25_CLEAR_CONFIRM
);
* Clear collision: Treat this clear packet as a confirmation.
* Clear confirmation received. This usually means the virtual
* circuit is now completely removed.
case CLEAR_CONF
+ SENT_CLEAR
:
* A clear confirmation on an unassigned logical channel - just
* ignore it. Note: All other packets on an unassigned channel
* Data packet received. Pass on to next level. Move the Q and M
* bits into the data portion for the next level.
case DATA
+ DATA_TRANSFER
:
if (lcp
-> lcd_reset_condition
) {
* Process the P(S) flow control information in this Data packet.
* Check that the packets arrive in the correct sequence and that
* they are within the "lcd_input_window". Input window rotation is
* initiated by the receive interface.
if (PS(xp
) != ((lcp
-> lcd_rsn
+ 1) % MODULUS
) ||
PS(xp
) == ((lcp
-> lcd_input_window
+ lcp
->lcd_windowsize
) % MODULUS
)) {
pk_procerror (RESET
, lcp
, "p(s) flow control error");
if (pk_ack (lcp
, PR(xp
)) != PACKET_OK
) {
m
-> m_data
+= PKHEADERLN
;
m
-> m_len
-= PKHEADERLN
;
if (lcp
-> lcd_flags
& X25_MQBIT
) {
* Discard Q-BIT packets if the application
* doesn't want to be informed of M and Q bit status
if (xp
-> q_bit
&& (lcp
-> lcd_flags
& X25_MQBIT
) == 0) {
* NB. This is dangerous: sending a RR here can
* cause sequence number errors if a previous data
* packet has not yet been passed up to the application
* (RR's are normally generated via PRU_RCVD).
lcp
-> lcd_template
= pk_template (lcp
-> lcd_lcn
, X25_RR
);
sbappendrecord (&so
-> so_rcv
, m
);
sbappend (&so
-> so_rcv
, m
);
* Interrupt packet received.
case INTERRUPT
+ DATA_TRANSFER
:
if (lcp
-> lcd_reset_condition
)
lcp
-> lcd_intrdata
= xp
-> packet_data
;
lcp
-> lcd_template
= pk_template (lcp
-> lcd_lcn
, X25_INTERRUPT_CONFIRM
);
* Interrupt confirmation packet received.
case INTERRUPT_CONF
+ DATA_TRANSFER
:
if (lcp
-> lcd_reset_condition
)
if (lcp
-> lcd_intrconf_pending
== TRUE
)
lcp
-> lcd_intrconf_pending
= FALSE
;
pk_procerror (RESET
, lcp
, "unexpected packet");
* Receiver ready received. Rotate the output window and output
* any data packets waiting transmission.
if (lcp
-> lcd_reset_condition
)
if (pk_ack (lcp
, PR(xp
)) != PACKET_OK
)
if (lcp
-> lcd_rnr_condition
== TRUE
)
lcp
-> lcd_rnr_condition
= FALSE
;
* Receiver Not Ready received. Packets up to the P(R) can be
* be sent. Condition is cleared with a RR.
case RNR
+ DATA_TRANSFER
:
if (lcp
-> lcd_reset_condition
)
if (pk_ack (lcp
, PR(xp
)) != PACKET_OK
)
lcp
-> lcd_rnr_condition
= TRUE
;
* Reset packet received. Set state to FLOW_OPEN. The Input and
* Output window edges ar set to zero. Both the send and receive
* numbers are reset. A confirmation is returned.
case RESET
+ DATA_TRANSFER
:
if (lcp
-> lcd_reset_condition
)
/* Reset collision. Just ignore packet. */
wakeup ((caddr_t
) & so
-> so_timeo
);
lcp
-> lcd_window_condition
= lcp
-> lcd_rnr_condition
=
lcp
-> lcd_intrconf_pending
= FALSE
;
lcp
-> lcd_output_window
= lcp
-> lcd_input_window
=
lcp
-> lcd_last_transmitted_pr
= 0;
lcp
-> lcd_rsn
= MODULUS
- 1;
lcp
-> lcd_template
= pk_template (lcp
-> lcd_lcn
, X25_RESET_CONFIRM
);
* Reset confirmation received.
case RESET_CONF
+ DATA_TRANSFER
:
if (lcp
-> lcd_reset_condition
) {
lcp
-> lcd_reset_condition
= FALSE
;
pk_procerror (RESET
, lcp
, "unexpected packet");
case INTERRUPT
+ SENT_CLEAR
:
case INTERRUPT_CONF
+ SENT_CLEAR
:
case RESET_CONF
+ SENT_CLEAR
:
/* Just ignore packet if we have sent a CLEAR already.
* Restart sets all the permanent virtual circuits to the "Data
* Transfer" stae and all the switched virtual circuits to the
switch (pkp
-> pk_state
) {
pkp
-> pk_state
= DTE_READY
;
pk_message (0, pkp
-> pk_xcp
,
"Packet level operational");
pk_restartcause (pkp
, xp
);
pkp
-> pk_chan
[0] -> lcd_template
= pk_template (0,
pk_output (pkp
-> pk_chan
[0]);
* Restart confirmation received. All logical channels are set
case RESTART_CONF
+ READY
:
switch (pkp
-> pk_state
) {
pkp
-> pk_state
= DTE_READY
;
pk_message (0, pkp
-> pk_xcp
,
"Packet level operational");
/* Restart local procedure error. */
pk_restart (pkp
, X25_RESTART_LOCAL_PROCEDURE_ERROR
);
pkp
-> pk_state
= DTE_SENT_RESTART
;
pk_procerror (CLEAR
, lcp
, "unknown packet error");
pk_message (lcn
, pkp
-> pk_xcp
,
"\"%s\" unexpected in \"%s\" state",
pk_name
[ptype
/MAXSTATES
], pk_state
[lcdstate
]);
else /* Packets arrived on an unassigned channel.
pk_message ((int)xp
->logical_channel_number
, pkp
-> pk_xcp
,
"packet arrived on unassigned lcn");
* This routine handles incoming call packets. It matches the protocol
* field on the Call User Data field (usually the first four bytes) with
* sockets awaiting connections.
incoming_call (pkp
, xp
, len
)
register struct pklcd
*lcp
= 0, *l
;
register struct sockaddr_x25
*sa
;
register struct x25_calladdr
*a
;
register struct socket
*so
= 0;
char *e
, *errstr
= "server unavailable";
int lcn
= xp
-> logical_channel_number
;
/* First, copy the data from the incoming call packet to a X25_socket
a
= (struct x25_calladdr
*) &xp
-> packet_data
;
l1
= a
-> calling_addrlen
;
l2
= a
-> called_addrlen
;
if ((m
= m_getclr (M_DONTWAIT
, MT_HEADER
)) == 0)
sa
= mtod (m
, struct sockaddr_x25
*);
u
= (octet
*) (a
-> address_field
+ l2
/ 2);
parse_facilities (u
, sa
);
sa
-> x25_udlen
= min (16, ((octet
*)xp
) + len
- u
);
bcopy ((caddr_t
)u
, sa
-> x25_udata
, (unsigned)sa
-> x25_udlen
);
* Now, loop through the listen sockets looking for a match on the
* PID. That is the first four octets of the user data field. This
* is the closest thing to a port number for X.25 packets. What it
* does provide is away of multiplexing services at the user level.
for (l
= pk_listenhead
; l
; l
= l
-> lcd_listen
) {
struct sockaddr_x25
*sxp
= l
-> lcd_ceaddr
;
if (bcmp (sxp
-> x25_udata
, sa
-> x25_udata
, sxp
->x25_udlen
))
sxp
-> x25_net
!= pkp
->pk_xc
.xc_addr
.x25_net
)
* don't accept incoming collect calls unless
* the server sets the reverse charging option.
if ((sxp
-> x25_opts
.op_flags
& (X25_OLDSOCKADDR
|X25_REVERSE_CHARGE
)) == 0 &&
sa
-> x25_opts
.op_flags
& X25_REVERSE_CHARGE
) {
errstr
= "incoming collect call refused";
if (so
= sonewconn (l
-> lcd_so
, SS_ISCONNECTED
))
lcp
= (struct pklcd
*) so
-> so_pcb
;
lcp
= pk_attach((struct socket
*) 0);
* Insufficient space or too many unaccepted
* connections. Just throw the call away.
errstr
= "server malfunction";
lcp
-> lcd_upper
= l
-> lcd_upper
;
lcp
-> lcd_upnext
= l
-> lcd_upnext
;
lcp
-> lcd_state
= RECEIVED_CALL
;
sa
-> x25_opts
.op_flags
|= sxp
-> x25_opts
.op_flags
&
lcp
-> lcd_template
= pk_template (lcp
-> lcd_lcn
, X25_CALL_ACCEPTED
);
} else if (lcp
->lcd_upper
)
* If the call fails for whatever reason, we still need to build a
* skeleton LCD in order to be able to properly receive the CLEAR
#ifdef WATERLOO /* be explicit */
if (l
== 0 && bcmp(sa
->x25_udata
, "ean", 3) == 0)
pk_message (lcn
, pkp
-> pk_xcp
, "host=%s ean%c: %s",
sa
->x25_addr
, sa
->x25_udata
[3] & 0xff, errstr
);
else if (l
== 0 && bcmp(sa
->x25_udata
, "\1\0\0\0", 4) == 0)
pk_message (lcn
, pkp
-> pk_xcp
, "host=%s x29d: %s",
pk_message (lcn
, pkp
-> pk_xcp
, "host=%s pid=%x %x %x %x: %s",
sa
-> x25_addr
, sa
-> x25_udata
[0] & 0xff,
sa
-> x25_udata
[1] & 0xff, sa
-> x25_udata
[2] & 0xff,
sa
-> x25_udata
[3] & 0xff, errstr
);
if ((m
= m_getclr (M_DONTWAIT
, MT_HEADER
)) == 0) {
(void) m_free (dtom (sa
));
lcp
= mtod (m
, struct pklcd
*);
lcp
-> lcd_state
= RECEIVED_CALL
;
(void) m_free (dtom (sa
));
call_accepted (lcp
, xp
, len
)
register struct x25_calladdr
*ap
;
lcp
-> lcd_state
= DATA_TRANSFER
;
soisconnected (lcp
-> lcd_so
);
ap
= (struct x25_calladdr
*) &xp
-> packet_data
;
fcp
= (octet
*) ap
-> address_field
+ (ap
-> calling_addrlen
+
ap
-> called_addrlen
+ 1) / 2;
if (fcp
+ *fcp
<= ((octet
*)xp
) + len
)
parse_facilities (fcp
, lcp
-> lcd_ceaddr
);
pk_assoc (lcp
-> lcd_pkp
, lcp
, lcp
-> lcd_ceaddr
);
parse_facilities (fcp
, sa
)
register struct sockaddr_x25
*sa
;
* Ignore national DCE or DTE facilities
if (*fcp
== 0 || *fcp
== 0xff)
case FACILITIES_WINDOWSIZE
:
sa
-> x25_opts
.op_wsize
= fcp
[1];
case FACILITIES_PACKETSIZE
:
sa
-> x25_opts
.op_psize
= fcp
[1];
case FACILITIES_THROUGHPUT
:
sa
-> x25_opts
.op_speed
= fcp
[1];
case FACILITIES_REVERSE_CHARGE
:
sa
-> x25_opts
.op_flags
|= X25_REVERSE_CHARGE
;
* Datapac specific: for a X.25(1976) DTE, bit 2
* indicates a "hi priority" (eg. international) call.
if (fcp
[1] & 02 && sa
-> x25_opts
.op_psize
== 0)
sa
-> x25_opts
.op_psize
= X25_PS128
;
/*printf("unknown facility %x, class=%d\n", *fcp, (*fcp & 0xc0) >> 6);*/
switch ((*fcp
& 0xc0) >> 6) {