/***********************************************************
Copyright IBM Corporation 1987
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of IBM not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
******************************************************************/
* ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
* $Header: if_cons.c,v 4.7 88/08/11 15:52:55 nhall Exp $
* $Source: /usr/argo/sys/netiso/RCS/if_cons.c,v $
* cons.c - Connection Oriented Network Service:
* including support for a) user transport-level service,
* b) COSNS below CLNP, and c) CONS below TP.
static char *rcsid
= "$Header: if_cons.c,v 4.7 88/08/11 15:52:55 nhall Exp $";
#include "argoxtwentyfive.h"
#include "../net/netisr.h"
#include "../net/route.h"
#include "../netiso/iso_errno.h"
#include "../netiso/argo_debug.h"
#include "../netiso/tp_trace.h"
#include "../netiso/iso.h"
#include "../netiso/cons.h"
#include "../netiso/iso_pcb.h"
#include "../netiso/cons_pcb.h"
#include "../caif/eicon.h"
#define MT_XCLOSE MT_DATA
#define MT_XCONFIRM MT_DATA
#define MT_XHEADER MT_HEADER
/*********************************************************************
* cons.c - CONS interface to the eicon adapter
* Includes connection manager - for (TP, CLNP)/x.25
* TODO: figure out what resources we might run out of besides mbufs.
* If we run out of any of them (including mbufs) close and recycle
* lru x% of the connections, for some parameter x.
* There are 4 interfaces from above:
* cons is an interface driver - CLNP calls
* cosns_output(ifp, m, dst), a device-type interface output routine
* that does some connection management stuff and queues a
* request on the eicon driver queue by calling ifp->if_output.
* The eicon's ifp structure contains cosns_output as its output routine
* rather than ifp_>if_output! Kludge, but we don't have much choice...
* X25 connections created in this manner may always be multiplexed
* but only with their own kind (not with connections servicing TP
* cons CO network service
* TP associates a transport connection with a network connection.
* cons_output( isop, m, len, isdgm==0 )
* It's a datagram service, like clnp is. - even though it calls
* cons_output( isop, m, len, isdgm==1 )
* it eventually goes through
* cosns_output(ifp, m, dst).
* TP4 permits multiplexing (reuse, possibly simultaneously) of the
* This means that many sockets (many tpcbs) may be associated with
* this cons_pcb, hence cannot have a back ptr from cons_pcb to a tpcb.
* co_socket is null since there may be many sockets that use this copcb.
* 3) from user: cons_usrreq(), cons_ctloutput()
* cons is a standard transport service interface.
* There is a 1-1 correspondence between net connections and sockets.
* co_socket points to a socket.
streams would really be nice. sigh.
eicon <--> cons interface: the first mbuf (the ecn_request structure)
had better NOT be a cluster.
PVCs could be handled by config-ing a cons with an address and with the
IFF_POINTTOPOINT flag on. This code would then have to skip the
connection setup stuff for pt-to-pt links.
We keep track of the ifp for each connection. Right now this is
unnecessary, but just in case someone comes up with some kind
of a kludge to allow > 1 eicon to be attached at a time,
(i.e., some meaningful netof( a type 37 address ) ),
we do keep track of this.
*********************************************************************/
#define touch(copcb) copcb->co_ttl = copcb->co_init_ttl
#define SET_CHANMASK( isop, chan )\
if( (u_int)(chan) < 32 ) \
(isop)->isop_chanmask = (1<<((chan)-1));\
(isop)->isop_negchanmask = (1<<((256-(chan))-1))
#define ADD_CHANMASK( isop, chan )\
if( (u_int)(chan) < 32 ) \
(isop)->isop_chanmask |= (1<<((chan)-1));\
(isop)->isop_negchanmask |= (1<<((256-(chan))-1))
struct ifnet
*consif
; /* TO BE REMOVED */
Static
int consinit(), consioctl(), consattach();
/* protosw pointers for getting to higher layer */
Static
struct protosw
*CLNP_proto
;
Static
struct protosw
*TP_proto
;
Static
struct protosw
*X25_proto
;
Static
int issue_clear_req();
extern struct ifaddr
*ifa_ifwithnet();
extern struct ifaddr
*ifa_ifwithaddr();
Static
struct socket dummysocket
; /* for use by cosns */
extern struct isopcb tp_isopcb
; /* chain of all TP pcbs */
struct isopcb cons_isopcb
; /* chain of all cons pcbs */
struct isopcb tp_incoming_pending
; /* incoming connections
struct isopcb
*Xpcblist
[] = {
Static
int parse_facil(), NSAPtoDTE(), make_partial_x25_packet();
Static
int FACILtoNSAP(), DTEtoNSAP();
Static
struct cons_pcb
*cons_chan_to_pcb();
* FUNCTION and ARGUMENTS:
* copies (len) nibbles from (src_octet), high or low nibble
* to (dst_octet), high or low nibble,
* src_nibble & dst_nibble should be:
* HIGH_NIBBLE (1) if leftmost 4 bits/ most significant nibble
* LOW_NIBBLE (0) if rightmost 4 bits/ least significant nibble
nibble_copy( src_octet
, src_nibble
, dst_octet
, dst_nibble
, len
)
register char *src_octet
;
register char *dst_octet
;
register unsigned src_nibble
;
register unsigned dst_nibble
;
register unsigned dshift
, sshift
;
printf("nibble_copy ( 0x%x, 0x%x, 0x%x, 0x%x 0x%x)\n",
src_octet
, src_nibble
, dst_octet
, dst_nibble
, len
);
dshift
= dst_nibble
<< 2;
sshift
= src_nibble
<< 2;
*dst_octet
&= ~(0xf<< dshift
);
*dst_octet
|= ( 0xf & (*src_octet
>> sshift
))<< dshift
;
src_nibble
= 1-src_nibble
;
dst_nibble
= 1-dst_nibble
;
printf("nibble_copy DONE\n");
* FUNCTION and ARGUMENTS:
* compares src_octet/src_nibble and dst_octet/dst_nibble for len nibbles.
* RETURNS: 0 if they differ, 1 if they are the same.
nibble_match( src_octet
, src_nibble
, dst_octet
, dst_nibble
, len
)
register char *src_octet
;
register char *dst_octet
;
register unsigned src_nibble
;
register unsigned dst_nibble
;
register unsigned dshift
, sshift
;
u_char nibble_a
, nibble_b
;
printf("nibble_match ( 0x%x, 0x%x, 0x%x, 0x%x 0x%x)\n",
src_octet
, src_nibble
, dst_octet
, dst_nibble
, len
);
dshift
= dst_nibble
<< 2;
sshift
= src_nibble
<< 2;
nibble_b
= ((*dst_octet
)>>dshift
) & 0xf;
nibble_a
= ( 0xf & (*src_octet
>> sshift
));
if( nibble_b
!= nibble_a
)
src_nibble
= 1-src_nibble
;
dst_nibble
= 1-dst_nibble
;
printf("nibble_match DONE\n");
register struct cons_pcb
*copcb
;
printf("XPCB DUMP %s\n", str
);
printf("\t copcb 0x%x next 0x%x head 0x%x socket 0x%x ifp 0x%x\n",
copcb
, copcb
->co_next
, copcb
->co_head
, copcb
->co_socket
, copcb
->co_ifp
);
printf("\t channel 0x%x state 0x%x flags 0x%x proto 0x%x\n",
copcb
->co_channel
, copcb
->co_state
, copcb
->co_flags
, copcb
->co_proto
);
dump_isoaddr(&copcb
->co_laddr
);
dump_isoaddr(&copcb
->co_faddr
);
printf("\tttl 0x%x init_ttl 0x%x pending: %d\n",
copcb
->co_ttl
, copcb
->co_init_ttl
, copcb
->co_pending
.ifq_len
);
* FUNCTION : choose_output - chooses between the eicon and loopback.
* This MUST be here because the ifp->if_output routine is cosns_output
* -- due to our need to look like a device driver for CLNP. sigh.
* ARGUMENTS & PURPOSE: (copcb) ptr to a protocol control block for
* x.25, (m) is an mbuf ptr. *m is a request destined either
* for the eicon driver or for the loopback driver.
* RETURNS : whatever error value the 2I or loopback returns.
choose_output( ifp
, m
, loop
)
error
= lpboutput( ifp
, m
);
error
= ecnoutput( ifp
, m
);
"choose_output: ifp m error loop\n",
printf("choose_output returns 0x%x\n", error
);
**************************** NET PROTOCOL cons ***************************
* initialize the protocol
/* protocol init stuff */
consintrq
.ifq_maxlen
= IFQ_MAXLEN
;
consintrq
.ifq_head
= consintrq
.ifq_tail
= (struct mbuf
*)0;
CLNP_proto
= pffindproto(AF_ISO
, ISOPROTO_CLNP
, SOCK_DGRAM
);
X25_proto
= pffindproto(AF_ISO
, ISOPROTO_X25
, SOCK_STREAM
);
TP_proto
= pffindproto(AF_ISO
, ISOPROTO_TP0
, SOCK_SEQPACKET
);
printf("cons_init end : cnlp_proto 0x%x cons proto 0x%x tp proto 0x%x\n",
CLNP_proto
, X25_proto
, TP_proto
);
cons_isopcb
.isop_next
= cons_isopcb
.isop_prev
= &cons_isopcb
;
tp_incoming_pending
.isop_next
= tp_incoming_pending
.isop_prev
=
* wherever we run out of mbufs (not used right yet)
* get rid of the num least recently used connections and
* NOTE: GROTESQUELY INEFFICIENT needs to be written nicely
register struct cons_pcb
**copcblist
= (struct cons_pcb
**)Xpcblist
;
register struct cons_pcb
*copcb
;
printf("cons_free_lru( 0x%x )\n", qty
);
while (qty
> 1) { /* GROT */
for( copcb
= *copcblist
; copcb
; copcb
= *(++copcblist
) ) {
copcb
= (struct cons_pcb
*)copcb
->co_next
;
while (copcb
!= *copcblist
) {
if( copcb
->co_ttl
< lru
->co_ttl
)
copcb
= (struct cons_pcb
*)copcb
->co_next
;
soisdisconnected(lru
->co_socket
);
sohasoutofband(lru
->co_socket
); /* signal */
cons_clear_and_detach( lru
, E_CO_HLI_RESYNC
, PRC_TIMXCEED_REASS
);
* get rid of any timed-out cons connections
* cons connections get "touched" with every use, meaning the
* time-to-live gets reset to its max value w/ every use.
* The slowtimo() rtn decrements the time-to-live for each
* cons connection. If one of them hits zero ---> zap the connection.
* This really only applies to those used for CLNP and TP4.
* TP4 keeps the connections open with keepalive.
* Have this happen ONLY for international connections since
* there's no connect time charge for domestic calls.
* Make default 5 min; make a user option to change it.
* Maybe if the ttl gets lower than a certain threshold, move this
* copcb to the END of its queue so it doesn't slow down the others.
register struct cons_pcb
**copcblist
= (struct cons_pcb
**)Xpcblist
;
register struct cons_pcb
*copcb
;
for( copcb
= *copcblist
; copcb
; copcb
= *(++copcblist
) ) {
if( copcb
== (struct cons_pcb
*)0 ) {
panic("TURNING OFF cons_slowtimo()!!! \n");
copcb
= (struct cons_pcb
*)copcb
->co_next
;
while (copcb
!= *copcblist
) {
printf("cons PANIC: slowtimo LOOP\n");
if( copcb
->co_init_ttl
== 0 ) {
ASSERT( (struct isopcb
*)(*copcblist
)==(struct isopcb
*)&tp_isopcb
);
copcb
= (struct cons_pcb
*)copcb
->co_next
;
ASSERT( copcb
!= (struct cons_pcb
*)0 );
qlen
+= copcb
->co_pending
.ifq_len
;
qdrops
+= copcb
->co_pending
.ifq_drops
;
/* don't want XTS, TP0 connections to be subject to time out */
copcb
= (struct cons_pcb
*)copcb
->co_next
;
if( -- (copcb
->co_ttl
) > 0 ) {
copcb
= (struct cons_pcb
*)copcb
->co_next
;
printf("TIMING OUT chan 0x%x copcb 0x%x flags 0x%x\n",
copcb
->co_channel
, copcb
, copcb
->co_flags
);
register struct cons_pcb
* next
=
(struct cons_pcb
*)copcb
->co_next
;
cons_clear_and_detach(copcb
,
E_CO_HLI_RESYNC
, PRC_TIMXCEED_REASS
);
cons_stat
.co_avg_qlen
= qlen
/ nvisited
;
cons_stat
.co_avg_qdrop
= qdrops
/ nvisited
;
cons_stat
.co_active
= nvisited
;
register struct cons_pcb
*copcb
;
register struct cons_pcb
**copcblist
= (struct cons_pcb
**)Xpcblist
;
for( copcb
= *copcblist
; copcb
; copcb
= *(++copcblist
) ) {
printf("FOR %d: 0x%x ", ++i
, copcb
);
copcb
= (struct cons_pcb
*)copcb
->co_next
;
printf(" next 0x%x, *copcblist 0x%x\n", copcb
, *copcblist
);
while (copcb
!= *copcblist
) {
ASSERT( copcb
!= (struct cons_pcb
*)0 );
printf("\tCOPCB 0x%x\n", copcb
);
dump_buf(copcb
, sizeof( *copcb
));
copcb
= (struct cons_pcb
*)copcb
->co_next
;
* cons_usrreq() when doing PRU_ATTACH,
* cons_incoming() when opening a new connection.
* FUNCTION and ARGUMENTS:
* The flags and proto arguments are stashed into the new pcb.
cons_pcballoc(so
, head
, flags
, proto
, dest
)
register struct cons_pcb
*copcb
;
printf("cons_pcballoc (0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
so
, head
, flags
, proto
, dest
);
if(proto
== (struct protosw
*)0)
if( ( error
= iso_pcballoc(so
, head
) ) == EOK
) {
/* Have allocated a cleared mbuf */
copcb
= (struct cons_pcb
*)so
->so_pcb
;
copcb
->co_ttl
= copcb
->co_init_ttl
= X25_TTL
;
copcb
->co_pending
.ifq_maxlen
= CONS_IFQMAXLEN
;
copcb
->co_myself
= copcb
;
copcb
->co_socket
= (struct socket
*)0;
printf("cons_pcballoc returns 0x%x: DUMP\n", copcb
);
dump_buf( copcb
, sizeof(*copcb
));
if( (flags
& CONSF_ICRE
) == 0) {
struct dte_addr
*dtea
= &(*dest
)->co_peer_dte
;
error
= iso_8208snparesolve(&(*dest
)->co_faddr
, dtea
, &len
);
ASSERT(len
== sizeof(struct dte_addr
));
* cons_usrreq() when opening a new connection.
* FUNCTION anD ARGUMENTS:
* Figures out which device to use, finding a route if one doesn't
* Builds an eicon connection request and gives it to the device.
register struct cons_pcb
*copcb
;
register struct eicon_request
*ecnrq
;
printf("cons_connect( 0x%x ) : ifp 0x%x\npeer: ", copcb
, copcb
->co_ifp
);
dump_isoaddr(&copcb
->co_faddr
);
dump_isoaddr(&copcb
->co_laddr
);
/* PHASE 2: this call is OK */
if( ifa
= ifa_ifwithaddr(&copcb
->co_faddr
) ) {
/* foreign address is me */
copcb
->co_ifp
= ifa
->ifa_ifp
;
printf("cons_connect: after if_withaddr copcb->co_ifp 0x%x\n",
if( (ifa
->ifa_ifp
->if_flags
&(IFF_LOOPBACK
|IFF_UP
)) ==
copcb
->co_flags
|= CONSF_LOOPBACK
;
bcopy((caddr_t
)&ifa
->ifa_addr
, (caddr_t
)&copcb
->co_laddr
,
sizeof(struct sockaddr
));
printf("cons_connect: co_flags 0x%x\n", copcb
->co_flags
);
printf(" cons_connect withaddr returns %s\n",
else if ( copcb
->co_ifp
== (struct ifnet
*)0 ) {
* We need to get the local nsap address.
* First, route to the destination. This will provide us with
* an ifp. Second, determine which local address linked on
* that ifp is appropriate
struct sockaddr_iso
*first_hop
; /* filled by clnp_route */
struct iso_addr
*localaddr
, *clnp_srcaddr();
if (error
= clnp_route(&copcb
->co_faddr
,
&((struct isopcb
*)copcb
)->isop_route
, /* flags */0,
&first_hop
, &copcb
->co_ifp
))
/* determine local address based upon ifp */
if ((localaddr
= clnp_srcaddr(copcb
->co_ifp
,
&first_hop
->siso_addr
)) == NULL
) {
copcb
->co_laddr
.siso_family
= AF_ISO
;
copcb
->co_laddr
.siso_addr
= *localaddr
;
/* Foreign addr isn't me (lpb). If still don't have an ifp or have
* an ifp but don't know its address, look for a route
if( ifa
= ifa_ifwithnet(&copcb
->co_faddr
) ) {
copcb
->co_ifp
= ifa
->ifa_ifp
;
printf(" cons_connect withnet returns %s\n",
printf("cons PANIC: connect: can't find SNPA \n");
if( ifa
== (struct ifaddr
*)0 ) {
struct ifaddr
* iso_ifwithidi();
if( ifa
= iso_ifwithidi(&copcb
->co_faddr
) ) {
copcb
->co_ifp
= ifa
->ifa_ifp
;
printf(" cons_connect withnet returns %s\n",
printf("cons PANIC: connect: can't find SNPA \n");
bcopy((caddr_t
)&ifa
->ifa_addr
, (caddr_t
)&copcb
->co_laddr
,
sizeof(struct sockaddr
));
copcb
->co_state
= CONNECTING
;
ASSERT( copcb
->co_ifp
!= (struct ifnet
*) 0);
if ( copcb
->co_ifp
== (struct ifnet
*)0 ) {
m
= m_getclr(M_DONTWAIT
, MT_XCONN
);
copcb
->co_ifp
->if_oerrors
++;
m
->m_len
= sizeof(struct eicon_request
);
ecnrq
= mtod(m
, struct eicon_request
*);
copcb
->co_myself
= copcb
;
ecnrq
->e_pcb
= (caddr_t
)copcb
;
LAST_CALL_PCB
= (unsigned) ecnrq
->e_pcb
;
ecnrq
->e_vc
= 0; /* mbz ? */
ecnrq
->e_info
= 0; /* mbz */
MGET(n
, M_DONTWAIT
, MT_XCONN
);
copcb
->co_ifp
->if_oerrors
++;
e_data(ecnrq
) = n
; /* e_data is really dtom(ecnrq)->m_next */
"calling make_partial_x25_packet( 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n",
&copcb
->co_laddr
, &copcb
->co_faddr
,
copcb
->co_proto
->pr_protocol
,
copcb
->co_flags
& CONSF_XTS
);
if( error
= make_partial_x25_packet( copcb
, e_data(ecnrq
)) ) {
copcb
->co_ifp
->if_oerrors
++;
printf("cons_connect ecnrq:\n");
dump_buf(ecnrq
, sizeof(*ecnrq
));
ASSERT( copcb
->co_channel
== 0);
if( copcb
->co_channel
!= 0) {
printf("cons_connect PANIC: channel is 0x%x\n", copcb
->co_channel
);
error
= choose_output(copcb
->co_ifp
, m
, copcb
->co_flags
& CONSF_LOOPBACK
);
printf("cons: PANIC: if_output returns 0x%x\n", error
);
cons_clear_and_detach( copcb
, DONTCLEAR
, PRC_ROUTEDEAD
);
"cons_connect: choose (copcb m) returned error\n",
* cons_find( CONSF_DGM, dst, proto, 0, 0) where
* proto is one of { TP_proto, CLNP_proto }
* FUNCTION and ARGUMENTS:
* Looks through list of connections for the destination,
* for one marked for the use indicated by flags.
* If none found, opens up a new connection.
* These connections will be eliminated by :
* b) the need for a new connection, when we've run out of resources.
* The argument flags describes the type of pcb we want - may
* specify multiplexing-ok, datagram use, etc.
* The argument proto points the the higher layer protocol that
* will be using this connection.
* returns a ptr to a pcb whose characteristics match those
* described by (flags, proto)
cons_find(flags
, dst
, proto
, addl_criteria
, mask
)
struct sockaddr_iso
*dst
;
register struct cons_pcb
*copcb
;
register struct cons_pcb
**copcblist
= (struct cons_pcb
**)Xpcblist
;
int s
= splnet(); /* or whatever, for the device! */
struct dte_addr dest_dte
;
struct copcb_descriptor
{
printf("cons_find( flags 0x%x proto 0x%x) ", flags
, proto
);
if ( iso_8208snparesolve(dst
, &dest_dte
, &dummy
)) {
return (struct cons_pcb
*)0; /* error */
ASSERT(dummy
== sizeof(struct dte_addr
));
for( copcb
= *copcblist
; copcb
; copcb
= *(++copcblist
) ) {
copcb
= (struct cons_pcb
*)copcb
->co_next
;
while (copcb
!= *copcblist
) {
"cons_find: chan 0x%x flags 0x%x proto 0x%x state 0x%x \n",
copcb
->co_channel
, copcb
->co_flags
, copcb
->co_proto
,
* if flags is a subset of the bits in co_flags, it will suffice
if( ((copcb
->co_flags
& flags
) == flags
) &&
/* PHASE2: where do we get the mask if we use nsaps ????
* If dte addresses are used, then use
* nibble compare otherwise...???
iso_addrmatch1(&(copcb
->co_faddr
.siso_addr
), &(dst
->siso_addr
))
dest_dte
.dtea_niblen
== copcb
->co_peer_dte
.dtea_niblen
&&
nibble_match( (char *)&(copcb
->co_peer_dte
.dtea_addr
),
HIGH_NIBBLE
, (char *)dest_dte
.dtea_addr
,
HIGH_NIBBLE
, dest_dte
.dtea_niblen
)
(copcb
->co_proto
== proto
) &&
(copcb
->co_state
>= MIN_USABLE_STATE
)) {
"cons_find: add'l criteria...\n" );
if((copcb
->co_state
!= OPEN
) &&
(next_best
.xd_qlen
> copcb
->co_pending
.ifq_len
)) {
next_best
.xd_pcb
= copcb
;
next_best
.xd_qlen
= copcb
->co_pending
.ifq_len
;
if( !addl_criteria
|| (*addl_criteria
)(copcb
, mask
) ) {
goto found
; /* have to break out of 2 loops */
copcb
= (struct cons_pcb
*)copcb
->co_next
;
* have a limit of the number of calls per desitination.
* If we didn't find one already open AND our limit for this
* destination hasn't been reached, return 0 'cause
* then the caller will open a new one.
* Otherwise return next_best.
* To do this we need some sort of per-destination info.
* Could go into the directory service. Oh, grotesque.
if( copcb
== (struct cons_pcb
*)0 ) {
copcb
= next_best
.xd_pcb
; /* may be zero too */
dump_copcb(copcb
, "find: next_best");
printf("returns 0x%x \n", copcb
);
* NAME: issue_clear_req()
* cons_clear() and wherever we get an error from x.25 that makes us
* want to close the vc on which it came, but don't have
* a copcb assoc. with that vc.
* FUNCTION and ARGUMENTS:
* Creates an eicon_request for a clear request, returns it in an mbuf.
* (chan) is the channel on which to do the clear, (reason) is the
* clear reason(diagnostic).
issue_clear_req(chan
, reason
, ifp
, loop
)
register struct mbuf
*cdm
;
register struct eicon_request
*ecnrq
;
struct e_clear_data
*ecd
;
printf("issue_clear_req(0x%x, 0x%x, 0x%x, 0x%x)\n",
chan
, reason
, ifp
, loop
);
m
= m_getclr(M_DONTWAIT
, MT_XCLOSE
);
m
->m_len
= sizeof(struct eicon_request
);
ecnrq
= mtod(m
, struct eicon_request
*);
ecnrq
->e_cmd
= ECN_CLEAR
;
ecnrq
->e_vc
= chan
& 0xff;
* see p. 149 of 8208 for reasons (diagnostic codes)
MGET(cdm
, M_DONTWAIT
, MT_XCLOSE
);
cdm
->m_len
= sizeof(struct e_clear_data
); /* cause, diagnostic */
ecd
= mtod(cdm
, struct e_clear_data
*);
ecd
->ecd_cause
= 0x0; /* DTE initiated, diagnostic tells more */
ecd
->ecd_diagnostic
= (u_char
)reason
;
return choose_output(ifp
, m
, loop
);
* cons_usrreq(), PRU_DISCONNECT,
* cons_slowtimo(), cons_free_lru()
* FUNCTION and ARGUMENTS:
* Builds a clear request for the connection represented by copcb,
* gives it to the device.
* ECN_CLEAR(request) takes e_vc only, returns adr_status.
cons_clear( copcb
, reason
)
register struct cons_pcb
*copcb
;
printf("cons_clear(0x%x, 0x%x)\n", copcb
, reason
);
printf("cons PANIC: clear: No copcb\n");
while( copcb
->co_pending
.ifq_len
> 0 ) {
register int s
= splimp();
IF_DEQUEUE( &copcb
->co_pending
, m
);
if( (copcb
->co_state
== CLOSED
) || (copcb
->co_state
== CLOSING
) )
if( copcb
->co_state
== CONNECTING
) {
dump_copcb(copcb
, "clear");
} else if( (copcb
->co_channel
== 0) || (copcb
->co_channel
== X_NOCHANNEL
) ) {
dump_copcb(copcb
, "clear");
copcb
->co_state
= CLOSING
;
printf("cons_clear: channel 0x%x copcb 0x%x dst: ",
copcb
->co_channel
, copcb
);
dump_isoaddr(&copcb
->co_faddr
);
dump_copcb(copcb
, "clear");
error
= issue_clear_req(copcb
->co_channel
, reason
, copcb
->co_ifp
,
copcb
->co_flags
& CONSF_LOOPBACK
);
copcb
->co_channel
= X_NOCHANNEL
;
copcb
->co_state
= CLOSED
;
* cons_output(), consoutput(), consintr()
* FUNCTION and ARGUMENTS:
* issued a data (write) command - if the device isn't ready,
* it enqueues the command on a per-connection queue.
* Is responsible for freeing m0!
register struct cons_pcb
*copcb
;
register struct eicon_request
*ecnrq
;
printf("cons_senddata( 0x%x, m 0x%x ) chan 0x%x",
copcb
, m0
, copcb
->co_channel
);
printf(" co_lport 0x%x\n", copcb
->co_lport
);
printf("cons_senddata : BAD MLEN? 0x%x", m0
->m_len
);
if( (copcb
->co_state
== CONNECTING
) || (copcb
->co_state
== ACKWAIT
) ) {
printf("senddata PUTTING ON PENDING Q copcb 0x%x state 0x%x\n",
if (IF_QFULL(&copcb
->co_pending
)) {
printf("senddata DROPPING m0 0x%x\n", m0
);
IF_DROP(&copcb
->co_pending
);
copcb
->co_ifp
->if_snd
.ifq_drops
++;
copcb
->co_ifp
->if_oerrors
++;
if( copcb
->co_proto
&& copcb
->co_proto
->pr_ctlinput
) {
(*copcb
->co_proto
->pr_ctlinput
)(PRC_QUENCH
,
(struct sockaddr_iso
*)&copcb
->co_faddr
,
printf("Putting 0x%x on 0x%x->pending Q\n", m0
, copcb
);
IF_ENQUEUE( &copcb
->co_pending
, m0
);
if(copcb
->co_channel
== 0 ) {
ASSERT( copcb
->co_state
== OPEN
);
m
= m_getclr(M_DONTWAIT
, MT_XDATA
);
copcb
->co_ifp
->if_oerrors
++;
m
->m_len
= sizeof(struct eicon_request
);
ecnrq
= mtod(m
, struct eicon_request
*);
ecnrq
->e_pcb
= (caddr_t
)copcb
;
if( copcb
->co_myself
!= copcb
) {
/* TODO: REMOVE THIS DEBUGGING HACK */
printf("BAD e_pcb from HL (0x%x,0x%x)\n", copcb
, copcb
->co_myself
);
if(mm
->m_type
== MT_FREE
)
ASSERT( copcb
->co_channel
!= 0);
ASSERT( copcb
->co_channel
!= X_NOCHANNEL
);
ecnrq
->e_vc
= (copcb
->co_channel
& 0xff);
/* TODO: REMOVE THIS DEBUGGING HACK */
struct mbuf
*thedata
= e_data(ecnrq
);
u_int
*firstint
= mtod( thedata
, u_int
*);
if( (*firstint
& 0xff000000) != 0x81000000 ) {
switch( ((*firstint
) & 0x00ff0000) >> 20 ) {
printf(" ECN_SEND! BAD DATA\n" );
dump_buf( thedata
, 20 + 12 );
printf("senddata ecnrq\n");
ASSERT( copcb
->co_state
== OPEN
);
copcb
->co_state
= ACKWAIT
;
if( copcb
->co_myself
!= copcb
) {
/* TODO: REMOVE this and all mention of co_myself */
printf("BAD e_pcb TO THE BOARD ecn (0x%x) cmd 0x%x\n",
ecnrq
->e_pcb
, ecnrq
->e_cmd
);
if(mm
->m_type
== MT_FREE
)
dump_buf (ecnrq
, sizeof (*ecnrq
));
choose_output(copcb
->co_ifp
, dtom(ecnrq
), copcb
->co_flags
&CONSF_LOOPBACK
);
* NAME: cons_send_on_vc()
* FUNCTION and ARGUMENTS:
* Take a packet(m0), of length (datalen) from tp and
* send it on the channel (chan).
* whatever (E*) is returned form the net layer output routine.
cons_send_on_vc(chan
, m
, datalen
)
struct cons_pcb
*copcb
= (struct cons_pcb
*)0;
cons_chan_to_pcb( chan
, __LINE__
)
) == (struct cons_pcb
*)0 )
printf("cons_send_on_vc m 0x%x m_len 0x%x\n", m
, m
->m_len
);
return cons_senddata( copcb
, m
);
* tpiso_output(), can have whatever interface we want it to...
* tpiso_output() decides whether to give a packet to CLNP or to
* cons; if the latter, it calls this routine.
* FUNCTION and ARGUMENTS:
* tp has alloc-ed a pcb - but it may not be open.
* some classes of tp may allow multiplexing, in which
* case, you may choose to send the data on ANOTHER cons connection.
* This decides which net connection to use, opens one if necessary.
* Then it sends the data.
cons_output(isop
, m
, len
, isdgm
)
struct cons_pcb
*copcb
= (struct cons_pcb
*)0;
printf("cons_output( isop 0x%x, m 0x%x, len 0x%x, dgm 0x%x )\n",
error
= cosns_output1(0, m
, &isop
->isop_faddr
, TP_proto
, isop
);
printf("cosns_output1 RETURNS ERROR 0x%x\n", error
);
if( isop
->isop_chanmask
|| isop
->isop_negchanmask
) {
register int mask
= isop
->isop_chanmask
;
mask
= isop
->isop_negchanmask
;
for ( chan
=1; (mask
& 1)==0; chan
++,mask
>>=1 ) ;
if( isop
->isop_chanmask
== 0 )
"cons_output: isop 0x%x cmask 0x%x negmask 0x%x, chan 0x%x\n",
isop
, isop
->isop_chanmask
, isop
->isop_negchanmask
, chan
);
copcb
= cons_chan_to_pcb( chan
, __LINE__
);
copcb
= cons_chan_to_pcb( chan
);
if( copcb
== (struct cons_pcb
*)0 ) {
if(( error
= cons_pcballoc(&dummysocket
, &cons_isopcb
, CONSF_OCRE
,
TP_proto
, &copcb
)) != EOK
) {
printf("cosns_output: no copcb; returns 0x%x\n", error
);
/* abbreviated form of iso_pcbconnect(): */
bcopy((caddr_t
)&isop
->isop_faddr
, (caddr_t
)&copcb
->co_faddr
,
sizeof(struct sockaddr_iso
));
if ( error
= cons_connect( copcb
) ) { /* if it doesn't work */
/* oh, dear, throw packet away */
remque((struct isopcb
*)copcb
);
(void) m_free(dtom(copcb
));
while( (copcb
->co_state
!= OPEN
) &&
!(error
= copcb
->co_socket
->so_error
) ) {
"SLEEP1 copcb 0x%x isop 0x%x state 0x%x chan 0x%x mask 0x%x neg 0x%x\n",
copcb
, isop
, copcb
->co_state
, copcb
->co_channel
,
((struct isopcb
*)isop
)->isop_chanmask
,
((struct isopcb
*)isop
)->isop_negchanmask
sleep( (caddr_t
)&copcb
->co_state
, PZERO
+1 );
printf("AFTER SLEEP 1 chan 0x%x chanmask 0x%x negchanmask 0x%x\n",
copcb
->co_channel
, isop
->isop_chanmask
,
SET_CHANMASK( isop
, copcb
->co_channel
);
printf("cons_output calling senddata(0x%x 0x%x)\n", copcb
, m
);
error
= cons_senddata( copcb
, m
);
* TP when it decides to open a VC for TP 0
* opens a connection and stashes the pcb info in the socket
* substitute for iso_pcbconnect/ in_pcbconnect for the class 0 case
cons_openvc(copcb
, faddr
, so
)
struct sockaddr_iso
*faddr
;
struct cons_pcb
*cons_chan_to_pcb();
ASSERT( copcb
->co_socket
== so
);
tptrace(TPPTmisc
, "cons_openvc( copcb so )\n", copcb
, so
, 0, 0);
printf("cons_openvc( copcb 0x%x, so 0x%x )\n", copcb
,so
);
* initialize the copcb part of the isopcb
copcb
->co_ttl
= copcb
->co_init_ttl
= X25_TTL
;
copcb
->co_flags
= CONSF_OCRE
;
copcb
->co_proto
= TP_proto
;
copcb
->co_pending
.ifq_maxlen
= CONS_IFQMAXLEN
;
/* abbreviated form of iso_pcbconnect(): */
bcopy((caddr_t
)faddr
, (caddr_t
)&copcb
->co_faddr
,
sizeof(struct sockaddr_iso
));
ASSERT( copcb
->co_socket
== so
);
if( error
= cons_connect( copcb
) )
while( (copcb
->co_state
!= OPEN
) && !(error
= so
->so_error
) ) {
"SLEEP2 copcb 0x%x state 0x%x chan 0x%x mask 0x%x neg 0x%x\n",
copcb
, copcb
->co_state
, copcb
->co_channel
,
sleep( (caddr_t
)&copcb
->co_state
, PZERO
+2 );
printf("AFTER SLEEP2 chan 0x%x chanmask 0x%x negchanmask 0x%x\n",
copcb
->co_channel
, copcb
->co_chanmask
,
SET_CHANMASK( (struct isopcb
*)copcb
, copcb
->co_channel
);
ASSERT( copcb
->co_socket
== so
);
printf("cons_openvc: copcb 0x%x error 0x%x\n", copcb
, error
);
* tp_route_to() when it decides to accept or reject an incoming
* connection it calls this.
* either closes the cons connection named by (channel)
* or associates the copcb with the channel #.
* and removes the old copcb from the tp_incoming_pending list.
cons_netcmd(cmd
, isop
, channel
, isdgm
)
struct cons_pcb
*copcb
= (struct cons_pcb
*)0;
struct cons_pcb
*cons_chan_to_pcb();
tptrace(TPPTmisc
, "cons_netcmd( cmd isopcb channel isdgm)\n",
cmd
,isop
,channel
, isdgm
);
printf("cons_netcmd( cmd 0x%x, isop 0x%x, channel 0x%x, isdgm 0x%x)\n",
cmd
,isop
,channel
, isdgm
);
printf("cons_netcmd: isop->socket 0x%x\n",
ASSERT(cmd
!= CONN_OPEN
);
/* Can we find a cons-level pcb based on channel? */
cons_chan_to_pcb( channel
, __LINE__
)
cons_chan_to_pcb( channel
)
) == (struct cons_pcb
*)0) {
if( copcb
== (struct cons_pcb
*) isop
) {
copcb
= (struct cons_pcb
*)0;
/* avoid operating on a pcb twice */
/* if isop is null (close/refuse):
* this would remove from the TP list, which is NOT what we want
* so only remove if there is an isop (gag)
remque((struct cons_pcb
*)copcb
); /* take it off pending list */
ASSERT( (cmd
== CONN_CLOSE
) || (cmd
== CONN_REFUSE
) );
/* now we have one of these cases:
* 1) isop is non-null and copcb is null
* 2) isop is non-null and copcb is non-null and they are different
* 3) isop is null and copcb is non-null
ASSERT( (isop
!= (struct isopcb
*)0) || (copcb
!= (struct cons_pcb
*)0));
/* we want two separate pcbs */
/* if we don't have a copcb, get one */
if( copcb
== (struct cons_pcb
*)0 ) {
if(( error
= cons_pcballoc(&dummysocket
, &cons_isopcb
,
((struct cons_pcb
*)isop
)->co_flags
,
TP_proto
, &copcb
)) != EOK
)
/* copy missing info from isop */
copcb
->co_laddr
= isop
->isop_laddr
;
copcb
->co_faddr
= isop
->isop_faddr
;
/* don't care about tsuffices */
((struct cons_pcb
*)isop
)->co_channel
= 0;
copcb
->co_ifp
= ((struct cons_pcb
*)isop
)->co_ifp
;
ASSERT( copcb
->co_pending
.ifq_len
== 0 );
insque((struct isopcb
*)copcb
,
(struct isopcb
*)&cons_isopcb
);
copcb
->co_flags
|= CONSF_DGM
;
copcb
->co_channel
= channel
;
ASSERT(copcb
->co_channel
!= 0);
printf("cons_netcmd: put 0x%x on regular list \n", copcb
);
/* must be TP 0, since this is never called from XTS code */
/* we want ONE pcb, namely isop.
* If this TPE were the active side,
* there ought not to be a copcb, since TP should
* know that you can't send a CR with dgm and negot down
* If this TPE were the passive side, we want to copy from
* the copcb that was on the pending list, and delete the
printf("cons_netcmd: copied info from 0x%x to 0x%x\n",
isop
->isop_laddr
= copcb
->co_laddr
;
isop
->isop_faddr
= copcb
->co_faddr
;
/* tsuffices, socket should be there already */
((struct cons_pcb
*)isop
)->co_flags
=
copcb
->co_flags
& ~CONSF_DGM
;
((struct cons_pcb
*)isop
)->co_init_ttl
= copcb
->co_init_ttl
;
touch(((struct cons_pcb
*)isop
));
((struct cons_pcb
*)isop
)->co_channel
= channel
;
((struct cons_pcb
*)isop
)->co_ifp
= copcb
->co_ifp
;
((struct cons_pcb
*)isop
)->co_proto
= copcb
->co_proto
;
((struct cons_pcb
*)isop
)->co_myself
=
SET_CHANMASK( isop
, ((struct cons_pcb
*)isop
)->co_channel
);
ASSERT( copcb
->co_pending
.ifq_len
== 0 );
/* get rid of the copcb that was on the pending list */
(void) m_free(dtom(copcb
));
((struct cons_pcb
*)isop
)->co_state
= OPEN
;
/* if dgm then ignore; the connections will
* be re-used or will time out
/* we should never come in here with both isop and copcb
* unless is dgm, hence the following assertion:
ASSERT( (copcb
== (struct cons_pcb
*)0) ||
(isop
== (struct isopcb
*)0) );
/* close whichever pcb we have */
error
= cons_clear(copcb
, (cmd
== CONN_CLOSE
)?
E_CO_HLI_DISCN
:E_CO_HLI_REJT
);
error
= cons_clear((struct cons_pcb
*)isop
, (cmd
== CONN_CLOSE
)?
E_CO_HLI_DISCN
:E_CO_HLI_REJT
);
if(copcb
&& (copcb
->co_socket
== (struct socket
*)0) ) {
ASSERT( copcb
->co_flags
& (CONSF_DGM
| CONSF_ICRE
) );
(void) m_free(dtom(copcb
)); /* detached */
/* isop will always be detached by the higher layer */
printf("cons_netcmd returns 0x%x: isop 0x%x\n", isop
, error
);
* NAME: addr_proto_consistency_check()
* CALLED FROM: cons_incoming()
* FUNCTION and ARGUMENTS:
* Enforces a set of rules regarding what addresses will serve
* what protocol stack. This is a kludge forced upon us by the
* fact that there's no way to tell which NET layer you want to
* run when opening a socket. Besides, no doubt, OSI directory
* services won't advertise any kind of a protocol stack with the
addr_proto_consistency_check(proto
, addr
)
struct sockaddr_iso
*addr
;
if (addr
->siso_addr
.isoa_afi
!= AFI_37
)
/* kludge - necessary because this is the only type of
* NSAP we build for an incoming NC
default: /* unsupported */
* consintr() for incoming OPEN
* FUNCTION and ARGUMENTS:
* Determines which higher layer gets this call, and
* thus whether to immediately accept, reject, or to let the
* higher layer determine this question.
cons_incoming(ifp
, ecnrq
)
register struct eicon_request
*ecnrq
;
struct sockaddr_iso peer
;
struct dte_addr peer_dte
;
printf("consincoming enter: ifp 0x%x ecnrq 0x%x\n", ifp
, ecnrq
);
error
= parse_facil( mtod(e_data(ecnrq
), caddr_t
),
(e_data(ecnrq
))->m_len
, &me
, &peer
, &proto
,
loop
= is_me( &peer
); /* <-- THIS may be a problem :
* We can only expect that WE will do it right
* and never will we get an error return from
* parse_facil on a facil that WE generated,
* so if garbage comes in, peer will be garbage,
* and loop will be false.
(void) issue_clear_req(ecnrq
->e_vc
, error
, ifp
, loop
);
IncStat(co_parse_facil_err
);
if( (error
= addr_proto_consistency_check(proto
, &me
)) != EOK
) {
/* problem with consistency */
(void) issue_clear_req(ecnrq
->e_vc
, error
, ifp
, loop
);
IncStat(co_addr_proto_consist_err
);
copcb
= (struct cons_pcb
*)
((struct cons_pcb
*)(&cons_isopcb
))->co_next
;
while (copcb
!= (struct cons_pcb
*)&cons_isopcb
) {
if( copcb
->co_lport
== me
.siso_tsuffix
) {
/* for cons "transport service",
* multiplexing is not allowed
if( !copcb
->co_socket
) {
"PANIC cons_incoming NOT TP but no sock\n");
copcb
= (struct cons_pcb
*)0;
if( copcb
->co_socket
->so_options
& SO_ACCEPTCONN
) {
newx
= (struct cons_pcb
*)
sonewconn(copcb
->co_socket
)->so_pcb
;
newx
->co_laddr
= copcb
->co_laddr
;
newx
->co_peer_dte
= peer_dte
;
newx
->co_proto
= copcb
->co_proto
;
soisconnected(copcb
->co_socket
);
} /* else keep looking */
copcb
= (struct cons_pcb
*)copcb
->co_next
;
if (copcb
== (struct cons_pcb
*)&cons_isopcb
)
copcb
= (struct cons_pcb
*) 0;
ASSERT( me
.siso_tsuffix
== 0 );
* We treat this rather like we do for CLNP.
* TP can't tell which socket
* wants this until the TP header comes in, so there's no way
* to associate this channel with a tpcb/isopcb.
* We assume data will arrive (a CR TPDU) and be given to TP along with
* the channel number. We can then expect TP to call us with
* the channel number and pcb ptr, telling us to keep this connection
* Now, tp will have created an isopcb in the tp_isopcb list.
* We will have to keep another copcb though, because there is no
* 1-1 correspondence between socket and copcb when multiplexing
* But we want to save the peer address, ifp, and state, proto.
* If the channel should clear before TP responds, we need
* to know that also, so we create a tp-pending list...
if( cons_pcballoc(&dummysocket
, &tp_incoming_pending
,
CONSF_ICRE
, TP_proto
, &copcb
) != EOK
) {
copcb
= (struct cons_pcb
*)0;
copcb
->co_peer_dte
= peer_dte
;
if( cons_pcballoc(&dummysocket
, &cons_isopcb
,
CONSF_ICRE
| CONSF_DGM
, CLNP_proto
, &copcb
) != EOK
) {
copcb
= (struct cons_pcb
*)0;
copcb
->co_peer_dte
= peer_dte
;
copcb
->co_channel
= (int)ecnrq
->e_vc
;
ASSERT( copcb
->co_channel
!= 0);
copcb
->co_flags
|= CONSF_LOOPBACK
;
printf("cons_incoming found XPCB 0x%x, loop 0x%x\n",
dump_buf(&copcb
->co_laddr
, sizeof(copcb
->co_laddr
));
dump_buf(&copcb
->co_faddr
, sizeof(copcb
->co_faddr
));
(void) issue_clear_req(ecnrq
->e_vc
, E_CO_OSI_UNSAP
, ifp
, loop
);
/* caller frees the mbuf so we don't have to do any such thing */
**************************** DEVICE cons ***************************
* clnp - this routine is given as the device-output routine
* FUNCTION and ARGUMENTS:
* (ifp) is the cons/adcom, found by routing function.
* (m0) is the clnp datagram.
* (dst) is the destination address
* This routine finds an x.25 connection for datagram use and
cosns_output(ifp
, m0
, dst
)
return cosns_output1(ifp
, m0
, dst
, CLNP_proto
, NULL
);
int total_pkts_to_clnp
= 0;
* The isop is passed here so that if we have set x25crud in the
* pcb, it can be passed down to cons_connect. It could be null
* however, in the case of tp4/x25/clnp
cosns_output1(ifp
, m0
, dst
, proto
, isop
)
register struct mbuf
*m0
;
struct sockaddr_iso
*dst
;
struct isopcb
*isop
; /* NULL if coming from clnp */
register struct cons_pcb
*copcb
;
{ register struct mbuf
*n
=m0
;
if (n
->m_next
== MNULL
) {
printf("cosns_output1( ifp 0x%x, m 0x%x, dst 0x%x )\n", ifp
, m0
, dst
);
if ( ! (copcb
= cons_find( CONSF_DGM
, dst
, proto
, 0, 0) )) {
struct cons_pcb
*newcopcb
; /* so we can pass addr of this to pcballoc */
if( (error
= cons_pcballoc(&dummysocket
, &cons_isopcb
,
CONSF_DGM
| CONSF_OCRE
, proto
, &newcopcb
) ) != EOK
) {
printf("cosns_output: no copcb; returns \n");
/* abbreviated form of iso_pcbconnect(): */
bcopy((caddr_t
)dst
, (caddr_t
)&copcb
->co_faddr
,
sizeof(struct sockaddr_iso
));
/* copy x25crud into copcb if necessary */
if ((isop
!= NULL
) && (isop
->isop_x25crud_len
> 0)) {
bcopy(isop
->isop_x25crud
, copcb
->co_x25crud
,
copcb
->co_x25crud_len
= isop
->isop_x25crud_len
;
copcb
->co_ifp
= ifp
; /* NULL IF COMING FROM TP4! */
if ( error
= cons_connect( copcb
) ) { /* if it doesn't work */
/* oh, dear, throw packet away */
remque((struct isopcb
*)copcb
);
(void) m_free(dtom(copcb
));
printf("cosns_output1 @ senddata: state 0x%x flags 0x%x channel 0x%x\n",
copcb
->co_state
, copcb
->co_flags
, copcb
->co_channel
);
ASSERT(copcb
->co_channel
!= X_NOCHANNEL
);
error
= cons_senddata(copcb
, m0
);
**************************** TRANSPORT cons ***************************
* cons_usrreq() on PRU_DETACH
* cons_netcmd() when TP releases a net connection
* cons_slowtimo() when timeout releases a net connection
* removes the copcb from the list of copcbs in use, and frees the mbufs.
* detaches the pcb from the socket, where a socket exists.
* ENOTCONN if it couldn't find the copcb in the list of connections.
register struct cons_pcb
*copcb
;
struct socket
*so
= copcb
->co_socket
;
printf("cons_detach( copcb 0x%x )\n", copcb
);
if (!soqremque(so
, 0) && !soqremque(so
, 1))
((struct isopcb
*)copcb
)->isop_options
= 0; /* kludge */
iso_pcbdetach(copcb
); /* detaches from so */
remque((struct isopcb
*)copcb
);
(void) m_free(dtom(copcb
));
cons_clear_and_detach(copcb
, clearreason
, ctlcmd
)
register struct cons_pcb
*copcb
;
printf("Clear and Detach (0x%x, 0x%x, 0x%x)\n",
copcb
, clearreason
, ctlcmd
);
if( clearreason
!= DONTCLEAR
) {
(void) cons_clear( copcb
, clearreason
);
if( copcb
->co_proto
&& copcb
->co_proto
->pr_ctlinput
)
(*copcb
->co_proto
->pr_ctlinput
)(ctlcmd
,
(struct sockaddr_iso
*)&copcb
->co_faddr
, (caddr_t
)copcb
);
if( copcb
->co_socket
== (struct socket
*)0 ) {
/* tp4, clnp users only */
(void) cons_detach( copcb
);
} /* else detach will be called by the socket's closing */
ASSERT( copcb
->co_socket
!= &dummysocket
);
ASSERT( (copcb
->co_flags
& CONSF_DGM
) == 0 );
printf("END OF Clear and Detach (0x%x, 0x%x, 0x%x)\n",
copcb
, clearreason
, ctlcmd
);
cons_pcbbind( copcb
, nam
)
register struct cons_pcb
*copcb
;
if( error
= iso_pcbbind( copcb
, nam
) )
/* iso_pcbbind already ensured that if port < 1024 it's superuser */
/* Now we check: must be in range 0 .. 23 or in range 1024 .. 99 */
if( (copcb
->co_lport
< X25_PORT_RESERVED
) ||
((copcb
->co_lport
>= ISO_PORT_RESERVED
) &&
(copcb
->co_lport
<= X25_PORT_USERMAX
))) {
munge( copcb
->co_lport
, (&copcb
->co_laddr
)->siso_addr
.t37_idi
+
ADDR37_IDI_LEN
, 1 /* nibble */);
munge( copcb
->co_fport
, (&copcb
->co_faddr
)->siso_addr
.t37_idi
+
ADDR37_IDI_LEN
, 1 /* nibble */);
* user level via proto switch
* FUNCTION and ARGUMENTS:
* req: which PRU* request
* m : data or mbuf ptr into which to stash data
* nam: mbuf ptr which is really a sockaddr_iso
* ifq: in PRU_CONTROL case, an ifnet structure
* ENOTCONN if trying to do something which requires a connection
* and it's not yet connected
* EISCONN if trying to do something which cannot be done to a connection
* ENOBUFS if ran out of mbufs
* EWOULDBLOCK if in nonblocking mode & can't send right away
* EOPNOSUPP if req isn't supported
* E* other passed up from lower layers or from other routines
cons_usrreq(so
, req
, m
, nam
, ifp
)
struct cons_pcb
*copcb
= (struct cons_pcb
*)so
->so_pcb
;
printf("cons_usrreq 0x%x so 0x%x copcb 0x%x\n", req
, so
, copcb
);
if (req
== PRU_CONTROL
) {
error
= iso_control(so
, (int)m
, (caddr_t
)nam
, (struct ifnet
*)ifp
);
if (copcb
== (struct cons_pcb
*)0 && req
!= PRU_ATTACH
) {
soreserve(so
, X25_SBSIZE
, X25_SBSIZE
); /* CONS size */
error
= cons_pcballoc(so
, &cons_isopcb
, CONSF_XTS
, X25_proto
, &copcb
);
case PRU_ABORT
: /* called from close() */
/* called for each incoming connect queued on the parent (accepting)
* socket (SO_ACCEPTCONN);
error
= cons_detach ( copcb
);
case PRU_DETACH
: /* called from close() */
/* called after disconnect was called iff was connected at the time
* of the close, or directly if socket never got connected */
error
= cons_detach ( copcb
);
/* recv end may have been released; local credit might be zero */
error
= cons_clear(copcb
, E_CO_HLI_DISCN
);
error
= cons_pcbbind( copcb
, nam
);
if (copcb
->co_lport
== 0)
error
= cons_pcbbind( copcb
, 0 );
struct sockaddr_iso
*siso
= mtod(nam
, struct sockaddr_iso
*);
nam
->m_len
= sizeof (struct sockaddr_iso
);
bcopy( (caddr_t
)&copcb
->co_laddr
,
(caddr_t
)siso
, sizeof(struct sockaddr_iso
) );
((struct sockaddr_iso
*)siso
)->siso_tsuffix
= copcb
->co_lport
;
if( (so
->so_state
& SS_ISCONNECTED
) &&
(so
->so_state
& SS_ISDISCONNECTING
) == 0) {
struct sockaddr_iso
*siso
= mtod(nam
, struct sockaddr_iso
*);
nam
->m_len
= sizeof (struct sockaddr_iso
);
bcopy( (caddr_t
)&copcb
->co_faddr
, (caddr_t
)siso
,
sizeof(struct sockaddr_iso
) );
/* TODO: We need to bind to the RIGHT interface.
* The only way to have the right interface is to have
printf("PRU_CONNECT 1: local tsuffix 0x%x so->so_head 0x%x nam:\n",
copcb
->co_lport
, so
->so_head
);
dump_isoaddr( mtod(nam
, struct sockaddr_iso
*) );
if (copcb
->co_lport
== 0) {
if( error
= cons_pcbbind( copcb
, 0 ))
printf("PRU_CONNECT 2: local tsuffix 0x%x so->so_head 0x%x nam:\n",
copcb
->co_lport
, so
->so_head
);
dump_isoaddr( mtod(nam
, struct sockaddr_iso
*) );
{ /* change the destination address so the last 2 digits
* are the port/suffix/selector (whatever you want to call it)
register struct sockaddr_iso
*siso
=
mtod(nam
, struct sockaddr_iso
*);
if( (siso
->siso_tsuffix
< X25_PORT_RESERVED
) ||
((siso
->siso_tsuffix
>= ISO_PORT_RESERVED
) &&
(siso
->siso_tsuffix
<= X25_PORT_USERMAX
)))
munge( siso
->siso_tsuffix
,
siso
->siso_addr
.t37_idi
+ ADDR37_IDI_LEN
,
if (error
= iso_pcbconnect(copcb
, nam
))
error
= cons_connect( copcb
);
remque((struct isopcb *)copcb);
(void) m_free(dtom(copcb));
while( (copcb
->co_state
!= OPEN
)&&(copcb
->co_socket
->so_error
== 0) ) {
printf("PRU_CONNECT: error 0x%x sleeping on 0x%x\n",
copcb
->co_socket
->so_error
,
(caddr_t
)&copcb
->co_state
);
sleep( (caddr_t
)&copcb
->co_state
, PZERO
+3 );
ASSERT( copcb
->co_channel
!= 0);
SET_CHANMASK ( (struct isopcb
*)copcb
, copcb
->co_channel
);
/* so here is the NEW socket */
if ((so
->so_state
& SS_NBIO
) && (so
->so_state
& SS_ISCONNECTED
)== 0) {
struct sockaddr_iso
*siso
= mtod(nam
, struct sockaddr_iso
*);
/* copy the peer's address into the return argument */
nam
->m_len
= sizeof (struct sockaddr_iso
);
bcopy( (caddr_t
)&copcb
->co_faddr
, (caddr_t
)siso
,
sizeof(struct sockaddr_iso
));
* sosend calls this until sbspace goes negative.
* Sbspace may be made negative by appending this mbuf chain,
* possibly by a whole cluster.
/* no need to actually queue this stuff and dequeue it,
* just bump the pointers in so_snd so that higher
* layer of socket code will cause it to sleep when
* we've run out of socket space
* Unfortunately that makes sbflush vomit so we have
* to allocate a single real mbuf (say size 240)
* and sballoc it and sbfree it upon CONS_SEND_DONE.
* Oh, my, is this sickening or what?
MGET(mx
, M_DONTWAIT
, MT_DATA
);
sbappend((caddr_t
)&copcb
->co_socket
->so_snd
, mx
);
printf("X.25 Usrreq calling cons_senddata(0x%x, 0x%x)\n",
error
= cons_senddata(copcb
, m
);
printf("PRU_SEND sent tsuffix 0x%x, m 0x%x error 0x%x\n",
copcb
->co_lport
, m
, error
);
if( req
== PRU_SENDEOT
) {
while(copcb
->co_socket
->so_snd
.sb_cc
> 0)
sbwait(&copcb
->co_socket
->so_snd
);
error
= cons_ioctl(so
, m
, (caddr_t
)nam
);
/* COULD support INTERRUPT packets as oob */
printf("cons_usrreq cmd 0x%x copcb 0x%x returned error 0x%x\n",
* consintr() through the isosw protosw for "transport" version of X25
cons_input(m
, faddr
, laddr
, so
)
struct sockaddr_iso
*faddr
, *laddr
; /* not used */
register struct socket
*so
;
printf("cons_input( m 0x%x, so 0x%x)\n", m
,so
);
sbappend(&so
->so_rcv
, m
);
* Presently the protosw has 0 in the ctloutput spot
* because we haven't inplemented anything yet.
* If there's reason to put some options in here,
* be sure to stick this routine name in the protosw in iso_proto.c
cons_ctloutput(cmd
, so
, level
, optname
, mp
)
* lower layer when ECN_CLEAR occurs : this routine is here
* for consistency - cons subnet service calls its higher layer
* through the protosw entry.
* cmd is a PRC_* command, list found in ../h/protosw.h
* This serves the higher-layer cons service.
* NOTE: this takes 3rd arg. because cons uses it to inform itself
* of things (timeouts, etc) but has a pcb instead of an address.
cons_ctlinput(cmd
, sa
, copcb
)
register struct cons_pcb
*copcb
;
extern u_char inetctlerrmap
[];
extern int iso_rtchange();
printf("cons_ctlinput( cmd 0x%x, copcb 0x%x)\n", cmd
, copcb
);
/* co_socket had better exist */
ASSERT( copcb
->co_socket
);
ASSERT( copcb
->co_flags
& CONSF_XTS
);
sbdrop((caddr_t
)&copcb
->co_socket
->so_snd
, MLEN
);
sbwakeup((caddr_t
)&copcb
->co_socket
->so_snd
);
iso_pcbnotify(&cons_pcb, sa,
(int)inetctlerrmap[cmd], iso_rtchange);
iso_pcbnotify(&tp_incoming_pending, sa,
(int)inetctlerrmap[cmd], tpiso_quench);
iso_pcbnotify(&tp_isopcb, sa,
(int)inetctlerrmap[cmd], tpiso_quench);
iso_pcbnotify(&cons_isopcb
, sa
,
(int)inetctlerrmap
[cmd
], iso_rtchange
);
iso_pcbnotify(&tp_incoming_pending
, sa
,
(int)inetctlerrmap
[cmd
], iso_rtchange
);
iso_pcbnotify(&tp_isopcb
, sa
,
(int)inetctlerrmap
[cmd
], iso_rtchange
);
printf("cons_ctlinput: unknown cmd 0x%x\n", cmd
);
soisdisconnected(copcb
->co_socket
);
sohasoutofband(copcb
->co_socket
);
*********************** SERVES ALL cons embodiments *******************
* NAME: cons_chan_to_pcb()
* cons_chan_to_tpcb() in tp_cons.c
* and in this file: incoming requests that give only a channel number, i.e.,
* ECN_ACCEPT, ECN_RECEIVE, ECN_CLEAR
* identify the pcb assoc with that channel
cons_chan_to_pcb( channel
, linenumber
)
cons_chan_to_pcb( channel
)
register struct cons_pcb
**copcblist
= (struct cons_pcb
**)Xpcblist
;
register struct cons_pcb
*copcb
;
channel
= channel
& 0xff;
for( copcb
= *copcblist
; copcb
; copcb
= *(++copcblist
) ) {
copcb
= (struct cons_pcb
*)copcb
->co_next
;
while (copcb
!= *copcblist
) {
if ( copcb
->co_channel
== channel
)
goto found
; /* want to break out of both loops */
copcb
= (struct cons_pcb
*)copcb
->co_next
;
found
: /* or maybe not... */
printf("cons_chan_to_pcb( 0x%x, %d ) %s 0x%x\n", channel
, linenumber
,
copcb
?"FOUND":"FAILED", copcb
);
* cons_incoming(). Perhaps could just expand in line.
* FUNCTION and ARGUMENTS:
* for the given remote address (remadr) if it exactly matches
* one of the addresses of ME, and I am up as loopback,
* return TRUE, else return FALSE.
struct sockaddr_iso
*remaddr
;
struct ifnet
*ifp
= consif
;
struct ifaddr
*ifa
= ifa_ifwithaddr(remaddr
);
printf("is_me: withaddr returns %s\n",
ifa
?ifa
->ifa_ifp
->if_name
:"NONE");
/* remaddr matches one of my interfaces exactly */
if( ifa
->ifa_ifp
->if_flags
& IFF_LOOPBACK
) {
ASSERT( ifp
== ifa
->ifa_ifp
);
find_error_reason( ecnrq
)
register struct eicon_request
*ecnrq
;
extern u_char x25_error_stats
[];
struct e_clear_data
*ecd
;
if( cdm
&& cdm
->m_len
> 0 ) {
ecd
= mtod(cdm
, struct e_clear_data
*);
switch( ecd
->ecd_cause
) {
/* DTE originated; look at the diagnostic */
error
= (CONL_ERROR_MASK
| ecd
->ecd_diagnostic
);
case 0x01: /* number busy */
case 0x09: /* Out of order */
case 0x11: /* Remot Procedure Error */
case 0x19: /* reverse charging accept not subscribed */
case 0x21: /* Incampat destination */
case 0x29: /* fast select accept not subscribed */
case 0x39: /* ship absent */
case 0x03: /* invalid facil request */
case 0x0b: /* access barred */
case 0x13: /* local procedure error */
case 0x05: /* network congestion */
case 0x8d: /* not obtainable */
case 0x95: /* RPOA out of order */
* so we don't have to have so many perror entries
error
= (CONL_ERROR_MASK
| 0x100 | (ecd
->ecd_cause
& ~0x80));
case 0xc1: /* gateway-detected proc error */
case 0xc3: /* gateway congestion */
error
= (CONL_ERROR_MASK
| 0x100 | ecd
->ecd_cause
);
/* otherwise, a *hopefully* valid perror exists in the e_reason field */
printf("Incoming PKT TYPE 0x%x with reason 0x%x\n",
} else if( error
& 0x1ff > sizeof(x25_error_stats
)) {
x25_error_stats
[error
& 0x1ff] ++;
* the eicon driver via software interrupt
* FUNCTION and ARGUMENTS:
* processes incoming indications, passing them
* along to clnp, tp, or x.25-transport as appropriate.
struct ifnet
*ifp
= consif
;
register struct eicon_request
*ecnrq
;
register struct cons_pcb
*copcb
= (struct cons_pcb
*)0;
* Get next request off input queue
IF_DEQUEUE(&consintrq
, m
);
printf("cons intr() 0x%x m_off 0x%x m_len 0x%x dequeued\n",
m
, m
?m
->m_off
:0, m
?m
->m_len
:0);
if((m
->m_off
!= MMINOFF
)||(m
->m_len
!= sizeof (struct eicon_request
))){
printf("Cons R DROP! BAD MBUF FROM LL 0x%x sizeof(...) 0x%x\n",
m
, sizeof(struct eicon_request
));
ecnrq
= mtod(m
, struct eicon_request
*);
printf("INTR: e_cmd 0x%x, e_data 0x%x\n", ecnrq
->e_cmd
,
if( e_data(ecnrq
) != 0 ) {
/* let's just look at the first few bytes */
dump_buf( e_data(ecnrq), (e_data(ecnrq))->m_len + 12);
dump_buf( e_data(ecnrq
), 20 + 12);
tptrace( TPPTmisc
, "INTR: req_type m lun\n",
ecnrq
->e_cmd
, m
, ecnrq
->e_vc
, 0);
case ECN_ACK
: /* data put on the board */
ASSERT( ecnrq
->e_vc
!= 0);
/* from ACKWAIT to OPEN */
cons_chan_to_pcb( (int)ecnrq
->e_vc
, __LINE__
)
cons_chan_to_pcb( (int)ecnrq
->e_vc
)
) == (struct cons_pcb
*)0 )
* Anything on the pending queue for this connection?
if( copcb
->co_pending
.ifq_len
== 0 ) {
if( copcb
->co_proto
->pr_ctlinput
)
/* for the sake of higher layer protocol (tp) */
(*copcb
->co_proto
->pr_ctlinput
)
(struct sockaddr_iso
*)&copcb
->co_faddr
,
register struct mbuf
*m0
;
IF_DEQUEUE( &copcb
->co_pending
, m0
);
/* CAN ONLY DO 1 item here
* if you change this if to while, HA HA
* it'll go right back onto
* the pending queue (which means things will
* be reordered on the queue!)
printf("ACK sending pending queue 0x%x len 0x%x\n",
(void) cons_senddata(copcb
, m0
);
case ECN_ACCEPT
: /* call accepted at other end */
/* adr_src, adr_dst are as given in the ECN_CALL
* pcb field is copied from our ECN_CALL
* request, confirm gives me a channel number
ASSERT( ecnrq
->e_vc
!= 0);
cons_chan_to_pcb((int)ecnrq
->e_vc
, __LINE__
)
cons_chan_to_pcb((int)ecnrq
->e_vc
)
/* error: already exists */
printf("cons PANIC: dbl confirm for channel 0x%x\n",
copcb
= (struct cons_pcb
*)ecnrq
->e_pcb
;
if( copcb
->co_myself
!= copcb
) {
printf("BAD e_pcb from ecn (0x%x) cmd 0x%x\n",
ecnrq
->e_pcb
, ecnrq
->e_cmd
);
if(mm
->m_type
== MT_FREE
)
dump_buf (ecnrq
, sizeof (*ecnrq
));
copcb
->co_channel
= (int)ecnrq
->e_vc
;
/* tp0 will take care of itself */
if( copcb
->co_flags
& CONSF_XTS
)
soisconnected(copcb
->co_socket
); /* wake 'em up */
wakeup( (caddr_t
)&copcb
->co_state
);
* Anything on the pending queue for this connection?
if( copcb
->co_pending
.ifq_len
> 0 ) {
register struct mbuf
*m0
;
IF_DEQUEUE( &copcb
->co_pending
, m0
);
/* CAN ONLY DO 1 item here
* if you change this if to while, HA HA
* it'll go right back onto
* the pending queue (which means things will
* be reordered on the queue!)
printf("ACPT sending pending queue 0x%x len 0x%x\n",
(void) cons_senddata(copcb
, m0
);
/* other end refused our connect request */
/* src, dst are as given in the ECN_CALL */
copcb
= (struct cons_pcb
*)ecnrq
->e_pcb
;
if( copcb
->co_myself
!= copcb
) {
printf("BAD e_pcb from ecn (0x%x) cmd 0x%x\n",
ecnrq
->e_pcb
, ecnrq
->e_cmd
);
if(mm
->m_type
== MT_FREE
)
dump_buf (ecnrq
, sizeof (*ecnrq
));
dump_buf (copcb
, sizeof (*copcb
));
copcb
->co_state
= CLOSED
; /* do we have to do a clear?? */
copcb
->co_channel
= X_NOCHANNEL
;
copcb
->co_socket
->so_error
= ECONNREFUSED
;
/* TODO: if there's diagnostic info in the
* packet, and it's more useful than this E*,
soisdisconnected(copcb
->co_socket
); /* wake 'em up */
printf("ECN_REFUSE: waking up 0x%x\n",
(caddr_t
)&copcb
->co_state
);
wakeup( (caddr_t
)&copcb
->co_state
);
* Anything on the pending queue for this connection?
while( copcb
->co_pending
.ifq_len
> 0 ) {
register struct mbuf
*m0
;
IF_DEQUEUE( &copcb
->co_pending
, m0
);
if ( ecnrq
->e_reason
== E_CO_NORESOURCES
) {
cons_clear_and_detach( copcb
, DONTCLEAR
, PRC_QUENCH
);
} else if(copcb
->co_socket
) {
copcb
->co_socket
->so_error
= find_error_reason( ecnrq
);
case ECN_CONNECT
: /* incoming call */
* ECN_CONNECT indication gives adc_src, adc_dst and channel
ASSERT( ecnrq
->e_vc
!= 0);
cons_incoming(ifp
, ecnrq
);
* ECN_CLEAR(indication) (if we can construct such a beast)
* Throw away anything queued pending on this connection
* give a reset indication to the upper layer if TP
ASSERT( ecnrq
->e_vc
!= 0);
if( ecnrq
->e_cmd
== ECN_CLEAR
)
if( ! (copcb
=cons_chan_to_pcb((int)ecnrq
->e_vc
, __LINE__
)) )
if( ! (copcb
=cons_chan_to_pcb((int)ecnrq
->e_vc
)) )
while( copcb
->co_pending
.ifq_len
) {
register struct mbuf
*m0
;
IF_DEQUEUE( &copcb
->co_pending
, m0
);
copcb
->co_state
= CLOSED
; /* do we have to do a clear? */
copcb
->co_channel
= X_NOCHANNEL
;
cons_clear_and_detach( copcb
, DONTCLEAR
, PRC_ROUTEDEAD
);
copcb
->co_socket
->so_error
= find_error_reason( ecnrq
);
ASSERT( ecnrq
->e_vc
!= 0);
struct mbuf
*thedata
= e_data(ecnrq
);
u_int
*firstint
= mtod( thedata
, u_int
*);
if( (*firstint
& 0xff000000) != 0x81000000 ) {
switch( ((*firstint
) & 0x00ff0000) >> 20 ) {
printf(" ECN_RECEIVE! BAD DATA\n" );
dump_buf( thedata
, 20 + 12 );
cons_chan_to_pcb( (int)ecnrq
->e_vc
, __LINE__
)
cons_chan_to_pcb( (int)ecnrq
->e_vc
)
) == (struct cons_pcb
*)0 ) {
tptrace(TPPTmisc
, "ECN_RECEIVE DROPPED chan \n",
if( ecnrq
->e_info
& ECN_INFO_RCVD_INT
) {
printf("consintr: interrupt pkttype : DROPPED\n");
IncStat(co_intrpt_pkts_in
);
if( copcb
->co_proto
== CLNP_proto
)
/* IP: put it on the queue and set soft interrupt */
extern struct ifqueue clnlintrq
;
register struct mbuf
*ifpp
; /* for ptr to ifp */
register struct mbuf
*data
= e_data(ecnrq
);
/* when acting as a subnet service, have to prepend a
* pointer to the ifnet before handing this to clnp
if( ( data
->m_off
> MMINOFF
+ sizeof(struct snpa_hdr
)) &&
( data
->m_off
<= MMAXOFF
)) {
data
->m_off
-= sizeof(struct snpa_hdr
);
data
->m_len
+= sizeof(struct snpa_hdr
);
MGET(ifpp
, M_DONTWAIT
, MT_XHEADER
);
m_freem(m
); /* frees everything */
ifpp
->m_len
= sizeof(struct snpa_hdr
);
tptrace(TPPTmisc
, "-->CLNP copcb\n", copcb
, 0, 0, 0);
* TODO: if we ever use esis/cons we have to
* think of something reasonable to stick in the
* snh_shost,snh_dhost fields. I guess
* the x.121 address is what we want.
* That would also require length fields in the
mtod(data
, struct snpa_hdr
*);
bzero((caddr_t
)&snh
, sizeof(struct snpa_hdr
));
bcopy((caddr_t
)&ifp
, (caddr_t
)&snh
->snh_ifp
,
*( mtod(data
, struct ifnet
**) ) = ifp
; /* KLUDGE */
printf("DROPPED! ecnrq 0x%x, data 0x%x\n", m
,data
);
"0x%x enqueued on ip Q: m_len 0x%x m_type 0x%x m_off 0x%x\n",
data
, data
->m_len
, data
->m_type
, data
->m_off
);
dump_buf(mtod(data
, caddr_t
), data
->m_len
);
e_data(ecnrq
) = (struct mbuf
*)0;
schednetisr(NETISR_CLNP
);
"-->HL pr_input so copcb channel\n",
copcb
->co_proto
->pr_input
,
printf( "0x%x --> HL proto 0x%x chan 0x%x\n",
e_data(ecnrq
), copcb
->co_proto
, copcb
->co_channel
);
(*copcb
->co_proto
->pr_input
)(e_data(ecnrq
),
copcb
->co_socket
, /* used by cons-transport interface */
(copcb
->co_flags
& CONSF_DGM
)?0:
copcb
->co_channel
);/* used by tp-cons interface */
* the pr_input will free the data chain, so we must
* zero the ptr to is so that m_free doesn't panic
e_data(ecnrq
) = (struct mbuf
*)0;
printf("consintr: unknown request\n");
printf("consintr: m_freem( 0x%x )\n", m
);
* Process an ioctl request.
* also set-time-limit, extend-time-limit
* for ALL channels, the time-limit ioctls will be done by open-a-dummy-socket,
* do ioctl with the channel number, close the socket (dumb!).
cons_ioctl(so
, cmd
, data
)
printf("cons_ioctl( cmd 0x%x )\n", cmd
);
*************************************************************
* Interface to CO Subnetwork service from CLNP *
* Must be a device interface. *****
* called through the ifnet structure.
* FUNCTION and ARGUMENTS:
consioctl(ifp
, cmd
, data
)
register struct ifnet
*ifp
;
register struct ifaddr
*ifa
= (struct ifaddr
*)data
;
register int s
= splimp();
register struct ifreq
*ifr
= (struct ifreq
*)data
;
switch (ifa
->ifa_addr
.sa_family
) {
if( (ifp
->if_flags
& IFF_UP
) == 0)
printf("CANNOT config cons with address family %d\n",
ifa
->ifa_addr
.sa_family
);
printf("consioctl: set flags to x%x\n", ifr
->ifr_flags
);
printf("consioctl: ifp flags are x%x\n", ifp
->if_flags
);
if( ifr
->ifr_flags
& IFF_LOOPBACK
)
ifp
->if_flags
|= IFF_LOOPBACK
;
ifp
->if_flags
&= ~IFF_LOOPBACK
;
/* if board is down but request takes it up, init the board */
if (ifr
->ifr_flags
& IFF_UP
&& (ifp
->if_flags
& IFF_UP
) == 0)
/* if board is up but request takes it down, shut the board down */
if (((ifr
->ifr_flags
& IFF_UP
) == 0) && (ifp
->if_flags
& IFF_UP
)) {
consshutdown(ifp
->if_unit
);
printf("consioctl: flags are x%x\n", ifp
->if_flags
);
/* warning: must coerse ifp to (struct ifstatus *) in order to use */
printf("consioctl: EICON status request\n");
ecnioctl(ifp
, cmd
, data
);
* cons_init() (which comes from autoconf)
* FUNCTION and ARGUMENTS:
* creates an ifp and fills it in; calls ifattach() on it.
register struct ifnet
*ifp
;
if(sizeof(struct ifnet
) > MLEN
) {
printf("Can't attach cons! sizeof(struct ifnet) > MLEN\n");
MGET(m
, M_DONTWAIT
, MT_IFADDR
);
printf("Can't attach cons! NO MBUFS!\n");
m
->m_len
= sizeof(struct ifnet
);
ifp
= consif
= mtod(m
, struct ifnet
*);
ifp
->if_ioctl
= consioctl
;
ifp
->if_output
= cosns_output
; /* called by clnp */
ifp
->if_flags
= IFF_LOOPBACK
; /* default */
printf("cons%d: pseudo device attached \n", ifp
->if_unit
);
* FUNCTION and ARGUMENTS:
* Initializes apropos data structures, etc.
* Marks the device as up.
* Calls device layer restart on the device if necessary.
register int _unit
; /* unit to initialize */
if ((ifp
= ecnifp(_unit
)) != (struct ifnet
*)0 ) {
if (consif
->if_addrlist
== (struct ifaddr
*)0)
if ((consif
->if_flags
& IFF_UP
) == 0) {
consif
->if_flags
|= IFF_UP
;
* cons_ioctl() when user takes down an interface w/ SIOCSIFFLAGS
* FUNCTION and ARGUMENTS:
* calls lower layer shutdown routine on the device.
* and marks the if as down if the if is the sw loopback pseudodevice.
register int _unit
; /* unit to shutdown */
extern struct ifnet
*ecnifp();
if ((ifp
= ecnifp(_unit
)) != (struct ifnet
*)0 ) {
if ((consif
->if_flags
& IFF_UP
) ) {
consif
->if_flags
&= ~IFF_UP
;
* cons_pcbbind(), cons_usrreq()
* FUNCTION and ARGUMENTS:
* Takes the argument (value) and stashes it into the last two
* nibbles of an X.121 address. Does this in the two nibbles beginning
* at the location defined by the character pointer (dst_octet) and the
* integer (dst_nibble). Nibble 0 is the lower nibble (high
* order 4 bits); nibble 1 is the low order 4 bits of *(dst_octet).
munge( value
, dst_octet
, dst_nibble
)
printf("MUNGE: value 0x%x dst_octet 0x%x, nibble 0x%x)\n",
value
, dst_octet
, dst_nibble
);
if (value
>= ISO_PORT_RESERVED
)
/* convert so it looks like a decimal number */
ones
= value
- (tens
* 10);
value
= tens
* 16 + ones
;
/* leave nibble same 'cause it's one after the last set nibble */
*dst_octet
&= ~(0xff<<(dst_nibble
<< 2)); /* zero it */
*dst_octet
|= ((value
>>4) << (dst_nibble
<<2));
dst_nibble
= 1-dst_nibble
;
*dst_octet
&= ~(0xff<<(dst_nibble
<< 2)); /* zero it */
*dst_octet
|= ((value
&0xff) << (dst_nibble
<<2));
* DTEtoNSAP(), FACILtoNSAP()
* FUNCTION and ARGUMENTS:
* return the port/tsuffix represented by the two digits found in a
* bcd string beginning at the (dst_nibble)th nibble of the
* octet BEFORE (dst_octet).
* dst_octet,dst_nibble is the nibble after the one we'll look at
* an integer, the port/tsuffix
* Note- converts to a port > 1000 if necessary.
unmunge( dst_octet
, dst_nibble
)
register u_short last
= 0;
/* leave nibble same 'cause it's one after the last set nibble */
printf("unmunge: *octet 0x%x, nibble 0x%x\n", *dst_octet
,
last
= ((*dst_octet
) & (0xff<<(dst_nibble
<<2)));
dst_nibble
= 1-dst_nibble
;
last
|= ((*dst_octet
) & (0xff<<(dst_nibble
<< 2)));
/* convert to a decimal number */
printf("unmunge computes 0x%x\n", last
);
if((int)last
+1000 >= ISO_PORT_RESERVED
)
printf("unmunge returns 0x%x\n", last
);
* NAME: make_partial_x25_packet()
* FUNCTION and ARGUMENTS:
* Makes part of an X.25 call packet, for use by the eicon board.
* (src) and (dst) are the NSAP-addresses of source and destination.
* (proto) is the higher-layer protocol number (in iso.h)
* (buf) is a ptr to a buffer into which to write this partial header.
* The partial header looks like (choke):
* 1 calling DTE len | called DTE len (lengths in nibbles)
* 2..n-1 called DTE addr | (<-- boundary may be middle of an octet)
* calling DTE addr | zero nibble to round to octet boundary.
* n Facility length (in octets)
* n+1 Facility field, which is a set of:
* m+1 facil param len (for >2-byte facilities) in octets
* m+2..p facil param field
* q user data (protocol identification octet)
int cons_use_udata
= 1; /* KLUDGE FOR DEBUGGING */
make_partial_x25_packet(copcb
, m
)
struct sockaddr_iso
*src
, *dst
;
caddr_t buf
= mtod(m
, caddr_t
);
register caddr_t ptr
= buf
+ 1; /* make room for 2 length nibbles */
proto
= copcb
->co_proto
->pr_protocol
,
flag
= copcb
->co_flags
& CONSF_XTS
;
printf("make_partial_x25_packet(0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n",
src
, dst
, proto
, m
, flag
);
* Note - order of addrs in x25 pkt hdr is wierd:
* calling len/called len/called addr/calling addr (p.40 ISO 8202)
if( (len
= copcb
->co_peer_dte
.dtea_niblen
) > 0 ) {
nibble_copy( (char *)(copcb
->co_peer_dte
.dtea_addr
), HIGH_NIBBLE
,
if ((len
= NSAPtoDTE( ptr
, HIGH_NIBBLE
, dst
)) <=0 ) {
*buf
= len
; /* fill in called dte addr length */
ptr
+= len
>>1; /* len is in nibbles */
if ((len
= NSAPtoDTE( ptr
, 1-(len
&0x1), src
)) <=0 ) {
ptr
+= len
>>1; /* len is in nibbles */
*buf
|= len
<< 4; /* fill in calling dte addr length */
printf("make_partial 2: ptr 0x%x, len 0x%x oddness 0x%x\n",
/* if either of the addresses were an odd length, the count is off by 1 */
/* ptr now points to facil length (len of whole facil field in OCTETS */
printf("make_partial calling: ptr 0x%x, len 0x%x\n", ptr
,
src
->siso_addr
.isoa_len
);
*ptr
= 0xcb; /* calling facility code */
ptr
++; /* leave room for facil param len (in OCTETS + 1) */
ptr
++; /* leave room for the facil param len (in nibbles),
* high two bits of which indicate full/partial NSAP
len
= src
->siso_addr
.isoa_len
;
bcopy( &src
->siso_addr
.isoa_afi
, ptr
, len
);
*(ptr
-2) = len
+2; /* facil param len in octets */
*(ptr
-1) = len
<<1; /* facil param len in nibbles */
printf("make_partial called: ptr 0x%x, len 0x%x\n", ptr
,
dst
->siso_addr
.isoa_len
);
*ptr
= 0xc9; /* called facility code */
ptr
++; /* leave room for facil param len (in OCTETS + 1) */
ptr
++; /* leave room for the facil param len (in nibbles),
* high two bits of which indicate full/partial NSAP
len
= dst
->siso_addr
.isoa_len
;
bcopy( &dst
->siso_addr
.isoa_afi
, ptr
, len
);
*(ptr
-2) = len
+2; /* facil param len = addr len + 1 for each of these
* two length fields, in octets */
*(ptr
-1) = len
<<1; /* facil param len in nibbles */
*facil_len
= ptr
- facil_len
- 1;
if(*facil_len
> X25_FACIL_LEN_MAX
)
if (copcb
->co_x25crud_len
> 0) {
* The user specified something. Stick it in
bcopy(copcb
->co_x25crud
, ptr
, copcb
->co_x25crud_len
);
ptr
+= copcb
->co_x25crud_len
;
/* protocol identifier */
/* unfortunately all are considered 1 protocol */
/* no user data for TP */
ptr
++; /* count the proto id byte! */
ptr
++; /* count the proto id byte! */
*ptr
= 0xff; /* reserved for future extensions */
/* we're stealing this value for local use */
ptr
++; /* count the proto id byte! */
buflen
= (int)(ptr
- buf
);
printf("ECN_CONNECT DATA buf 0x%x len %d (0x%x)\n",
for( i
=0; i
< buflen
; ) {
printf("+%d: %x %x %x %x %x %x %x %x\n",
*(buf
+i
), *(buf
+i
+1), *(buf
+i
+2), *(buf
+i
+3),
*(buf
+i
+4), *(buf
+i
+5), *(buf
+i
+6), *(buf
+i
+7));
printf("make_partial returns buf 0x%x size 0x%x bytes\n",
mtod(m
, caddr_t
), buflen
);
ASSERT( X25_PARTIAL_PKT_LEN_MAX
< MLEN
);
if(buflen
> X25_PARTIAL_PKT_LEN_MAX
)
* make_partial_x25_packet()
* FUNCTION and ARGUMENTS:
* get a DTE address from an NSAP-address (struct sockaddr_iso)
* (dst_octet) is the octet into which to begin stashing the DTE addr
* (dst_nibble) takes 0 or 1. 1 means begin filling in the DTE addr
* in the high-order nibble of dst_octet. 0 means low-order nibble.
* (addr) is the NSAP-address
* (flag) is true if the transport suffix is to become the
* last two digits of the DTE address
* A DTE address is a series of BCD digits
* A DTE address may have leading zeros. The are significant.
* 1 digit per nibble, may be an odd number of nibbles.
* An NSAP-address has the DTE address in the IDI. Leading zeros are
* significant. Trailing hex f indicates the end of the DTE address.
* Also is a series of BCD digits, one per nibble.
* # significant digits in the DTE address, -1 if error.
NSAPtoDTE( dst_octet
, dst_nibble
, addr
)
register struct sockaddr_iso
*addr
;
u_char x121string
[7]; /* maximum is 14 digits */
printf("NSAPtoDTE: nsap: %s\n", clnp_iso_addrp(&addr
->siso_addr
));
error
= iso_8208snparesolve(addr
, x121string
, &x121strlen
);
/* no snpa - cannot send */
printf("NSAPtoDTE: 8208resolve: %d\n", error
);
ASSERT(x121strlen
== sizeof(struct dte_addr
));
dtea
= (struct dte_addr
*)x121string
;
x121strlen
= dtea
->dtea_niblen
;
nibble_copy((char *)x121string
, HIGH_NIBBLE
,
dst_octet
, dst_nibble
, x121strlen
);
* FUNCTION and ARGUMENTS:
* Creates and NSAP in the sockaddr_iso (addr) from the
* x.25 facility found at (buf), of length (buf_len).
* 0 if ok, non-zero if error;
FACILtoNSAP( buf
, buf_len
, addr
)
u_char buf_len
; /* in bytes */
register struct sockaddr_iso
*addr
;
printf("FACILtoNSAP( 0x%x, 0x%x, 0x%x )\n",
/* despite the fact that X.25 makes us put a length in nibbles
* here, the NSAP-addrs are always in full octets
bzero( addr
, sizeof (struct sockaddr_iso
) );
ASSERT(buf_len
<= 1+sizeof (struct iso_addr
));
if(buf_len
> 1+sizeof (struct iso_addr
)) {
ASSERT(len_in_nibbles
== (buf_len
- 1)<<1);
if(len_in_nibbles
!= (buf_len
- 1)<<1) {
bcopy(buf
, &addr
->siso_addr
.isoa_afi
, buf_len
-1);
addr
->siso_addr
.isoa_len
= buf_len
-1;
printf("FACILtoNSAP: isoa_len 0x%x\n",
addr
->siso_addr
.isoa_len
);
addr
->siso_family
= AF_ISO
;
unmunge( ((caddr_t
)&addr
->siso_addr
.t37_idi
) + ADDR37_IDI_LEN
, 1 );
* FUNCTION and ARGUMENTS:
* Creates a type 37 NSAP in the sockaddr_iso (addr)
* from a DTE address found at the (src_nibble)th nibble of
* the octet (src_octet), of length (src_nib_len).
DTEtoNSAP(addr
, src_octet
, src_nibble
, src_nib_len
)
struct sockaddr_iso
*addr
;
int src_nibble
, src_nib_len
;
static char *z_pad
= "\0\0\0\0\0\0\0";
static char *f_pad
= "\021\021\021\021\021\021\021";
printf("DTEtoNSAP( 0x%x, 0x%x, 0x%x, 0x%x )\n",
src_octet
, src_nibble
, src_nib_len
, addr
);
bzero( addr
, sizeof(*addr
));
addr
->siso_family
= AF_ISO
;
* Coming from a DTE addr it's always type 37.
* src_octet <-- starting place in the NSAP-address of
* the embedded SNPA-address (x.121 addr or DTE addr).
addr
->siso_addr
.isoa_afi
= 0x37;
/* first, figure out what pad to use and pad */
first_nib
= (*src_octet
) >> (SHIFT
*(1-src_nibble
));
pad_len
= (ADDR37_IDI_LEN
<<1 - src_nib_len
);
nibble_copy(first_nib
? z_pad
: f_pad
, HIGH_NIBBLE
,
(caddr_t
) addr
->siso_addr
.t37_idi
, HIGH_NIBBLE
, pad_len
);
dst_octet
+= (pad_len
>>1);
dst_nibble
= 1-(pad_len
& 0x1);
printf("DTEtoNSAP 2( 0x%x, 0x%x, 0x%x, 0x%x )\n",
dst_octet
, dst_nibble
, pad_len
, src_nib_len
);
/* now copy the dte address */
nibble_copy( src_octet
, src_nibble
, dst_octet
, dst_nibble
, src_nib_len
);
addr
->siso_addr
.isoa_len
= ADDR37_IDI_LEN
+ ADDR37_DSP_LEN
+1 /* for afi */;
addr
->siso_tsuffix
= unmunge(
(caddr_t
) &(addr
->siso_addr
.t37_idi
[ADDR37_IDI_LEN
]), HIGH_NIBBLE
);
printf("DTEtoNSAP 3 returning 0 tsuffix 0x%x\n", addr
->siso_tsuffix
);
* FUNCTION and ARGUMENTS:
* parses (buf_len) bytes beginning at (buf) and finds
* a called nsap, a calling nsap, and protocol identifier.
parse_facil( buf
, buf_len
, called
, calling
, proto
, peer_dte
)
u_char buf_len
; /* in bytes */
register struct sockaddr_iso
*called
, *calling
;
struct dte_addr
*peer_dte
;
struct sockaddr_iso
*addr
;
int addrs_not_parsed
= (int)0xcb + (int)0xc9;
printf("parse_facil( 0x%x, 0x%x, 0x%x, 0x%x, 0x%x )\n",
buf
, buf_len
, called
, calling
, *proto
);
/* find the beginnings of the facility fields in buf
* by skipping over the called & calling DTE addresses
* i <- # nibbles in called + # nibbles in calling
* i += 1 so that an odd nibble gets rounded up to even
* before dividing by 2, then divide by two to get # octets
i
= (int)(*buf
>> 4) + (int)(*buf
&0xf);
ptr
= (caddr_t
) (buf
+ (i
>>1));
/* now i is number of octets */
ptr
++; /* plus one for the DTE lengths byte */
/* ptr now is at facil_length field */
printf("parse_facils: facil length is 0x%x\n", (int) *facil_len
);
while( ptr
<= (caddr_t
)(facil_len
+ (int)*facil_len
) ) {
/* get NSAP addresses from facilities */
addrs_not_parsed
-= 0xcb;
addrs_not_parsed
-= 0xc9;
/* from here to default are legit cases that I ignore */
case 0xca: /* end-to-end transit delay negot */
case 0xc6: /* network user id */
case 0xc5: /* charging info : indicating monetary unit */
case 0xc2: /* charging info : indicating segment count */
case 0xc1: /* charging info : indicating call duration */
case 0xc4: /* RPOA extended format */
case 0xc3: /* call redirection notification */
addr
= (struct sockaddr_iso
*)0;
case 0x0a: /* min. throughput class negot */
case 0x02: /* throughput class */
case 0x03: case 0x47: /* CUG shit */
case 0x0b: /* expedited data negot */
case 0x01: /* Fast select or reverse charging
(example of intelligent protocol design) */
case 0x04: /* charging info : requesting service */
case 0x08: /* called line addr modified notification */
addr
= (struct sockaddr_iso
*)0;
case 0x42: /* pkt size */
case 0x43: /* win size */
case 0x44: /* RPOA basic format */
case 0x41: /* bilateral CUG shit */
case 0x49: /* transit delay selection and indication */
addr
= (struct sockaddr_iso
*)0;
/* don't have any 3 octets */
"BOGUS FACILITY CODE facil_len 0x%x *facil_len 0x%x, ptr 0x%x *ptr 0x%x\n",
addr
= (struct sockaddr_iso
*)0;
/* facil that we don't handle */
ptr
++; /* one for facil code */
if(facil_param_len
== 0) /* variable length */
facil_param_len
= (int)*ptr
; /* 1 + the real facil param */
if( addr
&& FACILtoNSAP(ptr
+1, facil_param_len
-1, addr
) ) {
/* no facilities, get NSAP addresses from DTE addresses */
panic("Called DTE address absent");
DTEtoNSAP(called
, (buf
+ 1)/*octet*/,
printf("cons: panic: Calling DTE address absent");
nibble_copy((buf
+ (ed
>>1)+1)/*octet*/, 1-(ed
&0x1)/*nibble*/,
peer_dte
->dtea_addr
, HIGH_NIBBLE
, ing
);
DTEtoNSAP(calling
, (buf
+ (ed
>>1)+1)/*octet*/,
1-(ed
&0x1)/*nibble*/, ing
);
ASSERT( ptr
== (caddr_t
)(facil_len
+ 1 + (int)*facil_len
) );
* now look for user data to find protocol identifier
if( ptr
== buf
+ buf_len
) {
*proto
= ISOPROTO_TP
; /* to proto id --> use TP */
printf("NO USER DATA: use TP\n");
ASSERT ( ptr
< buf
+ buf_len
);
if ( ptr
>= buf
+ buf_len
) {
printf("ptr 0x%x buf 0x%x buf_len 0x%x buf+buf_len 0x%x\n",
ptr
, buf
, buf_len
, buf
+buf_len
);
printf("proto byte 0x%x, value 0x%x\n", ptr
, *ptr
);
*proto
= ISOPROTO_INACT_NL
;
/* can check for "an2" or can ignore the rest of the u data */
case 0xff: /* reserved for future extensions */
case 0x82: /* 9542 not implemented */
case 0x84: /* 8878/A SNDCP not implemented */
#endif NARGOXTWENTYFIVE > 0