* 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_subr.c 7.6 (Berkeley) %G%
int pk_sendspace
= 1024 * 2 + 8;
int pk_recvspace
= 1024 * 2 + 8;
struct x25_packet
*pk_template ();
* Attach X.25 protocol to socket, allocate logical channel descripter
* and buffer space, and enter LISTEN state if we are to accept
* IN-COMMING CALL packets.
register struct pklcd
*lcp
;
register int error
= ENOBUFS
;
MALLOC(lcp
, struct pklcd
*, sizeof(*lcp
), M_PCB
, M_NOWAIT
);
bzero((caddr_t
)lcp
, sizeof(*lcp
));
error
= soreserve (so
, pk_sendspace
, pk_recvspace
);
if (so
-> so_options
& SO_ACCEPTCONN
)
lcp
-> lcd_state
= LISTEN
;
lcp
-> lcd_state
= READY
;
sbreserve (&lcp
-> lcd_sb
, pk_sendspace
);
so
-> so_pcb
= (caddr_t
) lcp
;
* Disconnect X.25 protocol from socket.
register struct pklcd
*lcp
;
register struct socket
*so
= lcp
-> lcd_so
;
register struct pklcd
*l
, *p
;
switch (lcp
-> lcd_state
) {
for (p
= 0, l
= pk_listenhead
; l
&& l
!= lcp
; p
= l
, l
= l
-> lcd_listen
);
pk_listenhead
= l
-> lcd_listen
;
p
-> lcd_listen
= l
-> lcd_listen
;
* Close an X.25 Logical Channel. Discard all space held by the
* connection and internal descriptors. Wake up any sleepers.
register struct socket
*so
= lcp
-> lcd_so
;
sofree (so
); /* gak!!! you can't do that here */
* Create a template to be used to send X.25 packets on a logical
* channel. It allocates an mbuf and fills in a skeletal packet
* depending on its type. This packet is passed to pk_output where
* the remainer of the packet is filled in.
register struct x25_packet
*xp
;
MGET (m
, M_DONTWAIT
, MT_HEADER
);
* Efficiency hack: leave a four byte gap at the beginning
* of the packet level header with the hope that this will
* be enough room for the link level to insert its header.
xp
= mtod (m
, struct x25_packet
*);
*(long *)xp
= 0; /* ugly, but fast */
xp
-> fmt_identifier
= 1;
/* xp -> lc_group_number = 0;*/
xp
-> logical_channel_number
= lcn
;
xp
-> packet_type
= type
;
* This routine restarts all the virtual circuits. Actually,
* the virtual circuits are not "restarted" as such. Instead,
* any active switched circuit is simply returned to READY
pk_restart (pkp
, restart_cause
)
register struct pkcb
*pkp
;
register struct x25_packet
*xp
;
register struct pklcd
*lcp
;
/* Restart all logical channels. */
for (i
= 1; i
<= pkp
->pk_maxlcn
; ++i
)
if ((lcp
= pkp
->pk_chan
[i
]) != NULL
) {
lcp
->lcd_so
-> so_error
= ENETRESET
;
pkp
->pk_state
= DTE_SENT_RESTART
;
xp
= lcp
-> lcd_template
= pk_template (lcp
-> lcd_lcn
, X25_RESTART
);
xp
-> packet_data
= 0; /* DTE only */
* This procedure frees up the Logical Channel Descripter.
register struct pklcd
*lcp
;
m_freem (dtom (lcp
-> lcd_template
));
lcp
-> lcd_pkp
-> pk_chan
[lcp
-> lcd_lcn
] = NULL
;
free((caddr_t
)lcp
, M_PCB
);
* Bind a address and protocol value to a socket. The important
* part is the protocol value - the first four characters of the
register struct pkcb
*pkp
;
register struct pklcd
*pp
;
register struct sockaddr_x25
*sa
;
if (lcp
-> lcd_ceaddr
) /* XXX */
sa
= mtod (nam
, struct sockaddr_x25
*);
* If the user wishes to accept calls only from a particular
* net (net != 0), make sure the net is known
for (pkp
= pkcbhead
; ; pkp
= pkp
-> pk_next
) {
if (pkp
-> pk_xcp
-> xc_addr
.x25_net
== sa
-> x25_net
)
for (pp
= pk_listenhead
; pp
; pp
= pp
-> lcd_listen
)
if (bcmp (pp
-> lcd_ceaddr
-> x25_udata
, sa
-> x25_udata
,
min (pp
->lcd_ceaddr
->x25_udlen
, sa
->x25_udlen
)) == 0)
lcp
-> lcd_ceaddr
= &lcp
-> lcd_laddr
;
* Associate a logical channel descriptor with a network.
* Fill in the default network specific parameters and then
* set any parameters explicitly specified by the user or
register struct pkcb
*pkp
;
register struct pklcd
*lcp
;
register struct sockaddr_x25
*sa
;
lcp
-> lcd_packetsize
= pkp
-> pk_xcp
-> xc_psize
;
lcp
-> lcd_windowsize
= pkp
-> pk_xcp
-> xc_pwsize
;
lcp
-> lcd_rsn
= MODULUS
- 1;
pkp
-> pk_chan
[lcp
-> lcd_lcn
] = lcp
;
if (sa
-> x25_opts
.op_psize
)
lcp
-> lcd_packetsize
= sa
-> x25_opts
.op_psize
;
sa
-> x25_opts
.op_psize
= lcp
-> lcd_packetsize
;
if (sa
-> x25_opts
.op_wsize
)
lcp
-> lcd_windowsize
= sa
-> x25_opts
.op_wsize
;
sa
-> x25_opts
.op_wsize
= lcp
-> lcd_windowsize
;
sa
-> x25_net
= pkp
-> pk_xcp
-> xc_addr
.x25_net
;
lcp
-> lcd_flags
= sa
-> x25_opts
.op_flags
;
lcp
-> lcd_stime
= time
.tv_sec
;
pk_connect (lcp
, nam
, sa
)
register struct pklcd
*lcp
;
register struct sockaddr_x25
*sa
;
register struct pkcb
*pkp
;
register struct ifnet
*ifp
;
sa
= mtod (nam
, struct sockaddr_x25
*);
if (sa
-> x25_addr
[0] == '\0')
for (pkp
= pkcbhead
; ; pkp
= pkp
->pk_next
) {
* use first net configured (last in list
* headed by pkcbhead) if net is zero
if (sa
-> x25_net
== 0 && pkp
-> pk_next
== 0)
if (sa
-> x25_net
== pkp
-> pk_xcp
-> xc_addr
.x25_net
)
if (pkp
-> pk_state
!= DTE_READY
)
if ((lcp
-> lcd_lcn
= pk_getlcn (pkp
)) == 0)
lcp
-> lcd_ceaddr
= & lcp
->lcd_faddr
;
pk_assoc (pkp
, lcp
, lcp
-> lcd_ceaddr
);
soisconnecting (lcp
-> lcd_so
);
lcp
-> lcd_template
= pk_template (lcp
-> lcd_lcn
, X25_CALL
);
pk_callrequest (lcp
, lcp
-> lcd_ceaddr
, pkp
-> pk_xcp
);
return (*pkp
-> pk_start
)(lcp
);
* Build the rest of the CALL REQUEST packet. Fill in calling
* address, facilities fields and the user data field.
pk_callrequest (lcp
, sa
, xcp
)
register struct sockaddr_x25
*sa
;
register struct x25config
*xcp
;
register struct x25_calladdr
*a
;
register struct mbuf
*m
= dtom (lcp
-> lcd_template
);
a
= (struct x25_calladdr
*) &lcp
-> lcd_template
-> packet_data
;
a
-> calling_addrlen
= strlen (xcp
-> xc_addr
.x25_addr
);
a
-> called_addrlen
= strlen (sa
-> x25_addr
);
cp
= (octet
*) a
-> address_field
;
to_bcd (&cp
, (int)a
-> called_addrlen
, sa
-> x25_addr
, &posn
);
to_bcd (&cp
, (int)a
-> calling_addrlen
, xcp
-> xc_addr
.x25_addr
, &posn
);
build_facilities (&cp
, sa
, (int)xcp
-> xc_type
);
bcopy (sa
-> x25_udata
, (caddr_t
)cp
, (unsigned)sa
-> x25_udlen
);
m
-> m_len
+= cp
- (octet
*) a
;
for (cp
= mtod (m
, octet
*), posn
= 0; posn
< m
->m_len
; ++posn
)
build_facilities (cp
, sa
, type
)
revcharge
= sa
-> x25_opts
.op_flags
& X25_REVERSE_CHARGE
? 1 : 0;
* This is specific to Datapac X.25(1976) DTEs. International
* calls must have the "hi priority" bit on.
if (type
== X25_1976
&& sa
-> x25_opts
.op_psize
== X25_PS128
)
*fcp
++ = FACILITIES_REVERSE_CHARGE
;
*fcp
++ = FACILITIES_PACKETSIZE
;
*fcp
++ = sa
-> x25_opts
.op_psize
;
*fcp
++ = sa
-> x25_opts
.op_psize
;
*fcp
++ = FACILITIES_WINDOWSIZE
;
*fcp
++ = sa
-> x25_opts
.op_wsize
;
*fcp
++ = sa
-> x25_opts
.op_wsize
;
* This routine gets the first available logical channel number. The
* search is from the highest number to lowest number (DTE).
register struct pkcb
*pkp
;
for (i
= pkp
-> pk_maxlcn
; i
> 0; --i
)
if (pkp
-> pk_chan
[i
] == NULL
)
register struct sockaddr_x25
*sa
= mtod (m
, struct sockaddr_x25
*);
if (m
-> m_len
!= sizeof (struct sockaddr_x25
))
if (sa
-> x25_family
!= AF_CCITT
|| sa
-> x25_udlen
== 0 ||
sa
-> x25_udlen
> sizeof (sa
-> x25_udata
))
for (cp
= sa
-> x25_addr
; *cp
; cp
++) {
if (*cp
< '0' || *cp
> '9' ||
cp
>= &sa
-> x25_addr
[sizeof (sa
-> x25_addr
) - 1])
* This procedure sends a CLEAR request packet. The lc state is
register struct x25_packet
*xp
;
xp
= lcp
-> lcd_template
= pk_template (lcp
-> lcd_lcn
, X25_CLEAR
);
* This procedure sends a RESET request packet. It re-intializes
register struct pklcd
*lcp
;
register struct x25_packet
*xp
;
register struct socket
*so
;
if (lcp
-> lcd_state
!= DATA_TRANSFER
)
lcp
-> lcd_reset_condition
= TRUE
;
/* Reset all the control variables for the channel. */
lcp
-> lcd_window_condition
= lcp
-> lcd_rnr_condition
=
lcp
-> lcd_intrconf_pending
= FALSE
;
lcp
-> lcd_rsn
= MODULUS
- 1;
lcp
-> lcd_output_window
= lcp
-> lcd_input_window
=
lcp
-> lcd_last_transmitted_pr
= 0;
if (so
= lcp
-> lcd_so
) {
so
-> so_error
= ECONNRESET
;
xp
= lcp
-> lcd_template
= pk_template (lcp
-> lcd_lcn
, X25_RESET
);
(dtom (xp
)) -> m_len
+= 2;
* This procedure handles all local protocol procedure errors.
pk_procerror (error
, lcp
, errstr
)
register struct pklcd
*lcp
;
pk_message (lcp
-> lcd_lcn
, lcp
-> lcd_pkp
-> pk_xcp
, errstr
);
lcp
->lcd_so
-> so_error
= ECONNABORTED
;
soisdisconnecting (lcp
->lcd_so
);
* This procedure is called during the DATA TRANSFER state to check
* and process the P(R) values received in the DATA, RR OR RNR
register struct socket
*so
= lcp
-> lcd_so
;
if (lcp
-> lcd_output_window
== pr
)
if (lcp
-> lcd_output_window
< lcp
-> lcd_ssn
) {
if (pr
< lcp
-> lcd_output_window
|| pr
> lcp
-> lcd_ssn
) {
pk_procerror (RESET
, lcp
, "p(r) flow control error");
if (pr
< lcp
-> lcd_output_window
&& pr
> lcp
-> lcd_ssn
) {
pk_procerror (RESET
, lcp
, "p(r) flow control error");
lcp
-> lcd_output_window
= pr
; /* Rotate window. */
if (lcp
-> lcd_window_condition
== TRUE
)
lcp
-> lcd_window_condition
= FALSE
;
if (so
&& ((so
-> so_snd
.sb_flags
& SB_WAIT
) || so
-> so_snd
.sb_sel
))
(*lcp
-> lcd_upper
)(lcp
, 0);
* This procedure decodes the X.25 level 3 packet returning a
* code to be used in switchs or arrays.
register struct x25_packet
*xp
;
if (xp
-> fmt_identifier
!= 1)
* Make sure that the logical channel group number is 0.
* This restriction may be removed at some later date.
if (xp
-> lc_group_number
!= 0)
* Test for data packet first.
if (!(xp
-> packet_type
& DATA_PACKET_DESIGNATOR
))
* Test if flow control packet (RR or RNR).
if (!(xp
-> packet_type
& RR_OR_RNR_PACKET_DESIGNATOR
))
if (!(xp
-> packet_type
& RR_PACKET_DESIGNATOR
))
* Determine the rest of the packet types.
switch (xp
-> packet_type
) {
case X25_INTERRUPT_CONFIRM
:
case X25_RESTART_CONFIRM
:
* A restart packet has been received. Print out the reason
pk_restartcause (pkp
, xp
)
register struct x25_packet
*xp
;
register struct x25config
*xcp
= pkp
-> pk_xcp
;
register int lcn
= xp
-> logical_channel_number
;
switch (xp
-> packet_data
) {
case X25_RESTART_LOCAL_PROCEDURE_ERROR
:
pk_message (lcn
, xcp
, "restart: local procedure error");
case X25_RESTART_NETWORK_CONGESTION
:
pk_message (lcn
, xcp
, "restart: network congestion");
case X25_RESTART_NETWORK_OPERATIONAL
:
pk_message (lcn
, xcp
, "restart: network operational");
pk_message (lcn
, xcp
, "restart: unknown cause");
EXRESET
, EXROUT
, 0, EXRRPE
, 0, EXRLPE
, 0, EXRNCG
* A reset packet has arrived. Return the cause to the user.
register struct x25_packet
*xp
;
register struct pklcd
*lcp
= pkp
->pk_chan
[xp
-> logical_channel_number
];
register int code
= xp
-> packet_data
;
if (code
> MAXRESETCAUSE
)
lcp
->lcd_so
-> so_error
= Reset_cause
[code
];
EXCLEAR
, EXCBUSY
, 0, EXCINV
, 0, EXCNCG
, 0,
0, 0, EXCOUT
, 0, EXCAB
, 0, EXCNOB
, 0, 0, 0, EXCRPE
,
0, EXCLPE
, 0, 0, 0, 0, 0, EXCRRC
* A clear packet has arrived. Return the cause to the user.
register struct x25_packet
*xp
;
register struct pklcd
*lcp
= pkp
->pk_chan
[xp
-> logical_channel_number
];
register int code
= xp
-> packet_data
;
if (code
> MAXCLEARCAUSE
)
lcp
->lcd_so
-> so_error
= Clear_cause
[code
];
register struct x25config
*xcp
;
return (xcp
-> xc_addr
.x25_addr
);
pk_message (lcn
, xcp
, fmt
, a1
, a2
, a3
, a4
, a5
, a6
)
printf ("X.25(%s): lcn %d: ", format_ntn (xcp
), lcn
);
printf ("X.25: lcn %d: ", lcn
);
printf ("X.25(%s): ", format_ntn (xcp
));
printf (fmt
, a1
, a2
, a3
, a4
, a5
, a6
);