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