1/*	$NetBSD: ipxcp.c,v 1.1.1.1 2005/02/20 10:28:47 cube Exp $	*/
2
3/*
4 * ipxcp.c - PPP IPX Control Protocol.
5 *
6 * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in
17 *    the documentation and/or other materials provided with the
18 *    distribution.
19 *
20 * 3. The name "Carnegie Mellon University" must not be used to
21 *    endorse or promote products derived from this software without
22 *    prior written permission. For permission or any legal
23 *    details, please contact
24 *      Office of Technology Transfer
25 *      Carnegie Mellon University
26 *      5000 Forbes Avenue
27 *      Pittsburgh, PA  15213-3890
28 *      (412) 268-4387, fax: (412) 268-7395
29 *      tech-transfer@andrew.cmu.edu
30 *
31 * 4. Redistributions of any form whatsoever must retain the following
32 *    acknowledgment:
33 *    "This product includes software developed by Computing Services
34 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
35 *
36 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
37 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
38 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
39 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
41 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
42 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43 */
44
45#ifdef IPX_CHANGE
46
47#include <sys/cdefs.h>
48#ifndef lint
49#if 0
50#define RCSID	"Id: ipxcp.c,v 1.24 2005/08/25 23:59:34 paulus Exp"
51#else
52__RCSID("$NetBSD: ipxcp.c,v 1.1.1.1 2005/02/20 10:28:47 cube Exp $");
53#endif
54#endif
55
56/*
57 * TODO:
58 */
59
60#include <stdio.h>
61#include <string.h>
62#include <unistd.h>
63#include <ctype.h>
64#include <sys/types.h>
65#include <sys/socket.h>
66#include <netinet/in.h>
67
68#include "pppd.h"
69#include "fsm.h"
70#include "ipxcp.h"
71#include "pathnames.h"
72#include "magic.h"
73
74#ifdef RCSID
75static const char rcsid[] = RCSID;
76#endif
77
78/* global vars */
79ipxcp_options ipxcp_wantoptions[NUM_PPP];	/* Options that we want to request */
80ipxcp_options ipxcp_gotoptions[NUM_PPP];	/* Options that peer ack'd */
81ipxcp_options ipxcp_allowoptions[NUM_PPP];	/* Options we allow peer to request */
82ipxcp_options ipxcp_hisoptions[NUM_PPP];	/* Options that we ack'd */
83
84#define wo (&ipxcp_wantoptions[0])
85#define ao (&ipxcp_allowoptions[0])
86#define go (&ipxcp_gotoptions[0])
87#define ho (&ipxcp_hisoptions[0])
88
89/*
90 * Callbacks for fsm code.  (CI = Configuration Information)
91 */
92static void ipxcp_resetci __P((fsm *));	/* Reset our CI */
93static int  ipxcp_cilen __P((fsm *));		/* Return length of our CI */
94static void ipxcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
95static int  ipxcp_ackci __P((fsm *, u_char *, int));	/* Peer ack'd our CI */
96static int  ipxcp_nakci __P((fsm *, u_char *, int, int));/* Peer nak'd our CI */
97static int  ipxcp_rejci __P((fsm *, u_char *, int));	/* Peer rej'd our CI */
98static int  ipxcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
99static void ipxcp_up __P((fsm *));		/* We're UP */
100static void ipxcp_down __P((fsm *));		/* We're DOWN */
101static void ipxcp_finished __P((fsm *));	/* Don't need lower layer */
102static void ipxcp_script __P((fsm *, char *)); /* Run an up/down script */
103
104fsm ipxcp_fsm[NUM_PPP];		/* IPXCP fsm structure */
105
106static fsm_callbacks ipxcp_callbacks = { /* IPXCP callback routines */
107    ipxcp_resetci,		/* Reset our Configuration Information */
108    ipxcp_cilen,		/* Length of our Configuration Information */
109    ipxcp_addci,		/* Add our Configuration Information */
110    ipxcp_ackci,		/* ACK our Configuration Information */
111    ipxcp_nakci,		/* NAK our Configuration Information */
112    ipxcp_rejci,		/* Reject our Configuration Information */
113    ipxcp_reqci,		/* Request peer's Configuration Information */
114    ipxcp_up,			/* Called when fsm reaches OPENED state */
115    ipxcp_down,			/* Called when fsm leaves OPENED state */
116    NULL,			/* Called when we want the lower layer up */
117    ipxcp_finished,		/* Called when we want the lower layer down */
118    NULL,			/* Called when Protocol-Reject received */
119    NULL,			/* Retransmission is necessary */
120    NULL,			/* Called to handle protocol-specific codes */
121    "IPXCP"			/* String name of protocol */
122};
123
124/*
125 * Command-line options.
126 */
127static int setipxnode __P((char **));
128static void printipxnode __P((option_t *,
129			      void (*)(void *, char *, ...), void *));
130static int setipxname __P((char **));
131
132static option_t ipxcp_option_list[] = {
133    { "ipx", o_bool, &ipxcp_protent.enabled_flag,
134      "Enable IPXCP (and IPX)", OPT_PRIO | 1 },
135    { "+ipx", o_bool, &ipxcp_protent.enabled_flag,
136      "Enable IPXCP (and IPX)", OPT_PRIOSUB | OPT_ALIAS | 1 },
137    { "noipx", o_bool, &ipxcp_protent.enabled_flag,
138      "Disable IPXCP (and IPX)", OPT_PRIOSUB },
139    { "-ipx", o_bool, &ipxcp_protent.enabled_flag,
140      "Disable IPXCP (and IPX)", OPT_PRIOSUB | OPT_ALIAS },
141
142    { "ipx-network", o_uint32, &ipxcp_wantoptions[0].our_network,
143      "Set our IPX network number", OPT_PRIO, &ipxcp_wantoptions[0].neg_nn },
144
145    { "ipxcp-accept-network", o_bool, &ipxcp_wantoptions[0].accept_network,
146      "Accept peer IPX network number", 1,
147      &ipxcp_allowoptions[0].accept_network },
148
149    { "ipx-node", o_special, (void *)setipxnode,
150      "Set IPX node number", OPT_A2PRINTER, (void *)printipxnode },
151
152    { "ipxcp-accept-local", o_bool, &ipxcp_wantoptions[0].accept_local,
153      "Accept our IPX address", 1,
154      &ipxcp_allowoptions[0].accept_local },
155
156    { "ipxcp-accept-remote", o_bool, &ipxcp_wantoptions[0].accept_remote,
157      "Accept peer's IPX address", 1,
158      &ipxcp_allowoptions[0].accept_remote },
159
160    { "ipx-routing", o_int, &ipxcp_wantoptions[0].router,
161      "Set IPX routing proto number", OPT_PRIO,
162      &ipxcp_wantoptions[0].neg_router },
163
164    { "ipx-router-name", o_special, setipxname,
165      "Set IPX router name", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC,
166       &ipxcp_wantoptions[0].name },
167
168    { "ipxcp-restart", o_int, &ipxcp_fsm[0].timeouttime,
169      "Set timeout for IPXCP", OPT_PRIO },
170    { "ipxcp-max-terminate", o_int, &ipxcp_fsm[0].maxtermtransmits,
171      "Set max #xmits for IPXCP term-reqs", OPT_PRIO },
172    { "ipxcp-max-configure", o_int, &ipxcp_fsm[0].maxconfreqtransmits,
173      "Set max #xmits for IPXCP conf-reqs", OPT_PRIO },
174    { "ipxcp-max-failure", o_int, &ipxcp_fsm[0].maxnakloops,
175      "Set max #conf-naks for IPXCP", OPT_PRIO },
176
177    { NULL }
178};
179
180/*
181 * Protocol entry points.
182 */
183
184static void ipxcp_init __P((int));
185static void ipxcp_open __P((int));
186static void ipxcp_close __P((int, char *));
187static void ipxcp_lowerup __P((int));
188static void ipxcp_lowerdown __P((int));
189static void ipxcp_input __P((int, u_char *, int));
190static void ipxcp_protrej __P((int));
191static int  ipxcp_printpkt __P((u_char *, int,
192				void (*) __P((void *, char *, ...)), void *));
193
194struct protent ipxcp_protent = {
195    PPP_IPXCP,
196    ipxcp_init,
197    ipxcp_input,
198    ipxcp_protrej,
199    ipxcp_lowerup,
200    ipxcp_lowerdown,
201    ipxcp_open,
202    ipxcp_close,
203    ipxcp_printpkt,
204    NULL,
205    0,
206    "IPXCP",
207    "IPX",
208    ipxcp_option_list,
209    NULL,
210    NULL,
211    NULL
212};
213
214/*
215 * Lengths of configuration options.
216 */
217
218#define CILEN_VOID	2
219#define CILEN_COMPLETE	2	/* length of complete option */
220#define CILEN_NETN	6	/* network number length option */
221#define CILEN_NODEN	8	/* node number length option */
222#define CILEN_PROTOCOL	4	/* Minimum length of routing protocol */
223#define CILEN_NAME	3	/* Minimum length of router name */
224#define CILEN_COMPRESS	4	/* Minimum length of compression protocol */
225
226#define CODENAME(x)	((x) == CONFACK ? "ACK" : \
227			 (x) == CONFNAK ? "NAK" : "REJ")
228
229static int ipxcp_is_up;
230
231static char *ipx_ntoa __P((u_int32_t));
232
233/* Used in printing the node number */
234#define NODE(base) base[0], base[1], base[2], base[3], base[4], base[5]
235
236/* Used to generate the proper bit mask */
237#define BIT(num)   (1 << (num))
238
239/*
240 * Convert from internal to external notation
241 */
242
243static short int
244to_external(internal)
245short int internal;
246{
247    short int  external;
248
249    if (internal & BIT(IPX_NONE) )
250        external = IPX_NONE;
251    else
252        external = RIP_SAP;
253
254    return external;
255}
256
257/*
258 * Make a string representation of a network IP address.
259 */
260
261static char *
262ipx_ntoa(ipxaddr)
263u_int32_t ipxaddr;
264{
265    static char b[64];
266    slprintf(b, sizeof(b), "%x", ipxaddr);
267    return b;
268}
269
270
271static u_char *
272setipxnodevalue(src,dst)
273u_char *src, *dst;
274{
275    int indx;
276    int item;
277
278    for (;;) {
279        if (!isxdigit (*src))
280	    break;
281
282	for (indx = 0; indx < 5; ++indx) {
283	    dst[indx] <<= 4;
284	    dst[indx] |= (dst[indx + 1] >> 4) & 0x0F;
285	}
286
287	item = toupper (*src) - '0';
288	if (item > 9)
289	    item -= 7;
290
291	dst[5] = (dst[5] << 4) | item;
292	++src;
293    }
294    return src;
295}
296
297static int ipx_prio_our, ipx_prio_his;
298
299static int
300setipxnode(argv)
301    char **argv;
302{
303    char *end;
304    int have_his = 0;
305    u_char our_node[6];
306    u_char his_node[6];
307
308    memset (our_node, 0, 6);
309    memset (his_node, 0, 6);
310
311    end = setipxnodevalue (*argv, our_node);
312    if (*end == ':') {
313	have_his = 1;
314	end = setipxnodevalue (++end, his_node);
315    }
316
317    if (*end == '\0') {
318        ipxcp_wantoptions[0].neg_node = 1;
319	if (option_priority >= ipx_prio_our) {
320	    memcpy(&ipxcp_wantoptions[0].our_node[0], our_node, 6);
321	    ipx_prio_our = option_priority;
322	}
323	if (have_his && option_priority >= ipx_prio_his) {
324	    memcpy(&ipxcp_wantoptions[0].his_node[0], his_node, 6);
325	    ipx_prio_his = option_priority;
326	}
327        return 1;
328    }
329
330    option_error("invalid parameter '%s' for ipx-node option", *argv);
331    return 0;
332}
333
334static void
335printipxnode(opt, printer, arg)
336    option_t *opt;
337    void (*printer) __P((void *, char *, ...));
338    void *arg;
339{
340	unsigned char *p;
341
342	p = ipxcp_wantoptions[0].our_node;
343	if (ipx_prio_our)
344		printer(arg, "%.2x%.2x%.2x%.2x%.2x%.2x",
345			p[0], p[1], p[2], p[3], p[4], p[5]);
346	printer(arg, ":");
347	p = ipxcp_wantoptions[0].his_node;
348	if (ipx_prio_his)
349		printer(arg, "%.2x%.2x%.2x%.2x%.2x%.2x",
350			p[0], p[1], p[2], p[3], p[4], p[5]);
351}
352
353static int
354setipxname (argv)
355    char **argv;
356{
357    char *dest = ipxcp_wantoptions[0].name;
358    char *src  = *argv;
359    int  count;
360    char ch;
361
362    ipxcp_wantoptions[0].neg_name  = 1;
363    ipxcp_allowoptions[0].neg_name = 1;
364    memset (dest, '\0', sizeof (ipxcp_wantoptions[0].name));
365
366    count = 0;
367    while (*src) {
368        ch = *src++;
369	if (! isalnum (ch) && ch != '_') {
370	    option_error("IPX router name must be alphanumeric or _");
371	    return 0;
372	}
373
374	if (count >= sizeof (ipxcp_wantoptions[0].name) - 1) {
375	    option_error("IPX router name is limited to %d characters",
376			 sizeof (ipxcp_wantoptions[0].name) - 1);
377	    return 0;
378	}
379
380	dest[count++] = toupper (ch);
381    }
382    dest[count] = 0;
383
384    return 1;
385}
386
387/*
388 * ipxcp_init - Initialize IPXCP.
389 */
390static void
391ipxcp_init(unit)
392    int unit;
393{
394    fsm *f = &ipxcp_fsm[unit];
395
396    f->unit	 = unit;
397    f->protocol	 = PPP_IPXCP;
398    f->callbacks = &ipxcp_callbacks;
399    fsm_init(&ipxcp_fsm[unit]);
400
401    memset (wo->name,	  0, sizeof (wo->name));
402    memset (wo->our_node, 0, sizeof (wo->our_node));
403    memset (wo->his_node, 0, sizeof (wo->his_node));
404
405    wo->neg_nn	       = 1;
406    wo->neg_complete   = 1;
407    wo->network	       = 0;
408
409    ao->neg_node       = 1;
410    ao->neg_nn	       = 1;
411    ao->neg_name       = 1;
412    ao->neg_complete   = 1;
413    ao->neg_router     = 1;
414
415    ao->accept_local   = 0;
416    ao->accept_remote  = 0;
417    ao->accept_network = 0;
418
419    wo->tried_rip      = 0;
420    wo->tried_nlsp     = 0;
421}
422
423/*
424 * Copy the node number
425 */
426
427static void
428copy_node (src, dst)
429u_char *src, *dst;
430{
431    memcpy (dst, src, sizeof (ipxcp_wantoptions[0].our_node));
432}
433
434/*
435 * Compare node numbers
436 */
437
438static int
439compare_node (src, dst)
440u_char *src, *dst;
441{
442    return memcmp (dst, src, sizeof (ipxcp_wantoptions[0].our_node)) == 0;
443}
444
445/*
446 * Is the node number zero?
447 */
448
449static int
450zero_node (node)
451u_char *node;
452{
453    int indx;
454    for (indx = 0; indx < sizeof (ipxcp_wantoptions[0].our_node); ++indx)
455	if (node [indx] != 0)
456	    return 0;
457    return 1;
458}
459
460/*
461 * Increment the node number
462 */
463
464static void
465inc_node (node)
466u_char *node;
467{
468    u_char   *outp;
469    u_int32_t magic_num;
470
471    outp      = node;
472    magic_num = magic();
473    *outp++   = '\0';
474    *outp++   = '\0';
475    PUTLONG (magic_num, outp);
476}
477
478/*
479 * ipxcp_open - IPXCP is allowed to come up.
480 */
481static void
482ipxcp_open(unit)
483    int unit;
484{
485    fsm_open(&ipxcp_fsm[unit]);
486}
487
488/*
489 * ipxcp_close - Take IPXCP down.
490 */
491static void
492ipxcp_close(unit, reason)
493    int unit;
494    char *reason;
495{
496    fsm_close(&ipxcp_fsm[unit], reason);
497}
498
499
500/*
501 * ipxcp_lowerup - The lower layer is up.
502 */
503static void
504ipxcp_lowerup(unit)
505    int unit;
506{
507    fsm_lowerup(&ipxcp_fsm[unit]);
508}
509
510
511/*
512 * ipxcp_lowerdown - The lower layer is down.
513 */
514static void
515ipxcp_lowerdown(unit)
516    int unit;
517{
518    fsm_lowerdown(&ipxcp_fsm[unit]);
519}
520
521
522/*
523 * ipxcp_input - Input IPXCP packet.
524 */
525static void
526ipxcp_input(unit, p, len)
527    int unit;
528    u_char *p;
529    int len;
530{
531    fsm_input(&ipxcp_fsm[unit], p, len);
532}
533
534
535/*
536 * ipxcp_protrej - A Protocol-Reject was received for IPXCP.
537 *
538 * Pretend the lower layer went down, so we shut up.
539 */
540static void
541ipxcp_protrej(unit)
542    int unit;
543{
544    fsm_lowerdown(&ipxcp_fsm[unit]);
545}
546
547
548/*
549 * ipxcp_resetci - Reset our CI.
550 */
551static void
552ipxcp_resetci(f)
553    fsm *f;
554{
555    wo->req_node = wo->neg_node && ao->neg_node;
556    wo->req_nn	 = wo->neg_nn	&& ao->neg_nn;
557
558    if (wo->our_network == 0) {
559	wo->neg_node	   = 1;
560	ao->accept_network = 1;
561    }
562/*
563 * If our node number is zero then change it.
564 */
565    if (zero_node (wo->our_node)) {
566	inc_node (wo->our_node);
567	ao->accept_local = 1;
568	wo->neg_node	 = 1;
569    }
570/*
571 * If his node number is zero then change it.
572 */
573    if (zero_node (wo->his_node)) {
574	inc_node (wo->his_node);
575	ao->accept_remote = 1;
576    }
577/*
578 * If no routing agent was specified then we do RIP/SAP according to the
579 * RFC documents. If you have specified something then OK. Otherwise, we
580 * do RIP/SAP.
581 */
582    if (ao->router == 0) {
583	ao->router |= BIT(RIP_SAP);
584	wo->router |= BIT(RIP_SAP);
585    }
586
587    /* Always specify a routing protocol unless it was REJected. */
588    wo->neg_router = 1;
589/*
590 * Start with these default values
591 */
592    *go = *wo;
593}
594
595/*
596 * ipxcp_cilen - Return length of our CI.
597 */
598
599static int
600ipxcp_cilen(f)
601    fsm *f;
602{
603    int len;
604
605    len	 = go->neg_nn	    ? CILEN_NETN     : 0;
606    len += go->neg_node	    ? CILEN_NODEN    : 0;
607    len += go->neg_name	    ? CILEN_NAME + strlen (go->name) - 1 : 0;
608
609    /* RFC says that defaults should not be included. */
610    if (go->neg_router && to_external(go->router) != RIP_SAP)
611        len += CILEN_PROTOCOL;
612
613    return (len);
614}
615
616
617/*
618 * ipxcp_addci - Add our desired CIs to a packet.
619 */
620static void
621ipxcp_addci(f, ucp, lenp)
622    fsm *f;
623    u_char *ucp;
624    int *lenp;
625{
626/*
627 * Add the options to the record.
628 */
629    if (go->neg_nn) {
630	PUTCHAR (IPX_NETWORK_NUMBER, ucp);
631	PUTCHAR (CILEN_NETN, ucp);
632	PUTLONG (go->our_network, ucp);
633    }
634
635    if (go->neg_node) {
636	int indx;
637	PUTCHAR (IPX_NODE_NUMBER, ucp);
638	PUTCHAR (CILEN_NODEN, ucp);
639	for (indx = 0; indx < sizeof (go->our_node); ++indx)
640	    PUTCHAR (go->our_node[indx], ucp);
641    }
642
643    if (go->neg_name) {
644	int cilen = strlen (go->name);
645	int indx;
646	PUTCHAR (IPX_ROUTER_NAME, ucp);
647	PUTCHAR (CILEN_NAME + cilen - 1, ucp);
648	for (indx = 0; indx < cilen; ++indx)
649	    PUTCHAR (go->name [indx], ucp);
650    }
651
652    if (go->neg_router) {
653        short external = to_external (go->router);
654	if (external != RIP_SAP) {
655	    PUTCHAR  (IPX_ROUTER_PROTOCOL, ucp);
656	    PUTCHAR  (CILEN_PROTOCOL,      ucp);
657	    PUTSHORT (external,            ucp);
658	}
659    }
660}
661
662/*
663 * ipxcp_ackci - Ack our CIs.
664 *
665 * Returns:
666 *	0 - Ack was bad.
667 *	1 - Ack was good.
668 */
669static int
670ipxcp_ackci(f, p, len)
671    fsm *f;
672    u_char *p;
673    int len;
674{
675    u_short cilen, citype, cishort;
676    u_char cichar;
677    u_int32_t cilong;
678
679#define ACKCIVOID(opt, neg) \
680    if (neg) { \
681	if ((len -= CILEN_VOID) < 0) \
682	    break; \
683	GETCHAR(citype, p); \
684	GETCHAR(cilen, p); \
685	if (cilen != CILEN_VOID || \
686	    citype != opt) \
687	    break; \
688    }
689
690#define ACKCICOMPLETE(opt,neg)	ACKCIVOID(opt, neg)
691
692#define ACKCICHARS(opt, neg, val, cnt) \
693    if (neg) { \
694	int indx, count = cnt; \
695	len -= (count + 2); \
696	if (len < 0) \
697	    break; \
698	GETCHAR(citype, p); \
699	GETCHAR(cilen, p); \
700	if (cilen != (count + 2) || \
701	    citype != opt) \
702	    break; \
703	for (indx = 0; indx < count; ++indx) {\
704	    GETCHAR(cichar, p); \
705	    if (cichar != ((u_char *) &val)[indx]) \
706	       break; \
707	}\
708	if (indx != count) \
709	    break; \
710    }
711
712#define ACKCINODE(opt,neg,val) ACKCICHARS(opt,neg,val,sizeof(val))
713#define ACKCINAME(opt,neg,val) ACKCICHARS(opt,neg,val,strlen(val))
714
715#define ACKCINETWORK(opt, neg, val) \
716    if (neg) { \
717	if ((len -= CILEN_NETN) < 0) \
718	    break; \
719	GETCHAR(citype, p); \
720	GETCHAR(cilen, p); \
721	if (cilen != CILEN_NETN || \
722	    citype != opt) \
723	    break; \
724	GETLONG(cilong, p); \
725	if (cilong != val) \
726	    break; \
727    }
728
729#define ACKCIPROTO(opt, neg, val) \
730    if (neg) { \
731	if (len < 2) \
732	    break; \
733	GETCHAR(citype, p); \
734	GETCHAR(cilen, p); \
735	if (cilen != CILEN_PROTOCOL || citype != opt) \
736	    break; \
737	len -= cilen; \
738	if (len < 0) \
739	    break; \
740	GETSHORT(cishort, p); \
741	if (cishort != to_external (val) || cishort == RIP_SAP) \
742	    break; \
743      }
744/*
745 * Process the ACK frame in the order in which the frame was assembled
746 */
747    do {
748	ACKCINETWORK  (IPX_NETWORK_NUMBER,  go->neg_nn,	    go->our_network);
749	ACKCINODE     (IPX_NODE_NUMBER,	    go->neg_node,   go->our_node);
750	ACKCINAME     (IPX_ROUTER_NAME,	    go->neg_name,   go->name);
751	if (len > 0)
752		ACKCIPROTO    (IPX_ROUTER_PROTOCOL, go->neg_router, go->router);
753/*
754 * This is the end of the record.
755 */
756	if (len == 0)
757	    return (1);
758    } while (0);
759/*
760 * The frame is invalid
761 */
762    IPXCPDEBUG(("ipxcp_ackci: received bad Ack!"));
763    return (0);
764}
765
766/*
767 * ipxcp_nakci - Peer has sent a NAK for some of our CIs.
768 * This should not modify any state if the Nak is bad
769 * or if IPXCP is in the OPENED state.
770 *
771 * Returns:
772 *	0 - Nak was bad.
773 *	1 - Nak was good.
774 */
775
776static int
777ipxcp_nakci(f, p, len, treat_as_reject)
778    fsm *f;
779    u_char *p;
780    int len;
781    int treat_as_reject;
782{
783    u_char citype, cilen, *next;
784    u_short s;
785    u_int32_t l;
786    ipxcp_options no;		/* options we've seen Naks for */
787    ipxcp_options try;		/* options to request next time */
788
789    BZERO(&no, sizeof(no));
790    try = *go;
791
792    while (len >= CILEN_VOID) {
793	GETCHAR (citype, p);
794	GETCHAR (cilen,	 p);
795	len -= cilen;
796	if (cilen < CILEN_VOID || len < 0)
797	    goto bad;
798	next = &p [cilen - CILEN_VOID];
799
800	switch (citype) {
801	case IPX_NETWORK_NUMBER:
802	    if (!go->neg_nn || no.neg_nn || (cilen != CILEN_NETN))
803		goto bad;
804	    no.neg_nn = 1;
805
806	    GETLONG(l, p);
807	    if (treat_as_reject)
808		try.neg_nn = 0;
809	    else if (l && ao->accept_network)
810		try.our_network = l;
811	    break;
812
813	case IPX_NODE_NUMBER:
814	    if (!go->neg_node || no.neg_node || (cilen != CILEN_NODEN))
815		goto bad;
816	    no.neg_node = 1;
817
818	    if (treat_as_reject)
819		try.neg_node = 0;
820	    else if (!zero_node (p) && ao->accept_local &&
821		     ! compare_node (p, ho->his_node))
822		copy_node (p, try.our_node);
823	    break;
824
825	    /* This has never been sent. Ignore the NAK frame */
826	case IPX_COMPRESSION_PROTOCOL:
827	    goto bad;
828
829	case IPX_ROUTER_PROTOCOL:
830	    if (!go->neg_router || (cilen < CILEN_PROTOCOL))
831		goto bad;
832
833	    GETSHORT (s, p);
834	    if (s > 15)         /* This is just bad, but ignore for now. */
835	        break;
836
837	    s = BIT(s);
838	    if (no.router & s)  /* duplicate NAKs are always bad */
839		goto bad;
840
841	    if (no.router == 0) /* Reset on first NAK only */
842		try.router = 0;
843
844	    no.router      |= s;
845	    try.router     |= s;
846	    try.neg_router  = 1;
847	    break;
848
849	    /* These, according to the RFC, must never be NAKed. */
850	case IPX_ROUTER_NAME:
851	case IPX_COMPLETE:
852	    goto bad;
853
854	    /* These are for options which we have not seen. */
855	default:
856	    break;
857	}
858	p = next;
859    }
860
861    /*
862     * Do not permit the peer to force a router protocol which we do not
863     * support. However, default to the condition that will accept "NONE".
864     */
865    try.router &= (ao->router | BIT(IPX_NONE));
866    if (try.router == 0 && ao->router != 0)
867	try.router = BIT(IPX_NONE);
868
869    if (try.router != 0)
870        try.neg_router = 1;
871
872    /*
873     * OK, the Nak is good.  Now we can update state.
874     * If there are any options left, we ignore them.
875     */
876    if (f->state != OPENED)
877	*go = try;
878
879    return 1;
880
881bad:
882    IPXCPDEBUG(("ipxcp_nakci: received bad Nak!"));
883    return 0;
884}
885
886/*
887 * ipxcp_rejci - Reject some of our CIs.
888 */
889static int
890ipxcp_rejci(f, p, len)
891    fsm *f;
892    u_char *p;
893    int len;
894{
895    u_short cilen, citype, cishort;
896    u_char cichar;
897    u_int32_t cilong;
898    ipxcp_options try;		/* options to request next time */
899
900#define REJCINETWORK(opt, neg, val) \
901    if (neg && p[0] == opt) { \
902	if ((len -= CILEN_NETN) < 0) \
903	    break; \
904	GETCHAR(citype, p); \
905	GETCHAR(cilen, p); \
906	if (cilen != CILEN_NETN || \
907	    citype != opt) \
908	    break; \
909	GETLONG(cilong, p); \
910	if (cilong != val) \
911	    break; \
912	neg = 0; \
913    }
914
915#define REJCICHARS(opt, neg, val, cnt) \
916    if (neg && p[0] == opt) { \
917	int indx, count = cnt; \
918	len -= (count + 2); \
919	if (len < 0) \
920	    break; \
921	GETCHAR(citype, p); \
922	GETCHAR(cilen, p); \
923	if (cilen != (count + 2) || \
924	    citype != opt) \
925	    break; \
926	for (indx = 0; indx < count; ++indx) {\
927	    GETCHAR(cichar, p); \
928	    if (cichar != ((u_char *) &val)[indx]) \
929	       break; \
930	}\
931	if (indx != count) \
932	    break; \
933	neg = 0; \
934    }
935
936#define REJCINODE(opt,neg,val) REJCICHARS(opt,neg,val,sizeof(val))
937#define REJCINAME(opt,neg,val) REJCICHARS(opt,neg,val,strlen(val))
938
939#define REJCIVOID(opt, neg) \
940    if (neg && p[0] == opt) { \
941	if ((len -= CILEN_VOID) < 0) \
942	    break; \
943	GETCHAR(citype, p); \
944	GETCHAR(cilen, p); \
945	if (cilen != CILEN_VOID || citype != opt) \
946	    break; \
947	neg = 0; \
948    }
949
950/* a reject for RIP/SAP is invalid since we don't send it and you can't
951   reject something which is not sent. (You can NAK, but you can't REJ.) */
952#define REJCIPROTO(opt, neg, val, bit) \
953    if (neg && p[0] == opt) { \
954	if ((len -= CILEN_PROTOCOL) < 0) \
955	    break; \
956	GETCHAR(citype, p); \
957	GETCHAR(cilen, p); \
958	if (cilen != CILEN_PROTOCOL) \
959	    break; \
960	GETSHORT(cishort, p); \
961	if (cishort != to_external (val) || cishort == RIP_SAP) \
962	    break; \
963	neg = 0; \
964    }
965/*
966 * Any Rejected CIs must be in exactly the same order that we sent.
967 * Check packet length and CI length at each step.
968 * If we find any deviations, then this packet is bad.
969 */
970    try = *go;
971
972    do {
973	REJCINETWORK (IPX_NETWORK_NUMBER,  try.neg_nn,	   try.our_network);
974	REJCINODE    (IPX_NODE_NUMBER,	   try.neg_node,   try.our_node);
975	REJCINAME    (IPX_ROUTER_NAME,	   try.neg_name,   try.name);
976	REJCIPROTO   (IPX_ROUTER_PROTOCOL, try.neg_router, try.router, 0);
977/*
978 * This is the end of the record.
979 */
980	if (len == 0) {
981	    if (f->state != OPENED)
982		*go = try;
983	    return (1);
984	}
985    } while (0);
986/*
987 * The frame is invalid at this point.
988 */
989    IPXCPDEBUG(("ipxcp_rejci: received bad Reject!"));
990    return 0;
991}
992
993/*
994 * ipxcp_reqci - Check the peer's requested CIs and send appropriate response.
995 *
996 * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
997 * appropriately.  If reject_if_disagree is non-zero, doesn't return
998 * CONFNAK; returns CONFREJ if it can't return CONFACK.
999 */
1000static int
1001ipxcp_reqci(f, inp, len, reject_if_disagree)
1002    fsm *f;
1003    u_char *inp;		/* Requested CIs */
1004    int *len;			/* Length of requested CIs */
1005    int reject_if_disagree;
1006{
1007    u_char *cip, *next;		/* Pointer to current and next CIs */
1008    u_short cilen, citype;	/* Parsed len, type */
1009    u_short cishort;		/* Parsed short value */
1010    u_int32_t cinetwork;	/* Parsed address values */
1011    int rc = CONFACK;		/* Final packet return code */
1012    int orc;			/* Individual option return code */
1013    u_char *p;			/* Pointer to next char to parse */
1014    u_char *ucp = inp;		/* Pointer to current output char */
1015    int l = *len;		/* Length left */
1016
1017    /*
1018     * Reset all his options.
1019     */
1020    BZERO(ho, sizeof(*ho));
1021
1022    /*
1023     * Process all his options.
1024     */
1025    next = inp;
1026    while (l) {
1027	orc = CONFACK;			/* Assume success */
1028	cip = p = next;			/* Remember begining of CI */
1029	if (l < 2 ||			/* Not enough data for CI header or */
1030	    p[1] < 2 ||			/*  CI length too small or */
1031	    p[1] > l) {			/*  CI length too big? */
1032	    IPXCPDEBUG(("ipxcp_reqci: bad CI length!"));
1033	    orc = CONFREJ;		/* Reject bad CI */
1034	    cilen = l;			/* Reject till end of packet */
1035	    l = 0;			/* Don't loop again */
1036	    goto endswitch;
1037	}
1038	GETCHAR(citype, p);		/* Parse CI type */
1039	GETCHAR(cilen, p);		/* Parse CI length */
1040	l -= cilen;			/* Adjust remaining length */
1041	next += cilen;			/* Step to next CI */
1042
1043	switch (citype) {		/* Check CI type */
1044/*
1045 * The network number must match. Choose the larger of the two.
1046 */
1047	case IPX_NETWORK_NUMBER:
1048	    /* if we wont negotiate the network number or the length is wrong
1049	       then reject the option */
1050	    if ( !ao->neg_nn || cilen != CILEN_NETN ) {
1051		orc = CONFREJ;
1052		break;
1053	    }
1054	    GETLONG(cinetwork, p);
1055
1056	    /* If the network numbers match then acknowledge them. */
1057	    if (cinetwork != 0) {
1058		ho->his_network = cinetwork;
1059		ho->neg_nn	= 1;
1060		if (wo->our_network == cinetwork)
1061		    break;
1062/*
1063 * If the network number is not given or we don't accept their change or
1064 * the network number is too small then NAK it.
1065 */
1066		if (! ao->accept_network || cinetwork < wo->our_network) {
1067		    DECPTR (sizeof (u_int32_t), p);
1068		    PUTLONG (wo->our_network, p);
1069		    orc = CONFNAK;
1070		}
1071		break;
1072	    }
1073/*
1074 * The peer sent '0' for the network. Give it ours if we have one.
1075 */
1076	    if (go->our_network != 0) {
1077		DECPTR (sizeof (u_int32_t), p);
1078		PUTLONG (wo->our_network, p);
1079		orc = CONFNAK;
1080/*
1081 * We don't have one. Reject the value.
1082 */
1083	    } else
1084		orc = CONFREJ;
1085
1086	    break;
1087/*
1088 * The node number is required
1089 */
1090	case IPX_NODE_NUMBER:
1091	    /* if we wont negotiate the node number or the length is wrong
1092	       then reject the option */
1093	    if ( cilen != CILEN_NODEN ) {
1094		orc = CONFREJ;
1095		break;
1096	    }
1097
1098	    copy_node (p, ho->his_node);
1099	    ho->neg_node = 1;
1100/*
1101 * If the remote does not have a number and we do then NAK it with the value
1102 * which we have for it. (We never have a default value of zero.)
1103 */
1104	    if (zero_node (ho->his_node)) {
1105		orc = CONFNAK;
1106		copy_node (wo->his_node, p);
1107		INCPTR (sizeof (wo->his_node), p);
1108		break;
1109	    }
1110/*
1111 * If you have given me the expected network node number then I'll accept
1112 * it now.
1113 */
1114	    if (compare_node (wo->his_node, ho->his_node)) {
1115		orc = CONFACK;
1116		ho->neg_node = 1;
1117		INCPTR (sizeof (wo->his_node), p);
1118		break;
1119	    }
1120/*
1121 * If his node number is the same as ours then ask him to try the next
1122 * value.
1123 */
1124	    if (compare_node (ho->his_node, go->our_node)) {
1125		inc_node (ho->his_node);
1126		orc = CONFNAK;
1127		copy_node (ho->his_node, p);
1128		INCPTR (sizeof (wo->his_node), p);
1129		break;
1130	    }
1131/*
1132 * If we don't accept a new value then NAK it.
1133 */
1134	    if (! ao->accept_remote) {
1135		copy_node (wo->his_node, p);
1136		INCPTR (sizeof (wo->his_node), p);
1137		orc = CONFNAK;
1138		break;
1139	    }
1140	    orc = CONFACK;
1141	    ho->neg_node = 1;
1142	    INCPTR (sizeof (wo->his_node), p);
1143	    break;
1144/*
1145 * Compression is not desired at this time. It is always rejected.
1146 */
1147	case IPX_COMPRESSION_PROTOCOL:
1148	    orc = CONFREJ;
1149	    break;
1150/*
1151 * The routing protocol is a bitmask of various types. Any combination
1152 * of the values RIP_SAP and NLSP are permissible. 'IPX_NONE' for no
1153 * routing protocol must be specified only once.
1154 */
1155	case IPX_ROUTER_PROTOCOL:
1156	    if ( !ao->neg_router || cilen < CILEN_PROTOCOL ) {
1157		orc = CONFREJ;
1158		break;
1159	    }
1160
1161	    GETSHORT (cishort, p);
1162
1163	    if (wo->neg_router == 0) {
1164	        wo->neg_router = 1;
1165		wo->router     = BIT(IPX_NONE);
1166	    }
1167
1168	    if ((cishort == IPX_NONE && ho->router != 0) ||
1169		(ho->router & BIT(IPX_NONE))) {
1170		orc = CONFREJ;
1171		break;
1172	    }
1173
1174	    cishort = BIT(cishort);
1175	    if (ho->router & cishort) {
1176		orc = CONFREJ;
1177		break;
1178	    }
1179
1180	    ho->router	  |= cishort;
1181	    ho->neg_router = 1;
1182
1183	    /* Finally do not allow a router protocol which we do not
1184	       support. */
1185
1186	    if ((cishort & (ao->router | BIT(IPX_NONE))) == 0) {
1187	        int protocol;
1188
1189		if (cishort == BIT(NLSP) &&
1190		    (ao->router & BIT(RIP_SAP)) &&
1191		    !wo->tried_rip) {
1192		    protocol      = RIP_SAP;
1193		    wo->tried_rip = 1;
1194		} else
1195		    protocol = IPX_NONE;
1196
1197		DECPTR (sizeof (u_int16_t), p);
1198		PUTSHORT (protocol, p);
1199		orc = CONFNAK;
1200	    }
1201	    break;
1202/*
1203 * The router name is advisorary. Just accept it if it is not too large.
1204 */
1205	case IPX_ROUTER_NAME:
1206	    if (cilen >= CILEN_NAME) {
1207		int name_size = cilen - CILEN_NAME;
1208		if (name_size > sizeof (ho->name))
1209		    name_size = sizeof (ho->name) - 1;
1210		memset (ho->name, 0, sizeof (ho->name));
1211		memcpy (ho->name, p, name_size);
1212		ho->name [name_size] = '\0';
1213		ho->neg_name = 1;
1214		orc = CONFACK;
1215		break;
1216	    }
1217	    orc = CONFREJ;
1218	    break;
1219/*
1220 * This is advisorary.
1221 */
1222	case IPX_COMPLETE:
1223	    if (cilen != CILEN_COMPLETE)
1224		orc = CONFREJ;
1225	    else {
1226		ho->neg_complete = 1;
1227		orc = CONFACK;
1228	    }
1229	    break;
1230/*
1231 * All other entries are not known at this time.
1232 */
1233	default:
1234	    orc = CONFREJ;
1235	    break;
1236	}
1237endswitch:
1238	if (orc == CONFACK &&		/* Good CI */
1239	    rc != CONFACK)		/*  but prior CI wasnt? */
1240	    continue;			/* Don't send this one */
1241
1242	if (orc == CONFNAK) {		/* Nak this CI? */
1243	    if (reject_if_disagree)	/* Getting fed up with sending NAKs? */
1244		orc = CONFREJ;		/* Get tough if so */
1245	    if (rc == CONFREJ)		/* Rejecting prior CI? */
1246		continue;		/* Don't send this one */
1247	    if (rc == CONFACK) {	/* Ack'd all prior CIs? */
1248		rc  = CONFNAK;		/* Not anymore... */
1249		ucp = inp;		/* Backup */
1250	    }
1251	}
1252
1253	if (orc == CONFREJ &&		/* Reject this CI */
1254	    rc != CONFREJ) {		/*  but no prior ones? */
1255	    rc = CONFREJ;
1256	    ucp = inp;			/* Backup */
1257	}
1258
1259	/* Need to move CI? */
1260	if (ucp != cip)
1261	    BCOPY(cip, ucp, cilen);	/* Move it */
1262
1263	/* Update output pointer */
1264	INCPTR(cilen, ucp);
1265    }
1266
1267    /*
1268     * If we aren't rejecting this packet, and we want to negotiate
1269     * their address, and they didn't send their address, then we
1270     * send a NAK with a IPX_NODE_NUMBER option appended. We assume the
1271     * input buffer is long enough that we can append the extra
1272     * option safely.
1273     */
1274
1275    if (rc != CONFREJ && !ho->neg_node &&
1276	wo->req_nn && !reject_if_disagree) {
1277	if (rc == CONFACK) {
1278	    rc = CONFNAK;
1279	    wo->req_nn = 0;		/* don't ask again */
1280	    ucp = inp;			/* reset pointer */
1281	}
1282
1283	if (zero_node (wo->his_node))
1284	    inc_node (wo->his_node);
1285
1286	PUTCHAR (IPX_NODE_NUMBER, ucp);
1287	PUTCHAR (CILEN_NODEN, ucp);
1288	copy_node (wo->his_node, ucp);
1289	INCPTR (sizeof (wo->his_node), ucp);
1290    }
1291
1292    *len = ucp - inp;			/* Compute output length */
1293    IPXCPDEBUG(("ipxcp: returning Configure-%s", CODENAME(rc)));
1294    return (rc);			/* Return final code */
1295}
1296
1297/*
1298 * ipxcp_up - IPXCP has come UP.
1299 *
1300 * Configure the IP network interface appropriately and bring it up.
1301 */
1302
1303static void
1304ipxcp_up(f)
1305    fsm *f;
1306{
1307    int unit = f->unit;
1308
1309    IPXCPDEBUG(("ipxcp: up"));
1310
1311    /* The default router protocol is RIP/SAP. */
1312    if (ho->router == 0)
1313        ho->router = BIT(RIP_SAP);
1314
1315    if (go->router == 0)
1316        go->router = BIT(RIP_SAP);
1317
1318    /* Fetch the network number */
1319    if (!ho->neg_nn)
1320	ho->his_network = wo->his_network;
1321
1322    if (!ho->neg_node)
1323	copy_node (wo->his_node, ho->his_node);
1324
1325    if (!wo->neg_node && !go->neg_node)
1326	copy_node (wo->our_node, go->our_node);
1327
1328    if (zero_node (go->our_node)) {
1329        static char errmsg[] = "Could not determine local IPX node address";
1330	if (debug)
1331	    error(errmsg);
1332	ipxcp_close(f->unit, errmsg);
1333	return;
1334    }
1335
1336    go->network = go->our_network;
1337    if (ho->his_network != 0 && ho->his_network > go->network)
1338	go->network = ho->his_network;
1339
1340    if (go->network == 0) {
1341        static char errmsg[] = "Can not determine network number";
1342	if (debug)
1343	    error(errmsg);
1344	ipxcp_close (unit, errmsg);
1345	return;
1346    }
1347
1348    /* bring the interface up */
1349    if (!sifup(unit)) {
1350	if (debug)
1351	    warn("sifup failed (IPX)");
1352	ipxcp_close(unit, "Interface configuration failed");
1353	return;
1354    }
1355    ipxcp_is_up = 1;
1356
1357    /* set the network number for IPX */
1358    if (!sipxfaddr(unit, go->network, go->our_node)) {
1359	if (debug)
1360	    warn("sipxfaddr failed");
1361	ipxcp_close(unit, "Interface configuration failed");
1362	return;
1363    }
1364
1365    np_up(f->unit, PPP_IPX);
1366
1367    /*
1368     * Execute the ipx-up script, like this:
1369     *	/etc/ppp/ipx-up interface tty speed local-IPX remote-IPX
1370     */
1371
1372    ipxcp_script (f, _PATH_IPXUP);
1373}
1374
1375/*
1376 * ipxcp_down - IPXCP has gone DOWN.
1377 *
1378 * Take the IP network interface down, clear its addresses
1379 * and delete routes through it.
1380 */
1381
1382static void
1383ipxcp_down(f)
1384    fsm *f;
1385{
1386    IPXCPDEBUG(("ipxcp: down"));
1387
1388    if (!ipxcp_is_up)
1389	return;
1390    ipxcp_is_up = 0;
1391    np_down(f->unit, PPP_IPX);
1392    cipxfaddr(f->unit);
1393    sifnpmode(f->unit, PPP_IPX, NPMODE_DROP);
1394    sifdown(f->unit);
1395    ipxcp_script (f, _PATH_IPXDOWN);
1396}
1397
1398
1399/*
1400 * ipxcp_finished - possibly shut down the lower layers.
1401 */
1402static void
1403ipxcp_finished(f)
1404    fsm *f;
1405{
1406    np_finished(f->unit, PPP_IPX);
1407}
1408
1409
1410/*
1411 * ipxcp_script - Execute a script with arguments
1412 * interface-name tty-name speed local-IPX remote-IPX networks.
1413 */
1414static void
1415ipxcp_script(f, script)
1416    fsm *f;
1417    char *script;
1418{
1419    char strspeed[32],	 strlocal[32],	   strremote[32];
1420    char strnetwork[32], strpid[32];
1421    char *argv[14],	 strproto_lcl[32], strproto_rmt[32];
1422
1423    slprintf(strpid, sizeof(strpid), "%d", getpid());
1424    slprintf(strspeed, sizeof(strspeed),"%d", baud_rate);
1425
1426    strproto_lcl[0] = '\0';
1427    if (go->neg_router && ((go->router & BIT(IPX_NONE)) == 0)) {
1428	if (go->router & BIT(RIP_SAP))
1429	    strlcpy (strproto_lcl, "RIP ", sizeof(strproto_lcl));
1430	if (go->router & BIT(NLSP))
1431	    strlcat (strproto_lcl, "NLSP ", sizeof(strproto_lcl));
1432    }
1433
1434    if (strproto_lcl[0] == '\0')
1435	strlcpy (strproto_lcl, "NONE ", sizeof(strproto_lcl));
1436
1437    strproto_lcl[strlen (strproto_lcl)-1] = '\0';
1438
1439    strproto_rmt[0] = '\0';
1440    if (ho->neg_router && ((ho->router & BIT(IPX_NONE)) == 0)) {
1441	if (ho->router & BIT(RIP_SAP))
1442	    strlcpy (strproto_rmt, "RIP ", sizeof(strproto_rmt));
1443	if (ho->router & BIT(NLSP))
1444	    strlcat (strproto_rmt, "NLSP ", sizeof(strproto_rmt));
1445    }
1446
1447    if (strproto_rmt[0] == '\0')
1448	strlcpy (strproto_rmt, "NONE ", sizeof(strproto_rmt));
1449
1450    strproto_rmt[strlen (strproto_rmt)-1] = '\0';
1451
1452    strlcpy (strnetwork, ipx_ntoa (go->network), sizeof(strnetwork));
1453
1454    slprintf (strlocal, sizeof(strlocal), "%0.6B", go->our_node);
1455
1456    slprintf (strremote, sizeof(strremote), "%0.6B", ho->his_node);
1457
1458    argv[0]  = script;
1459    argv[1]  = ifname;
1460    argv[2]  = devnam;
1461    argv[3]  = strspeed;
1462    argv[4]  = strnetwork;
1463    argv[5]  = strlocal;
1464    argv[6]  = strremote;
1465    argv[7]  = strproto_lcl;
1466    argv[8]  = strproto_rmt;
1467    argv[9]  = go->name;
1468    argv[10] = ho->name;
1469    argv[11] = ipparam;
1470    argv[12] = strpid;
1471    argv[13] = NULL;
1472    run_program(script, argv, 0, NULL, NULL, 0);
1473}
1474
1475/*
1476 * ipxcp_printpkt - print the contents of an IPXCP packet.
1477 */
1478static char *ipxcp_codenames[] = {
1479    "ConfReq", "ConfAck", "ConfNak", "ConfRej",
1480    "TermReq", "TermAck", "CodeRej"
1481};
1482
1483static int
1484ipxcp_printpkt(p, plen, printer, arg)
1485    u_char *p;
1486    int plen;
1487    void (*printer) __P((void *, char *, ...));
1488    void *arg;
1489{
1490    int code, id, len, olen;
1491    u_char *pstart, *optend;
1492    u_short cishort;
1493    u_int32_t cilong;
1494
1495    if (plen < HEADERLEN)
1496	return 0;
1497    pstart = p;
1498    GETCHAR(code, p);
1499    GETCHAR(id, p);
1500    GETSHORT(len, p);
1501    if (len < HEADERLEN || len > plen)
1502	return 0;
1503
1504    if (code >= 1 && code <= sizeof(ipxcp_codenames) / sizeof(char *))
1505	printer(arg, " %s", ipxcp_codenames[code-1]);
1506    else
1507	printer(arg, " code=0x%x", code);
1508    printer(arg, " id=0x%x", id);
1509    len -= HEADERLEN;
1510    switch (code) {
1511    case CONFREQ:
1512    case CONFACK:
1513    case CONFNAK:
1514    case CONFREJ:
1515	/* print option list */
1516	while (len >= 2) {
1517	    GETCHAR(code, p);
1518	    GETCHAR(olen, p);
1519	    p -= 2;
1520	    if (olen < CILEN_VOID || olen > len) {
1521		break;
1522	    }
1523	    printer(arg, " <");
1524	    len -= olen;
1525	    optend = p + olen;
1526	    switch (code) {
1527	    case IPX_NETWORK_NUMBER:
1528		if (olen == CILEN_NETN) {
1529		    p += 2;
1530		    GETLONG(cilong, p);
1531		    printer (arg, "network %s", ipx_ntoa (cilong));
1532		}
1533		break;
1534	    case IPX_NODE_NUMBER:
1535		if (olen == CILEN_NODEN) {
1536		    p += 2;
1537		    printer (arg, "node ");
1538		    while (p < optend) {
1539			GETCHAR(code, p);
1540			printer(arg, "%.2x", (int) (unsigned int) (unsigned char) code);
1541		    }
1542		}
1543		break;
1544	    case IPX_COMPRESSION_PROTOCOL:
1545		if (olen == CILEN_COMPRESS) {
1546		    p += 2;
1547		    GETSHORT (cishort, p);
1548		    printer (arg, "compression %d", (int) cishort);
1549		}
1550		break;
1551	    case IPX_ROUTER_PROTOCOL:
1552		if (olen == CILEN_PROTOCOL) {
1553		    p += 2;
1554		    GETSHORT (cishort, p);
1555		    printer (arg, "router proto %d", (int) cishort);
1556		}
1557		break;
1558	    case IPX_ROUTER_NAME:
1559		if (olen >= CILEN_NAME) {
1560		    p += 2;
1561		    printer (arg, "router name \"");
1562		    while (p < optend) {
1563			GETCHAR(code, p);
1564			if (code >= 0x20 && code <= 0x7E)
1565			    printer (arg, "%c", (int) (unsigned int) (unsigned char) code);
1566			else
1567			    printer (arg, " \\%.2x", (int) (unsigned int) (unsigned char) code);
1568		    }
1569		    printer (arg, "\"");
1570		}
1571		break;
1572	    case IPX_COMPLETE:
1573		if (olen == CILEN_COMPLETE) {
1574		    p += 2;
1575		    printer (arg, "complete");
1576		}
1577		break;
1578	    default:
1579		break;
1580	    }
1581
1582	    while (p < optend) {
1583		GETCHAR(code, p);
1584		printer(arg, " %.2x", (int) (unsigned int) (unsigned char) code);
1585	    }
1586	    printer(arg, ">");
1587	}
1588	break;
1589
1590    case TERMACK:
1591    case TERMREQ:
1592	if (len > 0 && *p >= ' ' && *p < 0x7f) {
1593	    printer(arg, " ");
1594	    print_string(p, len, printer, arg);
1595	    p += len;
1596	    len = 0;
1597	}
1598	break;
1599    }
1600
1601    /* print the rest of the bytes in the packet */
1602    for (; len > 0; --len) {
1603	GETCHAR(code, p);
1604	printer(arg, " %.2x", (int) (unsigned int) (unsigned char) code);
1605    }
1606
1607    return p - pstart;
1608}
1609#endif /* ifdef IPX_CHANGE */
1610