* 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.8 (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 x25config
*xcp
;
register struct pkcb
*pkp
;
for (pkp
= pkcbhead
; pkp
; pkp
= pkp
-> pk_next
)
if (pkp
-> pk_xcp
== xcp
)
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 routine is called if there are semi-smart devices that do HDLC
* in hardware and want to queue the packet and call level 3 directly
register struct ifaddr
*ifa
;
register struct ifnet
*ifp
;
IF_DEQUEUE (&pkintrq
, m
);
if (m
->m_len
< PKHEADERLN
) {
printf ("pkintr: packet too short (len=%d)\n",
if ((m
->m_flags
& M_PKTHDR
) == 0)
* look up the appropriate control block
for (ifa
= ifp
->if_addrlist
; ifa
; ifa
= ifa
->ifa_next
)
if (ifa
->ifa_addr
->sa_family
== AF_CCITT
)
pk_input(m
, ((struct x25_ifaddr
*)ifa
)->ia_xcp
);
struct mbuf
*pk_bad_packet
;
* 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
*);
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
, m
, "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 (%d, %s) on lcn 0",
ptype
, pk_name
[ptype
/ MAXSTATES
]);
switch (ptype
+ lcdstate
) {
* Incoming Call packet received.
* Call collision: Just throw this "incoming call" away since
* the DCE will ignore it anyway.
pk_message ((int)lcn
, 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", 1);
if (pk_ack (lcp
, PR(xp
)) != PACKET_OK
) {
m
-> m_data
+= PKHEADERLN
;
m
-> m_len
-= PKHEADERLN
;
m
-> m_pkthdr
.len
-= PKHEADERLN
;
if (lcp
-> lcd_flags
& X25_MBS_HOLD
) {
register struct mbuf
*n
= lcp
-> lcd_cps
;
n
-> m_pkthdr
.len
+= m
-> m_pkthdr
.len
;
n
-> m_pkthdr
.len
> lcp
-> lcd_cpsmax
) {
pk_procerror (RESET
, lcp
,
q_and_d_bits
= 0xc0 & *(octet
*)xp
;
xp
= (struct x25_packet
*)
(mtod(m
, octet
*) - PKHEADERLN
);
*(octet
*)xp
|= q_and_d_bits
;
if (lcp
-> lcd_flags
& X25_MQBIT
) {
octet t
= (xp
-> q_bit
) ? t
= 0x80 : 0;
* 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
);
* 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
);
m
-> m_data
+= PKHEADERLN
;
m
-> m_len
-= PKHEADERLN
;
m
-> m_pkthdr
.len
-= PKHEADERLN
;
if (so
-> so_options
& SO_OOBINLINE
)
sbinsertoob (&so
-> so_rcv
, m
);
* 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", 43);
* Receiver ready received. Rotate the output window and output
* any data packets waiting transmission.
if (lcp
-> lcd_reset_condition
||
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
||
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. */
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
);
wakeup ((caddr_t
) & so
-> so_timeo
);
* Reset confirmation received.
case RESET_CONF
+ DATA_TRANSFER
:
if (lcp
-> lcd_reset_condition
) {
lcp
-> lcd_reset_condition
= FALSE
;
pk_procerror (RESET
, lcp
, "unexpected packet", 32);
case INTERRUPT
+ SENT_CLEAR
:
case INTERRUPT_CONF
+ SENT_CLEAR
:
case RESET_CONF
+ SENT_CLEAR
:
/* Just ignore p 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", 33);
pk_message (lcn
, pkp
-> pk_xcp
,
"\"%s\" unexpected in \"%s\" state",
pk_name
[ptype
/MAXSTATES
], pk_state
[lcdstate
]);
pk_message (lcn
, pkp
-> pk_xcp
,
"packet arrived on unassigned lcn");
if (so
== 0 && lcp
-> lcd_upper
&&
(lcdstate
== SENT_CALL
|| lcdstate
== DATA_TRANSFER
)) {
if (ptype
!= DATA
&& ptype
!= INTERRUPT
)
lcp
-> lcd_upper (lcp
, m
);
} else if (ptype
!= DATA
&& ptype
!= INTERRUPT
)
* 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.
register struct pklcd
*lcp
= 0, *l
;
register struct sockaddr_x25
*sa
;
register struct x25_calladdr
*a
;
register struct socket
*so
= 0;
struct x25_packet
*xp
= mtod(m0
, struct x25_packet
*);
int len
= m0
->m_pkthdr
.len
;
char *e
, *errstr
= "server unavailable";
/* 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_SONAME
)) == 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";
* don't accept incoming calls with the D-Bit on
* unless the server agrees
if (xp
-> d_bit
&& !(sxp
-> x25_opts
.op_flags
& X25_DBIT
)) {
errstr
= "incoming D-Bit mismatch";
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
);
if (lcp
-> lcd_flags
& X25_DBIT
) {
mtod(lcp
-> lcd_template
,
struct x25_packet
*) -> d_bit
= 1;
lcp
-> lcd_flags
&= ~X25_DBIT
;
if (so
-> so_options
& SO_OOBINLINE
)
save_extra(m0
, facp
, so
);
} else if (lcp
-> lcd_upper
) {
(*lcp
-> lcd_upper
) (lcp
, m
);
(void) m_free (m
); /* only m; m0 freed by caller */
* 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 ((lcp
= pk_attach((struct socket
*)0)) == 0) {
lcp
-> lcd_state
= RECEIVED_CALL
;
if (m
= m_copym (m
, 0, (int)M_COPYALL
)) {
int off
= fp
- mtod (m0
, octet
*);
int len
= m
->m_pkthdr
.len
- off
+ sizeof (cmsghdr
);
cmsghdr
.cmsg_level
= AF_CCITT
;
cmsghdr
.cmsg_type
= PK_FACILITIES
;
M_PREPEND (m
, sizeof(cmsghdr
), M_DONTWAIT
);
bcopy ((caddr_t
)&cmsghdr
, mtod (m
, caddr_t
), sizeof (cmsghdr
));
sbappendrecord(&so
-> so_rcv
, m
);
call_accepted (lcp
, xp
, len
)
register struct x25_calladdr
*ap
;
lcp
-> lcd_state
= DATA_TRANSFER
;
soisconnected (lcp
-> lcd_so
);
if ((lcp
-> lcd_flags
& X25_DBIT
) && (xp
-> d_bit
== 0))
lcp
-> lcd_flags
&= ~X25_DBIT
;
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) {