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