OpenSS7 SS7 for the Common Man |
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |
||||||||||||||||||||||||||
Home | Overview | Status | News | Documentation | Resources | About | |||||||||||||||||||||
File /code/strss7/drivers/inet/inet.c#ident "@(#) $RCSfile: inet.c,v $ $Name: $($Revision: 0.8.2.27 $) $Date: 2003/05/27 10:08:28 $" static char const ident[] = "$RCSfile: inet.c,v $ $Name: $($Revision: 0.8.2.27 $) $Date: 2003/05/27 10:08:28 $"; /* * This driver provides the functionality of IP (Internet Protocol) over a * connectionless network provider (NPI). It provides a STREAMS-based * encapsulation of the Linux IP stack. */ #include <linux/config.h> #include <linux/version.h> #ifdef MODVERSIONS #include <linux/modversions.h> #endif #include <linux/module.h> #include <linux/net.h> #include <linux/in.h> #include <linux/ip.h> #include <net/sock.h> #include <net/tcp.h> #include <sys/stream.h> #include <sys/stropts.h> #include <sys/cmn_err.h> #include <sys/tpi.h> #include <sys/tpi_inet.h> #include <sys/xti_inet.h> #if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE) #include <netinet/sctp.h> #endif #include "../debug.h" #include "../bufq.h" #define SS_DESCRIP "SOCKSYS STREAMS (TPI) DRIVER." "\n" \ "Part of the OpenSS7 Stack for LiS STREAMS." #define SS_COPYRIGHT "Copyright (c) 1997-2002 OpenSS7 Corporation. All Rights Reserved." #define SS_DEVICE "Supports OpenSS7 INET Drivers." #define SS_CONTACT "Brian Bidulock <bidulock@openss7.org>" #define SS_LICENSE "GPL" #define SS_BANNER SS_DESCRIP "\n" \ SS_COPYRIGHT "\n" \ SS_DEVICE "\n" \ SS_CONTACT MODULE_AUTHOR(SS_CONTACT); MODULE_DESCRIPTION(SS_DESCRIP); MODULE_SUPPORTED_DEVICE(SS_DEVICE); #ifdef MODULE_LICENSE MODULE_LICENSE(SS_LICENSE); #endif #ifndef SS_CMAJOR #define SS_CMAJOR 30 #define IP_CMINOR 32 #define ICMP_CMINOR 33 #define GGP_CMINOR 34 #define IPIP_CMINOR 35 #define TCP_CMINOR 36 #define EGP_CMINOR 37 #define PUP_CMINOR 38 #define UDP_CMINOR 39 #define IDP_CMINOR 40 #define RAWIP_CMINOR 41 #define FREE_CMINOR 50 #endif #ifndef SS_NMAJOR #define SS_NMAJOR 4 #define SS_NMINOR 256 #endif #ifndef SS_IOC_MAGIC #define SS_IOC_MAGIC 'i' #endif #ifdef LINUX_2_4 #define INT int #else #define INT void #endif /* * ========================================================================= * * STREAMS Definitions * * ========================================================================= */ #define SS_MOD_NAME "socksys" #define SS_MOD_ID ('I'<<8|SS_IOC_MAGIC) STATIC struct module_info ss_minfo = { mi_idnum:SS_MOD_ID, /* Module ID number */ mi_idname:SS_MOD_NAME, /* Module name */ mi_minpsz:0, /* Min packet size accepted */ mi_maxpsz:INFPSZ, /* Max packet size accepted */ mi_hiwat:1 << 15, /* Hi water mark */ mi_lowat:1 << 10 /* Lo water mark */ }; STATIC int ss_open(queue_t *, dev_t *, int, int, cred_t *); STATIC int ss_close(queue_t *, int, cred_t *); STATIC INT ss_rput(queue_t *, mblk_t *); STATIC INT ss_rsrv(queue_t *); STATIC struct qinit ss_rinit = { qi_putp:ss_rput, /* Read put (msg from below) */ qi_srvp:ss_rsrv, /* Read queue service */ qi_qopen:ss_open, /* Each open */ qi_qclose:ss_close, /* Last close */ qi_minfo:&ss_minfo, /* Information */ }; STATIC INT ss_wput(queue_t *, mblk_t *); STATIC INT ss_wsrv(queue_t *); STATIC struct qinit ss_winit = { qi_putp:ss_wput, /* Write put (msg from above) */ qi_srvp:ss_wsrv, /* Write queue service */ qi_minfo:&ss_minfo, /* Information */ }; STATIC struct streamtab ss_info = { st_rdinit:&ss_rinit, /* Upper read queue */ st_wrinit:&ss_winit, /* Lower read queue */ }; /* * Queue put and service return values */ #define QR_DONE 0 #define QR_ABSORBED 1 #define QR_TRIMMED 2 #define QR_LOOP 3 #define QR_PASSALONG 4 #define QR_PASSFLOW 5 #define QR_DISABLE 6 #define QR_STRIP 7 /* * TLI interface state flags */ #define TSF_UNBND ( 1 << TS_UNBND ) #define TSF_WACK_BREQ ( 1 << TS_WACK_BREQ ) #define TSF_WACK_UREQ ( 1 << TS_WACK_UREQ ) #define TSF_IDLE ( 1 << TS_IDLE ) #ifdef TS_WACK_OPTREQ #define TSF_WACK_OPTREQ ( 1 << TS_WACK_OPTREQ ) #endif #define TSF_WACK_CREQ ( 1 << TS_WACK_CREQ ) #define TSF_WCON_CREQ ( 1 << TS_WCON_CREQ ) #define TSF_WRES_CIND ( 1 << TS_WRES_CIND ) #define TSF_WACK_CRES ( 1 << TS_WACK_CRES ) #define TSF_DATA_XFER ( 1 << TS_DATA_XFER ) #define TSF_WIND_ORDREL ( 1 << TS_WIND_ORDREL ) #define TSF_WREQ_ORDREL ( 1 << TS_WREQ_ORDREL ) #define TSF_WACK_DREQ6 ( 1 << TS_WACK_DREQ6 ) #define TSF_WACK_DREQ7 ( 1 << TS_WACK_DREQ7 ) #define TSF_WACK_DREQ9 ( 1 << TS_WACK_DREQ9 ) #define TSF_WACK_DREQ10 ( 1 << TS_WACK_DREQ10 ) #define TSF_WACK_DREQ11 ( 1 << TS_WACK_DREQ11 ) #define TSF_NOSTATES ( 1 << TS_NOSTATES ) #define TSM_WACK_DREQ (TSF_WACK_DREQ6 \ |TSF_WACK_DREQ7 \ |TSF_WACK_DREQ9 \ |TSF_WACK_DREQ10 \ |TSF_WACK_DREQ11) #define TSM_LISTEN (TSF_IDLE \ |TSF_WRES_CIND) #define TSM_CONNECTED (TSF_WCON_CREQ\ |TSF_WRES_CIND\ |TSF_DATA_XFER\ |TSF_WIND_ORDREL\ |TSF_WREQ_ORDREL) #define TSM_DISCONN (TSF_IDLE\ |TSF_UNBND) #define TSM_INDATA (TSF_DATA_XFER\ |TSF_WIND_ORDREL) #define TSM_OUTDATA (TSF_DATA_XFER\ |TSF_WREQ_ORDREL) #ifndef T_PROVIDER #define T_PROVIDER 0 #define T_USER 1 #endif /* * Socket state masks */ /* * TCP state masks */ #define TCPM_CLOSING (TCPF_CLOSE\ |TCPF_TIME_WAIT\ |TCPF_CLOSE_WAIT) #define TCPM_CONNIND (TCPF_SYN_RECV\ |TCPF_ESTABLISHED\ |TCPF_LISTEN) /* * ========================================================================= * * IP Private Datastructures * * ========================================================================= */ typedef struct sockaddr_in ss_addr_t; typedef struct ss_dflt { struct ip_options opt; /* T_IP_OPTIONS */ ulong tos; /* T_IP_TOS */ ulong ttl; /* T_IP_TTL */ ulong reuse; /* T_IP_REUSE_ADDR */ ulong norte; /* T_IP_DONTROUTE */ ulong bcast; /* T_IP_BROADCAST */ ulong nodly; /* T_TCP_NODELAY, T_SCTP_NODELAY */ ulong mss; /* T_TCP_MAXSEG */ ulong alive; /* T_TCP_KEEPALIVE */ ulong csum; /* T_UDP_CHECKSUM */ #if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE) ulong cork; /* T_SCTP_CORK */ ulong ppi; /* T_SCTP_PPI */ ulong sid; /* T_SCTP_SID */ ulong ssn; /* T_SCTP_SSN */ ulong tsn; /* T_SCTP_TSN */ ulong recvopt; /* T_SCTP_RECVOPT */ ulong clife; /* T_SCTP_COOKIE_LIFE */ ulong sack; /* T_SCTP_SACK_DELAY */ ulong prtx; /* T_SCTP_PATH_MAX_RETRANS */ ulong artx; /* T_SCTP_ASSOC_MAX_RETRANS */ ulong irtx; /* T_SCTP_MAX_INIT_RETRIES */ ulong hitvl; /* T_SCTP_HEARTBEAT_ITVL */ ulong rtoinit; /* T_SCTP_RTO_INITIAL */ ulong rtomin; /* T_SCTP_RTO_MIN */ ulong rtomax; /* T_SCTP_RTO_MAX */ ulong ostr; /* T_SCTP_OSTREAMS */ ulong istr; /* T_SCTP_ISTREAMS */ ulong cinc; /* T_SCTP_COOKIE_INC */ ulong titvl; /* T_SCTP_THROTTLE_ITVL */ ulong mac; /* T_SCTP_MAC_TYPE */ #endif } ss_dflt_t; typedef struct ss_profile { struct { uint family; uint type; uint protocol; } prot; struct T_info_ack info; } ss_profile_t; typedef struct inet { struct inet *next; /* list of all IP-Users */ struct inet **prev; /* list of all IP-Users */ size_t refcnt; /* structure reference count */ lis_spin_lock_t lock; /* structure lock */ ushort cmajor; /* major device number */ ushort cminor; /* minor device number */ queue_t *rq; /* associated read queue */ queue_t *wq; /* associated write queue */ cred_t cred; /* credientials */ lis_spin_lock_t qlock; /* queue lock */ queue_t *rwait; /* RD queue waiting on lock */ queue_t *wwait; /* WR queue waiting on lock */ uint rbid; /* RD buffer call id */ uint wbid; /* WR buffer call id */ ushort port; /* port/protocol number */ int tcp_state; /* tcp state history */ ss_profile_t p; /* protocol profile */ struct { void (*state_change) (struct sock *); void (*data_ready) (struct sock *, int); void (*write_space) (struct sock *); void (*error_report) (struct sock *); } cb_save; /* call back holder */ ss_addr_t src; /* bound address */ ss_addr_t dst; /* connected address */ ss_dflt_t options; /* protocol options */ unsigned char _pad[40]; /* pad for options */ bufq_t conq; /* connection queue */ uint conind; /* number of connection indications */ struct socket *sock; /* socket pointer */ } ss_t; #define PRIV(__q) ((ss_t *)((__q)->q_ptr)) #define SOCK_PRIV(__sk) ((ss_t *)(__sk)->user_data) typedef struct ss_opts { uint flags; /* success flags */ struct ip_options *opt; /* T_IP_OPTIONS */ ulong *tos; /* T_IP_TOS */ ulong *ttl; /* T_IP_TTL */ ulong *reuse; /* T_IP_REUSE_ADDR */ ulong *norte; /* T_IP_DONTROUTE */ ulong *bcast; /* T_IP_BROADCAST */ ulong *nodly; /* T_TCP_NODELAY, T_SCTP_NODELAY */ ulong *mss; /* T_TCP_MAXSEG */ ulong *alive; /* T_TCP_KEEPALIVE */ ulong *csum; /* T_UDP_CHECKSUM */ ulong *ropt; struct in_pktinfo *pkinfo; #if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE) ulong *cork; /* T_SCTP_CORK */ ulong *ppi; /* T_SCTP_PPI */ ulong *sid; /* T_SCTP_SID */ ulong *ssn; /* T_SCTP_SSN */ ulong *tsn; /* T_SCTP_TSN */ ulong *recvopt; /* T_SCTP_RECVOPT */ ulong *clife; /* T_SCTP_COOKIE_LIFE */ ulong *sack; /* T_SCTP_SACK_DELAY */ ulong *prtx; /* T_SCTP_PATH_MAX_RETRANS */ ulong *artx; /* T_SCTP_ASSOC_MAX_RETRANS */ ulong *irtx; /* T_SCTP_MAX_INIT_RETRIES */ ulong *hitvl; /* T_SCTP_HEARTBEAT_ITVL */ ulong *rtoinit; /* T_SCTP_RTO_INITIAL */ ulong *rtomin; /* T_SCTP_RTO_MIN */ ulong *rtomax; /* T_SCTP_RTO_MAX */ ulong *ostr; /* T_SCTP_OSTREAMS */ ulong *istr; /* T_SCTP_ISTREAMS */ ulong *cinc; /* T_SCTP_COOKIE_INC */ ulong *titvl; /* T_SCTP_THROTTLE_ITVL */ ulong *mac; /* T_SCTP_MAC_TYPE */ struct t_sctp_hb *hb; /* T_SCTP_HB */ struct t_sctp_rto *rto; /* T_SCTP_RTO */ struct t_sctp_status *stat; /* T_SCTP_STATUS */ ulong *debug; /* T_SCTP_DEBUG */ #endif } ss_opts_t; #define ip_default_opt { 0, } #define ip_default_tos 1 #define ip_default_ttl 64 #define ip_default_reuse 1 #define ip_default_norte 1 #define ip_default_bcast 1 #define tcp_default_nodly 1 #define tcp_default_mss 576 #define tcp_default_alive 1 #define udp_default_csum 0 #define TF_IP_OPTIONS (1<< 0) #define TF_IP_TOS (1<< 1) #define TF_IP_TTL (1<< 2) #define TF_IP_REUSEADDR (1<< 3) #define TF_IP_DONTROUTE (1<< 4) #define TF_IP_BROADCAST (1<< 5) #define TF_IP_PKTINFO (1<< 6) #define TF_IP_RETOPTS (1<< 7) #define TF_TCP_NODELAY (1<< 8) #define TF_TCP_MAXSEG (1<< 9) #define TF_TCP_KEEPALIVE (1<<10) #define TF_UDP_CHECKSUM (1<<11) #if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE) #define TF_SCTP_NODELAY TF_TCP_NODELAY #define TF_SCTP_MAXSEG TF_TCP_MAXSEG #define TF_SCTP_CORK (1<<10) #define TF_SCTP_PPI (1<<11) #define TF_SCTP_SID (1<<12) #define TF_SCTP_SSN (1<<13) #define TF_SCTP_TSN (1<<14) #define TF_SCTP_RECVOPT (1<<15) #define TF_SCTP_COOKIE_LIFE (1<<16) #define TF_SCTP_SACK_DELAY (1<<17) #define TF_SCTP_MAX_RETRANS (1<<18) #define TF_SCTP_ASSOC_MAX_RETRANS (1<<19) #define TF_SCTP_MAX_INIT_RETRIES (1<<20) #define TF_SCTP_HEARTBEAT_ITVL (1<<21) #define TF_SCTP_RTO_INITIAL (1<<22) #define TF_SCTP_RTO_MIN (1<<23) #define TF_SCTP_RTO_MAX (1<<24) #define TF_SCTP_OSTREAMS (1<<25) #define TF_SCTP_ISTREAMS (1<<26) #define TF_SCTP_COOKIE_INC (1<<27) #define TF_SCTP_THROTTLE_ITVL (1<<28) #define TF_SCTP_MAC_TYPE (1<<29) #define TF_SCTP_HB (1<<30) #define TF_SCTP_RTO (1<<31) #define TF_SCTP_STATUS (1<< 6) /* FIXME */ #define TF_SCTP_DEBUG (1<< 7) /* FIXME */ #endif #define TM_OPT_SENDMSG ( TF_IP_OPTIONS \ | TF_IP_TOS \ | TF_IP_TTL \ | TF_IP_DONTROUTE \ | TF_IP_PKTINFO \ ) #define _T_CHECK 1 #define _T_NEGOTIATE 2 #define _T_DEFAULT 3 #define _T_CURRENT 4 #define TF_CHECK (1<<_T_CHECK) #define TF_NEGOTIATE (1<<_T_NEGOTIATE) #define TF_DEFAULT (1<<_T_DEFAULT) #define TF_CURRENT (1<<_T_CURRENT) typedef struct ss_event { struct sock *sk; /* sock (child) for event */ int state; /* state at time of event */ } ss_event_t; STATIC lis_spin_lock_t ss_lock; /* protects ss_opens lists */ STATIC ss_t *ss_opens = NULL; #if 0 /* * for later when we support default destinations and default listeners */ STATIC ss_t *ss_dflt_dest = NULL; STATIC ss_t *ss_dflt_lstn = NULL; #endif /* * ========================================================================= * * Socket Callbacks * * ========================================================================= */ STATIC void ss_state_change(struct sock *sk); STATIC void ss_write_space(struct sock *sk); STATIC void ss_error_report(struct sock *sk); STATIC void ss_data_ready(struct sock *sk, int len); STATIC void ss_socket_put(struct socket *sock) { struct sock *sk; ensure(sock, return); if ((sk = sock->sk)) { write_lock(&sk->callback_lock); { ss_t *ss; if ((ss = SOCK_PRIV(sk))) { SOCK_PRIV(sk) = NULL; ss->refcnt--; sk->state_change = ss->cb_save.state_change; sk->data_ready = ss->cb_save.data_ready; sk->write_space = ss->cb_save.write_space; sk->error_report = ss->cb_save.error_report; } else assure(ss); } write_unlock(&sock->sk->callback_lock); } else assure(sk); sock_release(sock); } STATIC void ss_socket_get(struct socket *sock, ss_t * ss) { struct sock *sk; ensure(sock, return); sk = sock->sk; ensure(sk, return); write_lock(&sock->sk->callback_lock); { SOCK_PRIV(sk) = ss; ss->refcnt++; ss->cb_save.state_change = sk->state_change; ss->cb_save.data_ready = sk->data_ready; ss->cb_save.write_space = sk->write_space; ss->cb_save.error_report = sk->error_report; sk->state_change = ss_state_change; sk->data_ready = ss_data_ready; sk->write_space = ss_write_space; sk->error_report = ss_error_report; #ifdef LINUX_2_4 sk->protinfo.af_inet.recverr = 1; #else sk->ip_recverr = 1; #endif ss->tcp_state = sk->state; /* initialized tcp state */ } write_unlock(&sock->sk->callback_lock); } /* * ========================================================================= * * Locking * * ========================================================================= */ STATIC int ss_trylockq(queue_t * q) { int res; ss_t *ss = PRIV(q); if (!(res = lis_spin_trylock(&ss->qlock))) { if (q == ss->rq) ss->rwait = q; if (q == ss->wq) ss->wwait = q; } return (res); } STATIC void ss_unlockq(queue_t * q) { ss_t *ss = PRIV(q); lis_spin_unlock(&ss->qlock); if (ss->rwait) qenable(xchg(&ss->rwait, NULL)); if (ss->wwait) qenable(xchg(&ss->wwait, NULL)); } /* * ========================================================================= * * Buffer Allocation * * ========================================================================= */ /* * BUFSRV calls service routine * ------------------------------------------------------------------------- */ STATIC void ss_bufsrv(long data) { queue_t *q = (queue_t *) data; if (q) { ss_t *ss = PRIV(q); if (q == ss->rq) if (ss->rbid) { ss->rbid = 0; ss->refcnt--; } if (q == ss->wq) if (ss->wbid) { ss->wbid = 0; ss->refcnt--; } qenable(q); } } /* * UNBUFCALL * ------------------------------------------------------------------------- */ STATIC void ss_unbufcall(queue_t * q) { ss_t *ss = PRIV(q); if (ss->rbid) { unbufcall(xchg(&ss->rbid, 0)); ss->refcnt--; } if (ss->wbid) { unbufcall(xchg(&ss->wbid, 0)); ss->refcnt--; } } /* * ALLOCB * ------------------------------------------------------------------------- */ STATIC mblk_t *ss_allocb(queue_t * q, size_t size, int prior) { mblk_t *mp; if ((mp = allocb(size, prior))) return (mp); else { ss_t *ss = PRIV(q); if (q == ss->rq) { if (!ss->rbid) { ss->rbid = bufcall(size, prior, &ss_bufsrv, (long) q); ss->refcnt++; } return (NULL); } if (q == ss->wq) { if (!ss->wbid) { ss->wbid = bufcall(size, prior, &ss_bufsrv, (long) q); ss->refcnt++; } return (NULL); } } swerr(); return (NULL); } #if 0 /* * ESBALLOC * ------------------------------------------------------------------------- */ STATIC mblk_t *ss_esballoc(queue_t * q, unsigned char *base, size_t size, int prior, frtn_t * frtn) { mblk_t *mp; if ((mp = esballoc(base, size, prior, frtn))) return (mp); else { ss_t *ss = PRIV(q); if (q == ss->rq) { if (!ss->rbid) { ss->rbid = esbbcall(prior, &ss_bufsrv, (long) q); ss->refcnt++; } return (NULL); } if (q == ss->wq) { if (!ss->wbid) { ss->wbid = esbbcall(prior, &ss_bufsrv, (long) q); ss->refcnt++; } return (NULL); } swerr(); return (NULL); } } #endif /* * ========================================================================= * * OPTION Handling * * ========================================================================= */ #define _T_ALIGN_SIZEOF(s) \ ((sizeof((s)) + _T_ALIGN_SIZE - 1) & ~(_T_ALIGN_SIZE - 1)) STATIC size_t ss_opts_size(ss_t * ss, ss_opts_t * ops) { size_t len = 0; const size_t hlen = sizeof(struct t_opthdr); /* 32 bytes */ if (ops) { if (ops->opt) len += hlen + _T_ALIGN_SIZEOF(*(ops->opt)); if (ops->tos) len += hlen + _T_ALIGN_SIZEOF(*(ops->tos)); if (ops->ttl) len += hlen + _T_ALIGN_SIZEOF(*(ops->ttl)); if (ops->reuse) len += hlen + _T_ALIGN_SIZEOF(*(ops->reuse)); if (ops->norte) len += hlen + _T_ALIGN_SIZEOF(*(ops->norte)); if (ops->bcast) len += hlen + _T_ALIGN_SIZEOF(*(ops->bcast)); if (ss->p.prot.protocol == T_INET_TCP) { if (ops->nodly) len += hlen + _T_ALIGN_SIZEOF(*(ops->nodly)); if (ops->mss) len += hlen + _T_ALIGN_SIZEOF(*(ops->mss)); if (ops->alive) len += hlen + _T_ALIGN_SIZEOF(*(ops->alive)); } if (ss->p.prot.protocol == T_INET_UDP) { if (ops->csum) len += hlen + _T_ALIGN_SIZEOF(*(ops->csum)); } #if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE) if (ss->p.prot.protocol == T_INET_SCTP) { if (ops->nodly) len += hlen + _T_ALIGN_SIZEOF(*(ops->nodly)); if (ops->cork) len += hlen + _T_ALIGN_SIZEOF(*(ops->cork)); if (ops->mss) len += hlen + _T_ALIGN_SIZEOF(*(ops->mss)); if (ops->ppi) len += hlen + _T_ALIGN_SIZEOF(*(ops->ppi)); if (ops->sid) len += hlen + _T_ALIGN_SIZEOF(*(ops->sid)); if (ops->ssn) len += hlen + _T_ALIGN_SIZEOF(*(ops->ssn)); if (ops->tsn) len += hlen + _T_ALIGN_SIZEOF(*(ops->tsn)); if (ops->recvopt) len += hlen + _T_ALIGN_SIZEOF(*(ops->recvopt)); if (ops->clife) len += hlen + _T_ALIGN_SIZEOF(*(ops->clife)); if (ops->sack) len += hlen + _T_ALIGN_SIZEOF(*(ops->sack)); if (ops->prtx) len += hlen + _T_ALIGN_SIZEOF(*(ops->prtx)); if (ops->artx) len += hlen + _T_ALIGN_SIZEOF(*(ops->artx)); if (ops->irtx) len += hlen + _T_ALIGN_SIZEOF(*(ops->irtx)); if (ops->hitvl) len += hlen + _T_ALIGN_SIZEOF(*(ops->hitvl)); if (ops->rtoinit) len += hlen + _T_ALIGN_SIZEOF(*(ops->rtoinit)); if (ops->rtomin) len += hlen + _T_ALIGN_SIZEOF(*(ops->rtomin)); if (ops->rtomax) len += hlen + _T_ALIGN_SIZEOF(*(ops->rtomax)); if (ops->ostr) len += hlen + _T_ALIGN_SIZEOF(*(ops->ostr)); if (ops->istr) len += hlen + _T_ALIGN_SIZEOF(*(ops->istr)); if (ops->cinc) len += hlen + _T_ALIGN_SIZEOF(*(ops->cinc)); if (ops->titvl) len += hlen + _T_ALIGN_SIZEOF(*(ops->titvl)); if (ops->mac) len += hlen + _T_ALIGN_SIZEOF(*(ops->mac)); if (ops->hb) len += hlen + _T_ALIGN_SIZEOF(*(ops->hb)); if (ops->rto) len += hlen + _T_ALIGN_SIZEOF(*(ops->rto)); if (ops->stat) len += hlen + _T_ALIGN_SIZEOF(*(ops->stat)); /* FIXME add more */ if (ops->debug) len += hlen + _T_ALIGN_SIZEOF(*(ops->debug)); } #endif } return (len); } STATIC void ss_build_opts(ss_t * ss, ss_opts_t * ops, unsigned char **p) { struct t_opthdr *oh; const size_t hlen = sizeof(struct t_opthdr); const size_t olen = hlen + sizeof(t_scalar_t); if (ops) { if (ops->opt) { oh = ((typeof(oh)) * p)++; oh->len = hlen + sizeof(struct ip_options); oh->level = T_INET_IP; oh->name = T_IP_OPTIONS; oh->status = (ops->flags & TF_IP_OPTIONS) ? T_SUCCESS : T_FAILURE; *((struct ip_options *) *p) = *(ops->opt); *p += _T_ALIGN_SIZEOF(*ops->opt); } if (ops->tos) { oh = ((typeof(oh)) * p)++; oh->len = olen; oh->level = T_INET_IP; oh->name = T_IP_TOS; oh->status = (ops->flags & TF_IP_TOS) ? T_SUCCESS : T_FAILURE; *((t_scalar_t *) * p) = *(ops->tos); *p += _T_ALIGN_SIZEOF(*ops->tos); } if (ops->ttl) { oh = ((typeof(oh)) * p)++; oh->len = olen; oh->level = T_INET_IP; oh->name = T_IP_TTL; oh->status = (ops->flags & TF_IP_TTL) ? T_SUCCESS : T_FAILURE; *((t_scalar_t *) * p) = *(ops->ttl); *p += _T_ALIGN_SIZEOF(*ops->ttl); } if (ops->reuse) { oh = ((typeof(oh)) * p)++; oh->len = olen; oh->level = T_INET_IP; oh->name = T_IP_REUSEADDR; oh->status = (ops->flags & TF_IP_REUSEADDR) ? T_SUCCESS : T_FAILURE; *((t_scalar_t *) * p) = *(ops->reuse); *p += _T_ALIGN_SIZEOF(*ops->reuse); } if (ops->norte) { oh = ((typeof(oh)) * p)++; oh->len = olen; oh->level = T_INET_IP; oh->name = T_IP_DONTROUTE; oh->status = (ops->flags & TF_IP_DONTROUTE) ? T_SUCCESS : T_FAILURE; *((t_scalar_t *) * p) = *(ops->norte); *p += _T_ALIGN_SIZEOF(*ops->norte); } if (ops->bcast) { oh = ((typeof(oh)) * p)++; oh->len = olen; oh->level = T_INET_IP; oh->name = T_IP_BROADCAST; oh->status = (ops->flags & TF_IP_BROADCAST) ? T_SUCCESS : T_FAILURE; *((t_scalar_t *) * p) = *(ops->bcast); *p += _T_ALIGN_SIZEOF(*ops->bcast); } if (ss->p.prot.protocol == T_INET_TCP) { if (ops->nodly) { oh = ((typeof(oh)) * p)++; oh->len = olen; oh->level = T_INET_TCP; oh->name = T_TCP_NODELAY; oh->status = (ops->flags & TF_TCP_NODELAY) ? T_SUCCESS : T_FAILURE; *((t_scalar_t *) * p) = *(ops->nodly); *p += _T_ALIGN_SIZEOF(*ops->nodly); } if (ops->mss) { oh = ((typeof(oh)) * p)++; oh->len = olen; oh->level = T_INET_TCP; oh->name = T_TCP_MAXSEG; oh->status = (ops->flags & TF_TCP_MAXSEG) ? T_SUCCESS : T_FAILURE; *((t_scalar_t *) * p) = *(ops->mss); *p += _T_ALIGN_SIZEOF(*ops->mss); } if (ops->alive) { oh = ((typeof(oh)) * p)++; oh->len = olen; oh->level = T_INET_TCP; oh->name = T_TCP_KEEPALIVE; oh->status = (ops->flags & TF_TCP_KEEPALIVE) ? T_SUCCESS : T_FAILURE; *((t_scalar_t *) * p) = *(ops->alive); *p += _T_ALIGN_SIZEOF(*ops->alive); } } if (ss->p.prot.protocol == T_INET_UDP) { if (ops->csum) { oh = ((typeof(oh)) * p)++; oh->len = olen; oh->level = T_INET_UDP; oh->name = T_UDP_CHECKSUM; oh->status = (ops->flags & TF_UDP_CHECKSUM) ? T_SUCCESS : T_FAILURE; *((t_scalar_t *) * p) = *(ops->csum); *p += _T_ALIGN_SIZEOF(*ops->csum); } } } } STATIC int ss_parse_opts(ss_t * ss, ss_opts_t * ops, unsigned char *op, size_t len) { struct t_opthdr *oh; for (oh = _T_OPT_FIRSTHDR_OFS(op, len, 0); oh; oh = _T_OPT_NEXTHDR_OFS(op, len, oh, 0)) { switch (oh->level) { case T_INET_IP: switch (oh->name) { case T_IP_OPTIONS: ops->opt = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_IP_OPTIONS; continue; case T_IP_TOS: ops->tos = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_IP_TOS; continue; case T_IP_TTL: ops->ttl = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_IP_TTL; continue; case T_IP_REUSEADDR: ops->reuse = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_IP_REUSEADDR; continue; case T_IP_DONTROUTE: ops->norte = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_IP_DONTROUTE; continue; case T_IP_BROADCAST: ops->bcast = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_IP_BROADCAST; continue; } break; case T_INET_TCP: if (ss->p.prot.protocol == T_INET_TCP) switch (oh->name) { case T_TCP_NODELAY: ops->nodly = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_TCP_NODELAY; continue; case T_TCP_MAXSEG: ops->mss = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_TCP_MAXSEG; continue; case T_TCP_KEEPALIVE: ops->alive = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_TCP_KEEPALIVE; continue; } break; case T_INET_UDP: if (ss->p.prot.protocol == T_INET_UDP) switch (oh->name) { case T_UDP_CHECKSUM: ops->csum = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_UDP_CHECKSUM; continue; } break; #if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE) case T_INET_SCTP: if (ss->p.prot.protocol == T_INET_SCTP) switch (oh->name) { case T_SCTP_NODELAY: ops->nodly = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_NODELAY; continue; case T_SCTP_CORK: ops->cork = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_CORK; continue; case T_SCTP_PPI: ops->ppi = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_PPI; continue; case T_SCTP_SID: ops->sid = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_SID; continue; case T_SCTP_SSN: ops->ssn = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_SSN; continue; case T_SCTP_TSN: ops->tsn = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_TSN; continue; case T_SCTP_RECVOPT: ops->recvopt = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_RECVOPT; continue; case T_SCTP_COOKIE_LIFE: ops->clife = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_COOKIE_LIFE; continue; case T_SCTP_SACK_DELAY: ops->sack = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_SACK_DELAY; continue; case T_SCTP_PATH_MAX_RETRANS: ops->prtx = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_MAX_RETRANS; continue; case T_SCTP_ASSOC_MAX_RETRANS: ops->artx = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_ASSOC_MAX_RETRANS; continue; case T_SCTP_MAX_INIT_RETRIES: ops->irtx = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_MAX_INIT_RETRIES; continue; case T_SCTP_HEARTBEAT_ITVL: ops->hitvl = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_HEARTBEAT_ITVL; continue; case T_SCTP_RTO_INITIAL: ops->rtoinit = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_RTO_INITIAL; continue; case T_SCTP_RTO_MIN: ops->rtomin = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_RTO_MIN; continue; case T_SCTP_RTO_MAX: ops->rtomax = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_RTO_MAX; continue; case T_SCTP_OSTREAMS: ops->ostr = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_OSTREAMS; continue; case T_SCTP_ISTREAMS: ops->istr = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_ISTREAMS; continue; case T_SCTP_COOKIE_INC: ops->cinc = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_COOKIE_INC; continue; case T_SCTP_THROTTLE_ITVL: ops->titvl = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_THROTTLE_ITVL; continue; case T_SCTP_MAC_TYPE: ops->mac = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_MAC_TYPE; continue; case T_SCTP_HB: ops->hb = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_HB; continue; case T_SCTP_RTO: ops->rto = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_RTO; continue; case T_SCTP_MAXSEG: ops->mss = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_MAXSEG; continue; case T_SCTP_STATUS: ops->stat = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_STATUS; continue; case T_SCTP_DEBUG: ops->debug = (void *) _T_OPT_DATA_OFS(oh, 0); ops->flags |= TF_SCTP_DEBUG; continue; } break; #endif } } if (oh) return (TBADOPT); return (0); } STATIC size_t ss_cmsg_size(ss_opts_t * ops, int flags) { size_t len = 0; const size_t hlen = CMSG_ALIGN(sizeof(struct cmsghdr)); if (ops) { if (ops->opt && flags & TF_IP_OPTIONS) len += hlen + CMSG_ALIGN(sizeof(*(ops->opt))); if (ops->tos && flags & TF_IP_TOS) len += hlen + CMSG_ALIGN(sizeof(*(ops->tos))); if (ops->ttl && flags & TF_IP_TTL) len += hlen + CMSG_ALIGN(sizeof(*(ops->ttl))); if (ops->reuse && flags & TF_IP_REUSEADDR) len += hlen + CMSG_ALIGN(sizeof(*(ops->reuse))); if (ops->norte && flags & TF_IP_DONTROUTE) len += hlen + CMSG_ALIGN(sizeof(*(ops->norte))); if (ops->bcast && flags & TF_IP_BROADCAST) len += hlen + CMSG_ALIGN(sizeof(*(ops->bcast))); if (ops->nodly && flags & TF_TCP_NODELAY) len += hlen + CMSG_ALIGN(sizeof(*(ops->nodly))); if (ops->mss && flags & TF_TCP_MAXSEG) len += hlen + CMSG_ALIGN(sizeof(*(ops->mss))); if (ops->alive && flags & TF_TCP_KEEPALIVE) len += hlen + CMSG_ALIGN(sizeof(*(ops->alive))); #ifdef LINUX_2_4 #endif #ifdef UDP_CHECKSUM if (ops->csum && flags & TF_UDP_CHECKSUM) len += hlen + CMSG_ALIGN(sizeof(*(ops->csum))); #endif #if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE) #endif } return (len); } STATIC void ss_build_cmsg(ss_opts_t * ops, struct msghdr *msg, int flags) { struct cmsghdr *ch; unsigned char *p = msg->msg_control; const size_t hlen = CMSG_ALIGN(sizeof(struct cmsghdr)); if (ops) { if (ops->opt && flags & TF_IP_OPTIONS) { ch = ((typeof(ch)) p)++; ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->opt)); ch->cmsg_level = SOL_IP; ch->cmsg_type = IP_OPTIONS; *((typeof(ops->opt)) p) = *(ops->opt); p += CMSG_ALIGN(sizeof(*ops->opt)); } if (ops->tos && flags & TF_IP_TOS) { ch = ((typeof(ch)) p)++; ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->tos)); ch->cmsg_level = SOL_IP; ch->cmsg_type = IP_TOS; *((typeof(ops->tos)) p) = *(ops->tos); p += CMSG_ALIGN(sizeof(*ops->tos)); } if (ops->ttl && flags & TF_IP_TTL) { ch = ((typeof(ch)) p)++; ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->ttl)); ch->cmsg_level = SOL_IP; ch->cmsg_type = IP_TTL; *((typeof(ops->ttl)) p) = *(ops->ttl); p += CMSG_ALIGN(sizeof(*ops->ttl)); } if (ops->reuse && flags & TF_IP_REUSEADDR) { ch = ((typeof(ch)) p)++; ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->reuse)); ch->cmsg_level = SOL_SOCKET; ch->cmsg_type = SO_REUSEADDR; *((typeof(ops->reuse)) p) = *(ops->reuse); p += CMSG_ALIGN(sizeof(*ops->reuse)); } if (ops->norte && flags & TF_IP_DONTROUTE) { ch = ((typeof(ch)) p)++; ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->norte)); ch->cmsg_level = SOL_SOCKET; ch->cmsg_type = SO_DONTROUTE; *((typeof(ops->norte)) p) = *(ops->norte); p += CMSG_ALIGN(sizeof(*ops->norte)); } if (ops->bcast && flags & TF_IP_BROADCAST) { ch = ((typeof(ch)) p)++; ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->bcast)); ch->cmsg_level = SOL_SOCKET; ch->cmsg_type = SO_BROADCAST; *((typeof(ops->bcast)) p) = *(ops->bcast); p += CMSG_ALIGN(sizeof(*ops->bcast)); } if (ops->nodly && flags & TF_TCP_NODELAY) { ch = ((typeof(ch)) p)++; ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->nodly)); ch->cmsg_level = SOL_TCP; ch->cmsg_type = TCP_NODELAY; *((typeof(ops->nodly)) p) = *(ops->nodly); p += CMSG_ALIGN(sizeof(*ops->nodly)); } if (ops->mss && flags & TF_TCP_MAXSEG) { ch = ((typeof(ch)) p)++; ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->mss)); ch->cmsg_level = SOL_TCP; ch->cmsg_type = TCP_MAXSEG; *((typeof(ops->mss)) p) = *(ops->mss); p += CMSG_ALIGN(sizeof(*ops->mss)); } if (ops->alive && flags & TF_TCP_KEEPALIVE) { ch = ((typeof(ch)) p)++; ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->alive)); ch->cmsg_level = SOL_SOCKET; ch->cmsg_type = SO_KEEPALIVE; *((typeof(ops->alive)) p) = *(ops->alive); p += CMSG_ALIGN(sizeof(*ops->alive)); } #ifdef UDP_CHECKSUM if (ops->csum && flags & TF_UDP_CHECKSUM) { ch = ((typeof(ch)) p)++; ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->csum)); ch->cmsg_level = SOL_UDP; ch->cmsg_type = UDP_CHECKSUM; *((typeof(ops->csum)) p) = *(ops->csum); p += CMSG_ALIGN(sizeof(*ops->csum)); } #endif } } STATIC int ss_parse_cmsg(struct msghdr *msg, ss_opts_t * opts) { struct cmsghdr *cmsg; for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { if (cmsg->cmsg_len < sizeof(struct cmsghdr) || (unsigned long) (((char *) cmsg - (char *) msg->msg_control) + cmsg->cmsg_len) > msg->msg_controllen) { return -EINVAL; } switch (cmsg->cmsg_level) { case SOL_IP: switch (cmsg->cmsg_type) { case IP_OPTIONS: if (cmsg->cmsg_len <= 40) { opts->opt = (typeof(opts->opt)) CMSG_DATA(cmsg); opts->flags |= TF_IP_OPTIONS; } break; case IP_TTL: if (cmsg->cmsg_len == sizeof(*opts->ttl)) { opts->ttl = (typeof(opts->ttl)) CMSG_DATA(cmsg); opts->flags |= TF_IP_TTL; } break; case IP_TOS: if (cmsg->cmsg_len == sizeof(*opts->tos)) { opts->tos = (typeof(opts->tos)) CMSG_DATA(cmsg); opts->flags |= TF_IP_TOS; } break; case IP_RETOPTS: if (cmsg->cmsg_len == sizeof(*opts->ropt)) { opts->ropt = (typeof(opts->ropt)) CMSG_DATA(cmsg); opts->flags |= TF_IP_RETOPTS; } break; case IP_PKTINFO: if (cmsg->cmsg_len == sizeof(*opts->pkinfo)) { opts->pkinfo = (typeof(opts->pkinfo)) CMSG_DATA(cmsg); opts->flags |= TF_IP_PKTINFO; } break; case IP_HDRINCL: case IP_ROUTER_ALERT: case IP_RECVOPTS: case IP_PKTOPTIONS: case IP_MTU_DISCOVER: case IP_RECVERR: case IP_RECVTTL: case IP_RECVTOS: case IP_MTU: break; } break; case SOL_TCP: switch (cmsg->cmsg_type) { case TCP_NODELAY: case TCP_MAXSEG: case TCP_CORK: #ifdef LINUX_2_4 case TCP_KEEPIDLE: case TCP_KEEPINTVL: case TCP_KEEPCNT: case TCP_SYNCNT: case TCP_LINGER2: case TCP_DEFER_ACCEPT: case TCP_WINDOW_CLAMP: case TCP_INFO: case TCP_QUICKACK: #endif break; } break; case SOL_UDP: switch (cmsg->cmsg_type) { } break; #if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE) case SOL_SCTP: switch (cmsg->cmsg_type) { case SCTP_NODELAY: case SCTP_MAXSEG: case SCTP_CORK: case SCTP_RECVSID: case SCTP_RECVPPI: case SCTP_RECVTSN: case SCTP_SID: case SCTP_PPI: case SCTP_SSN: case SCTP_TSN: case SCTP_HB: case SCTP_RTO: case SCTP_COOKIE_LIFE: case SCTP_SACK_DELAY: case SCTP_PATH_MAX_RETRANS: case SCTP_ASSOC_MAX_RETRANS: case SCTP_MAX_INIT_RETRIES: case SCTP_HEARTBEAT_ITVL: case SCTP_RTO_INITIAL: case SCTP_RTO_MIN: case SCTP_RTO_MAX: case SCTP_OSTREAMS: case SCTP_ISTREAMS: case SCTP_COOKIE_INC: case SCTP_THROTTLE_ITVL: case SCTP_MAC_TYPE: case SCTP_CKSUM_TYPE: case SCTP_DEBUG_OPTIONS: case SCTP_STATUS: case SCTP_ALI: case SCTP_PR: case SCTP_DISPOSITION: case SCTP_LIFETIME: case SCTP_ADD: case SCTP_ADD_IP: case SCTP_DEL_IP: case SCTP_SET: case SCTP_SET_IP: case SCTP_ECN: case SCTP_MAX_BURST: break; } break; #endif default: break; } } return (0); } /* * ========================================================================= * * OPTIONS Handling * * ========================================================================= */ STATIC ss_dflt_t opt_defaults = { ip_default_opt, ip_default_tos, ip_default_ttl, ip_default_reuse, ip_default_norte, ip_default_bcast, tcp_default_nodly, tcp_default_mss, tcp_default_alive, udp_default_csum }; /* * CHECK Options * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ STATIC int ss_opt_check(ss_t * ss, ss_opts_t * opt) { if (opt->flags) { opt->flags = 0; if (opt->opt) opt->flags |= TF_IP_OPTIONS; if (opt->tos && !(*(opt->tos) & 0x1e)) opt->flags |= TF_IP_TOS; if (opt->ttl && !(*(opt->ttl) & 0xff)) opt->flags |= TF_IP_TTL; if (opt->reuse) opt->flags |= TF_IP_REUSEADDR; if (opt->norte) opt->flags |= TF_IP_DONTROUTE; if (opt->bcast) opt->flags |= TF_IP_BROADCAST; if (ss->p.prot.protocol == T_INET_TCP) { if (opt->nodly) opt->flags |= TF_TCP_NODELAY; if (opt->mss) opt->flags |= TF_TCP_MAXSEG; if (opt->alive) opt->flags |= TF_TCP_KEEPALIVE; } if (ss->p.prot.protocol == T_INET_UDP) { if (opt->csum) opt->flags |= TF_UDP_CHECKSUM; } } return (0); } /* * DEFAULT Options * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ STATIC int ss_opt_default(ss_t * ss, ss_opts_t * opt) { int flags = opt->flags; opt->flags = 0; if (!flags || opt->tos) { opt->tos = &opt_defaults.tos; opt->flags |= TF_IP_TOS; } if (!flags || opt->ttl) { opt->ttl = &opt_defaults.ttl; opt->flags |= TF_IP_TTL; } if (!flags || opt->reuse) { opt->reuse = &opt_defaults.reuse; opt->flags |= TF_IP_REUSEADDR; } if (!flags || opt->norte) { opt->norte = &opt_defaults.norte; opt->flags |= TF_IP_DONTROUTE; } if (!flags || opt->bcast) { opt->bcast = &opt_defaults.bcast; opt->flags |= TF_IP_BROADCAST; } if (ss->p.prot.protocol == T_INET_TCP) { if (!flags || opt->nodly) { opt->nodly = &opt_defaults.nodly; opt->flags |= TF_TCP_NODELAY; } if (!flags || opt->mss) { opt->mss = &opt_defaults.mss; opt->flags |= TF_TCP_MAXSEG; } if (!flags || opt->alive) { opt->alive = &opt_defaults.alive; opt->flags |= TF_TCP_KEEPALIVE; } } if (ss->p.prot.protocol == T_INET_UDP) { if (!flags || opt->csum) { opt->csum = &opt_defaults.csum; opt->flags |= TF_UDP_CHECKSUM; } } return (0); } /* * NEGOTIATE Options * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ STATIC int ss_opt_negotiate(ss_t * ss, ss_opts_t * opt) { struct sock *sk = NULL; if (ss->sock) sk = ss->sock->sk; if (opt) { opt->flags = 0; if (opt->opt) { ss->options.opt = *(opt->opt); #ifdef LINUX_2_4 if (sk) sk->protinfo.af_inet.opt = &ss->options.opt; #else if (sk) sk->opt = &ss->options.opt; #endif opt->flags |= TF_IP_OPTIONS; } if (opt->tos) { ss->options.tos = *(opt->tos); #ifdef LINUX_2_4 if (sk) sk->protinfo.af_inet.tos = ss->options.tos; #else if (sk) sk->ip_tos = ss->options.tos; #endif opt->flags |= TF_IP_TOS; } if (opt->ttl) { ss->options.ttl = *(opt->ttl); #ifdef LINUX_2_4 if (sk) sk->protinfo.af_inet.ttl = ss->options.ttl; #else if (sk) sk->ip_ttl = ss->options.ttl; #endif opt->flags |= TF_IP_TTL; } if (opt->reuse) { ss->options.reuse = *(opt->reuse) ? 1 : 0; if (sk) sk->reuse = ss->options.reuse; opt->flags |= TF_IP_REUSEADDR; } if (opt->norte) { ss->options.norte = *(opt->norte) ? 1 : 0; if (sk) sk->localroute = ss->options.norte; opt->flags |= TF_IP_DONTROUTE; } if (opt->bcast) { ss->options.bcast = *(opt->bcast) ? 1 : 0; if (sk) sk->broadcast = ss->options.bcast; opt->flags |= TF_IP_BROADCAST; } if (opt->nodly) { ss->options.nodly = *(opt->nodly) ? 1 : 0; #ifdef LINUX_2_4 if (sk) sk->tp_pinfo.af_tcp.nonagle = ss->options.nodly; #else if (sk) sk->nonagle = ss->options.nodly; #endif opt->flags |= TF_TCP_NODELAY; } if (opt->mss) { ss->options.mss = *(opt->mss) ? 1 : 0; if (sk) sk->tp_pinfo.af_tcp.user_mss = ss->options.mss; opt->flags |= TF_TCP_MAXSEG; } if (opt->alive) { ss->options.alive = *(opt->alive) ? 1 : 0; if (sk) sk->keepopen = ss->options.alive; opt->flags |= TF_TCP_KEEPALIVE; } if (opt->csum) { ss->options.csum = *(opt->csum) ? 1 : 0; // if ( sk ) sk->csum = ss->options.csum; opt->flags |= TF_UDP_CHECKSUM; } } return (0); } /* * CURRENT Options * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ STATIC int ss_opt_current(ss_t * ss, ss_opts_t * opt) { uint flags = opt->flags; opt->flags = 0; if (!flags || opt->opt) { opt->opt = &ss->options.opt; opt->flags |= TF_IP_OPTIONS; } if (!flags || opt->tos) { opt->tos = &ss->options.tos; opt->flags |= TF_IP_TOS; } if (!flags || opt->ttl) { opt->ttl = &ss->options.ttl; opt->flags |= TF_IP_TTL; } if (!flags || opt->reuse) { opt->reuse = &ss->options.reuse; opt->flags |= TF_IP_REUSEADDR; } if (!flags || opt->norte) { opt->norte = &ss->options.norte; opt->flags |= TF_IP_DONTROUTE; } if (!flags || opt->bcast) { opt->bcast = &ss->options.bcast; opt->flags |= TF_IP_BROADCAST; } if (ss->p.prot.protocol == T_INET_TCP) { if (!flags || opt->nodly) { opt->nodly = &ss->options.nodly; opt->flags |= TF_TCP_NODELAY; } if (!flags || opt->mss) { opt->mss = &ss->options.mss; opt->flags |= TF_TCP_MAXSEG; } if (!flags || opt->alive) { opt->alive = &ss->options.alive; opt->flags |= TF_TCP_KEEPALIVE; } } if (ss->p.prot.protocol == T_INET_UDP) { if (!flags || opt->csum) { opt->csum = &ss->options.csum; opt->flags |= TF_UDP_CHECKSUM; } } return (0); } /* * ========================================================================= * * STATE Changes * * ========================================================================= */ #ifdef _DEBUG STATIC const char *state_name(long state) { switch (state) { case TS_UNBND: return ("TS_UNBND"); case TS_WACK_BREQ: return ("TS_WACK_BREQ"); case TS_WACK_UREQ: return ("TS_WACK_UREQ"); case TS_IDLE: return ("TS_IDLE"); case TS_WACK_OPTREQ: return ("TS_WACK_OPTREQ"); case TS_WACK_CREQ: return ("TS_WACK_CREQ"); case TS_WCON_CREQ: return ("TS_WCON_CREQ"); case TS_WRES_CIND: return ("TS_WRES_CIND"); case TS_WACK_CRES: return ("TS_WACK_CRES"); case TS_DATA_XFER: return ("TS_DATA_XFER"); case TS_WIND_ORDREL: return ("TS_WIND_ORDREL"); case TS_WREQ_ORDREL: return ("TS_WREQ_ORDREL"); case TS_WACK_DREQ6: return ("TS_WACK_DREQ6"); case TS_WACK_DREQ7: return ("TS_WACK_DREQ7"); case TS_WACK_DREQ9: return ("TS_WACK_DREQ9"); case TS_WACK_DREQ10: return ("TS_WACK_DREQ10"); case TS_WACK_DREQ11: return ("TS_WACK_DREQ11"); case TS_NOSTATES: return ("TS_NOSTATES"); default: return ("(unknown)"); } } #endif STATIC void ss_set_state(ss_t * ss, long state) { printd(("%s: %p: %s <- %s\n", SS_MOD_NAME, ss, state_name(state), state_name(ss->p.info.CURRENT_state))); ss->p.info.CURRENT_state = state; } STATIC long ss_get_state(ss_t * ss) { return (ss->p.info.CURRENT_state); } #ifdef _DEBUG STATIC const char *tcp_state_name(int state) { switch (state) { case TCP_ESTABLISHED: return ("TCP_ESTABLISHED"); case TCP_SYN_SENT: return ("TCP_SYN_SENT"); case TCP_SYN_RECV: return ("TCP_SYN_RECV"); case TCP_FIN_WAIT1: return ("TCP_FIN_WAIT1"); case TCP_FIN_WAIT2: return ("TCP_FIN_WAIT2"); case TCP_TIME_WAIT: return ("TCP_TIME_WAIT"); case TCP_CLOSE: return ("TCP_CLOSE"); case TCP_CLOSE_WAIT: return ("TCP_CLOSE_WAIT"); case TCP_LAST_ACK: return ("TCP_LAST_ACK"); case TCP_LISTEN: return ("TCP_LISTEN"); case TCP_CLOSING: return ("TCP_CLOSING"); case TCP_MAX_STATES: return ("TCP_MAX_STATES"); default: return ("(unknown)"); } } #endif /* * ------------------------------------------------------------------------ * * Socket Calls * * ------------------------------------------------------------------------ * These are wrappered versions of socket calls. */ /* * SOCKET CREATE * ------------------------------------------------------------------------ */ STATIC int ss_socket(ss_t * ss) { int err; int family, type, protocol; ensure(ss, return (-EFAULT)); unless(ss->sock, return (-EFAULT)); family = ss->p.prot.family; type = ss->p.prot.type; protocol = (ss->p.prot.protocol == IPPROTO_RAW) ? ss->port : ss->p.prot.protocol; printd(("%s: %p: SS_CREATE %d:%d:%d\n", SS_MOD_NAME, ss, family, type, protocol)); if (!(err = sock_create(family, type, protocol, &ss->sock))) { ensure(ss->sock, return (-EFAULT)); ensure(ss->sock->sk, return (-EFAULT)); ss->sock->sk->allocation = GFP_ATOMIC; ss_socket_get(ss->sock, ss); return (0); } printd(("%s: %p: ERROR: from sock_create %d\n", SS_MOD_NAME, ss, err)); return (err); } /* * SOCKET BIND * ------------------------------------------------------------------------ */ STATIC int ss_bind(ss_t * ss, ss_addr_t * add) { int err; ensure(ss, return (-EFAULT)); ensure(ss->sock, return (-EFAULT)); ensure(ss->sock->sk, return (-EFAULT)); ensure(ss->sock->ops, return (-EFAULT)); ensure(ss->sock->ops->bind, return (-EFAULT)); printd(("%s: %p: SS_BIND\n", SS_MOD_NAME, ss)); if (!(err = ss->sock->ops->bind(ss->sock, (struct sockaddr *) add, sizeof(*add)))) { ss->src = *add; return (0); } printd(("%s: %p: ERROR: from sock->ops->bind %d\n", SS_MOD_NAME, ss, err)); return (err); } /* * SOCKET LISTEN * ------------------------------------------------------------------------ */ STATIC int ss_listen(ss_t * ss, uint cons) { int err; int type; ensure(ss, return (-EFAULT)); ensure(ss->sock, return (-EFAULT)); ensure(ss->sock->sk, return (-EFAULT)); ensure(ss->sock->ops, return (-EFAULT)); ensure(ss->sock->ops->listen, return (-EFAULT)); type = ss->p.prot.type; ensure(type == SOCK_STREAM || type == SOCK_SEQPACKET, return (-EFAULT)); printd(("%s: %p: SS_LISTEN %d\n", SS_MOD_NAME, ss, cons)); if (!(err = ss->sock->ops->listen(ss->sock, cons))) { ss->conind = cons; ss->tcp_state = ss->sock->sk->state; return (0); } printd(("%s: %p: ERROR: from sock->ops->listen %d\n", SS_MOD_NAME, ss, err)); return (err); } /* * SOCKET ACCEPT * ------------------------------------------------------------------------ * Unfortunately, sock->ops->accept will only accept the next sequential * connection indication. In TLI's case, we want to be able to accept or * release connection indications other than the next sequential indication. * To do this we must muck with TCP's accept queue when the SEQ_number is not * the next in the queue. To do this we mimic some of the tcp_accept * behavior. */ STATIC int ss_accept(ss_t * ss, struct socket **newsock, mblk_t * cp) { struct socket *sock; ensure(newsock, return (-EFAULT)); ensure(cp, return (-EFAULT)); ensure(ss, return (-EFAULT)); ensure(ss->sock, return (-EFAULT)); ensure(ss->sock->sk, return (-EFAULT)); ensure(ss->sock->ops, return (-EFAULT)); ensure(ss->sock->ops->accept, return (-EFAULT)); printd(("%s: %p: SS_ACCEPT\n", SS_MOD_NAME, ss)); if ((sock = sock_alloc())) { struct sock *sk = ss->sock->sk; struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; struct open_request *req, *req_prev, **reqp; struct sock *ask = ((ss_event_t *) cp->b_rptr)->sk; sock->type = ss->sock->type; sock->ops = ss->sock->ops; lock_sock(sk); if (tp->accept_queue) { /* find connection in queue */ for (reqp = &tp->accept_queue, req_prev = NULL; *reqp && (*reqp)->sk != ask; req_prev = (*reqp), reqp = &(*reqp)->dl_next) ; if ((req = *reqp)) { if (!((*reqp) = (*reqp)->dl_next)) tp->accept_queue_tail = req_prev; sk->ack_backlog--; tcp_openreq_fastfree(req); } release_sock(sk); lock_sock(ask); sock_graft(ask, sock); release_sock(ask); sock->state = SS_CONNECTED; *newsock = sock; bufq_unlink(&ss->conq, cp); freemsg(cp); return (0); } release_sock(sk); ss_socket_put(sock); printd(("%s: %p: invalid accept\n", SS_MOD_NAME, ss)); return (-EAGAIN); } printd(("%s: %p: ERROR: couldn't allocate accepting socket\n", SS_MOD_NAME, ss)); return (-EFAULT); } /* * SOCKET UNBIND * ------------------------------------------------------------------------ * There is no good way to unbind and rebind a socket in Linux, so we just * close the entire socket. The next time we go to bind, we will create * a fresh socket to bind. */ STATIC int ss_unbind(ss_t * ss) { ensure(ss, return (-EFAULT)); ensure(ss->sock, return (-EFAULT)); ensure(ss->sock->sk, return (-EFAULT)); printd(("%s: %p: SS_UNBIND\n", SS_MOD_NAME, ss)); ss_socket_put(xchg(&ss->sock, NULL)); return (0); } /* * SOCKET CONNECT * ------------------------------------------------------------------------ */ STATIC int ss_connect(ss_t * ss, ss_addr_t * dst) { int err; ensure(ss, return (-EFAULT)); ensure(ss->sock, return (-EFAULT)); ensure(ss->sock->sk, return (-EFAULT)); ensure(ss->sock->ops, return (-EFAULT)); ensure(ss->sock->ops->connect, return (-EFAULT)); if ((err = ss->sock->ops->connect(ss->sock, (struct sockaddr *) dst, sizeof(*dst), O_NONBLOCK)) == 0 || err == -EINPROGRESS) { return (0); } printd(("%s: %p: ERROR: from sock->ops->connect %d\n", SS_MOD_NAME, ss, err)); return (err); } /* * SOCKET SENDMSG * ------------------------------------------------------------------------ */ STATIC int ss_sendmsg(ss_t * ss, struct msghdr *msg, int len) { int res; ensure(ss, return (-EFAULT)); ensure(ss->sock, return (-EFAULT)); ensure(ss->sock->ops, return (-EFAULT)); ensure(ss->sock->ops->sendmsg, return (-EFAULT)); ensure(ss->sock->sk, return (-EFAULT)); ensure(ss->sock->sk->prot, return (-EFAULT)); ensure(ss->sock->sk->prot->sendmsg, return (-EFAULT)); { mm_segment_t fs = get_fs(); set_fs(KERNEL_DS); res = sock_sendmsg(ss->sock, msg, len); set_fs(fs); } if (res <= 0) printd(("%s: %p: ERROR: from sock->sk->prot->sendmsg %d\n", SS_MOD_NAME, ss, res)); return (res); } /* * SOCKET RECVMSG * ------------------------------------------------------------------------ */ STATIC int ss_recvmsg(ss_t * ss, struct msghdr *msg, int size) { int res; int sflags = MSG_DONTWAIT | MSG_NOSIGNAL; ensure(ss, return (-EFAULT)); ensure(ss->sock, return (-EFAULT)); ensure(ss->sock->ops, return (-EFAULT)); ensure(ss->sock->ops->recvmsg, return (-EFAULT)); ensure(ss->sock->sk, return (-EFAULT)); ensure(ss->sock->sk->prot, return (-EFAULT)); ensure(ss->sock->sk->prot->recvmsg, return (-EFAULT)); { mm_segment_t fs = get_fs(); set_fs(KERNEL_DS); res = sock_recvmsg(ss->sock, msg, size, sflags); set_fs(fs); } if (res < 0) printd(("%s: %p: ERROR: from sock->ops->recvmsg %d\n", SS_MOD_NAME, ss, res)); return (res); } /* * SOCKET DISCONNECT * ------------------------------------------------------------------------ * Performing a sock_release (ss_socket_put) from the established state does * not affect an abortive release for TCP, but rather, initiates an orderly * shutdown rather than an abortive release. We can try performing a * protocol disconnect and see if that works better. */ STATIC int ss_disconnect(ss_t * ss) { int err; ensure(ss, return (-EFAULT)); ensure(ss->sock->ops, return (-EFAULT)); ensure(ss->sock->ops->connect, return (-EFAULT)); ensure(ss->sock->sk, return (-EFAULT)); ensure(ss->sock->sk->prot, return (-EFAULT)); ensure(ss->sock->sk->prot->disconnect, return (-EFAULT)); if (!(err = ss->sock->sk->prot->disconnect(ss->sock->sk, O_NONBLOCK))) { ss->sock->state = SS_UNCONNECTED; return (0); } ss->sock->state = SS_DISCONNECTING; printd(("%s: %p: ERROR: from sock->sk->prot->disconnect %d\n", SS_MOD_NAME, ss, err)); return (err); } /* * ========================================================================= * * IP T-Provider --> T-User Primitives (Indication, Confirmation and Ack) * * ========================================================================= */ /* * M_ERROR * --------------------------------------------------------------- */ STATIC int m_error(queue_t * q, int error) { ss_t *ss = PRIV(q); mblk_t *mp; int hangup = 0; if (error < 0) error = -error; switch (error) { case EBUSY: case ENOBUFS: case EAGAIN: case ENOMEM: return (-error); case EPIPE: case ENETDOWN: case EHOSTUNREACH: hangup = 1; } if ((mp = ss_allocb(q, 2, BPRI_HI))) { if (ss->sock) ss_socket_put(xchg(&ss->sock, NULL)); if (hangup) { printd(("%s: %p: <- M_HANGUP\n", SS_MOD_NAME, ss)); mp->b_datap->db_type = M_HANGUP; putnext(ss->rq, mp); error = EPIPE; } else { printd(("%s: %p: <- M_ERROR %d\n", SS_MOD_NAME, ss, error)); mp->b_datap->db_type = M_ERROR; *(mp->b_wptr)++ = error; *(mp->b_wptr)++ = error; putnext(ss->rq, mp); } return (-error); } rare(); return (-ENOBUFS); } /* * T_CONN_IND 11 - Connection Indication * --------------------------------------------------------------- */ STATIC int t_conn_ind(queue_t * q, ss_addr_t * src, ss_opts_t * opts, mblk_t * cp) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_conn_ind *p; size_t src_len = src ? sizeof(*src) : 0; size_t opt_len = ss_opts_size(ss, opts); if (bufq_length(&ss->conq) <= ss->conind) { if (canputnext(ss->rq)) { if ((mp = ss_allocb(q, sizeof(*p) + src_len + opt_len, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_CONN_IND; p->SRC_length = src_len; p->SRC_offset = src_len ? sizeof(*p) : 0; p->OPT_length = opt_len; p->OPT_offset = opt_len ? sizeof(*p) + src_len : 0; p->SEQ_number = (ulong) cp; if (src_len) { bcopy(src, mp->b_wptr, src_len); mp->b_wptr += src_len; } if (opt_len) { ss_build_opts(ss, opts, &mp->b_wptr); } bufq_queue(&ss->conq, cp); ss_set_state(ss, TS_WRES_CIND); printd(("%s: %p: <- T_CONN_IND\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (QR_ABSORBED); /* absorbed cp */ } ptrace(("%s: ERROR: no buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } ptrace(("%s: ERROR: flow controlled\n", SS_MOD_NAME)); return (-EBUSY); } ptrace(("%s: ERROR: too many conn inds\n", SS_MOD_NAME)); return (-EAGAIN); } /* * T_CONN_CON 12 - Connection Confirmation * --------------------------------------------------------------- */ STATIC int t_conn_con(queue_t * q, ss_addr_t * res, ss_opts_t * opts, mblk_t * dp) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_conn_con *p; size_t res_len = res ? sizeof(*res) : 0; size_t opt_len = ss_opts_size(ss, opts); /* * this shouldn't happen, we probably shouldn't even check */ if (canputnext(ss->rq)) { if ((mp = ss_allocb(q, sizeof(*p) + res_len + opt_len, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; mp->b_band = 1; /* expedite */ p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_CONN_CON; p->RES_length = res_len; p->RES_offset = res_len ? sizeof(*p) : 0; p->OPT_length = opt_len; p->OPT_offset = opt_len ? sizeof(*p) + res_len : 0; if (res_len) { bcopy(res, mp->b_wptr, res_len); mp->b_wptr += res_len; } if (opt_len) { ss_build_opts(ss, opts, &mp->b_wptr); } ss_set_state(ss, TS_DATA_XFER); printd(("%s: %p: <- T_CONN_CON\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (0); } ptrace(("%s: ERROR: no buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } ptrace(("%s: ERROR: flow controlled\n", SS_MOD_NAME)); return (-EBUSY); } /* * T_DISCON_IND 13 - Disconnect Indication * --------------------------------------------------------------- */ STATIC mblk_t *t_seq_find(ss_t * ss, mblk_t * rp) { mblk_t *mp; if ((mp = rp)) { struct sock *sk = ((ss_event_t *) rp->b_rptr)->sk; lis_spin_lock(&ss->conq.q_lock); { for (mp = bufq_head(&ss->conq); mp && ((ss_event_t *) mp->b_rptr)->sk != sk; mp = mp->b_next) ; } lis_spin_unlock(&ss->conq.q_lock); } return (mp); } STATIC ulong t_seq_delete(ss_t * ss, mblk_t * rp) { mblk_t *mp; if ((mp = t_seq_find(ss, rp))) { struct socket *sock = NULL; if (!ss_accept(ss, &sock, rp) && sock) sock_release(sock); return ((ulong) mp); } return (0); } STATIC int t_discon_ind(queue_t * q, ss_addr_t * res, uint orig, uint reason, mblk_t * cp, mblk_t * dp) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_discon_ind *p; ulong seq = 0; (void) res; if (canputnext(ss->rq)) { if ((mp = ss_allocb(q, sizeof(*p), BPRI_MED))) { if (!cp || (seq = t_seq_delete(ss, cp))) { mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_DISCON_IND; p->DISCON_reason = reason; p->SEQ_number = seq; if (!bufq_length(&ss->conq)) ss_set_state(ss, TS_IDLE); else ss_set_state(ss, TS_WRES_CIND); mp->b_cont = dp; printd(("%s: %p: <- T_DISCON_IND\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (0); } freemsg(mp); ptrace(("%s: ERROR: bad sequence number\n", SS_MOD_NAME)); return (-EFAULT); } ptrace(("%s: ERROR: no buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } ptrace(("%s: ERROR: flow controlled\n", SS_MOD_NAME)); return (-EBUSY); } /* * T_DATA_IND 14 - Data Indication * --------------------------------------------------------------- */ STATIC int t_data_ind(queue_t * q, uint flags, mblk_t * dp) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_data_ind *p; if ((mp = ss_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_DATA_IND; p->MORE_flag = flags; mp->b_cont = dp; printd(("%s: %p: <- T_DATA_IND\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (QR_ABSORBED); } ptrace(("%s: ERROR: no buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } /* * T_EXDATA_IND 15 - Expedited Data Indication * --------------------------------------------------------------- */ STATIC int t_exdata_ind(queue_t * q, uint flags, mblk_t * dp) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_exdata_ind *p; if ((mp = ss_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; mp->b_band = 1; /* expedite */ p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_EXDATA_IND; p->MORE_flag = flags; mp->b_cont = dp; printd(("%s: %p: <- T_EXDATA_IND\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (QR_ABSORBED); } ptrace(("%s: ERROR: no buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } /* * T_INFO_ACK 16 - Information acknowledgement * --------------------------------------------------------------- */ STATIC int t_info_ack(queue_t * q) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_info_ack *p; if ((mp = ss_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; *p = ss->p.info; printd(("%s: %p: <- T_INFO_ACK\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (0); } ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } /* * T_BIND_ACK 17 - Bind Acknowledgement * --------------------------------------------------------------- */ STATIC int t_bind_ack(queue_t * q, ss_addr_t * add, ulong conind) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_bind_ack *p; size_t add_len = add ? sizeof(*add) : 0; if ((mp = ss_allocb(q, sizeof(*p) + add_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_BIND_ACK; p->ADDR_length = add_len; p->ADDR_offset = add_len ? sizeof(*p) : 0; p->CONIND_number = conind; if (add_len) { bcopy(add, mp->b_wptr, add_len); mp->b_wptr += add_len; } ss_set_state(ss, TS_IDLE); printd(("%s: %p: <- T_BIND_ACK\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (0); } ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } /* * T_ERROR_ACK 18 - Error Acknowledgement * ------------------------------------------------------------------------- */ STATIC int t_error_ack(queue_t * q, ulong prim, long error) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_error_ack *p; switch (error) { case -EBUSY: case -EAGAIN: case -ENOMEM: case -ENOBUFS: seldom(); return (error); case 0: never(); return (error); } if (!(mp = ss_allocb(q, sizeof(*p), BPRI_MED))) goto enobufs; mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_ERROR_ACK; p->ERROR_prim = prim; p->TLI_error = error < 0 ? TSYSERR : error; p->UNIX_error = error < 0 ? -error : 0; /* * This is to only try and get the state correct for putnext. */ if (error != TOUTSTATE) { switch (ss_get_state(ss)) { #ifdef TS_WACK_OPTREQ case TS_WACK_OPTREQ: ss_set_state(ss, TS_IDLE); break; #endif case TS_WACK_UREQ: ss_set_state(ss, TS_IDLE); break; case TS_WACK_CREQ: ss_set_state(ss, TS_IDLE); break; case TS_WACK_BREQ: ss_set_state(ss, TS_UNBND); break; case TS_WACK_CRES: ss_set_state(ss, TS_WRES_CIND); break; case TS_WACK_DREQ6: ss_set_state(ss, TS_WCON_CREQ); break; case TS_WACK_DREQ7: ss_set_state(ss, TS_WRES_CIND); break; case TS_WACK_DREQ9: ss_set_state(ss, TS_DATA_XFER); break; case TS_WACK_DREQ10: ss_set_state(ss, TS_WIND_ORDREL); break; case TS_WACK_DREQ11: ss_set_state(ss, TS_WREQ_ORDREL); break; /* * Note: if we are not in a WACK state we simply do * not change state. This occurs normally when we * are responding to a T_OPTMGMT_REQ in other than * TS_IDLE state. */ } } printd(("%s: %p: <- T_ERROR_ACK\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); /* * Returning -EPROTO here will make sure that the old state is * restored correctly. If we return QR_DONE, then the state will * never be restored. */ if (error < 0) return (error); return (-EPROTO); enobufs: ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } /* * T_OK_ACK 19 - Success Acknowledgement * ------------------------------------------------------------------------- */ STATIC int t_ok_ack(queue_t * q, ulong prim, mblk_t * cp, ss_t * as) { int err = -EFAULT; ss_t *ss = PRIV(q); mblk_t *mp; struct T_ok_ack *p; struct socket *sock = NULL; if ((mp = ss_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_OK_ACK; p->CORRECT_prim = prim; switch (ss_get_state(ss)) { case TS_WACK_CREQ: if ((err = ss_connect(ss, &ss->dst))) goto free_error; ss_set_state(ss, TS_WCON_CREQ); break; case TS_WACK_UREQ: if ((err = ss_unbind(ss))) goto free_error; ss_set_state(ss, TS_UNBND); break; case TS_WACK_CRES: ensure(as && cp, goto free_error); if ((err = ss_accept(ss, &sock, cp))) goto free_error; if (as->sock) ss_socket_put(as->sock); /* get rid of old socket */ as->sock = sock; ss_socket_get(as->sock, as); ss_set_state(as, TS_DATA_XFER); if (bufq_length(&ss->conq)) ss_set_state(ss, TS_WRES_CIND); else { ss_set_state(ss, TS_IDLE); /* make sure any backed up indications are processed */ qenable(ss->rq); } break; case TS_WACK_DREQ7: ensure(cp, goto free_error); if (!(err = ss_accept(ss, &sock, cp))) sock_release(sock); if (bufq_length(&ss->conq)) ss_set_state(ss, TS_WRES_CIND); else { ss_set_state(ss, TS_IDLE); /* make sure any backed up indications are processed */ qenable(ss->rq); } break; case TS_WACK_DREQ6: case TS_WACK_DREQ9: case TS_WACK_DREQ10: case TS_WACK_DREQ11: if ((err = ss_disconnect(ss))) goto free_error; ss_set_state(ss, TS_IDLE); break; default: break; /* * Note: if we are not in a WACK state we simply do * not change state. This occurs normally when we * are responding to a T_OPTMGMT_REQ in other than * the TS_IDLE state. */ } printd(("%s: %p: <- T_OK_ACK\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (QR_DONE); } ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME)); return (-ENOBUFS); free_error: freemsg(mp); return t_error_ack(q, prim, err); } /* * T_UNIDATA_IND 20 - Unitdata indication * ------------------------------------------------------------------------- */ STATIC int t_unitdata_ind(queue_t * q, ss_addr_t * src, ss_opts_t * opts, mblk_t * dp) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_unitdata_ind *p; size_t src_len = src ? sizeof(*src) : 0; size_t opt_len = ss_opts_size(ss, opts); if ((mp = ss_allocb(q, sizeof(*p) + src_len + opt_len, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_UNITDATA_IND; p->SRC_length = src_len; p->SRC_offset = src_len ? sizeof(*p) : 0; p->OPT_length = opt_len; p->OPT_offset = opt_len ? sizeof(*p) + src_len : 0; if (src_len) { bcopy(src, mp->b_wptr, src_len); mp->b_wptr += src_len; } if (opt_len) { ss_build_opts(ss, opts, &mp->b_wptr); } mp->b_cont = dp; printd(("%s: %p: <- T_UNITDATA_IND\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (QR_ABSORBED); } ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } /* * T_UDERROR_IND 21 - Unitdata Error Indication * ------------------------------------------------------------------------- * This primitive indicates to the transport user that a datagram with the * specified destination address and options produced an error. */ STATIC INLINE int t_uderror_ind(queue_t * q, ss_addr_t * src, ss_addr_t * dst, ss_opts_t * opts, mblk_t * dp, int etype) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_uderror_ind *p; size_t dst_len = dst ? sizeof(*dst) : 0; size_t opt_len = ss_opts_size(ss, opts); (void) src; if (canputnext(ss->rq)) { if ((mp = ss_allocb(q, sizeof(*p) + dst_len + opt_len, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; mp->b_band = 2; /* XXX move ahead of data indications */ p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_UDERROR_IND; p->DEST_length = dst_len; p->DEST_offset = dst_len ? sizeof(*p) : 0; p->OPT_length = opt_len; p->OPT_offset = opt_len ? sizeof(*p) + dst_len : 0; p->ERROR_type = etype; if (dst_len) { bcopy(dst, mp->b_wptr, dst_len); mp->b_wptr += dst_len; } if (opt_len) { ss_build_opts(ss, opts, &mp->b_wptr); } printd(("%s: %p: <- T_UDERROR_IND\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (0); } ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } ptrace(("%s: ERROR: Flow controlled\n", SS_MOD_NAME)); return (-EBUSY); } /* * T_OPTMGMT_ACK 22 - Options Management Acknowledge * ------------------------------------------------------------------------- */ STATIC int t_optmgmt_ack(queue_t * q, ulong flags, ss_opts_t * opts) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_optmgmt_ack *p; size_t opt_len = ss_opts_size(ss, opts); if ((mp = ss_allocb(q, sizeof(*p) + opt_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_OPTMGMT_ACK; p->OPT_length = opt_len; p->OPT_offset = opt_len ? sizeof(*p) : 0; p->MGMT_flags = flags; if (opt_len) { ss_build_opts(ss, opts, &mp->b_wptr); } #ifdef TS_WACK_OPTREQ if (ss_get_state(ss) == TS_WACK_OPTREQ) ss_set_state(ss, TS_IDLE); #endif printd(("%s: %p: <- T_OPTMGMT_ACK\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (0); } ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } /* * T_ORDREL_IND 23 - Orderly Release Indication * ------------------------------------------------------------------------- */ STATIC int t_ordrel_ind(queue_t * q) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_ordrel_ind *p; if (canputnext(ss->rq)) { if ((mp = ss_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_ORDREL_IND; switch (ss_get_state(ss)) { case TS_DATA_XFER: ss_set_state(ss, TS_WREQ_ORDREL); break; case TS_WIND_ORDREL: ss_set_state(ss, TS_IDLE); break; } printd(("%s: %p: <- T_ORDREL_IND\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (0); } ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } ptrace(("%s: ERROR: Flow controlled\n", SS_MOD_NAME)); return (-EBUSY); } /* * T_OPTDATA_IND 26 - Data with Options Indication * ------------------------------------------------------------------------- */ STATIC int t_optdata_ind(queue_t * q, uint flags, ss_opts_t * opts, mblk_t * dp) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_optdata_ind *p; size_t opt_len = ss_opts_size(ss, opts); if ((mp = ss_allocb(q, sizeof(*p) + opt_len, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; mp->b_band = flags & T_ODF_EX ? 1 : 0; /* expedite */ p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_OPTDATA_IND; p->DATA_flag = flags & (T_ODF_MORE | T_ODF_EX); p->OPT_length = opt_len; p->OPT_offset = opt_len ? sizeof(*p) : 0; if (opt_len) { ss_build_opts(ss, opts, &mp->b_wptr); } mp->b_cont = dp; printd(("%s: %p: <- T_OPTDATA_IND\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (QR_ABSORBED); } ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } #ifdef T_ADDR_ACK /* * T_ADDR_ACK 27 - Address Acknowledgement * ------------------------------------------------------------------------- */ STATIC int t_addr_ack(queue_t * q, ss_addr_t * loc, ss_addr_t * rem) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_addr_ack *p; size_t loc_len = loc ? sizeof(*loc) : 0; size_t rem_len = rem ? sizeof(*rem) : 0; if ((mp = ss_allocb(q, sizeof(*p) + loc_len + rem_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_ADDR_ACK; p->LOCADDR_length = loc_len; p->LOCADDR_offset = loc_len ? sizeof(*p) : 0; p->REMADDR_length = rem_len; p->REMADDR_offset = rem_len ? sizeof(*p) + loc_len : 0; if (loc_len) { bcopy(loc, mp->b_wptr, loc_len); mp->b_wptr += loc_len; } if (rem_len) { bcopy(rem, mp->b_wptr, rem_len); mp->b_wptr += rem_len; } printd(("%s: %p: <- T_ADDR_ACK\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (0); } ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } #endif #ifdef T_CAPABILITY_ACK /* * T_CAPABILITY_ACK ?? - Protocol Capability Ack * ------------------------------------------------------------------------- */ STATIC int t_capability_ack(queue_t * q, ulong caps) { ss_t *ss = PRIV(q); mblk_t *mp; struct T_capability_ack *p; if ((mp = ss_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = T_CAPABILITY_ACK; p->CAP_bits1 = TC1_INFO; p->ACCPETOR_id = (caps & TC1_ACCEPTOR) ? (ulong) ss->rq : 0; if (caps & TC1_INFO) p->INFO_ack = ss->p.info; else bzero(&p->INFO_ack, sizeof(p->INFO_ack)); printd(("%s: %p: <- T_CAPABILITY_ACK\n", SS_MOD_NAME, ss)); putnext(ss->rq, mp); return (0); } ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME)); return (-ENOBUFS); } #endif /* * ========================================================================= * * CONNECTION INNDICATIONS * * ========================================================================= */ /* * CONIND * ------------------------------------------------------------------------- * An unfortunate mis-match between the socket model and the TLI model is * that in TLI it is possible to determine the remote address from a * T_CONN_IND *before* accepting the connection. Under sockets, the * connection must be accepted before it is possible to determine the peer's * address. This means that connection must be accepted and then released, * rather than refused. */ STATIC int ss_conn_ind(queue_t * q, mblk_t * cp) { ss_t *ss = PRIV(q); struct sock *sk; struct ss_event *p = (typeof(p)) cp->b_rptr; ss_addr_t dst = { AF_UNSPEC, 0, }; ss_opts_t opt = { 0, }; if ((1 << ss_get_state(ss)) & ~TSM_LISTEN) goto outstate; if (!ss->conind || ss->sock->sk->state != TCP_LISTEN) goto nolisten; if (cp->b_wptr < cp->b_rptr + sizeof(*p)) goto einval; if (ss->sock->sk == p->sk) goto duplicate; sk = p->sk; dst.sin_family = AF_INET; dst.sin_port = sk->dport; dst.sin_addr.s_addr = sk->daddr; #ifdef LINUX_2_4 opt.opt = sk->protinfo.af_inet.opt; opt.tos = (ulong *) & sk->protinfo.af_inet.tos; opt.ttl = (ulong *) & sk->protinfo.af_inet.ttl; #else opt.opt = sk->opt; opt.tos = (ulong *) & sk->ip_tos; opt.ttl = (ulong *) & sk->ip_ttl; #endif opt.reuse = (ulong *) & sk->reuse; opt.norte = (ulong *) & sk->localroute; opt.bcast = (ulong *) & sk->broadcast; if (opt.opt) opt.flags |= TF_IP_OPTIONS; opt.flags |= TF_IP_TOS; opt.flags |= TF_IP_TTL; opt.flags |= TF_IP_REUSEADDR; opt.flags |= TF_IP_DONTROUTE; opt.flags |= TF_IP_BROADCAST; if (ss->p.prot.protocol == T_INET_TCP) { #ifdef LINUX_2_4 opt.nodly = (ulong *) & sk->tp_pinfo.af_tcp.nonagle; #else opt.nodly = (ulong *) & sk->nonagle; #endif opt.mss = (ulong *) & sk->tp_pinfo.af_tcp.user_mss; opt.alive = (ulong *) & sk->keepopen; opt.flags |= TF_TCP_NODELAY; opt.flags |= TF_TCP_MAXSEG; opt.flags |= TF_TCP_KEEPALIVE; } if (ss->p.prot.protocol == T_INET_SCTP) { /* * skip for now */ } return t_conn_ind(q, &dst, &opt, cp); einval: swerr(); ptrace(("%s: SWERR: invalid primitive format\n", SS_MOD_NAME)); return (-EFAULT); outstate: ptrace(("%s: ERROR: connect indication in wrong state %ld\n", SS_MOD_NAME, ss_get_state(ss))); return (-EPROTO); nolisten: ptrace(("%s: ERROR: connect indication received while not listening\n", SS_MOD_NAME)); return (-EPROTO); duplicate: ptrace(("%s: %p: INFO: discarding duplicate connection indication\n", SS_MOD_NAME, ss)); return (QR_DONE); } /* * ========================================================================= * * SENDMSG and RECVMSG * * ========================================================================= */ /* * SENDMSG * ------------------------------------------------------------------------- * Convert the mblk to send into an iovec and the options into a control * message and then call sendmsg on the socket with the kernel data segment. * The socket will handle moving data from the mblks. */ STATIC int ss_sock_sendmsg(ss_t * ss, mblk_t * mp, ss_addr_t * dst, int flags, ss_opts_t * opts) { int err = 0; int len, sdu, n; mblk_t *dp; for (len = 0, n = 0, dp = mp; dp; dp = dp->b_cont) { if (dp->b_datap->db_type == M_DATA && dp->b_wptr > dp->b_rptr) { len += dp->b_wptr - dp->b_rptr; n++; } } err = -EPROTO; if (!(ss->p.info.PROVIDER_flag & T_SNDZERO) && (!n || !len)) goto out; if ((sdu = (int) ss->p.info.TSDU_size) > 0 && (!n || len > sdu)) goto out; err = -EBUSY; if (len > sock_wspace(ss->sock->sk)) goto out; /* * This has the ramification that we can never do zero length writes, * but we have ~T_SNDZERO set anyway. */ { int i; struct iovec iov[n]; struct msghdr msg; size_t clen = opts ? ss_cmsg_size(opts, TF_IP_OPTIONS | TF_IP_PKTINFO) : 0; unsigned char cbuf[clen > 1024 ? 1024 : clen]; flags |= (MSG_DONTWAIT | MSG_NOSIGNAL); flags |= ss->options.norte ? MSG_DONTROUTE : 0; msg.msg_name = dst; msg.msg_namelen = dst ? sizeof(*dst) : 0; msg.msg_iov = iov; msg.msg_iovlen = n; msg.msg_control = clen ? cbuf : NULL; msg.msg_controllen = clen; msg.msg_flags = flags; /* * convert message blocks to an iovec */ for (i = 0, dp = mp; dp; dp = dp->b_cont) { if (dp->b_datap->db_type == M_DATA && dp->b_wptr > dp->b_rptr) { iov[i].iov_base = dp->b_rptr; iov[i].iov_len = dp->b_wptr - dp->b_rptr; i++; } } if (opts) { ss_build_cmsg(opts, &msg, TF_IP_OPTIONS | TF_IP_PKTINFO); ss_opt_negotiate(ss, opts); } ptrace(("%s: %p: sendmsg with len = %d\n", SS_MOD_NAME, ss, len)); err = ss_sendmsg(ss, &msg, len); } if (err < 0) goto out; if (!err) { err = -EBUSY; goto out; } if (err >= len) return (QR_DONE); /* full write */ switch (ss->p.prot.type) { /* partial write */ case SOCK_DGRAM: case SOCK_RAW: case SOCK_RDM: err = -EMSGSIZE; goto out; case SOCK_SEQPACKET: case SOCK_STREAM: adjmsg(mp, err); err = -EAGAIN; goto out; } err = -EFAULT; out: switch (-err) { case ENOMEM: /* * This buffer call is just to kick us. Because LiS uses kmalloc for * mblks, if we can allocate an mblk, then another kernel routine can * allocate that much memory too. */ if (ss->wbid) { unbufcall(xchg(&ss->wbid, 0)); ss->refcnt--; } ss->wbid = bufcall(len, BPRI_LO, &ss_bufsrv, (long) ss->wq); ss->refcnt++; default: return m_error(ss->rq, err); } } /* * RECVMSG * ------------------------------------------------------------------------- */ STATIC int ss_sock_recvmsg(queue_t * q) { ss_t *ss = PRIV(q); mblk_t *mp; int more, expd, res, oldsize = 0x7fffffff, size = 0; struct msghdr msg = { NULL, }; unsigned char cbuf[128] = { 0, }; ss_addr_t src = { AF_INET, 0, {0}, }; struct sk_buff *skb; while (canputnext(ss->rq)) { switch (ss->p.prot.type) { case SOCK_DGRAM: case SOCK_RAW: case SOCK_RDM: case SOCK_STREAM: { size = (skb = skb_peek(&ss->sock->sk->receive_queue)) ? skb->len : 0; break; } case SOCK_SEQPACKET: size = atomic_read(&ss->sock->sk->rmem_alloc); break; } if (!size || size >= oldsize) return (QR_DONE); oldsize = size; if (!(mp = ss_allocb(ss->rq, size, BPRI_MED))) return (-ENOBUFS); { struct iovec iov[1]; iov[0].iov_base = mp->b_rptr; iov[0].iov_len = size; if (ss->p.prot.type != SOCK_STREAM) { msg.msg_name = &src; msg.msg_namelen = sizeof(src); } else { msg.msg_name = NULL; msg.msg_namelen = 0; } msg.msg_iov = iov; msg.msg_iovlen = sizeof(iov) / sizeof(struct iovec); msg.msg_control = cbuf; msg.msg_controllen = sizeof(cbuf); msg.msg_flags = 0; if ((res = ss_recvmsg(ss, &msg, size)) < 0) { freemsg(mp); return (res); } if (res == 0) { freemsg(mp); return (QR_DONE); } mp->b_wptr = mp->b_rptr + res; ptrace(("%s: %p: recvmsg with len = %d\n", SS_MOD_NAME, ss, res)); } #if 0 if (msg.msg_flags & MSG_TRUNC) { freemsg(mp); return (-EMSGSIZE); } #endif { int err; ss_opts_t opt = { 0, }; if (msg.msg_controllen) ss_parse_cmsg(&msg, &opt); more = (msg.msg_flags & MSG_EOR) ? 0 : T_ODF_MORE; expd = (msg.msg_flags & MSG_OOB) ? T_ODF_EX : 0; if (ss->p.info.SERV_type == T_CLTS) { if ((err = t_unitdata_ind(q, &src, &opt, mp)) != QR_ABSORBED) return (err); } else if (!opt.flags) { if (msg.msg_flags & MSG_OOB) { if ((err = t_exdata_ind(q, more, mp)) != QR_ABSORBED) return (err); } else { if ((err = t_data_ind(q, more, mp)) != QR_ABSORBED) return (err); } } else { if ((err = t_optdata_ind(q, more | expd, &opt, mp)) != QR_ABSORBED) return (err); } } } return (-EBUSY); } /* * ========================================================================= * * STATE MACHINE * * ========================================================================= */ /* * ------------------------------------------------------------------------- * * Socket CALLBACKS * * ------------------------------------------------------------------------- */ STATIC void ss_putctl(ss_t * ss, queue_t * q, int type, void (*func) (long), struct sock *sk) { int flags; mblk_t *mp; ss_event_t *p; if ((mp = allocb(sizeof(*p), BPRI_HI))) { mp->b_datap->db_type = type; p = ((typeof(p)) mp->b_wptr)++; p->sk = sk; p->state = sk->state; /* capture current state */ putq(q, mp); return (void) (0); } /* * set up bufcall so we don't lose events */ lis_spin_lock_irqsave(&ss->lock, &flags); { /* * make sure bufcalls don't happen now */ if (q == ss->rq) { if (ss->rbid) unbufcall(xchg(&ss->rbid, 0)); ss->rbid = bufcall(FASTBUF, BPRI_HI, func, (long) sk); } else if (q == ss->wq) { if (ss->wbid) unbufcall(xchg(&ss->wbid, 0)); ss->wbid = bufcall(FASTBUF, BPRI_HI, func, (long) sk); } } lis_spin_unlock_irqrestore(&ss->lock, &flags); } /* * STATE change * ------------------------------------------------------------------------- * We get state changes on sockets that we hold. We also get state changes * on accepting sockets. */ STATIC void _ss_sock_state_change(long data) { struct sock *sk; ss_t *ss; if ((sk = (struct sock *) data)) { if ((ss = SOCK_PRIV(sk))) { read_lock(&sk->callback_lock); ss_putctl(ss, ss->rq, M_PCRSE, &_ss_sock_state_change, sk); read_unlock(&sk->callback_lock); } else assure(ss); } else assure(sk); } STATIC void ss_state_change(struct sock *sk) { if (sk) { ss_t *ss; if ((ss = SOCK_PRIV(sk))) { _ss_sock_state_change((long) sk); // ss->cb_save.state_change(sk); } else assure(ss); } else assure(sk); } /* * WRITE Available * ------------------------------------------------------------------------- */ STATIC void _ss_sock_write_space(long data) { struct sock *sk; ss_t *ss; if ((sk = (struct sock *) data)) { if ((ss = SOCK_PRIV(sk))) { read_lock(&sk->callback_lock); ss_putctl(ss, ss->wq, M_READ, &_ss_sock_write_space, sk); read_unlock(&sk->callback_lock); } else assure(ss); } else assure(sk); } STATIC void ss_write_space(struct sock *sk) { if (sk) { ss_t *ss; if ((ss = SOCK_PRIV(sk))) { _ss_sock_write_space((long) sk); // ss->cb_save.write_space(sk); } else assure(ss); } else assure(sk); } /* * ERROR Available * ------------------------------------------------------------------------- */ STATIC void _ss_sock_error_report(long data) { struct sock *sk; ss_t *ss; if ((sk = (struct sock *) data)) { if ((ss = SOCK_PRIV(sk))) { read_lock(&sk->callback_lock); ss_putctl(ss, ss->rq, M_ERROR, &_ss_sock_error_report, sk); read_unlock(&sk->callback_lock); } else assure(ss); } else assure(sk); } STATIC void ss_error_report(struct sock *sk) { if (sk) { ss_t *ss; if ((ss = SOCK_PRIV(sk))) { _ss_sock_error_report((long) sk); // ss->cb_save.error_report(sk); } else assure(ss); } else assure(sk); } /* * READ Available * ------------------------------------------------------------------------- */ STATIC void _ss_sock_data_ready(long data) { struct sock *sk; ss_t *ss; if ((sk = (struct sock *) data)) { if ((ss = SOCK_PRIV(sk))) { read_lock(&sk->callback_lock); ss_putctl(ss, ss->rq, M_READ, &_ss_sock_data_ready, sk); read_unlock(&sk->callback_lock); } else assure(ss); } else assure(sk); } STATIC void ss_data_ready(struct sock *sk, int len) { if (sk) { ss_t *ss; if ((ss = SOCK_PRIV(sk))) { (void) len; _ss_sock_data_ready((long) sk); // ss->cb_save.data_ready(sk, len); } else assure(ss); } else assure(sk); } /* * ========================================================================= * * IP T-User --> T-Provider Primitives (Request and Response) * * ========================================================================= * * T_CONN_REQ 0 - TC Request * ------------------------------------------------------------------- */ STATIC int t_conn_req(queue_t * q, mblk_t * mp) { int err = -EFAULT; ss_t *ss = PRIV(q); const struct T_conn_req *p = (typeof(p)) mp->b_rptr; if (ss->p.info.SERV_type == T_CLTS) goto notsupport; if (ss_get_state(ss) != TS_IDLE) goto outstate; if (mp->b_wptr < mp->b_rptr + sizeof(*p) || mp->b_wptr < mp->b_rptr + p->DEST_offset + p->DEST_length || mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length) goto einval; else { ss_addr_t *dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset); if (p->DEST_length < sizeof(*dst)) goto badaddr; if (dst->sin_port == 0) goto badaddr; if (dst->sin_family != ss->p.prot.family) goto badaddr; if (ss->cred.cr_uid != 0 && htons(dst->sin_port) == IPPROTO_RAW) goto acces; else { unsigned char *opt = mp->b_rptr + p->OPT_offset; size_t opt_len = p->OPT_length; struct ss_opts opts = { 0L, NULL, }; if (opt_len && ss_parse_opts(ss, &opts, opt, opt_len)) goto badopt; /* * TODO: set options first */ ss->dst = *dst; if (mp->b_cont) { putbq(q, mp->b_cont); /* hold back data */ mp->b_cont = NULL; /* abosrbed mp->b_cont */ } ss_set_state(ss, TS_WACK_CREQ); return t_ok_ack(q, T_CONN_REQ, NULL, NULL); } } badopt: err = TBADOPT; ptrace(("%s: ERROR: bad options\n", SS_MOD_NAME)); goto error; acces: err = TACCES; ptrace(("%s: ERROR: no permission for address\n", SS_MOD_NAME)); goto error; badaddr: err = TBADADDR; ptrace(("%s: ERROR: address is unusable\n", SS_MOD_NAME)); goto error; einval: err = -EINVAL; ptrace(("%s: ERROR: invalid message format\n", SS_MOD_NAME)); goto error; outstate: err = TOUTSTATE; ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME)); goto error; notsupport: err = TNOTSUPPORT; ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME)); goto error; error: return t_error_ack(q, T_CONN_REQ, err); } /* * T_CONN_RES 1 - Accept previous connection indication * ------------------------------------------------------------------- */ STATIC mblk_t *t_seq_check(ss_t * ss, ulong seq) { mblk_t *mp; lis_spin_lock(&ss->conq.q_lock); { for (mp = bufq_head(&ss->conq); mp && mp != (mblk_t *) seq; mp = mp->b_next) ; } lis_spin_unlock(&ss->conq.q_lock); usual(mp); return (mp); } STATIC ss_t *t_tok_check(ulong acceptor) { ss_t *as; queue_t *aq = (queue_t *) acceptor; for (as = ss_opens; as && as->rq != aq; as = as->next) ; usual(as); return (as); } STATIC int t_conn_res(queue_t * q, mblk_t * mp) { ss_t *ss = PRIV(q); int err = 0; mblk_t *cp; ss_t *as; const size_t mlen = mp->b_wptr - mp->b_rptr; const struct T_conn_res *p = (typeof(p)) mp->b_rptr; struct ss_opts opts = { 0L, NULL, }; size_t opt_len, opt_off; unsigned char *opt; if (ss->p.info.SERV_type == T_CLTS) goto notsupport; if (ss_get_state(ss) != TS_WRES_CIND) goto outstate; ss_set_state(ss, TS_WACK_CRES); if (mlen < sizeof(*p)) goto inval; if ((cp = t_seq_check(ss, p->SEQ_number)) == NULL) goto badseq; if (((as = t_tok_check(p->ACCEPTOR_id)) == NULL) || !(as == ss || ((1 << ss_get_state(as)) & TSM_DISCONN))) goto badf; if (ss->p.prot.protocol != as->p.prot.protocol) goto provmismatch; if (ss_get_state(as) == TS_IDLE && as->conind) goto resqlen; if (ss->cred.cr_uid != 0 && as->cred.cr_uid == 0) goto acces; opt_len = p->OPT_length; opt_off = p->OPT_offset; opt = mp->b_rptr + opt_off; if (opt_len && (mlen < opt_off + opt_len || (err = ss_parse_opts(ss, &opts, opt, opt_len)))) goto badopt; if (mp->b_cont != NULL && msgdsize(mp->b_cont)) goto baddata; return t_ok_ack(q, T_CONN_RES, cp, as); baddata: err = TBADDATA; ptrace(("%s: ERROR: bad connection data\n", SS_MOD_NAME)); goto error; badopt: err = TBADOPT; ptrace(("%s: ERROR: bad options\n", SS_MOD_NAME)); goto error; acces: err = TACCES; ptrace(("%s: ERROR: no access to accepting queue\n", SS_MOD_NAME)); goto error; resqlen: err = TRESQLEN; ptrace(("%s: ERROR: accepting queue is listening\n", SS_MOD_NAME)); goto error; provmismatch: err = TPROVMISMATCH; ptrace(("%s: ERROR: not same transport provider\n", SS_MOD_NAME)); goto error; badf: err = TBADF; ptrace(("%s: ERROR: accepting queue id is invalid\n", SS_MOD_NAME)); goto error; badseq: err = TBADSEQ; ptrace(("%s: ERROR: sequence number is invalid\n", SS_MOD_NAME)); goto error; inval: err = -EINVAL; ptrace(("%s: ERROR: invalid primitive format\n", SS_MOD_NAME)); goto error; outstate: err = TOUTSTATE; ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME)); goto error; notsupport: err = TNOTSUPPORT; ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME)); goto error; error: return t_error_ack(q, T_CONN_RES, err); } /* * T_DISCON_REQ 2 - TC disconnection request * ------------------------------------------------------------------- */ STATIC int t_discon_req(queue_t * q, mblk_t * mp) { ss_t *ss = PRIV(q); int err; mblk_t *cp = NULL; struct T_discon_req *p = (typeof(p)) mp->b_rptr; if (ss->p.info.SERV_type == T_CLTS) goto notsupport; if ((1 << ss_get_state(ss)) & ~TSM_CONNECTED) goto outstate; if (mp->b_wptr - mp->b_rptr < sizeof(*p)) goto einval; switch (ss_get_state(ss)) { case TS_WCON_CREQ: ss_set_state(ss, TS_WACK_DREQ6); break; case TS_WRES_CIND: if ((cp = t_seq_check(ss, p->SEQ_number))) { ss_set_state(ss, TS_WACK_DREQ7); break; } goto badseq; case TS_DATA_XFER: ss_set_state(ss, TS_WACK_DREQ9); break; case TS_WIND_ORDREL: ss_set_state(ss, TS_WACK_DREQ10); break; case TS_WREQ_ORDREL: ss_set_state(ss, TS_WACK_DREQ11); break; } return t_ok_ack(q, T_DISCON_REQ, cp, NULL); badseq: err = TBADSEQ; ptrace(("%s: ERROR: sequence number is invalid\n", SS_MOD_NAME)); goto error; einval: err = -EINVAL; ptrace(("%s: ERROR: invalid primitive format\n", SS_MOD_NAME)); goto error; outstate: err = TOUTSTATE; ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME)); goto error; notsupport: err = TNOTSUPPORT; ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME)); goto error; error: return t_error_ack(q, T_DISCON_REQ, err); } /* * M_DATA * ------------------------------------------------------------------- */ STATIC int ss_w_data(queue_t * q, mblk_t * mp) { int flags, mlen, mmax; ss_t *ss = PRIV(q); if (ss->p.info.SERV_type == T_CLTS) goto notsupport; if (ss_get_state(ss) == TS_IDLE) goto discard; if ((1 << ss_get_state(ss)) & ~TSM_OUTDATA) goto outstate; mlen = msgdsize(mp); mmax = ss->p.info.TIDU_size; if (mmax < ss->p.info.TSDU_size && ss->p.info.TSDU_size != T_INVALID) mmax = ss->p.info.ETSDU_size; if (mlen > mmax) goto emsgsize; flags = (ss->p.prot.type == SOCK_SEQPACKET) ? MSG_EOR : 0; return ss_sock_sendmsg(ss, mp, NULL, flags, NULL); emsgsize: ptrace(("%s: ERROR: message too large %d > %d\n", SS_MOD_NAME, mlen, mmax)); goto error; outstate: ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME)); goto error; discard: ptrace(("%s: ERROR: ignore in idle state\n", SS_MOD_NAME)); return (QR_DONE); notsupport: ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME)); goto error; error: return m_error(q, EPROTO); } /* * T_DATA_REQ 3 - Connection-Mode data transfer request * ------------------------------------------------------------------- */ STATIC int t_data_req(queue_t * q, mblk_t * mp) { int flags, mlen, mmax; ss_t *ss = PRIV(q); const struct T_data_req *p = (typeof(p)) mp->b_rptr; if (ss->p.info.SERV_type == T_CLTS) goto notsupport; if (ss_get_state(ss) == TS_IDLE) goto discard; if (mp->b_wptr - mp->b_rptr < sizeof(*p)) goto einval; if ((1 << ss_get_state(ss)) & ~TSM_OUTDATA) goto outstate; mlen = msgdsize(mp); mmax = ss->p.info.TIDU_size; if (mmax < ss->p.info.TSDU_size && ss->p.info.TSDU_size != T_INVALID) mmax = ss->p.info.ETSDU_size; if (mlen > mmax) goto emsgsize; flags = (ss->p.prot.type == SOCK_SEQPACKET && !p->MORE_flag) ? MSG_EOR : 0; return ss_sock_sendmsg(ss, mp, NULL, flags, NULL); emsgsize: ptrace(("%s: ERROR: message too large %d > %d\n", SS_MOD_NAME, mlen, mmax)); goto error; outstate: ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME)); goto error; einval: ptrace(("%s: ERROR: invalid primitive format\n", SS_MOD_NAME)); goto error; discard: ptrace(("%s: ERROR: ignore in idle state\n", SS_MOD_NAME)); return (QR_DONE); notsupport: ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME)); goto error; error: return m_error(q, EPROTO); } /* * T_EXDATA_REQ 4 - Expedited data request * ------------------------------------------------------------------- */ STATIC int t_exdata_req(queue_t * q, mblk_t * mp) { int flags, mlen, mmax; ss_t *ss = PRIV(q); const struct T_exdata_req *p = (typeof(p)) mp->b_rptr; if (ss->p.info.SERV_type == T_CLTS) goto notsupport; if (ss_get_state(ss) == TS_IDLE) goto discard; if (mp->b_wptr - mp->b_rptr < sizeof(*p)) goto einval; if ((1 << ss_get_state(ss)) & ~TSM_OUTDATA) goto outstate; mlen = msgdsize(mp); mmax = ss->p.info.TIDU_size; if (mmax < ss->p.info.ETSDU_size && ss->p.info.ETSDU_size != T_INVALID) mmax = ss->p.info.ETSDU_size; if (mlen > mmax) goto emsgsize; flags = (ss->p.prot.type == SOCK_SEQPACKET && !p->MORE_flag) ? MSG_EOR : 0; return ss_sock_sendmsg(ss, mp, NULL, MSG_OOB | flags, NULL); emsgsize: ptrace(("%s: ERROR: message too large %d > %d\n", SS_MOD_NAME, mlen, mmax)); goto error; outstate: ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME)); goto error; einval: ptrace(("%s: ERROR: invalid primitive format\n", SS_MOD_NAME)); goto error; discard: ptrace(("%s: ERROR: ignore in idle state\n", SS_MOD_NAME)); return (QR_DONE); notsupport: ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME)); goto error; error: return m_error(q, EPROTO); } /* * T_INFO_REQ 5 - Information Request * ------------------------------------------------------------------- */ STATIC int t_info_req(queue_t * q, mblk_t * mp) { ss_t *ss = PRIV(q); (void) mp; (void) ss; return t_info_ack(q); } /* * T_BIND_REQ 6 - Bind a TS user to a transport address * ------------------------------------------------------------------- */ STATIC int t_bind_req(queue_t * q, mblk_t * mp) { ss_t *ss = PRIV(q); int err; const struct T_bind_req *p = (typeof(p)) mp->b_rptr; if (ss_get_state(ss) != TS_UNBND) goto outstate; ss_set_state(ss, TS_WACK_BREQ); if (mp->b_wptr - mp->b_rptr < sizeof(*p)) goto einval; if (ss->p.info.SERV_type == T_CLTS && p->CONIND_number) goto notsupport; if ((mp->b_wptr - mp->b_rptr < p->ADDR_offset + p->ADDR_length) || (p->ADDR_length != sizeof(ss_addr_t))) goto badaddr; { ss_addr_t *add = (typeof(add)) (mp->b_rptr + p->ADDR_offset); /* * we don't allow wildcards yet */ if (!add->sin_port) goto noaddr; if (add->sin_family != ss->p.prot.family) goto badaddr; if (ss->p.prot.type == SOCK_RAW && ss->cred.cr_uid != 0) goto acces; ss->port = htons(add->sin_port); if ((err = ss_socket(ss))) goto error; if ((err = ss_bind(ss, add))) goto error_close; ss->conind = 0; if (p->CONIND_number && (ss->p.prot.type == SOCK_STREAM || ss->p.prot.type == SOCK_SEQPACKET)) if ((err = ss_listen(ss, p->CONIND_number))) goto error_close; return t_bind_ack(q, &ss->src, p->CONIND_number); } acces: err = TACCES; ptrace(("%s: ERROR: no permission for address\n", SS_MOD_NAME)); goto error; noaddr: err = TNOADDR; ptrace(("%s: ERROR: couldn't allocate address\n", SS_MOD_NAME)); goto error; badaddr: err = TBADADDR; ptrace(("%s: ERROR: address is invalid\n", SS_MOD_NAME)); goto error; einval: err = -EINVAL; ptrace(("%s: ERROR: invalid primitive format\n", SS_MOD_NAME)); goto error; outstate: err = TOUTSTATE; ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME)); goto error; notsupport: err = TNOTSUPPORT; ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME)); goto error; error_close: ss_socket_put(xchg(&ss->sock, NULL)); error: return t_error_ack(q, T_BIND_REQ, err); } /* * T_UNBIND_REQ 7 - Unbind TS user from transport address * ------------------------------------------------------------------- */ STATIC int t_unbind_req(queue_t * q, mblk_t * mp) { ss_t *ss = PRIV(q); if (ss_get_state(ss) != TS_IDLE) goto outstate; ss_set_state(ss, TS_WACK_UREQ); return t_ok_ack(q, T_UNBIND_REQ, NULL, NULL); outstate: ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME)); return t_error_ack(q, T_UNBIND_REQ, TOUTSTATE); } /* * T_UNITDATA_REQ 8 -Unitdata Request * ------------------------------------------------------------------- */ STATIC int t_unitdata_req(queue_t * q, mblk_t * mp) { ss_t *ss = PRIV(q); size_t mlen = mp->b_wptr - mp->b_rptr; size_t dlen = mp->b_cont ? msgdsize(mp->b_cont) : 0; const struct T_unitdata_req *p = (typeof(p)) mp->b_rptr; if (ss->p.info.SERV_type != T_CLTS) goto notsupport; if (dlen == 0 && !(ss->p.info.PROVIDER_flag & T_SNDZERO)) goto baddata; if (dlen > ss->p.info.TSDU_size || dlen > ss->p.info.TIDU_size) goto baddata; if (ss_get_state(ss) != TS_IDLE) goto outstate; if (mlen < sizeof(*p)) goto einval; if ((mlen < p->DEST_offset + p->DEST_length) || (p->DEST_length != sizeof(ss_addr_t))) goto badadd; if (ss->p.prot.type == SOCK_RAW && ss->cred.cr_uid != 0) goto acces; else { size_t opt_len = p->OPT_length; size_t opt_off = p->OPT_offset; unsigned char *opt = mp->b_rptr + opt_off; struct ss_opts opts = { 0L, NULL, }; if (opt_len && (mlen < opt_off + opt_len || ss_parse_opts(ss, &opts, opt, opt_len))) goto badopt; else { int flags = (opts.norte && *(opts.norte)) ? MSG_DONTROUTE : 0; ss_addr_t *d = (typeof(d)) (mp->b_rptr + p->DEST_offset); return ss_sock_sendmsg(ss, mp, d, flags, &opts); } } badopt: ptrace(("%s: ERROR: bad options\n", SS_MOD_NAME)); goto error; acces: ptrace(("%s: ERROR: no permission to address\n", SS_MOD_NAME)); goto error; badadd: ptrace(("%s: ERROR: bad destination address\n", SS_MOD_NAME)); goto error; einval: ptrace(("%s: ERROR: invalid primitive\n", SS_MOD_NAME)); goto error; outstate: ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME)); goto error; baddata: ptrace(("%s: ERROR: bad data size\n", SS_MOD_NAME)); goto error; notsupport: ptrace(("%s: ERROR: primitive not supported for T_COTS\n", SS_MOD_NAME)); goto error; error: return m_error(q, EPROTO); } /* * T_OPTMGMT_REQ 9 - Options management request * ------------------------------------------------------------------- */ STATIC int t_optmgmt_req(queue_t * q, mblk_t * mp) { ss_t *ss = PRIV(q); int err = 0; const size_t mlen = mp->b_wptr - mp->b_rptr; const struct T_optmgmt_req *p = (typeof(p)) mp->b_rptr; struct ss_opts opts = { 0L, NULL, }; size_t opt_len, opt_off; unsigned char *opt; #ifdef TS_WACK_OPTREQ if (ss_get_state(ss) == TS_IDLE) ss_set_state(ss, TS_WACK_OPTREQ); #endif if (mlen < sizeof(*p)) goto einval; opt_len = p->OPT_length; opt_off = p->OPT_offset; opt = mp->b_rptr + opt_off; if (opt_len && (mlen < opt_off + opt_len || (err = ss_parse_opts(ss, &opts, opt, opt_len)))) goto badopt; switch (p->MGMT_flags) { case T_CHECK: err = ss_opt_check(ss, &opts); break; case T_NEGOTIATE: if (!opts.flags) ss_opt_default(ss, &opts); else if ((err = ss_opt_check(ss, &opts))) break; err = ss_opt_negotiate(ss, &opts); break; case T_DEFAULT: err = ss_opt_default(ss, &opts); break; case T_CURRENT: err = ss_opt_current(ss, &opts); break; default: goto badflag; } if (err) goto provspec; return t_optmgmt_ack(q, p->MGMT_flags, &opts); provspec: err = err; ptrace(("%s: ERROR: provider specific\n", SS_MOD_NAME)); goto error; badflag: err = TBADFLAG; ptrace(("%s: ERROR: bad options flags\n", SS_MOD_NAME)); goto error; badopt: err = TBADOPT; ptrace(("%s: ERROR: bad options\n", SS_MOD_NAME)); goto error; einval: err = -EINVAL; ptrace(("%s: ERROR: invalid primitive format\n", SS_MOD_NAME)); goto error; error: return t_error_ack(q, T_OPTMGMT_REQ, err); } /* * T_ORDREL_REQ 10 - TS user is finished sending * ------------------------------------------------------------------- */ STATIC int t_ordrel_req(queue_t * q, mblk_t * mp) { ss_t *ss = PRIV(q); const struct T_ordrel_req *p = (typeof(p)) mp->b_rptr; if (ss->p.info.SERV_type != T_COTS_ORD) goto notsupport; if ((1 << ss_get_state(ss)) & ~TSM_OUTDATA) goto outstate; ss->sock->ops->shutdown(ss->sock, SEND_SHUTDOWN); switch (ss_get_state(ss)) { case TS_DATA_XFER: ss_set_state(ss, TS_WIND_ORDREL); break; case TS_WREQ_ORDREL: ss_set_state(ss, TS_IDLE); break; } return (QR_DONE); outstate: ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME)); goto error; notsupport: ptrace(("%s: ERROR: primitive not supported for T_CLTS or T_COTS\n", SS_MOD_NAME)); goto error; error: return m_error(q, EPROTO); } /* * T_OPTDATA_REQ 24 - Data with options request * ------------------------------------------------------------------- */ STATIC int t_optdata_req(queue_t * q, mblk_t * mp) { int err; ss_t *ss = PRIV(q); const size_t mlen = mp->b_wptr - mp->b_rptr; const struct T_optdata_req *p = (typeof(p)) mp->b_rptr; if (ss->p.info.SERV_type == T_CLTS) goto notsupport; if (ss_get_state(ss) == TS_IDLE) goto discard; if (mlen < sizeof(*p)) goto einval; if ((1 << ss_get_state(ss)) & ~TSM_OUTDATA) goto outstate; else { struct ss_opts opts = { 0L, NULL, }; size_t opt_len = p->OPT_length; size_t opt_off = p->OPT_offset; unsigned char *opt = mp->b_rptr + opt_off; if (opt_len && (mlen < opt_off + opt_len || ss_parse_opts(ss, &opts, opt, opt_len))) goto badopt; else { int flags = 0; if (opts.norte && *(opts.norte)) flags |= MSG_DONTROUTE; if ((p->DATA_flag & T_ODF_EX)) flags |= MSG_OOB; if (!(p->DATA_flag & T_ODF_MORE) && ss->p.prot.type == SOCK_SEQPACKET) flags |= MSG_EOR; return ss_sock_sendmsg(ss, mp, NULL, flags, &opts); } } badopt: err = TBADOPT; ptrace(("%s: ERROR: bad options\n", SS_MOD_NAME)); goto error; outstate: err = TOUTSTATE; ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME)); goto error; einval: ptrace(("%s: ERROR: invalid primitive format\n", SS_MOD_NAME)); return m_error(q, EPROTO); discard: ptrace(("%s: ERROR: ignore in idle state\n", SS_MOD_NAME)); return (QR_DONE); notsupport: err = TNOTSUPPORT; ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME)); goto error; error: return t_error_ack(q, T_OPTDATA_REQ, err); } #ifdef T_ADDR_REQ /* * T_ADDR_REQ 25 - Address Request * ------------------------------------------------------------------- */ STATIC int t_addr_req(queue_t * q, mblk_t * mp) { ss_t *ss = PRIV(q); (void) mp; switch (ss_get_state(ss)) { case TS_UNBND: return t_addr_ack(q, NULL, NULL); case TS_IDLE: return t_addr_ack(q, &ss->src, NULL); case TS_WCON_CREQ: case TS_DATA_XFER: case TS_WIND_ORDREL: case TS_WREQ_ORDREL: return t_addr_ack(q, &ss->src, &ss->dst); case TS_WRES_CIND: return t_addr_ack(q, NULL, &ss->dst); } return t_error_ack(q, T_ADDR_REQ, TOUTSTATE); } #endif #ifdef T_CAPABILITY_REQ /* * T_CAPABILITY_REQ ?? - Capability Request * ------------------------------------------------------------------- */ #endif /* * Other primitives XX - other invalid primitives * ------------------------------------------------------------------------- */ STATIC int t_other_req(queue_t * q, mblk_t * mp) { ulong prim = *((ulong *) mp->b_rptr); return t_error_ack(q, prim, TNOTSUPPORT); } /* * ------------------------------------------------------------------------- * * IO Controls * * ------------------------------------------------------------------------- */ #if 0 STATIC int ti_getinfo(queue_t * q, mblk_t * mp) { } STATIC int ti_optmgmt(queue_t * q, mblk_t * mp) { } STATIC int ti_bind(queue_t * q, mblk_t * mp) { } STATIC int ti_unbind(queue_t * q, mblk_t * mp) { } STATIC int ti_getmyname(queue_t * q, mblk_t * mp) { } STATIC int ti_getpeername(queue_t * q, mblk_t * mp) { } STATIC int ti_setmyname(queue_t * q, mblk_t * mp) { } STATIC int ti_setpeername(queue_t * q, mblk_t * mp) { } #endif /* * ========================================================================= * * STREAMS Message Handling * * ========================================================================= * * M_IOCTL Handling * * ------------------------------------------------------------------------- * There are a standard set of IOCTLs for TLI. Check the iBCS package for * the numbering. */ STATIC int ss_w_ioctl(queue_t * q, mblk_t * mp) { (void) q; (void) mp; fixme(("Support finalized IOCTLs\n")); #if 0 /* these are supported by timod which can be pushed */ switch (nr) { case _IOC_NR(TI_GETINFO): rtn = ti_getinfo(q, mp); break; case _IOC_NR(TI_OPTMGMT): rtn = ti_optmgmt(q, mp); break; case _IOC_NR(TI_BIND): rtn = ti_bind(q, mp); break; case _IOC_NR(TI_UNBIND): rtn = ti_unbind(q, mp); break; case _IOC_NR(TI_GETMYNAME): rtn = ti_getmyname(q, mp); break; case _IOC_NR(TI_GETPEERNAME): rtn = ti_getpeername(q, mp); break; case _IOC_NR(TI_SETMYNAME): rtn = ti_setmyname(q, mp); break; case _IOC_NR(TI_SETPEERNAME): rtn = ti_setpeername(q, mp); break; default: rtn = -EOPNOTSUPP; break; } #endif return (-EOPNOTSUPP); } /* * ------------------------------------------------------------------------- * * M_PROTO, M_PCPROTO Handling * * ------------------------------------------------------------------------- */ STATIC int ss_w_proto(queue_t * q, mblk_t * mp) { int rtn; ulong prim; ss_t *ss = PRIV(q); ulong oldstate = ss_get_state(ss); switch ((prim = *((ulong *) mp->b_rptr))) { case T_CONN_REQ: printd(("%s: %p: -> T_CONN_REQ\n", SS_MOD_NAME, ss)); rtn = t_conn_req(q, mp); break; case T_CONN_RES: printd(("%s: %p: -> T_CONN_RES\n", SS_MOD_NAME, ss)); rtn = t_conn_res(q, mp); break; case T_DISCON_REQ: printd(("%s: %p: -> T_DISCON_REQ\n", SS_MOD_NAME, ss)); rtn = t_discon_req(q, mp); break; case T_DATA_REQ: printd(("%s: %p: -> T_DATA_REQ\n", SS_MOD_NAME, ss)); rtn = t_data_req(q, mp); break; case T_EXDATA_REQ: printd(("%s: %p: -> T_EXDATA_REQ\n", SS_MOD_NAME, ss)); rtn = t_exdata_req(q, mp); break; case T_INFO_REQ: printd(("%s: %p: -> T_INFO_REQ\n", SS_MOD_NAME, ss)); rtn = t_info_req(q, mp); break; case T_BIND_REQ: printd(("%s: %p: -> T_BIND_REQ\n", SS_MOD_NAME, ss)); rtn = t_bind_req(q, mp); break; case T_UNBIND_REQ: printd(("%s: %p: -> T_UNBIND_REQ\n", SS_MOD_NAME, ss)); rtn = t_unbind_req(q, mp); break; case T_OPTMGMT_REQ: printd(("%s: %p: -> T_OPTMGMT_REQ\n", SS_MOD_NAME, ss)); rtn = t_optmgmt_req(q, mp); break; case T_UNITDATA_REQ: printd(("%s: %p: -> T_UNITDATA_REQ\n", SS_MOD_NAME, ss)); rtn = t_unitdata_req(q, mp); break; case T_ORDREL_REQ: printd(("%s: %p: -> T_ORDREL_REQ\n", SS_MOD_NAME, ss)); rtn = t_ordrel_req(q, mp); break; case T_OPTDATA_REQ: printd(("%s: %p: -> T_OPTDATA_REQ\n", SS_MOD_NAME, ss)); rtn = t_optdata_req(q, mp); break; #ifdef T_ADDR_REQ case T_ADDR_REQ: printd(("%s: %p: -> T_ADDR_REQ\n", SS_MOD_NAME, ss)); rtn = t_addr_req(q, mp); break; #endif #ifdef T_CAPABILITY_REQ case T_CAPABILITY_REQ: printd(("%s: %p: -> T_CAPABILITY_REQ\n", SS_MOD_NAME, ss)); rtn = t_capability_req(q, mp); break; #endif default: rtn = t_other_req(q, mp); break; } if (rtn < 0) { rare(); ss_set_state(ss, oldstate); } return (rtn); } /* * ------------------------------------------------------------------------- * * M_FLUSH Handling * * ------------------------------------------------------------------------- */ STATIC int ss_w_flush(queue_t * q, mblk_t * mp) { if (*mp->b_rptr & FLUSHW) { if (*mp->b_rptr & FLUSHBAND) flushband(q, mp->b_rptr[1], FLUSHALL); else flushq(q, FLUSHALL); *mp->b_rptr &= ~FLUSHW; } if (*mp->b_rptr & FLUSHR) { if (*mp->b_rptr & FLUSHBAND) flushband(OTHER(q), mp->b_rptr[1], FLUSHALL); else flushq(OTHER(q), FLUSHALL); qreply(q, mp); return (QR_ABSORBED); } return (QR_DONE); } /* * ------------------------------------------------------------------------- * * M_PCRSE Handling * * ------------------------------------------------------------------------- * On the read queue, an M_PCRSE is used to indicate a state_change callback * on the socket. This can mean that the socket has completed connecting or * has disconnected, or it may signal a connection indication on a listening * socket. * * We always examine both the state of the TPI interface and the state of the * socket to determine which actions need to be performed. This makes the * state machine interworking more robust. * * Note that the sk pointer passed in here is not necessarily the same as * ss->sock->sk, sk may be a child (connection indication) of the primary * socket. */ STATIC int ss_r_pcrse(queue_t * q, mblk_t * mp) { ss_t *ss = PRIV(q); ss_event_t *p = (typeof(p)) mp->b_rptr; int oldstate = xchg(&ss->tcp_state, p->state); if (!ss->sock) goto discard; assure(p->state != oldstate); printd(("%s: %p: state_change [%s <- %s] %p\n", SS_MOD_NAME, ss, tcp_state_name(p->state), tcp_state_name(oldstate), p->sk)); switch (ss->p.prot.type) { case SOCK_STREAM: case SOCK_SEQPACKET: switch (ss_get_state(ss)) { case TS_WCON_CREQ: switch (p->state) { case TCP_ESTABLISHED: return (t_conn_con(q, &ss->dst, NULL, NULL)); case TCP_TIME_WAIT: case TCP_CLOSE: return (t_discon_ind(q, &ss->dst, T_PROVIDER, 0, NULL, NULL)); } break; case TS_WIND_ORDREL: switch (p->state) { case TCP_TIME_WAIT: case TCP_CLOSE: switch (oldstate) { case TCP_FIN_WAIT2: return (t_ordrel_ind(q)); case TCP_ESTABLISHED: case TCP_FIN_WAIT1: return (t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL)); } break; case TCP_FIN_WAIT1: case TCP_FIN_WAIT2: return (QR_DONE); } break; case TS_DATA_XFER: switch (p->state) { case TCP_CLOSE_WAIT: return (t_ordrel_ind(q)); case TCP_TIME_WAIT: case TCP_CLOSE: return (t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL)); } break; case TS_WREQ_ORDREL: switch (p->state) { case TCP_TIME_WAIT: case TCP_CLOSE: switch (oldstate) { case TCP_CLOSE_WAIT: return (t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL)); } break; } break; case TS_IDLE: case TS_WRES_CIND: switch (p->state) { case TCP_LAST_ACK: switch (oldstate) { case TCP_CLOSE_WAIT: return (QR_DONE); } break; case TCP_TIME_WAIT: case TCP_CLOSE: switch (oldstate) { case TCP_FIN_WAIT2: case TCP_LAST_ACK: return (QR_DONE); case TCP_LISTEN: { mblk_t *cp; /* state change was on child */ ss->tcp_state = TCP_LISTEN; /* look for the child */ if ((cp = t_seq_find(ss, mp))) return (t_discon_ind(q, NULL, T_PROVIDER, 0, cp, NULL)); return (QR_DONE); } } break; case TCP_ESTABLISHED: switch (oldstate) { case TCP_LISTEN: /* state change was on child */ ss->tcp_state = TCP_LISTEN; return (ss_conn_ind(q, mp)); } break; } break; } printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss, tcp_state_name(p->state), state_name(ss_get_state(ss)))); return (-EFAULT); case SOCK_DGRAM: case SOCK_RAW: case SOCK_RDM: printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss, tcp_state_name(p->state), state_name(ss_get_state(ss)))); return (-EFAULT); default: printd(("%s: SWERR: unsupported socket type\n", SS_MOD_NAME)); return (-EFAULT); } discard: printd(("%s: ERROR: lingering event, ignoring\n", SS_MOD_NAME)); return (QR_DONE); } /* * ------------------------------------------------------------------------- * * M_READ Handling * * ------------------------------------------------------------------------- * * M_READ on the RD(q) * ------------------------------------------------------------------------- * * On the read queue, an M_READ is used to indicate a data_ready callback on * the socket. This can mean that the socket is now readable (when it was * blocked before), or it may mean that a socket has completed connecting, or * disconnected, or that a connection indication may be availabel on a * listening socket. * * We always examine both the state of the TPI interface and the state of the * socket to determine which actions need to be performed. This makes the * state machine interworking more robust. * * Note that the sk pointer passed in here is not necessarily the same as * ss->sock->sk, sk may be a child (connection indication) of the primary * socket. */ STATIC int ss_r_read(queue_t * q, mblk_t * mp) { ss_t *ss = PRIV(q); ss_event_t *p = (typeof(p)) mp->b_rptr; if (!ss->sock) goto discard; assure(ss->tcp_state == p->state || ss->tcp_state == TCP_LISTEN); printd(("%s: %p: data_ready [%s <- %s] %p\n", SS_MOD_NAME, ss, tcp_state_name(p->state), tcp_state_name(ss->tcp_state), p->sk)); switch (ss->p.prot.type) { case SOCK_STREAM: /* TCP */ case SOCK_SEQPACKET: /* SCTP */ switch (ss_get_state(ss)) { case TS_IDLE: case TS_WRES_CIND: if (p->state == TCP_LISTEN) return (QR_DONE); case TS_DATA_XFER: case TS_WIND_ORDREL: case TS_WREQ_ORDREL: /* TCP bug I believe */ return ss_sock_recvmsg(q); } printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss, tcp_state_name(p->state), state_name(ss_get_state(ss)))); return (-EFAULT); case SOCK_DGRAM: case SOCK_RAW: case SOCK_RDM: switch (ss_get_state(ss)) { case TS_IDLE: return ss_sock_recvmsg(q); } printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss, tcp_state_name(p->state), state_name(ss_get_state(ss)))); return (-EFAULT); } printd(("%s: SWERR: unsupported socket type %d\n", SS_MOD_NAME, ss->p.prot.type)); return (-EFAULT); discard: printd(("%s: ERROR: lingering event, ignoring\n", SS_MOD_NAME)); return (QR_DONE); } /* * M_READ on the WR(q) * ------------------------------------------------------------------------- * * On the write queue, an M_READ is used to indicate a write_space callback * on the socket. This can mean that the socket is now writeable (when it * was blocked before). This can also mean that a connecting socket has * completed connecting (or has disconnected), or that a listening socket has * a connection indication on the socket. * * We always examine both the state of the TPI interface and the state of the * socket to determine which actions need to be performed. This makes the * state machine interworking more robust. * * Note that the sk pointer passed in here is not necessarily the same as * ss->sock->sk, sk may be a child (connection indication) of the primary * socket. * */ STATIC int ss_w_read(queue_t * q, mblk_t * mp) { ss_t *ss = PRIV(q); ss_event_t *p = (typeof(p)) mp->b_rptr; if (!ss->sock) goto discard; assure(ss->tcp_state == p->state || ss->tcp_state == TCP_LISTEN); printd(("%s: %p: write_space [%s <- %s] %p\n", SS_MOD_NAME, ss, tcp_state_name(p->state), tcp_state_name(ss->tcp_state), p->sk)); switch (ss->p.prot.type) { case SOCK_STREAM: case SOCK_SEQPACKET: switch (ss_get_state(ss)) { case TS_DATA_XFER: case TS_WREQ_ORDREL: return (QR_DONE); } printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss, tcp_state_name(p->state), state_name(ss_get_state(ss)))); return (-EFAULT); case SOCK_DGRAM: case SOCK_RAW: case SOCK_RDM: switch (ss_get_state(ss)) { case TS_IDLE: return (QR_DONE); } printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss, tcp_state_name(p->state), state_name(ss_get_state(ss)))); return (-EFAULT); } printd(("%s: SWERR: unsupported socket type %d\n", SS_MOD_NAME, ss->p.prot.type)); return (-EFAULT); discard: printd(("%s: ERROR: lingering event, ignoring\n", SS_MOD_NAME)); return (QR_DONE); } /* * ------------------------------------------------------------------------- * * M_ERROR Handling * * ------------------------------------------------------------------------- * On the read queue, an M_ERROR is used to indicate an error_report callback * on the socket. This can mean that the socket has disconected. We might * not received any other indication of the error. * * We always examine both the state of the TPI interface and the state of the * socket to determine which actions need to be performed. This makes the * state machine interworking more robust. * * Note that the sk pointer passed in here is not necessarily the same as * ss->sock->sk, sk may be a child (connection indication) of the primary * socket. */ STATIC int ss_r_error(queue_t * q, mblk_t * mp) { int err; ss_t *ss = PRIV(q); ss_event_t *p = (typeof(p)) mp->b_rptr; err = sock_error(p->sk); if (!ss->sock) goto discard; assure(ss->tcp_state == p->state || ss->tcp_state == TCP_LISTEN); printd(("%s: %p: error_report [%s <- %s] %p\n", SS_MOD_NAME, ss, tcp_state_name(p->state), tcp_state_name(ss->tcp_state), p->sk)); switch (ss->p.prot.type) { case SOCK_STREAM: case SOCK_SEQPACKET: switch (ss_get_state(ss)) { default: fixme(("%s: %p: FIXME: save errors for later\n", SS_MOD_NAME, ss)); return (QR_DONE); case TS_IDLE: printd(("%s: %p: INFO: ignoring error event %d\n", SS_MOD_NAME, ss, err)); return (QR_DONE); } printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss, tcp_state_name(p->state), state_name(ss_get_state(ss)))); return (-EFAULT); case SOCK_DGRAM: case SOCK_RAW: case SOCK_RDM: switch (ss_get_state(ss)) { case TS_IDLE: fixme(("%s: %p: FIXME: generate uderror\n", SS_MOD_NAME, ss)); printd(("%s: %p: INFO: ignoring error event %d\n", SS_MOD_NAME, ss, err)); return (QR_DONE); } printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss, tcp_state_name(p->state), state_name(ss_get_state(ss)))); return (-EFAULT); } printd(("%s: SWERR: unsupported socket type %d\n", SS_MOD_NAME, ss->p.prot.type)); return (-EFAULT); discard: printd(("%s: ERROR: lingering event, ignoring\n", SS_MOD_NAME)); return (QR_DONE); } /* * ========================================================================= * * QUEUE PUT and SERVICE routines * * ========================================================================= * * WRITE PUT ad SERVICE (Message from above IP-User --> IP-Provider * ------------------------------------------------------------------------- */ STATIC int ss_w_prim(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_DATA: return ss_w_data(q, mp); case M_PROTO: case M_PCPROTO: return ss_w_proto(q, mp); case M_CTL: case M_READ: return ss_w_read(q, mp); case M_FLUSH: return ss_w_flush(q, mp); case M_IOCTL: return ss_w_ioctl(q, mp); } return (-EOPNOTSUPP); } /* * READ PUT ad SERVICE (Message from below IP-Provider --> IP-User * ------------------------------------------------------------------------- */ STATIC int ss_r_prim(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_RSE: case M_PCRSE: return ss_r_pcrse(q, mp); case M_CTL: case M_READ: return ss_r_read(q, mp); case M_ERROR: return ss_r_error(q, mp); } return (QR_PASSFLOW); } /* * ========================================================================= * * QUEUE PUT and SERVICE routines * * ========================================================================= * * PUTQ Put Routine * ----------------------------------- */ STATIC int ss_putq(queue_t * q, mblk_t * mp, int (*proc) (queue_t *, mblk_t *)) { int rtn = 0; if (mp->b_datap->db_type < QPCTL || q->q_count) { putq(q, mp); return (0); } if (ss_trylockq(q)) { do { /* * Fast Path */ if ((rtn = (*proc) (q, mp)) == QR_DONE) { freemsg(mp); break; } switch (rtn) { case QR_DONE: freemsg(mp); case QR_ABSORBED: break; case QR_STRIP: if (mp->b_cont) putq(q, mp->b_cont); case QR_TRIMMED: freeb(mp); break; case QR_LOOP: if (!q->q_next) { qreply(q, mp); break; } case QR_PASSALONG: if (q->q_next) { putnext(q, mp); break; } rtn = -EOPNOTSUPP; default: freemsg(mp); break; case QR_DISABLE: putq(q, mp); rtn = 0; break; case QR_PASSFLOW: if (mp->b_datap->db_type >= QPCTL || canputnext(q)) { putnext(q, mp); break; } case -ENOBUFS: case -EBUSY: case -EAGAIN: case -ENOMEM: putq(q, mp); break; } } while (0); ss_unlockq(q); } else { putq(q, mp); } return (rtn); } /* * SRVQ Service Routine * ----------------------------------- */ STATIC int ss_srvq(queue_t * q, int (*proc) (queue_t *, mblk_t *)) { int rtn = 0; if (ss_trylockq(q)) { mblk_t *mp; while ((mp = getq(q))) { /* * Fast Path */ if ((rtn = proc(q, mp)) == QR_DONE) { freemsg(mp); continue; } switch (rtn) { case QR_DONE: freemsg(mp); case QR_ABSORBED: continue; case QR_STRIP: if (mp->b_cont) putbq(q, mp->b_cont); case QR_TRIMMED: freeb(mp); continue; case QR_LOOP: if (!q->q_next) { qreply(q, mp); continue; } case QR_PASSALONG: if (q->q_next) { putnext(q, mp); continue; } rtn = -EOPNOTSUPP; default: ptrace(("%s: ERROR: (q dropping) %d\n", SS_MOD_NAME, rtn)); freemsg(mp); continue; case QR_DISABLE: ptrace(("%s: ERROR: (q disabling)\n", SS_MOD_NAME)); noenable(q); putbq(q, mp); rtn = 0; break; case QR_PASSFLOW: if (mp->b_datap->db_type >= QPCTL || canputnext(q)) { putnext(q, mp); continue; } case -ENOBUFS: /* caller must schedule bufcall */ case -EBUSY: /* caller must have failed canput */ case -EAGAIN: /* caller must re-enable queue */ case -ENOMEM: /* caller must re-enable queue */ if (mp->b_datap->db_type < QPCTL) { ptrace(("%s: ERROR: (q stalled) %d\n", SS_MOD_NAME, rtn)); putbq(q, mp); break; } /* * Be careful not to put a priority message * back on the queue. */ switch (mp->b_datap->db_type) { case M_PCPROTO: mp->b_datap->db_type = M_PROTO; break; case M_PCRSE: mp->b_datap->db_type = M_RSE; break; case M_READ: mp->b_datap->db_type = M_CTL; break; default: ptrace(("%s: ERROR: (q dropping) %d\n", SS_MOD_NAME, rtn)); freemsg(mp); continue; } mp->b_band = 255; putq(q, mp); break; } break; } ss_unlockq(q); } return (rtn); } STATIC INT ss_rput(queue_t * q, mblk_t * mp) { return (INT) ss_putq(q, mp, &ss_r_prim); } STATIC INT ss_rsrv(queue_t * q) { return (INT) ss_srvq(q, &ss_r_prim); } STATIC INT ss_wput(queue_t * q, mblk_t * mp) { return (INT) ss_putq(q, mp, &ss_w_prim); } STATIC INT ss_wsrv(queue_t * q) { return (INT) ss_srvq(q, &ss_w_prim); } /* * ========================================================================= * * Private Structure allocation, deallocation and cache * * ========================================================================= */ STATIC kmem_cache_t *ss_priv_cachep = NULL; STATIC int ss_init_caches(void) { if (!ss_priv_cachep && !(ss_priv_cachep = kmem_cache_create("ss_priv_cachep", sizeof(ss_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL))) { cmn_err(CE_PANIC, "%s: Cannot allocate ss_priv_cachep", __FUNCTION__); return (-ENOMEM); } else printd(("%s: initialized driver private structure cache\n", SS_MOD_NAME)); return (0); } STATIC void ss_term_caches(void) { if (ss_priv_cachep) { if (kmem_cache_destroy(ss_priv_cachep)) cmn_err(CE_WARN, "%s: did not destroy ss_priv_cachep", __FUNCTION__); else printd(("%s: destroyed ss_priv_cachep\n", SS_MOD_NAME)); } return; } STATIC ss_t *ss_alloc_priv(queue_t * q, ss_t ** slp, ushort cmajor, ushort cminor, cred_t * crp, const ss_profile_t * prof) { ss_t *ss; if ((ss = kmem_cache_alloc(ss_priv_cachep, SLAB_ATOMIC))) { // printd(("%s: allocated module private structure\n", SS_MOD_NAME)); bzero(ss, sizeof(*ss)); ss->cmajor = cmajor; ss->cminor = cminor; ss->rq = RD(q); ss->rq->q_ptr = ss; ss->refcnt++; ss->wq = WR(q); ss->wq->q_ptr = ss; ss->refcnt++; ss->cred = *crp; ss->p = *prof; lis_spin_lock_init(&ss->qlock, "ss-queue-lock"); ss->rwait = NULL; ss->wwait = NULL; ss_set_state(ss, TS_UNBND); lis_spin_lock_init(&ss->lock, "ss-priv-lock"); bufq_init(&ss->conq); if ((ss->next = *slp)) ss->next->prev = &ss->next; ss->prev = slp; *slp = ss; ss->refcnt++; // printd(("%s: linked private structure, reference count %d\n", SS_MOD_NAME, // ss->refcnt)); } else ptrace(("%s: ERROR: Could not allocate module private structure\n", SS_MOD_NAME)); return (ss); } STATIC void ss_free_priv(queue_t * q) { ss_t *ss = PRIV(q); int flags = 0; // printd(("%s: unlinking private structure, reference count = %d\n", SS_MOD_NAME, // ss->refcnt)); lis_spin_lock_irqsave(&ss->lock, &flags); { bufq_purge(&ss->conq); if (ss->sock) ss_socket_put(ss->sock); // printd(("%s: removed socket, reference count = %d\n", SS_MOD_NAME, ss->refcnt)); ss_unbufcall(q); // printd(("%s: removed bufcalls, reference count = %d\n", SS_MOD_NAME, // ss->refcnt)); if ((*ss->prev = ss->next)) ss->next->prev = ss->prev; ss->next = NULL; ss->prev = NULL; ss->refcnt--; // printd(("%s: unlinked, reference count = %d\n", SS_MOD_NAME, ss->refcnt)); ss->rq->q_ptr = NULL; ss->refcnt--; ss->wq->q_ptr = NULL; ss->refcnt--; } lis_spin_unlock_irqrestore(&ss->lock, &flags); if (ss->refcnt) { assure(ss->refcnt); printd(("%s: WARNING: ss->refcnt = %d\n", SS_MOD_NAME, ss->refcnt)); } kmem_cache_free(ss_priv_cachep, ss); // printd(("%s: freed module private structure\n", SS_MOD_NAME)); return; } /* * ========================================================================= * * OPEN and CLOSE * * ========================================================================= * * OPEN * ------------------------------------------------------------------------- */ STATIC const ss_profile_t ss_profiles[] = { {{PF_INET, SOCK_RAW, IPPROTO_ICMP}, {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID, sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}}, {{PF_INET, SOCK_RAW, IPPROTO_IGMP}, {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID, sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}}, {{PF_INET, SOCK_RAW, IPPROTO_IPIP}, {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID, sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}}, {{PF_INET, SOCK_STREAM, IPPROTO_TCP}, {T_INFO_ACK, T_INVALID, 1, T_INVALID, T_INVALID, sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_COTS_ORD, TS_UNBND, XPG4_1 & ~T_SNDZERO}}, {{PF_INET, SOCK_RAW, IPPROTO_EGP}, {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID, sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}}, {{PF_INET, SOCK_RAW, IPPROTO_PUP}, {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID, sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}}, {{PF_INET, SOCK_DGRAM, IPPROTO_UDP}, {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID, sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}}, {{PF_INET, SOCK_RAW, IPPROTO_IDP}, {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID, sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}}, {{PF_INET, SOCK_RAW, IPPROTO_RAW}, {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID, sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO} } }; STATIC int ss_majors[SS_NMAJOR] = { SS_CMAJOR, }; STATIC int ss_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp) { int flags, mindex = 0; int cmajor = getmajor(*devp); int cminor = getminor(*devp); ss_t *ss, **ipp = &ss_opens; const ss_profile_t *prof; MOD_INC_USE_COUNT; /* keep module from unloading in our face */ if (q->q_ptr != NULL) { MOD_DEC_USE_COUNT; return (0); /* already open */ } if (sflag == MODOPEN || WR(q)->q_next) { ptrace(("%s: ERROR: can't push as module\n", SS_MOD_NAME)); MOD_DEC_USE_COUNT; return (EIO); } if (cmajor != SS_CMAJOR || cminor < ICMP_CMINOR || cminor > RAWIP_CMINOR) { MOD_DEC_USE_COUNT; return (ENXIO); } prof = &ss_profiles[cminor - ICMP_CMINOR]; cminor = FREE_CMINOR; lis_spin_lock_irqsave(&ss_lock, &flags); for (; *ipp; ipp = &(*ipp)->next) { if (cmajor != (*ipp)->cmajor) break; if (cmajor == (*ipp)->cmajor) { if (cminor < (*ipp)->cminor) break; if (cminor == (*ipp)->cminor) { if (++cminor >= SS_NMINOR) { if (++mindex >= SS_NMAJOR || !(cmajor = ss_majors[mindex])) break; cminor = 0; } continue; } } } if (mindex >= SS_NMAJOR || !cmajor) { ptrace(("%s: ERROR: no device numbers available\n", SS_MOD_NAME)); lis_spin_unlock_irqrestore(&ss_lock, &flags); MOD_DEC_USE_COUNT; return (ENXIO); } printd(("%s: opened character device %d:%d\n", SS_MOD_NAME, cmajor, cminor)); *devp = makedevice(cmajor, cminor); if (!(ss = ss_alloc_priv(q, ipp, cmajor, cminor, crp, prof))) { ptrace(("%s: ERROR: No memory\n", SS_MOD_NAME)); lis_spin_unlock_irqrestore(&ss_lock, &flags); MOD_DEC_USE_COUNT; return (ENOMEM); } lis_spin_unlock_irqrestore(&ss_lock, &flags); return (0); } /* * CLOSE * ------------------------------------------------------------------------- */ STATIC int ss_close(queue_t * q, int flag, cred_t * crp) { ss_t *ss = PRIV(q); int flags; (void) flag; (void) crp; (void) ss; printd(("%s: closing character device %d:%d\n", SS_MOD_NAME, ss->cmajor, ss->cminor)); lis_spin_lock_irqsave(&ss_lock, &flags); ss_free_priv(q); lis_spin_unlock_irqrestore(&ss_lock, &flags); MOD_DEC_USE_COUNT; return (0); } /* * ========================================================================= * * LiS Module Initialization * * ========================================================================= */ STATIC int ss_initialized = 0; STATIC void ss_init(void) { int err, mindex; cmn_err(CE_NOTE, SS_BANNER); /* console splash */ if ((err = ss_init_caches())) { cmn_err(CE_PANIC, "%s: ERROR: Counld not allocated caches", SS_MOD_NAME); ss_initialized = err; return; } for (mindex = 0; mindex < SS_NMAJOR; mindex++) { if ((err = lis_register_strdev(ss_majors[mindex], &ss_info, SS_NMINOR, SS_MOD_NAME)) < 0) { if (!mindex) { cmn_err(CE_PANIC, "%s: Can't register 1'st major %d", SS_MOD_NAME, ss_majors[0]); ss_term_caches(); ss_initialized = err; return; } cmn_err(CE_WARN, "%s: Can't register %d'th major", SS_MOD_NAME, mindex + 1); ss_majors[mindex] = 0; } else if (mindex) ss_majors[mindex] = err; } lis_spin_lock_init(&ss_lock, "ss-open-list-lock"); ss_initialized = 1; return; } STATIC void ss_terminate(void) { int err, mindex; for (mindex = 0; mindex < SS_NMAJOR; mindex++) { if (ss_majors[mindex]) { if ((err = lis_unregister_strdev(ss_majors[mindex]))) cmn_err(CE_PANIC, "%s: Can't unregister major %d\n", SS_MOD_NAME, ss_majors[mindex]); if (mindex) ss_majors[mindex] = 0; } } ss_term_caches(); return; } /* * ========================================================================= * * LINUX MODULE INITIALIZATION * * ========================================================================= */ int init_module(void) { ss_init(); if (ss_initialized < 0) return ss_initialized; return (0); } void cleanup_module(void) { ss_terminate(); }
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |