* Copyright (c) University of British Columbia, 1984
* Copyright (c) 1991 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.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* from: @(#)pk_input.c 7.14 (Berkeley) 7/16/91
* $Id: pk_input.c,v 1.2 1993/10/16 19:46:50 rgrimes Exp $
register struct x25config
*xcp
= &ia
->ia_xc
;
register struct pkcb
*pkp
;
register struct pklcd
*lcp
;
register struct protosw
*pp
;
pp
= pffindproto (AF_CCITT
, (int)xcp
-> xc_lproto
, 0);
if (pp
== 0 || pp
-> pr_output
== 0) {
pk_message (0, xcp
, "link level protosw error");
return ((struct pkcb
*)0);
* Allocate a network control block structure
size
= sizeof (struct pkcb
);
pkp
= (struct pkcb
*)malloc(size
, M_PCB
, M_WAITOK
);
return ((struct pkcb
*)0);
bzero ((caddr_t
)pkp
, size
);
pkp
-> pk_lloutput
= pp
-> pr_output
;
pkp
-> pk_state
= DTE_WAITING
;
pkp
-> pk_next
= pkcbhead
;
pkp
-> pk_llnext
= llnext
;
if (xcp
-> xc_pwsize
== 0)
xcp
-> xc_pwsize
= DEFAULT_WINDOW_SIZE
;
if (xcp
-> xc_psize
== 0)
xcp
-> xc_psize
= X25_PS128
;
* Allocate logical channel descriptor vector
register struct pkcb
*pkp
;
struct pklcd
*dev_lcp
= 0;
struct x25config
*xcp
= pkp
-> pk_xcp
;
(pkp
-> pk_maxlcn
!= xcp
-> xc_maxlcn
)) {
pk_restart (pkp
, X25_RESTART_NETWORK_CONGESTION
);
dev_lcp
= pkp
-> pk_chan
[0];
free ((caddr_t
)pkp
-> pk_chan
, M_IFADDR
);
if (pkp
-> pk_chan
== 0) {
pkp
-> pk_maxlcn
= xcp
-> xc_maxlcn
;
size
= (pkp
-> pk_maxlcn
+ 1) * sizeof (struct pklcd
*);
(struct pklcd
**) malloc (size
, M_IFADDR
, M_WAITOK
);
bzero ((caddr_t
)pkp
-> pk_chan
, size
);
* Allocate a logical channel descriptor for lcn 0
(dev_lcp
= pk_attach ((struct socket
*)0)) == 0)
dev_lcp
-> lcd_state
= READY
;
dev_lcp
-> lcd_pkp
= pkp
;
pkp
-> pk_chan
[0] = dev_lcp
;
* 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 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",
struct mbuf
*pk_bad_packet
;
struct mbuf_cache pk_input_cache
= {0 };
* 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.
* We change the original conventions of the UBC code here --
* since there may be multiple pkcb's for 802.2 class 2
* for a given interface, we must be informed which one it is;
* so we overwrite the pkthdr.rcvif; it can be recovered if necessary.
register struct x25_packet
*xp
;
register struct pklcd
*lcp
;
register struct socket
*so
= 0;
register struct pkcb
*pkp
;
int ptype
, lcn
, lcdstate
= LISTEN
;
if (pk_input_cache
.mbc_size
|| pk_input_cache
.mbc_oldsize
)
mbuf_cache(&pk_input_cache
, m
);
if ((m
->m_flags
& M_PKTHDR
) == 0)
if ((pkp
= (struct pkcb
*)m
->m_pkthdr
.rcvif
) == 0)
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.
pk_incoming_call (pkp
, m
);
* 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
:
pk_call_accepted (lcp
, m
);
* 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
);
lcp
-> lcd_upper (lcp
, m
);
* 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
case CLEAR_CONF
+ LISTEN
:
* 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
;
pk_flowcontrol(lcp
, 0, 1);
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).
pk_flowcontrol(lcp
, 0, 1);
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
&& lcp
-> lcd_upper
&& lcdstate
== DATA_TRANSFER
) {
if (ptype
!= DATA
&& ptype
!= INTERRUPT
)
lcp
-> lcd_upper (lcp
, m
);
} else if (ptype
!= DATA
&& ptype
!= INTERRUPT
)
prune_dnic(from
, to
, dnicname
, xcp
)
char *from
, *to
, *dnicname
;
register struct x25config
*xcp
;
register char *cp1
= from
, *cp2
= from
;
if (xcp
->xc_prepnd0
&& *cp1
== '0') {
for (cp1
= dnicname
; *cp2
= *cp1
++;)
for (cp1
= dnicname
; *cp2
= *cp1
++;)
pk_simple_bsd (from
, to
, lower
, len
)
register octet
*from
, *to
;
c
&= 0x0f; c
|= 0x30; *to
++ = c
; lower
++;
pk_from_bcd (a
, iscalling
, sa
, xcp
)
register struct x25_calladdr
*a
;
register struct sockaddr_x25
*sa
;
register struct x25config
*xcp
;
bzero ((caddr_t
)sa
, sizeof (*sa
));
sa
-> x25_len
= sizeof (*sa
);
sa
-> x25_family
= AF_CCITT
;
cp
= a
-> address_field
+ (a
-> called_addrlen
/ 2);
count
= a
-> calling_addrlen
;
pk_simple_bsd (cp
, buf
, a
-> called_addrlen
, count
);
count
= a
-> called_addrlen
;
pk_simple_bsd (a
-> address_field
, buf
, 0, count
);
if (xcp
-> xc_addr
.x25_net
&& (xcp
-> xc_nodnic
|| xcp
->xc_prepnd0
)) {
octet dnicname
[sizeof(long) * NBBY
/3 + 2];
sprintf (dnicname
, "%d", xcp
-> xc_addr
.x25_net
);
prune_dnic (buf
, sa
-> x25_addr
, dnicname
, xcp
);
bcopy ((caddr_t
)buf
, (caddr_t
)sa
-> x25_addr
, count
+ 1);
if (m
= m_copym (m
, 0, (int)M_COPYALL
), M_DONTWAIT
) {
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
);
* 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.
pk_incoming_call (pkp
, m0
)
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
*);
struct x25config
*xcp
= pkp
-> pk_xcp
;
int len
= m0
->m_pkthdr
.len
;
char *errstr
= "server unavailable";
/* First, copy the data from the incoming call packet to a X25 address
descriptor. It is to be regretted that you have
to parse the facilities into a sockaddr to determine
if reverse charging is being requested */
if ((m
= m_get (M_DONTWAIT
, MT_SONAME
)) == 0)
sa
= mtod (m
, struct sockaddr_x25
*);
a
= (struct x25_calladdr
*) &xp
-> packet_data
;
facp
= u
= (octet
*) (a
-> address_field
+
((a
-> called_addrlen
+ a
-> calling_addrlen
+ 1) / 2));
udlen
= min (16, ((octet
*)xp
) + len
- u
);
pk_from_bcd (a
, 1, sa
, pkp
-> pk_xcp
); /* get calling address */
pk_parse_facilities (facp
, sa
);
bcopy ((caddr_t
)u
, sa
-> x25_udata
, udlen
);
* Now, loop through the listen sockets looking for a match on the
* PID. That is the first few octets of the user data field.
* This is the closest thing to a port number for X.25 packets.
* It does provide a way 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
, u
, sxp
->x25_udlen
))
sxp
-> x25_net
!= xcp
-> xc_addr
.x25_net
)
* 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";
* 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
&
~X25_REVERSE_CHARGE
) | l
-> lcd_flags
;
lcp
-> lcd_laddr
.x25_udlen
= sxp
-> x25_udlen
;
lcp
-> lcd_craddr
= &lcp
->lcd_faddr
;
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
, m0
);
* 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
;
pk_call_accepted (lcp
, m
)
register struct x25_calladdr
*ap
;
struct x25_packet
*xp
= mtod (m
, struct x25_packet
*);
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
)
pk_parse_facilities (fcp
, lcp
-> lcd_ceaddr
);
pk_assoc (lcp
-> lcd_pkp
, lcp
, lcp
-> lcd_ceaddr
);
if (lcp
-> lcd_so
== 0 && lcp
-> lcd_upper
)
lcp
-> lcd_upper(lcp
, m
);
pk_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) {