tcp_usrreq.c revision 6510
1/*
2 * Copyright (c) 1982, 1986, 1988, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 *	From: @(#)tcp_usrreq.c	8.2 (Berkeley) 1/3/94
34 *	$Id: tcp_usrreq.c,v 1.10 1995/02/16 01:42:45 wollman Exp $
35 */
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/kernel.h>
40#include <sys/malloc.h>
41#include <sys/mbuf.h>
42#include <sys/socket.h>
43#include <sys/socketvar.h>
44#include <sys/protosw.h>
45#include <sys/errno.h>
46#include <sys/stat.h>
47
48#include <net/if.h>
49#include <net/route.h>
50
51#include <netinet/in.h>
52#include <netinet/in_systm.h>
53#include <netinet/ip.h>
54#include <netinet/in_pcb.h>
55#include <netinet/ip_var.h>
56#include <netinet/tcp.h>
57#include <netinet/tcp_fsm.h>
58#include <netinet/tcp_seq.h>
59#include <netinet/tcp_timer.h>
60#include <netinet/tcp_var.h>
61#include <netinet/tcpip.h>
62#ifdef TCPDEBUG
63#include <netinet/tcp_debug.h>
64#endif
65
66/*
67 * TCP protocol interface to socket abstraction.
68 */
69extern	char *tcpstates[];
70
71/*
72 * Process a TCP user request for TCP tb.  If this is a send request
73 * then m is the mbuf chain of send data.  If this is a timer expiration
74 * (called from the software clock routine), then timertype tells which timer.
75 */
76/*ARGSUSED*/
77int
78tcp_usrreq(so, req, m, nam, control)
79	struct socket *so;
80	int req;
81	struct mbuf *m, *nam, *control;
82{
83	register struct inpcb *inp;
84	register struct tcpcb *tp = 0;
85	struct sockaddr_in *sinp;
86	int s;
87	int error = 0;
88#ifdef TCPDEBUG
89	int ostate;
90#endif
91
92	if (req == PRU_CONTROL)
93		return (in_control(so, (int)m, (caddr_t)nam,
94			(struct ifnet *)control));
95	if (control && control->m_len) {
96		m_freem(control);
97		if (m)
98			m_freem(m);
99		return (EINVAL);
100	}
101
102	s = splnet();
103	inp = sotoinpcb(so);
104	/*
105	 * When a TCP is attached to a socket, then there will be
106	 * a (struct inpcb) pointed at by the socket, and this
107	 * structure will point at a subsidary (struct tcpcb).
108	 */
109	if (inp == 0 && req != PRU_ATTACH) {
110		splx(s);
111		return (EINVAL);		/* XXX */
112	}
113	if (inp) {
114		tp = intotcpcb(inp);
115		/* WHAT IF TP IS 0? */
116#ifdef KPROF
117		tcp_acounts[tp->t_state][req]++;
118#endif
119#ifdef TCPDEBUG
120		ostate = tp->t_state;
121	} else
122		ostate = 0;
123#else /* TCPDEBUG */
124	}
125#endif /* TCPDEBUG */
126
127	switch (req) {
128
129	/*
130	 * TCP attaches to socket via PRU_ATTACH, reserving space,
131	 * and an internet control block.
132	 */
133	case PRU_ATTACH:
134		if (inp) {
135			error = EISCONN;
136			break;
137		}
138		error = tcp_attach(so);
139		if (error)
140			break;
141		if ((so->so_options & SO_LINGER) && so->so_linger == 0)
142			so->so_linger = TCP_LINGERTIME * hz;
143		tp = sototcpcb(so);
144		break;
145
146	/*
147	 * PRU_DETACH detaches the TCP protocol from the socket.
148	 * If the protocol state is non-embryonic, then can't
149	 * do this directly: have to initiate a PRU_DISCONNECT,
150	 * which may finish later; embryonic TCB's can just
151	 * be discarded here.
152	 */
153	case PRU_DETACH:
154		if (tp->t_state > TCPS_LISTEN)
155			tp = tcp_disconnect(tp);
156		else
157			tp = tcp_close(tp);
158		break;
159
160	/*
161	 * Give the socket an address.
162	 */
163	case PRU_BIND:
164		/*
165		 * Must check for multicast addresses and disallow binding
166		 * to them.
167		 */
168		sinp = mtod(nam, struct sockaddr_in *);
169		if (sinp->sin_family == AF_INET &&
170		    IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) {
171			error = EAFNOSUPPORT;
172			break;
173		}
174		error = in_pcbbind(inp, nam);
175		if (error)
176			break;
177		break;
178
179	/*
180	 * Prepare to accept connections.
181	 */
182	case PRU_LISTEN:
183		if (inp->inp_lport == 0)
184			error = in_pcbbind(inp, (struct mbuf *)0);
185		if (error == 0)
186			tp->t_state = TCPS_LISTEN;
187		break;
188
189	/*
190	 * Initiate connection to peer.
191	 * Create a template for use in transmissions on this connection.
192	 * Enter SYN_SENT state, and mark socket as connecting.
193	 * Start keep-alive timer, and seed output sequence space.
194	 * Send initial segment on connection.
195	 */
196	case PRU_CONNECT:
197		/*
198		 * Must disallow TCP ``connections'' to multicast addresses.
199		 */
200		sinp = mtod(nam, struct sockaddr_in *);
201		if (sinp->sin_family == AF_INET
202		    && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) {
203			error = EAFNOSUPPORT;
204			break;
205		}
206
207		if ((error = tcp_connect(tp, nam)) != 0)
208			break;
209		error = tcp_output(tp);
210		break;
211
212	/*
213	 * Create a TCP connection between two sockets.
214	 */
215	case PRU_CONNECT2:
216		error = EOPNOTSUPP;
217		break;
218
219	/*
220	 * Initiate disconnect from peer.
221	 * If connection never passed embryonic stage, just drop;
222	 * else if don't need to let data drain, then can just drop anyways,
223	 * else have to begin TCP shutdown process: mark socket disconnecting,
224	 * drain unread data, state switch to reflect user close, and
225	 * send segment (e.g. FIN) to peer.  Socket will be really disconnected
226	 * when peer sends FIN and acks ours.
227	 *
228	 * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB.
229	 */
230	case PRU_DISCONNECT:
231		tp = tcp_disconnect(tp);
232		break;
233
234	/*
235	 * Accept a connection.  Essentially all the work is
236	 * done at higher levels; just return the address
237	 * of the peer, storing through addr.
238	 */
239	case PRU_ACCEPT:
240		in_setpeeraddr(inp, nam);
241		break;
242
243	/*
244	 * Mark the connection as being incapable of further output.
245	 */
246	case PRU_SHUTDOWN:
247		socantsendmore(so);
248		tp = tcp_usrclosed(tp);
249		if (tp)
250			error = tcp_output(tp);
251		break;
252
253	/*
254	 * After a receive, possibly send window update to peer.
255	 */
256	case PRU_RCVD:
257		(void) tcp_output(tp);
258		break;
259
260	/*
261	 * Do a send by putting data in output queue and updating urgent
262	 * marker if URG set.  Possibly send more data.
263	 */
264	case PRU_SEND_EOF:
265	case PRU_SEND:
266		sbappend(&so->so_snd, m);
267		if (nam && tp->t_state < TCPS_SYN_SENT) {
268			/*
269			 * Do implied connect if not yet connected,
270			 * initialize window to default value, and
271			 * initialize maxseg/maxopd using peer's cached
272			 * MSS.
273			 */
274			error = tcp_connect(tp, nam);
275			if (error)
276				break;
277			tp->snd_wnd = TTCP_CLIENT_SND_WND;
278			tcp_mss(tp, -1);
279		}
280
281		if (req == PRU_SEND_EOF) {
282			/*
283			 * Close the send side of the connection after
284			 * the data is sent.
285			 */
286			socantsendmore(so);
287			tp = tcp_usrclosed(tp);
288		}
289		if (tp != NULL)
290			error = tcp_output(tp);
291		break;
292
293	/*
294	 * Abort the TCP.
295	 */
296	case PRU_ABORT:
297		tp = tcp_drop(tp, ECONNABORTED);
298		break;
299
300	case PRU_SENSE:
301		((struct stat *) m)->st_blksize = so->so_snd.sb_hiwat;
302		(void) splx(s);
303		return (0);
304
305	case PRU_RCVOOB:
306		if ((so->so_oobmark == 0 &&
307		    (so->so_state & SS_RCVATMARK) == 0) ||
308		    so->so_options & SO_OOBINLINE ||
309		    tp->t_oobflags & TCPOOB_HADDATA) {
310			error = EINVAL;
311			break;
312		}
313		if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) {
314			error = EWOULDBLOCK;
315			break;
316		}
317		m->m_len = 1;
318		*mtod(m, caddr_t) = tp->t_iobc;
319		if (((int)nam & MSG_PEEK) == 0)
320			tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA);
321		break;
322
323	case PRU_SENDOOB:
324		if (sbspace(&so->so_snd) < -512) {
325			m_freem(m);
326			error = ENOBUFS;
327			break;
328		}
329		/*
330		 * According to RFC961 (Assigned Protocols),
331		 * the urgent pointer points to the last octet
332		 * of urgent data.  We continue, however,
333		 * to consider it to indicate the first octet
334		 * of data past the urgent section.
335		 * Otherwise, snd_up should be one lower.
336		 */
337		sbappend(&so->so_snd, m);
338		tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
339		tp->t_force = 1;
340		error = tcp_output(tp);
341		tp->t_force = 0;
342		break;
343
344	case PRU_SOCKADDR:
345		in_setsockaddr(inp, nam);
346		break;
347
348	case PRU_PEERADDR:
349		in_setpeeraddr(inp, nam);
350		break;
351
352	/*
353	 * TCP slow timer went off; going through this
354	 * routine for tracing's sake.
355	 */
356	case PRU_SLOWTIMO:
357		tp = tcp_timers(tp, (int)nam);
358#ifdef TCPDEBUG
359		req |= (int)nam << 8;		/* for debug's sake */
360#endif
361		break;
362
363	default:
364		panic("tcp_usrreq");
365	}
366#ifdef TCPDEBUG
367	if (tp && (so->so_options & SO_DEBUG))
368		tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0, req);
369#endif
370	splx(s);
371	return (error);
372}
373
374/*
375 * Common subroutine to open a TCP connection to remote host specified
376 * by struct sockaddr_in in mbuf *nam.  Call in_pcbbind to assign a local
377 * port number if needed.  Call in_pcbladdr to do the routing and to choose
378 * a local host address (interface).  If there is an existing incarnation
379 * of the same connection in TIME-WAIT state and if the remote host was
380 * sending CC options and if the connection duration was < MSL, then
381 * truncate the previous TIME-WAIT state and proceed.
382 * Initialize connection parameters and enter SYN-SENT state.
383 */
384int
385tcp_connect(tp, nam)
386	register struct tcpcb *tp;
387	struct mbuf *nam;
388{
389	struct inpcb *inp = tp->t_inpcb, *oinp;
390	struct socket *so = inp->inp_socket;
391	struct tcpcb *otp;
392	struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
393	struct sockaddr_in *ifaddr;
394	int error;
395
396	if (inp->inp_lport == 0) {
397		error = in_pcbbind(inp, NULL);
398		if (error)
399			return error;
400	}
401
402	/*
403	 * Cannot simply call in_pcbconnect, because there might be an
404	 * earlier incarnation of this same connection still in
405	 * TIME_WAIT state, creating an ADDRINUSE error.
406	 */
407	error = in_pcbladdr(inp, nam, &ifaddr);
408	oinp = in_pcblookup(inp->inp_head,
409	    sin->sin_addr, sin->sin_port,
410	    inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr
411						: ifaddr->sin_addr,
412	    inp->inp_lport,  0);
413	if (oinp) {
414		if (oinp != inp && (otp = intotcpcb(oinp)) != NULL &&
415		otp->t_state == TCPS_TIME_WAIT &&
416		    otp->t_duration < TCPTV_MSL &&
417		    (otp->t_flags & TF_RCVD_CC))
418			otp = tcp_close(otp);
419		else
420			return EADDRINUSE;
421	}
422	if (inp->inp_laddr.s_addr == INADDR_ANY)
423		inp->inp_laddr = ifaddr->sin_addr;
424	inp->inp_faddr = sin->sin_addr;
425	inp->inp_fport = sin->sin_port;
426
427	tp->t_template = tcp_template(tp);
428	if (tp->t_template == 0) {
429		in_pcbdisconnect(inp);
430		return ENOBUFS;
431	}
432
433	/* Compute window scaling to request.  */
434	while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
435	    (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat)
436		tp->request_r_scale++;
437
438	soisconnecting(so);
439	tcpstat.tcps_connattempt++;
440	tp->t_state = TCPS_SYN_SENT;
441	tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
442	tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2;
443	tcp_sendseqinit(tp);
444	tp->cc_send = CC_INC(tcp_ccgen);
445
446	return 0;
447}
448
449int
450tcp_ctloutput(op, so, level, optname, mp)
451	int op;
452	struct socket *so;
453	int level, optname;
454	struct mbuf **mp;
455{
456	int error = 0, s;
457	struct inpcb *inp;
458	register struct tcpcb *tp;
459	register struct mbuf *m;
460	register int i;
461
462	s = splnet();
463	inp = sotoinpcb(so);
464	if (inp == NULL) {
465		splx(s);
466		if (op == PRCO_SETOPT && *mp)
467			(void) m_free(*mp);
468		return (ECONNRESET);
469	}
470	if (level != IPPROTO_TCP) {
471		error = ip_ctloutput(op, so, level, optname, mp);
472		splx(s);
473		return (error);
474	}
475	tp = intotcpcb(inp);
476
477	switch (op) {
478
479	case PRCO_SETOPT:
480		m = *mp;
481		switch (optname) {
482
483		case TCP_NODELAY:
484			if (m == NULL || m->m_len < sizeof (int))
485				error = EINVAL;
486			else if (*mtod(m, int *))
487				tp->t_flags |= TF_NODELAY;
488			else
489				tp->t_flags &= ~TF_NODELAY;
490			break;
491
492		case TCP_MAXSEG:
493			if (m && (i = *mtod(m, int *)) > 0 && i <= tp->t_maxseg)
494				tp->t_maxseg = i;
495			else
496				error = EINVAL;
497			break;
498
499		case TCP_NOOPT:
500			if (m == NULL || m->m_len < sizeof (int))
501				error = EINVAL;
502			else if (*mtod(m, int *))
503				tp->t_flags |= TF_NOOPT;
504			else
505				tp->t_flags &= ~TF_NOOPT;
506			break;
507
508		case TCP_NOPUSH:
509			if (m == NULL || m->m_len < sizeof (int))
510				error = EINVAL;
511			else if (*mtod(m, int *))
512				tp->t_flags |= TF_NOPUSH;
513			else
514				tp->t_flags &= ~TF_NOPUSH;
515			break;
516
517		default:
518			error = ENOPROTOOPT;
519			break;
520		}
521		if (m)
522			(void) m_free(m);
523		break;
524
525	case PRCO_GETOPT:
526		*mp = m = m_get(M_WAIT, MT_SOOPTS);
527		m->m_len = sizeof(int);
528
529		switch (optname) {
530		case TCP_NODELAY:
531			*mtod(m, int *) = tp->t_flags & TF_NODELAY;
532			break;
533		case TCP_MAXSEG:
534			*mtod(m, int *) = tp->t_maxseg;
535			break;
536		case TCP_NOOPT:
537			*mtod(m, int *) = tp->t_flags & TF_NOOPT;
538			break;
539		case TCP_NOPUSH:
540			*mtod(m, int *) = tp->t_flags & TF_NOPUSH;
541			break;
542		default:
543			error = ENOPROTOOPT;
544			break;
545		}
546		break;
547	}
548	splx(s);
549	return (error);
550}
551
552/*
553 * tcp_sendspace and tcp_recvspace are the default send and receive window
554 * sizes, respectively.  These are obsolescent (this information should
555 * be set by the route).
556 */
557u_long	tcp_sendspace = 1024*16;
558u_long	tcp_recvspace = 1024*16;
559
560/*
561 * Attach TCP protocol to socket, allocating
562 * internet protocol control block, tcp control block,
563 * bufer space, and entering LISTEN state if to accept connections.
564 */
565int
566tcp_attach(so)
567	struct socket *so;
568{
569	register struct tcpcb *tp;
570	struct inpcb *inp;
571	int error;
572
573	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
574		error = soreserve(so, tcp_sendspace, tcp_recvspace);
575		if (error)
576			return (error);
577	}
578	error = in_pcballoc(so, &tcb);
579	if (error)
580		return (error);
581	inp = sotoinpcb(so);
582	tp = tcp_newtcpcb(inp);
583	if (tp == 0) {
584		int nofd = so->so_state & SS_NOFDREF;	/* XXX */
585
586		so->so_state &= ~SS_NOFDREF;	/* don't free the socket yet */
587		in_pcbdetach(inp);
588		so->so_state |= nofd;
589		return (ENOBUFS);
590	}
591	tp->t_state = TCPS_CLOSED;
592	return (0);
593}
594
595/*
596 * Initiate (or continue) disconnect.
597 * If embryonic state, just send reset (once).
598 * If in ``let data drain'' option and linger null, just drop.
599 * Otherwise (hard), mark socket disconnecting and drop
600 * current input data; switch states based on user close, and
601 * send segment to peer (with FIN).
602 */
603struct tcpcb *
604tcp_disconnect(tp)
605	register struct tcpcb *tp;
606{
607	struct socket *so = tp->t_inpcb->inp_socket;
608
609	if (tp->t_state < TCPS_ESTABLISHED)
610		tp = tcp_close(tp);
611	else if ((so->so_options & SO_LINGER) && so->so_linger == 0)
612		tp = tcp_drop(tp, 0);
613	else {
614		soisdisconnecting(so);
615		sbflush(&so->so_rcv);
616		tp = tcp_usrclosed(tp);
617		if (tp)
618			(void) tcp_output(tp);
619	}
620	return (tp);
621}
622
623/*
624 * User issued close, and wish to trail through shutdown states:
625 * if never received SYN, just forget it.  If got a SYN from peer,
626 * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
627 * If already got a FIN from peer, then almost done; go to LAST_ACK
628 * state.  In all other cases, have already sent FIN to peer (e.g.
629 * after PRU_SHUTDOWN), and just have to play tedious game waiting
630 * for peer to send FIN or not respond to keep-alives, etc.
631 * We can let the user exit from the close as soon as the FIN is acked.
632 */
633struct tcpcb *
634tcp_usrclosed(tp)
635	register struct tcpcb *tp;
636{
637
638	switch (tp->t_state) {
639
640	case TCPS_CLOSED:
641	case TCPS_LISTEN:
642		tp->t_state = TCPS_CLOSED;
643		tp = tcp_close(tp);
644		break;
645
646	case TCPS_SYN_SENT:
647	case TCPS_SYN_RECEIVED:
648		tp->t_flags |= TF_NEEDFIN;
649		break;
650
651	case TCPS_ESTABLISHED:
652		tp->t_state = TCPS_FIN_WAIT_1;
653		break;
654
655	case TCPS_CLOSE_WAIT:
656		tp->t_state = TCPS_LAST_ACK;
657		break;
658	}
659	if (tp && tp->t_state >= TCPS_FIN_WAIT_2)
660		soisdisconnected(tp->t_inpcb->inp_socket);
661	return (tp);
662}
663
664/*
665 * Sysctl for tcp variables.
666 */
667int
668tcp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
669	int *name;
670	u_int namelen;
671	void *oldp;
672	size_t *oldlenp;
673	void *newp;
674	size_t newlen;
675{
676	extern	int tcp_do_rfc1323; /* XXX */
677	extern	int tcp_do_rfc1644; /* XXX */
678	extern	int tcp_mssdflt; /* XXX */
679	extern	int tcp_rttdflt; /* XXX */
680
681	/* All sysctl names at this level are terminal. */
682	if (namelen != 1)
683		return (ENOTDIR);
684
685	switch (name[0]) {
686	case TCPCTL_DO_RFC1323:
687		return (sysctl_int(oldp, oldlenp, newp, newlen,
688		    &tcp_do_rfc1323));
689	case TCPCTL_DO_RFC1644:
690		return (sysctl_int(oldp, oldlenp, newp, newlen,
691		    &tcp_do_rfc1644));
692	case TCPCTL_MSSDFLT:
693		return (sysctl_int(oldp, oldlenp, newp, newlen,
694		    &tcp_mssdflt));
695	case TCPCTL_STATS:
696		return (sysctl_rdstruct(oldp, oldlenp, newp, &tcpstat,
697					sizeof tcpstat));
698	case TCPCTL_RTTDFLT:
699		return (sysctl_int(oldp, oldlenp, newp, newlen, &tcp_rttdflt));
700	case TCPCTL_KEEPIDLE:
701		return (sysctl_int(oldp, oldlenp, newp, newlen,
702				   &tcp_keepidle));
703	case TCPCTL_KEEPINTVL:
704		return (sysctl_int(oldp, oldlenp, newp, newlen,
705				   &tcp_keepintvl));
706	case TCPCTL_SENDSPACE:
707		return (sysctl_int(oldp, oldlenp, newp, newlen,
708				   (int *)&tcp_sendspace)); /* XXX */
709	case TCPCTL_RECVSPACE:
710		return (sysctl_int(oldp, oldlenp, newp, newlen,
711				   (int *)&tcp_recvspace)); /* XXX */
712	default:
713		return (ENOPROTOOPT);
714	}
715	/* NOTREACHED */
716}
717