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