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