1*9833Ssam /* nsp_usrreq.c 1.5 82/12/18 */ 26830Ssam 36830Ssam #include "../h/param.h" 46830Ssam #include "../h/systm.h" 56830Ssam #include "../h/mbuf.h" 66830Ssam #include "../h/socket.h" 76830Ssam #include "../h/socketvar.h" 86830Ssam #include "../h/protosw.h" 98409Swnj #include "../netdecnet/decnet.h" 108409Swnj #include "../netdecnet/dn_systm.h" 116830Ssam #include "../net/if.h" 128409Swnj #include "../netdecnet/nsp.h" 138409Swnj #include "../netdecnet/nsp_var.h" 148409Swnj #include <errno.h> 156830Ssam 166830Ssam /* 176830Ssam * NSP protocol interface to socket abstraction. 186830Ssam */ 196830Ssam struct nspcb *nsp_newnspcb(); 206830Ssam 216830Ssam /* 226830Ssam * Process an NSP user request for NSP np. If this is a send request 236830Ssam * then m is the mbuf chain of send data. If this is a timer expiration 246830Ssam * (called from the software clock routine), then timertype tells which timer. 256830Ssam */ 266830Ssam nsp_usrreq(so, req, m, addr) 276830Ssam struct socket *so; 286830Ssam int req; 296830Ssam struct mbuf *m; 306830Ssam caddr_t addr; 316830Ssam { 326830Ssam register struct nspcb *np = sotonspcb(so); 336830Ssam int s = splnet(); 346830Ssam int error = 0; 356830Ssam int ostate; 366830Ssam COUNT(NSP_USRREQ); 376830Ssam 386830Ssam /* 396830Ssam * When an NSP is attached to a socket, then there will be 406830Ssam * a (struct nspcb) pointed at by the socket. 416830Ssam * The normal sequence of events is: 426830Ssam * PRU_ATTACH creating these structures 436830Ssam * PRU_CONNECT connecting to a remote peer 446830Ssam * (PRU_SEND|PRU_RCVD)* exchanging data 456830Ssam * PRU_DISCONNECT disconnecting from remote peer 466830Ssam * PRU_DETACH deleting the structures 476830Ssam * With the operations from PRU_CONNECT through PRU_DISCONNECT 486830Ssam * possible repeated several times. 496830Ssam * 506830Ssam * MULTIPLE CONNECTS ARE NOT YET IMPLEMENTED. 516830Ssam */ 526830Ssam if (np == 0 && req != PRU_ATTACH) { 536830Ssam splx(s); 546830Ssam return (EINVAL); /* XXX */ 556830Ssam } 566830Ssam if (np) { 576830Ssam ostate = np->n_state; 586830Ssam } 596830Ssam switch (req) { 606830Ssam 616830Ssam /* 626830Ssam * NSP attaches to socket via PRU_ATTACH, reserving space 636830Ssam * and NSP control block. 646830Ssam **** If the socket is to receive connections, 656830Ssam **** then the LISTEN state is entered. 666830Ssam */ 676830Ssam case PRU_ATTACH: 686830Ssam if (np) { 696830Ssam error = EISCONN; 706830Ssam break; 716830Ssam } 726830Ssam error = nsp_attach(so, (struct sockaddr *)addr); 736830Ssam if (error) 746830Ssam break; 756830Ssam np = sotonspcb(so); 766830Ssam break; 776830Ssam 786830Ssam /* 796830Ssam * PRU_DETACH detaches the NSP protocol from the socket. 806830Ssam * If the protocol state is non-embryonic, then can't 816830Ssam * do this directly: have to initiate a PRU_DISCONNECT, 826830Ssam * which may finish later; embryonic nspcb's can just 836830Ssam * be discarded here. 846830Ssam */ 856830Ssam case PRU_DETACH: 866830Ssam if (np->n_state != NS_O && np->n_state != NS_CL 876830Ssam && np->n_state != NS_LI) 886830Ssam nsp_disconnect(np, <reason>); 896830Ssam else { 906830Ssam nsp_close(np); 916830Ssam np = 0; 926830Ssam } 936830Ssam break; 946830Ssam 956830Ssam /* 966830Ssam * Initiate connection to peer. 976830Ssam * Enter CI state, and mark socket as connecting. 986830Ssam **** Start keep-alive timer, and seed output sequence space. 996830Ssam **** Send initial segment on connection. 1006830Ssam */ 1016830Ssam case PRU_CONNECT: 1026830Ssam error = dn_pcbconnect(np, (struct sockaddr_dn *)addr); 1036830Ssam if (error) 1046830Ssam break; 1056830Ssam soisconnecting(so); 1066830Ssam nsp_connect(np); 1076830Ssam break; 1086830Ssam 1096830Ssam /* 1106830Ssam * Initiate disconnect from peer. 1116830Ssam * If connection never passed embryonic stage, just drop; 1126830Ssam * else if don't need to let data drain, then can just drop anyways, 1136830Ssam * else have to begin NSP shutdown process: mark socket disconnecting, 1146830Ssam * drain unread data, state switch to reflect user close, and 1156830Ssam * send segment (e.g. DI) to peer. Socket will be really disconnected 1166830Ssam * when peer sends DC to ack our DI. 1176830Ssam * 1186830Ssam * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC NSPCB. 1196830Ssam */ 1206830Ssam case PRU_DISCONNECT: 1216830Ssam nsp_disconnect(np); 1226830Ssam break; 1236830Ssam 1246830Ssam /* 1256830Ssam * Accept a connection. Essentially all the work is 1266830Ssam * done at higher levels; just return the address 1276830Ssam * of the peer, storing through addr. 1286830Ssam */ 1296830Ssam case PRU_ACCEPT: 1306830Ssam dn_pcbconnaddr(np, (struct sockaddr *)addr); 1316830Ssam break; 1326830Ssam 1336830Ssam /*** BEGIN NOT MODIFIED FOR NSP ***/ 1346830Ssam /* 1356830Ssam * Mark the connection as being incapable of further output. 1366830Ssam */ 1376830Ssam case PRU_SHUTDOWN: 1386830Ssam socantsendmore(so); 1396830Ssam nsp_usrclosed(np); 1406830Ssam (void) nsp_output(np); 1416830Ssam break; 1426830Ssam 1436830Ssam /* 1446830Ssam * After a receive, possibly send window update to peer. 1456830Ssam */ 1466830Ssam case PRU_RCVD: 1476830Ssam (void) nsp_output(np); 1486830Ssam break; 1496830Ssam /*** END NOT MODIFIED FOR NSP ***/ 1506830Ssam 1516830Ssam /* 1526830Ssam * Do a send by putting data in output queue and 1536830Ssam * calling output processor. 1546830Ssam */ 1556830Ssam case PRU_SEND: 1566830Ssam sbpappend(&so->so_snd, m); 1576830Ssam (void) nsp_output(np); 1586830Ssam break; 1596830Ssam 1606830Ssam /*** BEGIN NOT MODIFIED FOR NSP ***/ 1616830Ssam /* 1626830Ssam * Abort the NSP. 1636830Ssam */ 1646830Ssam case PRU_ABORT: 1656830Ssam nsp_drop(np, ECONNABORTED); 1666830Ssam break; 1676830Ssam 1686830Ssam /* SOME AS YET UNIMPLEMENTED HOOKS */ 1696830Ssam case PRU_CONTROL: 1706830Ssam error = EOPNOTSUPP; 1716830Ssam break; 1726830Ssam 1736830Ssam case PRU_SENSE: 1746830Ssam error = EOPNOTSUPP; 1756830Ssam break; 1766830Ssam /* END UNIMPLEMENTED HOOKS */ 1776830Ssam 1786830Ssam case PRU_RCVOOB: 1796830Ssam if (so->so_oobmark == 0 && 1806830Ssam (so->so_state & SS_RCVATMARK) == 0) { 1816830Ssam error = EINVAL; 1826830Ssam break; 1836830Ssam } 1846830Ssam if ((np->n_flags & NSP_RCVINTR) == 0) { 1856830Ssam error = EWOULDBLOCK; 1866830Ssam break; 1876830Ssam } 1886830Ssam /* RETURN THE DATA */ 1896830Ssam break; 1906830Ssam 1916830Ssam case PRU_SENDOOB: 1926830Ssam /* 1936830Ssam if interrupt data present return error (can't queue) 1946830Ssam if len > 16 return error 1956830Ssam put in xmt mbuf 1966830Ssam mark interrupt data available 1976830Ssam call nsp_output 1986830Ssam */ 1996830Ssam break; 2006830Ssam 2016830Ssam /* 2026830Ssam * NSP slow timer went off; going through this 2036830Ssam * routine for tracing's sake. 2046830Ssam */ 2056830Ssam case PRU_SLOWTIMO: 2066830Ssam nsp_timers(np, (int)addr); 2076830Ssam req |= (int)addr << 8; /* for debug's sake */ 2086830Ssam break; 2096830Ssam /*** END NOT MODIFIED FOR NSP ***/ 2106830Ssam 2116830Ssam default: 2126830Ssam panic("nsp_usrreq"); 2136830Ssam } 2146830Ssam if (np && (so->so_options & SO_DEBUG)) 2156830Ssam nsp_trace(NA_USER, ostate, np, (struct XXXXXXXX *)0, req); 2166830Ssam splx(s); 2176830Ssam return (error); 2186830Ssam } 2196830Ssam 2206830Ssam /* 2216830Ssam * Attach NSP protocol to socket, allocating NSP control block, 2226830Ssam * bufer space, and entering LISTEN state if to accept connections. 2236830Ssam */ 2246830Ssam nsp_attach(so, sa) 2256830Ssam struct socket *so; 2266830Ssam struct sockaddr *sa; 2276830Ssam { 2286830Ssam register struct nspcb *np; 2296830Ssam struct sockaddr_dn *sdn = (struct sockaddr_dn *)sa; 2306830Ssam struct mbuf *m; 2316830Ssam int error; 2326830Ssam 2336830Ssam if (sdn) { 2346830Ssam if (sdn->sdn_family != AF_DECNET) 2356830Ssam return (EAFNOSUPPORT); 2366830Ssam /* the user has specified a sockaddr with a socreate. 2376830Ssam all this can do is allow the user to specify an object 2386830Ssam type or other info if he is going to wait for a connection. 2396830Ssam figure this out later. */ 2406830Ssam } else { 2416830Ssam /* nothing specified, will expect a connect request soon */ 2426830Ssam } 243*9833Ssam m = m_getclr(MT_CANTWAIT, MT_PCB); 2446830Ssam if (m == 0) 2456830Ssam return (ENOBUFS); 2466830Ssam if (sbreserve(&so->so_snd, 1024) == 0) { 2476830Ssam bad: 2488774Sroot (void) m_free(m); 2496830Ssam return (ENOBUFS); 2506830Ssam } 2516830Ssam if (sbreserve(&so->so_rcv, 1024) == 0) { 2526830Ssam sbrelease(&so->so_snd); 2536830Ssam goto bad; 2546830Ssam } 2556830Ssam np = mtod(m, struct nspcb *); 2566830Ssam np->n_head = &ncb; 2576830Ssam insque(np, &ncb); 2586830Ssam sp->so_pcb = (caddr_t)np; 2596830Ssam sdn = (struct sockaddr_dn *)&so->so_addr; 2606830Ssam sdn->sdn_family == AF_DECNET; 2616830Ssam sdn->sdn_addr = WHAT ELSE NEEDS TO BE FILLED IN HERE? 2626830Ssam if (so->so_options & SO_ACCEPTCONN) { 2636830Ssam np->n_state = NS_LI; 2646830Ssam } else 2656830Ssam np->n_state = NS_O; 2666830Ssam return (0); 2676830Ssam } 2686830Ssam 2696830Ssam /*** BEGIN NOT MODIFIED FOR NSP ***/ 2706830Ssam /* 2716830Ssam * Initiate (or continue) disconnect. 2726830Ssam * If embryonic state, just send reset (once). 2736830Ssam * If not in ``let data drain'' option, just drop. 2746830Ssam * Otherwise (hard), mark socket disconnecting and drop 2756830Ssam * current input data; switch states based on user close, and 2766830Ssam * send segment to peer (with FIN). 2776830Ssam */ 2786830Ssam nsp_disconnect(np) 2796830Ssam struct nspcb *np; 2806830Ssam { 2816830Ssam struct socket *so = np->n_socket; 2826830Ssam 2836830Ssam if (np->n_state < NSPS_ESTABLISHED) 2846830Ssam nsp_close(np); 2856830Ssam else if (so->so_linger == 0) 2866830Ssam nsp_drop(np, 0); 2876830Ssam else { 2886830Ssam soisdisconnecting(so); 2896830Ssam sbflush(&so->so_rcv); 2906830Ssam nsp_usrclosed(np); 2916830Ssam (void) nsp_output(np); 2926830Ssam } 2936830Ssam } 2946830Ssam 2956830Ssam /* 2966830Ssam * User issued close, and wish to trail through shutdown states: 2976830Ssam * if never received SYN, just forget it. If got a SYN from peer, 2986830Ssam * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. 2996830Ssam * If already got a FIN from peer, then almost done; go to LAST_ACK 3006830Ssam * state. In all other cases, have already sent FIN to peer (e.g. 3016830Ssam * after PRU_SHUTDOWN), and just have to play tedious game waiting 3026830Ssam * for peer to send FIN or not respond to keep-alives, etc. 3036830Ssam */ 3046830Ssam nsp_usrclosed(np) 3056830Ssam struct nspcb *np; 3066830Ssam { 3076830Ssam 3086830Ssam switch (np->n_state) { 3096830Ssam 3106830Ssam case NSPS_LISTEN: 3116830Ssam case NSPS_SYN_SENT: 3126830Ssam np->n_state = NSPS_CLOSED; 3136830Ssam nsp_close(np); 3146830Ssam break; 3156830Ssam 3166830Ssam case NSPS_SYN_RECEIVED: 3176830Ssam case NSPS_ESTABLISHED: 3186830Ssam np->n_state = NSPS_FIN_WAIT_1; 3196830Ssam break; 3206830Ssam 3216830Ssam case NSPS_CLOSE_WAIT: 3226830Ssam np->n_state = NSPS_LAST_ACK; 3236830Ssam break; 3246830Ssam } 3256830Ssam } 326