xref: /netbsd-src/usr.sbin/rbootd/rmpproto.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: rmpproto.c,v 1.14 2004/07/06 13:05:25 mycroft Exp $	*/
2 
3 /*
4  * Copyright (c) 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * the Center for Software Science of the University of Utah Computer
9  * Science Department.  CSS requests users of this software to return
10  * to css-dist@cs.utah.edu any improvements that they make and grant
11  * CSS redistribution rights.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	from: @(#)rmpproto.c	8.1 (Berkeley) 6/4/93
38  *
39  * From: Utah Hdr: rmpproto.c 3.1 92/07/06
40  * Author: Jeff Forys, University of Utah CSS
41  */
42 
43 /*
44  * Copyright (c) 1988, 1992 The University of Utah and the Center
45  *	for Software Science (CSS).
46  *
47  * This code is derived from software contributed to Berkeley by
48  * the Center for Software Science of the University of Utah Computer
49  * Science Department.  CSS requests users of this software to return
50  * to css-dist@cs.utah.edu any improvements that they make and grant
51  * CSS redistribution rights.
52  *
53  * Redistribution and use in source and binary forms, with or without
54  * modification, are permitted provided that the following conditions
55  * are met:
56  * 1. Redistributions of source code must retain the above copyright
57  *    notice, this list of conditions and the following disclaimer.
58  * 2. Redistributions in binary form must reproduce the above copyright
59  *    notice, this list of conditions and the following disclaimer in the
60  *    documentation and/or other materials provided with the distribution.
61  * 3. All advertising materials mentioning features or use of this software
62  *    must display the following acknowledgement:
63  *	This product includes software developed by the University of
64  *	California, Berkeley and its contributors.
65  * 4. Neither the name of the University nor the names of its contributors
66  *    may be used to endorse or promote products derived from this software
67  *    without specific prior written permission.
68  *
69  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
70  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
71  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
72  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
73  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
74  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
75  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
76  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
77  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
78  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
79  * SUCH DAMAGE.
80  *
81  *	from: @(#)rmpproto.c	8.1 (Berkeley) 6/4/93
82  *
83  * From: Utah Hdr: rmpproto.c 3.1 92/07/06
84  * Author: Jeff Forys, University of Utah CSS
85  */
86 
87 #include <sys/cdefs.h>
88 #ifndef lint
89 #if 0
90 static char sccsid[] = "@(#)rmpproto.c	8.1 (Berkeley) 6/4/93";
91 #else
92 __RCSID("$NetBSD: rmpproto.c,v 1.14 2004/07/06 13:05:25 mycroft Exp $");
93 #endif
94 #endif /* not lint */
95 
96 #include <sys/param.h>
97 #include <sys/time.h>
98 
99 #include <errno.h>
100 #include <fcntl.h>
101 #include <stdio.h>
102 #include <string.h>
103 #include <syslog.h>
104 #include <unistd.h>
105 #include "defs.h"
106 
107 /*
108 **  ProcessPacket -- determine packet type and do what's required.
109 **
110 **	An RMP BOOT packet has been received.  Look at the type field
111 **	and process Boot Requests, Read Requests, and Boot Complete
112 **	packets.  Any other type will be dropped with a warning msg.
113 **
114 **	Parameters:
115 **		rconn - the new connection
116 **		client - list of files available to this host
117 **
118 **	Returns:
119 **		Nothing.
120 **
121 **	Side Effects:
122 **		- If this is a valid boot request, it will be added to
123 **		  the linked list of outstanding requests (RmpConns).
124 **		- If this is a valid boot complete, its associated
125 **		  entry in RmpConns will be deleted.
126 **		- Also, unless we run out of memory, a reply will be
127 **		  sent to the host that sent the packet.
128 */
129 void
130 ProcessPacket(rconn, client)
131 	RMPCONN *rconn;
132 	CLIENT *client;
133 {
134 	struct rmp_packet *rmp;
135 	RMPCONN *rconnout;
136 
137 	rmp = &rconn->rmp;		/* cache pointer to RMP packet */
138 
139 	switch(rmp->r_type) {		/* do what we came here to do */
140 		case RMP_BOOT_REQ:		/* boot request */
141 			if ((rconnout = NewConn(rconn)) == NULL)
142 				return;
143 
144 			/*
145 			 *  If the Session ID is 0xffff, this is a "probe"
146 			 *  packet and we do not want to add the connection
147 			 *  to the linked list of active connections.  There
148 			 *  are two types of probe packets, if the Sequence
149 			 *  Number is 0 they want to know our host name, o/w
150 			 *  they want the name of the file associated with
151 			 *  the number spec'd by the Sequence Number.
152 			 *
153 			 *  If this is an actual boot request, open the file
154 			 *  and send a reply.  If SendBootRepl() does not
155 			 *  return 0, add the connection to the linked list
156 			 *  of active connections, otherwise delete it since
157 			 *  an error was encountered.
158 			 */
159 			if (ntohs(rmp->r_brq.rmp_session) == RMP_PROBESID) {
160 				if (WORDZE(rmp->r_brq.rmp_seqno))
161 					(void) SendServerID(rconnout);
162 				else
163 					(void) SendFileNo(rmp, rconnout,
164 					                  client? client->files:
165 					                          BootFiles);
166 				FreeConn(rconnout);
167 			} else {
168 				if (SendBootRepl(rmp, rconnout,
169 				    client? client->files: BootFiles))
170 					AddConn(rconnout);
171 				else
172 					FreeConn(rconnout);
173 			}
174 			break;
175 
176 		case RMP_BOOT_REPL:		/* boot reply (not valid) */
177 			syslog(LOG_WARNING, "%s: sent a boot reply",
178 			       EnetStr(rconn));
179 			break;
180 
181 		case RMP_READ_REQ:		/* read request */
182 			/*
183 			 *  Send a portion of the boot file.
184 			 */
185 			(void) SendReadRepl(rconn);
186 			break;
187 
188 		case RMP_READ_REPL:		/* read reply (not valid) */
189 			syslog(LOG_WARNING, "%s: sent a read reply",
190 			       EnetStr(rconn));
191 			break;
192 
193 		case RMP_BOOT_DONE:		/* boot complete */
194 			/*
195 			 *  Remove the entry from the linked list of active
196 			 *  connections.
197 			 */
198 			(void) BootDone(rconn);
199 			break;
200 
201 		default:			/* unknown RMP packet type */
202 			syslog(LOG_WARNING, "%s: unknown packet type (%u)",
203 			       EnetStr(rconn), rmp->r_type);
204 	}
205 }
206 
207 /*
208 **  SendServerID -- send our host name to who ever requested it.
209 **
210 **	Parameters:
211 **		rconn - the reply packet to be formatted.
212 **
213 **	Returns:
214 **		1 on success, 0 on failure.
215 **
216 **	Side Effects:
217 **		none.
218 */
219 int
220 SendServerID(rconn)
221 	RMPCONN *rconn;
222 {
223 	struct rmp_packet *rpl;
224 	char *src, *dst;
225 	u_int8_t *size;
226 
227 	rpl = &rconn->rmp;			/* cache ptr to RMP packet */
228 
229 	/*
230 	 *  Set up assorted fields in reply packet.
231 	 */
232 	rpl->r_brpl.rmp_type = RMP_BOOT_REPL;
233 	rpl->r_brpl.rmp_retcode = RMP_E_OKAY;
234 	ZEROWORD(rpl->r_brpl.rmp_seqno);
235 	rpl->r_brpl.rmp_session = 0;
236 	rpl->r_brpl.rmp_version = htons(RMP_VERSION);
237 
238 	size = &rpl->r_brpl.rmp_flnmsize;	/* ptr to length of host name */
239 
240 	/*
241 	 *  Copy our host name into the reply packet incrementing the
242 	 *  length as we go.  Stop at RMP_HOSTLEN or the first dot.
243 	 */
244 	src = MyHost;
245 	dst = (char *) &rpl->r_brpl.rmp_flnm;
246 	for (*size = 0; *size < RMP_HOSTLEN; (*size)++) {
247 		if (*src == '.' || *src == '\0')
248 			break;
249 		*dst++ = *src++;
250 	}
251 
252 	rconn->rmplen = RMPBOOTSIZE(*size);	/* set packet length */
253 
254 	return(SendPacket(rconn));		/* send packet */
255 }
256 
257 /*
258 **  SendFileNo -- send the name of a bootable file to the requester.
259 **
260 **	Parameters:
261 **		req - RMP BOOT packet containing the request.
262 **		rconn - the reply packet to be formatted.
263 **		filelist - list of files available to the requester.
264 **
265 **	Returns:
266 **		1 on success, 0 on failure.
267 **
268 **	Side Effects:
269 **		none.
270 */
271 int
272 SendFileNo(req, rconn, filelist)
273 	struct rmp_packet *req;
274 	RMPCONN *rconn;
275 	char *filelist[];
276 {
277 	struct rmp_packet *rpl;
278 	char *src, *dst;
279 	u_int8_t *size;
280 	int i;
281 
282 	GETWORD(req->r_brpl.rmp_seqno, i);	/* SeqNo is really FileNo */
283 	rpl = &rconn->rmp;			/* cache ptr to RMP packet */
284 
285 	/*
286 	 *  Set up assorted fields in reply packet.
287 	 */
288 	rpl->r_brpl.rmp_type = RMP_BOOT_REPL;
289 	PUTWORD(i, rpl->r_brpl.rmp_seqno);
290 	i--;
291 	rpl->r_brpl.rmp_session = 0;
292 	rpl->r_brpl.rmp_version = htons(RMP_VERSION);
293 
294 	size = &rpl->r_brpl.rmp_flnmsize;	/* ptr to length of filename */
295 	*size = 0;				/* init length to zero */
296 
297 	/*
298 	 *  Copy the file name into the reply packet incrementing the
299 	 *  length as we go.  Stop at end of string or when RMPBOOTDATA
300 	 *  characters have been copied.  Also, set return code to
301 	 *  indicate success or "no more files".
302 	 */
303 	if (i < C_MAXFILE && filelist[i] != NULL) {
304 		src = filelist[i];
305 		dst = (char *)&rpl->r_brpl.rmp_flnm;
306 		for (; *src && *size < RMPBOOTDATA; (*size)++) {
307 			if (*src == '\0')
308 				break;
309 			*dst++ = *src++;
310 		}
311 		rpl->r_brpl.rmp_retcode = RMP_E_OKAY;
312 	} else
313 		rpl->r_brpl.rmp_retcode = RMP_E_NODFLT;
314 
315 	rconn->rmplen = RMPBOOTSIZE(*size);	/* set packet length */
316 
317 	return(SendPacket(rconn));		/* send packet */
318 }
319 
320 /*
321 **  SendBootRepl -- open boot file and respond to boot request.
322 **
323 **	Parameters:
324 **		req - RMP BOOT packet containing the request.
325 **		rconn - the reply packet to be formatted.
326 **		filelist - list of files available to the requester.
327 **
328 **	Returns:
329 **		1 on success, 0 on failure.
330 **
331 **	Side Effects:
332 **		none.
333 */
334 int
335 SendBootRepl(req, rconn, filelist)
336 	struct rmp_packet *req;
337 	RMPCONN *rconn;
338 	char *filelist[];
339 {
340 	int retval;
341 	char *filename, filepath[RMPBOOTDATA+1];
342 	RMPCONN *oldconn;
343 	struct rmp_packet *rpl;
344 	char *src, *dst1, *dst2;
345 	u_int8_t i;
346 
347 	/*
348 	 *  If another connection already exists, delete it since we
349 	 *  are obviously starting again.
350 	 */
351 	if ((oldconn = FindConn(rconn)) != NULL) {
352 		syslog(LOG_WARNING, "%s: dropping existing connection",
353 		       EnetStr(oldconn));
354 		RemoveConn(oldconn);
355 	}
356 
357 	rpl = &rconn->rmp;			/* cache ptr to RMP packet */
358 
359 	/*
360 	 *  Set up assorted fields in reply packet.
361 	 */
362 	rpl->r_brpl.rmp_type = RMP_BOOT_REPL;
363 	COPYWORD(req->r_brq.rmp_seqno, rpl->r_brpl.rmp_seqno);
364 	rpl->r_brpl.rmp_session = htons(GenSessID());
365 	rpl->r_brpl.rmp_version = htons(RMP_VERSION);
366 	rpl->r_brpl.rmp_flnmsize = req->r_brq.rmp_flnmsize;
367 
368 	/*
369 	 *  Copy file name to `filepath' string, and into reply packet.
370 	 */
371 	dst1 = filepath;
372 	dst2 = &rpl->r_brpl.rmp_flnm;
373 	if (req->r_brq.rmp_flnmsize)
374 		src = &req->r_brq.rmp_flnm;
375 	else {
376 		/* no file supplied, substitute the first one */
377 		src = filelist[0];
378 		req->r_brq.rmp_flnmsize = strlen(src);
379 	}
380 	for (i = 0; i < req->r_brq.rmp_flnmsize; i++)
381 		*dst1++ = *dst2++ = *src++;
382 	*dst1 = '\0';
383 
384 	/*
385 	 *  If we are booting HP-UX machines, their secondary loader will
386 	 *  ask for files like "/hp-ux".  As a security measure, we do not
387 	 *  allow boot files to lay outside the boot directory (unless they
388 	 *  are purposely link'd out.  So, make `filename' become the path-
389 	 *  stripped file name and spoof the client into thinking that it
390 	 *  really got what it wanted.
391 	 */
392 	if ((filename = strrchr(filepath,'/')) != NULL)
393 		filename++;
394 	else
395 		filename = filepath;
396 
397 	/*
398 	 *  Check that this is a valid boot file name.
399 	 */
400 	for (i = 0; i < C_MAXFILE && filelist[i] != NULL; i++)
401 		if (STREQN(filename, filelist[i]))
402 			goto match;
403 
404 	/*
405 	 *  Invalid boot file name, set error and send reply packet.
406 	 */
407 	rpl->r_brpl.rmp_retcode = RMP_E_NOFILE;
408 	retval = 0;
409 	goto sendpkt;
410 
411 match:
412 	/*
413 	 *  This is a valid boot file.  Open the file and save the file
414 	 *  descriptor associated with this connection and set success
415 	 *  indication.  If the file couldnt be opened, set error:
416 	 *  	"no such file or dir" - RMP_E_NOFILE
417 	 *	"file table overflow" - RMP_E_BUSY
418 	 *	"too many open files" - RMP_E_BUSY
419 	 *	anything else         - RMP_E_OPENFILE
420 	 */
421 	if ((rconn->bootfd = open(filename, O_RDONLY, 0600)) < 0) {
422 		rpl->r_brpl.rmp_retcode = (errno == ENOENT)? RMP_E_NOFILE:
423 			(errno == EMFILE || errno == ENFILE)? RMP_E_BUSY:
424 			RMP_E_OPENFILE;
425 		retval = 0;
426 	} else {
427 		rpl->r_brpl.rmp_retcode = RMP_E_OKAY;
428 		retval = 1;
429 	}
430 
431 sendpkt:
432 	syslog(LOG_INFO, "%s: request to boot %s (%s)",
433 	       EnetStr(rconn), filename, retval? "granted": "denied");
434 
435 	rconn->rmplen = RMPBOOTSIZE(rpl->r_brpl.rmp_flnmsize);
436 
437 	return (retval & SendPacket(rconn));
438 }
439 
440 /*
441 **  SendReadRepl -- send a portion of the boot file to the requester.
442 **
443 **	Parameters:
444 **		rconn - the reply packet to be formatted.
445 **
446 **	Returns:
447 **		1 on success, 0 on failure.
448 **
449 **	Side Effects:
450 **		none.
451 */
452 int
453 SendReadRepl(rconn)
454 	RMPCONN *rconn;
455 {
456 	int retval = 0;
457 	RMPCONN *oldconn;
458 	struct rmp_packet *rpl, *req;
459 	int size = 0;
460 	int madeconn = 0;
461 
462 	/*
463 	 *  Find the old connection.  If one doesnt exist, create one only
464 	 *  to return the error code.
465 	 */
466 	if ((oldconn = FindConn(rconn)) == NULL) {
467 		if ((oldconn = NewConn(rconn)) == NULL)
468 			return(0);
469 		syslog(LOG_ERR, "SendReadRepl: no active connection (%s)",
470 		       EnetStr(rconn));
471 		madeconn++;
472 	}
473 
474 	req = &rconn->rmp;		/* cache ptr to request packet */
475 	rpl = &oldconn->rmp;		/* cache ptr to reply packet */
476 
477 	if (madeconn) {			/* no active connection above; abort */
478 		rpl->r_rrpl.rmp_retcode = RMP_E_ABORT;
479 		retval = 1;
480 		goto sendpkt;
481 	}
482 
483 	/*
484 	 *  Make sure Session ID's match.
485 	 */
486 	if (ntohs(req->r_rrq.rmp_session) !=
487 	    ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session):
488 	                                     ntohs(rpl->r_rrpl.rmp_session))) {
489 		syslog(LOG_ERR, "SendReadRepl: bad session id (%s)",
490 		       EnetStr(rconn));
491 		rpl->r_rrpl.rmp_retcode = RMP_E_BADSID;
492 		retval = 1;
493 		goto sendpkt;
494 	}
495 
496 	/*
497 	 *  If the requester asks for more data than we can fit,
498 	 *  silently clamp the request size down to RMPREADDATA.
499 	 *
500 	 *  N.B. I do not know if this is "legal", however it seems
501 	 *  to work.  This is necessary for bpfwrite() on machines
502 	 *  with MCLBYTES less than 1514.
503 	 */
504 	if (ntohs(req->r_rrq.rmp_size) > RMPREADDATA)
505 		req->r_rrq.rmp_size = htons(RMPREADDATA);
506 
507 	/*
508 	 *  Position read head on file according to info in request packet.
509 	 */
510 	GETWORD(req->r_rrq.rmp_offset, size);
511 	if (lseek(oldconn->bootfd, (off_t)size, SEEK_SET) < 0) {
512 		syslog(LOG_ERR, "SendReadRepl: lseek: %m (%s)",
513 		       EnetStr(rconn));
514 		rpl->r_rrpl.rmp_retcode = RMP_E_ABORT;
515 		retval = 1;
516 		goto sendpkt;
517 	}
518 
519 	/*
520 	 *  Read data directly into reply packet.
521 	 */
522 	if ((size = read(oldconn->bootfd, &rpl->r_rrpl.rmp_data,
523 	                 (int) ntohs(req->r_rrq.rmp_size))) <= 0) {
524 		if (size < 0) {
525 			syslog(LOG_ERR, "SendReadRepl: read: %m (%s)",
526 			       EnetStr(rconn));
527 			rpl->r_rrpl.rmp_retcode = RMP_E_ABORT;
528 		} else {
529 			rpl->r_rrpl.rmp_retcode = RMP_E_EOF;
530 		}
531 		retval = 1;
532 		goto sendpkt;
533 	}
534 
535 	/*
536 	 *  Set success indication.
537 	 */
538 	rpl->r_rrpl.rmp_retcode = RMP_E_OKAY;
539 
540 sendpkt:
541 	/*
542 	 *  Set up assorted fields in reply packet.
543 	 */
544 	rpl->r_rrpl.rmp_type = RMP_READ_REPL;
545 	COPYWORD(req->r_rrq.rmp_offset, rpl->r_rrpl.rmp_offset);
546 	rpl->r_rrpl.rmp_session = req->r_rrq.rmp_session;
547 
548 	oldconn->rmplen = RMPREADSIZE(size);	/* set size of packet */
549 
550 	retval &= SendPacket(oldconn);		/* send packet */
551 
552 	if (madeconn)				/* clean up after ourself */
553 		FreeConn(oldconn);
554 
555 	return (retval);
556 }
557 
558 /*
559 **  BootDone -- free up memory allocated for a connection.
560 **
561 **	Parameters:
562 **		rconn - incoming boot complete packet.
563 **
564 **	Returns:
565 **		1 on success, 0 on failure.
566 **
567 **	Side Effects:
568 **		none.
569 */
570 int
571 BootDone(rconn)
572 	RMPCONN *rconn;
573 {
574 	RMPCONN *oldconn;
575 	struct rmp_packet *rpl;
576 
577 	/*
578 	 *  If we cant find the connection, ignore the request.
579 	 */
580 	if ((oldconn = FindConn(rconn)) == NULL) {
581 		syslog(LOG_ERR, "BootDone: no existing connection (%s)",
582 		       EnetStr(rconn));
583 		return(0);
584 	}
585 
586 	rpl = &oldconn->rmp;			/* cache ptr to RMP packet */
587 
588 	/*
589 	 *  Make sure Session ID's match.
590 	 */
591 	if (ntohs(rconn->rmp.r_rrq.rmp_session) !=
592 	    ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session):
593 	                                    ntohs(rpl->r_rrpl.rmp_session))) {
594 		syslog(LOG_ERR, "BootDone: bad session id (%s)",
595 		       EnetStr(rconn));
596 		return(0);
597 	}
598 
599 	RemoveConn(oldconn);			/* remove connection */
600 
601 	syslog(LOG_INFO, "%s: boot complete", EnetStr(rconn));
602 
603 	return(1);
604 }
605 
606 /*
607 **  SendPacket -- send an RMP packet to a remote host.
608 **
609 **	Parameters:
610 **		rconn - packet to be sent.
611 **
612 **	Returns:
613 **		1 on success, 0 on failure.
614 **
615 **	Side Effects:
616 **		none.
617 */
618 int
619 SendPacket(rconn)
620 	RMPCONN *rconn;
621 {
622 	/*
623 	 *  Set Ethernet Destination address to Source (BPF and the enet
624 	 *  driver will take care of getting our source address set).
625 	 */
626 	memmove((char *)&rconn->rmp.hp_hdr.daddr[0],
627 	    (char *)&rconn->rmp.hp_hdr.saddr[0], RMP_ADDRLEN);
628 	rconn->rmp.hp_hdr.len = htons(rconn->rmplen - sizeof(struct hp_hdr));
629 
630 	/*
631 	 *  Reverse 802.2/HP Extended Source & Destination Access Pts.
632 	 */
633 	rconn->rmp.hp_llc.dxsap = htons(HPEXT_SXSAP);
634 	rconn->rmp.hp_llc.sxsap = htons(HPEXT_DXSAP);
635 
636 	/*
637 	 *  Last time this connection was active.
638 	 */
639 	(void) gettimeofday(&rconn->tstamp, (struct timezone *)0);
640 
641 	if (DbgFp != NULL)			/* display packet */
642 		DispPkt(rconn,DIR_SENT);
643 
644 	/*
645 	 *  Send RMP packet to remote host.
646 	 */
647 	return(BpfWrite(rconn));
648 }
649