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