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