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