]> git.ozlabs.org Git - ppp.git/blob - pppd/ipv6cp.c
Fix the rcsid's for non-ansi compilers
[ppp.git] / pppd / ipv6cp.c
1 /*
2  * ipv6cp.c - PPP IPV6 Control Protocol.
3  *
4  * Derived from :
5  *
6  *
7  * ipcp.c - PPP IP Control Protocol.
8  *
9  * Copyright (c) 1989 Carnegie Mellon University.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms are permitted
13  * provided that the above copyright notice and this paragraph are
14  * duplicated in all such forms and that any documentation,
15  * advertising materials, and other materials related to such
16  * distribution and use acknowledge that the software was developed
17  * by Carnegie Mellon University.  The name of the
18  * University may not be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
22  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23  *
24  * $Id: ipv6cp.c,v 1.2 1999/08/13 06:46:13 paulus Exp $ 
25  *
26  *
27  * Original version by Inria (www.inria.fr)
28  * Modified to match RFC2472 by Tommi Komulainen <Tommi.Komulainen@iki.fi>
29  */
30
31 #define RCSID   "$Id: ipv6cp.c,v 1.2 1999/08/13 06:46:13 paulus Exp $"
32
33 /*
34  * TODO: 
35  *
36  * Better defines for selecting the ordering of
37  *   interface up / set address. (currently checks for __linux__,
38  *   since SVR4 && (SNI || __USLC__) didn't work properly)
39  */
40
41 #include <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <netdb.h>
45 #include <sys/param.h>
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49
50 #include "pppd.h"
51 #include "fsm.h"
52 #include "ipcp.h"
53 #include "ipv6cp.h"
54 #include "magic.h"
55 #include "pathnames.h"
56
57 static const char rcsid[] = RCSID;
58
59 /* global vars */
60 ipv6cp_options ipv6cp_wantoptions[NUM_PPP];     /* Options that we want to request */
61 ipv6cp_options ipv6cp_gotoptions[NUM_PPP];      /* Options that peer ack'd */
62 ipv6cp_options ipv6cp_allowoptions[NUM_PPP];    /* Options we allow peer to request */
63 ipv6cp_options ipv6cp_hisoptions[NUM_PPP];      /* Options that we ack'd */
64 int no_ifaceid_neg = 0;
65
66 /* local vars */
67 static int ipv6cp_is_up;
68
69 /*
70  * Callbacks for fsm code.  (CI = Configuration Information)
71  */
72 static void ipv6cp_resetci __P((fsm *));        /* Reset our CI */
73 static int  ipv6cp_cilen __P((fsm *));          /* Return length of our CI */
74 static void ipv6cp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
75 static int  ipv6cp_ackci __P((fsm *, u_char *, int));   /* Peer ack'd our CI */
76 static int  ipv6cp_nakci __P((fsm *, u_char *, int));   /* Peer nak'd our CI */
77 static int  ipv6cp_rejci __P((fsm *, u_char *, int));   /* Peer rej'd our CI */
78 static int  ipv6cp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
79 static void ipv6cp_up __P((fsm *));             /* We're UP */
80 static void ipv6cp_down __P((fsm *));           /* We're DOWN */
81 static void ipv6cp_finished __P((fsm *));       /* Don't need lower layer */
82
83 fsm ipv6cp_fsm[NUM_PPP];                /* IPV6CP fsm structure */
84
85 static fsm_callbacks ipv6cp_callbacks = { /* IPV6CP callback routines */
86     ipv6cp_resetci,             /* Reset our Configuration Information */
87     ipv6cp_cilen,               /* Length of our Configuration Information */
88     ipv6cp_addci,               /* Add our Configuration Information */
89     ipv6cp_ackci,               /* ACK our Configuration Information */
90     ipv6cp_nakci,               /* NAK our Configuration Information */
91     ipv6cp_rejci,               /* Reject our Configuration Information */
92     ipv6cp_reqci,               /* Request peer's Configuration Information */
93     ipv6cp_up,                  /* Called when fsm reaches OPENED state */
94     ipv6cp_down,                /* Called when fsm leaves OPENED state */
95     NULL,                       /* Called when we want the lower layer up */
96     ipv6cp_finished,            /* Called when we want the lower layer down */
97     NULL,                       /* Called when Protocol-Reject received */
98     NULL,                       /* Retransmission is necessary */
99     NULL,                       /* Called to handle protocol-specific codes */
100     "IPV6CP"                    /* String name of protocol */
101 };
102
103 /*
104  * Command-line options.
105  */
106 static int setifaceid __P((char *arg));
107
108 static option_t ipv6cp_option_list[] = {
109     { "ipv6", o_special, setifaceid,
110       "Set interface identifiers for IPV6" },
111     { "noipv6", o_bool, &ipv6cp_protent.enabled_flag,
112       "Disable IPv6 and IPv6CP" },
113     { "-ipv6", o_bool, &ipv6cp_protent.enabled_flag,
114       "Disable IPv6 and IPv6CP" },
115
116     { "ipv6cp-accept-local", o_bool, &ipv6cp_allowoptions[0].accept_local,
117       "Accept peer's interface identifier for us", 1 },
118     { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip,
119       "Use (default) IPv4 address as interface identifier", 0 },
120
121     { "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime,
122       "Set timeout for IPv6CP" },
123     { "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits,
124       "Set max #xmits for term-reqs" },
125     { "ipv6cp-max-configure", o_int, &ipv6cp_fsm[0].maxconfreqtransmits,
126       "Set max #xmits for conf-reqs" },
127     { "ipv6cp-max-failure", o_int, &ipv6cp_fsm[0].maxnakloops,
128       "Set max #conf-naks for IPv6CP" },
129
130    { NULL }
131 };
132
133
134 /*
135  * Protocol entry points from main code.
136  */
137 static void ipv6cp_init __P((int));
138 static void ipv6cp_open __P((int));
139 static void ipv6cp_close __P((int, char *));
140 static void ipv6cp_lowerup __P((int));
141 static void ipv6cp_lowerdown __P((int));
142 static void ipv6cp_input __P((int, u_char *, int));
143 static void ipv6cp_protrej __P((int));
144 static int  ipv6cp_printpkt __P((u_char *, int,
145                                void (*) __P((void *, char *, ...)), void *));
146 static void ipv6_check_options __P((void));
147 static int  ipv6_demand_conf __P((int));
148 static int  ipv6_active_pkt __P((u_char *, int));
149
150 struct protent ipv6cp_protent = {
151     PPP_IPV6CP,
152     ipv6cp_init,
153     ipv6cp_input,
154     ipv6cp_protrej,
155     ipv6cp_lowerup,
156     ipv6cp_lowerdown,
157     ipv6cp_open,
158     ipv6cp_close,
159     ipv6cp_printpkt,
160     NULL,
161     0,
162     "IPV6CP",
163     "IPV6",
164     ipv6cp_option_list,
165     ipv6_check_options,
166     ipv6_demand_conf,
167     ipv6_active_pkt
168 };
169
170 static void ipv6cp_clear_addrs __P((int));
171 static void ipv6cp_script __P((char *));
172 static void ipv6cp_script_done __P((void *));
173
174 /*
175  * Lengths of configuration options.
176  */
177 #define CILEN_VOID      2
178 #define CILEN_COMPRESS  4       /* length for RFC2023 compress opt. */
179 #define CILEN_IFACEID   10      /* RFC2472, interface identifier    */
180
181 #define CODENAME(x)     ((x) == CONFACK ? "ACK" : \
182                          (x) == CONFNAK ? "NAK" : "REJ")
183
184 /*
185  * This state variable is used to ensure that we don't
186  * run an ipcp-up/down script while one is already running.
187  */
188 static enum script_state {
189     s_down,
190     s_up,
191 } ipv6cp_script_state;
192 static pid_t ipv6cp_script_pid;
193
194 /*
195  * setifaceid - set the interface identifiers manually
196  */
197 static int
198 setifaceid(arg)
199     char *arg;
200 {
201     char *comma;
202     ipv6cp_options *wo = &ipv6cp_wantoptions[0];
203     struct in6_addr addr;
204     
205 #define VALIDID(a) (    ((a).s6_addr64[0] == 0) && \
206                         ((a).s6_addr64[1] != 0) )
207     
208     if ((comma = strchr(arg, ',')) == NULL)
209         comma = arg + strlen(arg);
210     
211     /* 
212      * If comma first character, then no local identifier
213      */
214     if (comma != arg) {
215         *comma = '\0';
216
217         if (inet_pton(AF_INET6, arg, &addr) == 0 || !VALIDID(addr)) {
218             option_error("Illegal interface identifier: %s", arg);
219             return 0;
220         }
221         
222         eui64_copy(addr.s6_addr32[2], wo->ourid);
223         wo->opt_local = 1;
224         *comma = ',';
225     }
226     
227     /*
228      * If comma last character, the no remote identifier
229      */
230     if (*comma != 0 && *++comma != '\0') {
231         if (inet_pton(AF_INET6, comma, &addr) == 0 || !VALIDID(addr)) {
232             option_error("Illegal interface identifier: %s", comma);
233             return 0;
234         }
235         eui64_copy(addr.s6_addr32[2], wo->hisid);
236         wo->opt_remote = 1;
237     }
238
239     return 1;
240 }
241
242 /*
243  * Make a string representation of a network address.
244  */
245 char *
246 llv6_ntoa(ifaceid)
247     eui64_t ifaceid;
248 {
249     static char b[64];
250
251     sprintf(b, "fe80::%s", eui64_ntoa(ifaceid));
252     return b;
253 }
254
255
256 /*
257  * ipv6cp_init - Initialize IPV6CP.
258  */
259 static void
260 ipv6cp_init(unit)
261     int unit;
262 {
263     fsm *f = &ipv6cp_fsm[unit];
264     ipv6cp_options *wo = &ipv6cp_wantoptions[unit];
265     ipv6cp_options *ao = &ipv6cp_allowoptions[unit];
266
267     f->unit = unit;
268     f->protocol = PPP_IPV6CP;
269     f->callbacks = &ipv6cp_callbacks;
270     fsm_init(&ipv6cp_fsm[unit]);
271
272     memset(wo, 0, sizeof(*wo));
273     memset(ao, 0, sizeof(*ao));
274
275     wo->accept_local = 1;
276     wo->neg_ifaceid = 1;
277     ao->neg_ifaceid = 1;
278
279 #ifdef IPV6CP_COMP
280     wo->neg_vj = 1;
281     ao->neg_vj = 1;
282     wo->vj_protocol = IPV6CP_COMP;
283 #endif
284
285 }
286
287
288 /*
289  * ipv6cp_open - IPV6CP is allowed to come up.
290  */
291 static void
292 ipv6cp_open(unit)
293     int unit;
294 {
295     fsm_open(&ipv6cp_fsm[unit]);
296 }
297
298
299 /*
300  * ipv6cp_close - Take IPV6CP down.
301  */
302 static void
303 ipv6cp_close(unit, reason)
304     int unit;
305     char *reason;
306 {
307     fsm_close(&ipv6cp_fsm[unit], reason);
308 }
309
310
311 /*
312  * ipv6cp_lowerup - The lower layer is up.
313  */
314 static void
315 ipv6cp_lowerup(unit)
316     int unit;
317 {
318     fsm_lowerup(&ipv6cp_fsm[unit]);
319 }
320
321
322 /*
323  * ipv6cp_lowerdown - The lower layer is down.
324  */
325 static void
326 ipv6cp_lowerdown(unit)
327     int unit;
328 {
329     fsm_lowerdown(&ipv6cp_fsm[unit]);
330 }
331
332
333 /*
334  * ipv6cp_input - Input IPV6CP packet.
335  */
336 static void
337 ipv6cp_input(unit, p, len)
338     int unit;
339     u_char *p;
340     int len;
341 {
342     fsm_input(&ipv6cp_fsm[unit], p, len);
343 }
344
345
346 /*
347  * ipv6cp_protrej - A Protocol-Reject was received for IPV6CP.
348  *
349  * Pretend the lower layer went down, so we shut up.
350  */
351 static void
352 ipv6cp_protrej(unit)
353     int unit;
354 {
355     fsm_lowerdown(&ipv6cp_fsm[unit]);
356 }
357
358
359 /*
360  * ipv6cp_resetci - Reset our CI.
361  */
362 static void
363 ipv6cp_resetci(f)
364     fsm *f;
365 {
366     ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
367     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
368
369     wo->req_ifaceid = wo->neg_ifaceid && ipv6cp_allowoptions[f->unit].neg_ifaceid;
370     
371     if (!wo->opt_local) {
372         eui64_magic_nz(wo->ourid);
373     }
374     
375     *go = *wo;
376     eui64_zero(go->hisid);      /* last proposed interface identifier */
377 }
378
379
380 /*
381  * ipv6cp_cilen - Return length of our CI.
382  */
383 static int
384 ipv6cp_cilen(f)
385     fsm *f;
386 {
387     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
388
389 #define LENCIVJ(neg)            (neg ? CILEN_COMPRESS : 0)
390 #define LENCIIFACEID(neg)       (neg ? CILEN_IFACEID : 0)
391
392     return (LENCIIFACEID(go->neg_ifaceid) +
393             LENCIVJ(go->neg_vj));
394 }
395
396
397 /*
398  * ipv6cp_addci - Add our desired CIs to a packet.
399  */
400 static void
401 ipv6cp_addci(f, ucp, lenp)
402     fsm *f;
403     u_char *ucp;
404     int *lenp;
405 {
406     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
407     int len = *lenp;
408
409 #define ADDCIVJ(opt, neg, val) \
410     if (neg) { \
411         int vjlen = CILEN_COMPRESS; \
412         if (len >= vjlen) { \
413             PUTCHAR(opt, ucp); \
414             PUTCHAR(vjlen, ucp); \
415             PUTSHORT(val, ucp); \
416             len -= vjlen; \
417         } else \
418             neg = 0; \
419     }
420
421 #define ADDCIIFACEID(opt, neg, val1) \
422     if (neg) { \
423         int idlen = CILEN_IFACEID; \
424         if (len >= idlen) { \
425             PUTCHAR(opt, ucp); \
426             PUTCHAR(idlen, ucp); \
427             eui64_put(val1, ucp); \
428             len -= idlen; \
429         } else \
430             neg = 0; \
431     }
432
433     ADDCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
434
435     ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
436
437     *lenp -= len;
438 }
439
440
441 /*
442  * ipv6cp_ackci - Ack our CIs.
443  *
444  * Returns:
445  *      0 - Ack was bad.
446  *      1 - Ack was good.
447  */
448 static int
449 ipv6cp_ackci(f, p, len)
450     fsm *f;
451     u_char *p;
452     int len;
453 {
454     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
455     u_short cilen, citype, cishort;
456     eui64_t ifaceid;
457
458     /*
459      * CIs must be in exactly the same order that we sent...
460      * Check packet length and CI length at each step.
461      * If we find any deviations, then this packet is bad.
462      */
463
464 #define ACKCIVJ(opt, neg, val) \
465     if (neg) { \
466         int vjlen = CILEN_COMPRESS; \
467         if ((len -= vjlen) < 0) \
468             goto bad; \
469         GETCHAR(citype, p); \
470         GETCHAR(cilen, p); \
471         if (cilen != vjlen || \
472             citype != opt)  \
473             goto bad; \
474         GETSHORT(cishort, p); \
475         if (cishort != val) \
476             goto bad; \
477     }
478
479 #define ACKCIIFACEID(opt, neg, val1) \
480     if (neg) { \
481         int idlen = CILEN_IFACEID; \
482         if ((len -= idlen) < 0) \
483             goto bad; \
484         GETCHAR(citype, p); \
485         GETCHAR(cilen, p); \
486         if (cilen != idlen || \
487             citype != opt) \
488             goto bad; \
489         eui64_get(ifaceid, p); \
490         if (! eui64_equals(val1, ifaceid)) \
491             goto bad; \
492     }
493
494     ACKCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
495
496     ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
497
498     /*
499      * If there are any remaining CIs, then this packet is bad.
500      */
501     if (len != 0)
502         goto bad;
503     return (1);
504
505 bad:
506     IPV6CPDEBUG(("ipv6cp_ackci: received bad Ack!"));
507     return (0);
508 }
509
510 /*
511  * ipv6cp_nakci - Peer has sent a NAK for some of our CIs.
512  * This should not modify any state if the Nak is bad
513  * or if IPV6CP is in the OPENED state.
514  *
515  * Returns:
516  *      0 - Nak was bad.
517  *      1 - Nak was good.
518  */
519 static int
520 ipv6cp_nakci(f, p, len)
521     fsm *f;
522     u_char *p;
523     int len;
524 {
525     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
526     u_char citype, cilen, *next;
527     u_short cishort;
528     eui64_t ifaceid;
529     ipv6cp_options no;          /* options we've seen Naks for */
530     ipv6cp_options try;         /* options to request next time */
531
532     BZERO(&no, sizeof(no));
533     try = *go;
534
535     /*
536      * Any Nak'd CIs must be in exactly the same order that we sent.
537      * Check packet length and CI length at each step.
538      * If we find any deviations, then this packet is bad.
539      */
540 #define NAKCIIFACEID(opt, neg, code) \
541     if (go->neg && \
542         len >= (cilen = CILEN_IFACEID) && \
543         p[1] == cilen && \
544         p[0] == opt) { \
545         len -= cilen; \
546         INCPTR(2, p); \
547         eui64_get(ifaceid, p); \
548         no.neg = 1; \
549         code \
550     }
551
552 #define NAKCIVJ(opt, neg, code) \
553     if (go->neg && \
554         ((cilen = p[1]) == CILEN_COMPRESS) && \
555         len >= cilen && \
556         p[0] == opt) { \
557         len -= cilen; \
558         INCPTR(2, p); \
559         GETSHORT(cishort, p); \
560         no.neg = 1; \
561         code \
562     }
563
564     /*
565      * Accept the peer's idea of {our,his} interface identifier, if different
566      * from our idea, only if the accept_{local,remote} flag is set.
567      */
568     NAKCIIFACEID(CI_IFACEID, neg_ifaceid,
569               if (go->accept_local) {
570                   while (eui64_iszero(ifaceid) || 
571                          eui64_equals(ifaceid, go->hisid)) /* bad luck */
572                       eui64_magic(ifaceid);
573                   try.ourid = ifaceid;
574                   IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid)));
575               }
576               );
577
578 #ifdef IPV6CP_COMP
579     NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
580             {
581                 if (cishort == IPV6CP_COMP) {
582                     try.vj_protocol = cishort;
583                 } else {
584                     try.neg_vj = 0;
585                 }
586             }
587             );
588 #else
589     NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
590             {
591                 try.neg_vj = 0;
592             }
593             );
594 #endif
595
596     /*
597      * There may be remaining CIs, if the peer is requesting negotiation
598      * on an option that we didn't include in our request packet.
599      * If they want to negotiate about interface identifier, we comply.
600      * If they want us to ask for compression, we refuse.
601      */
602     while (len > CILEN_VOID) {
603         GETCHAR(citype, p);
604         GETCHAR(cilen, p);
605         if( (len -= cilen) < 0 )
606             goto bad;
607         next = p + cilen - 2;
608
609         switch (citype) {
610         case CI_COMPRESSTYPE:
611             if (go->neg_vj || no.neg_vj ||
612                 (cilen != CILEN_COMPRESS))
613                 goto bad;
614             no.neg_vj = 1;
615             break;
616         case CI_IFACEID:
617             if (go->neg_ifaceid || no.neg_ifaceid || cilen != CILEN_IFACEID)
618                 goto bad;
619             try.neg_ifaceid = 1;
620             eui64_get(ifaceid, p);
621             if (go->accept_local) {
622                 while (eui64_iszero(ifaceid) || 
623                        eui64_equals(ifaceid, go->hisid)) /* bad luck */
624                     eui64_magic(ifaceid);
625                 try.ourid = ifaceid;
626             }
627             no.neg_ifaceid = 1;
628             break;
629         }
630         p = next;
631     }
632
633     /* If there is still anything left, this packet is bad. */
634     if (len != 0)
635         goto bad;
636
637     /*
638      * OK, the Nak is good.  Now we can update state.
639      */
640     if (f->state != OPENED)
641         *go = try;
642
643     return 1;
644
645 bad:
646     IPV6CPDEBUG(("ipv6cp_nakci: received bad Nak!"));
647     return 0;
648 }
649
650
651 /*
652  * ipv6cp_rejci - Reject some of our CIs.
653  */
654 static int
655 ipv6cp_rejci(f, p, len)
656     fsm *f;
657     u_char *p;
658     int len;
659 {
660     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
661     u_char cilen;
662     u_short cishort;
663     eui64_t ifaceid;
664     ipv6cp_options try;         /* options to request next time */
665
666     try = *go;
667     /*
668      * Any Rejected CIs must be in exactly the same order that we sent.
669      * Check packet length and CI length at each step.
670      * If we find any deviations, then this packet is bad.
671      */
672 #define REJCIIFACEID(opt, neg, val1) \
673     if (go->neg && \
674         len >= (cilen = CILEN_IFACEID) && \
675         p[1] == cilen && \
676         p[0] == opt) { \
677         len -= cilen; \
678         INCPTR(2, p); \
679         eui64_get(ifaceid, p); \
680         /* Check rejected value. */ \
681         if (! eui64_equals(ifaceid, val1)) \
682             goto bad; \
683         try.neg = 0; \
684     }
685
686 #define REJCIVJ(opt, neg, val) \
687     if (go->neg && \
688         p[1] == CILEN_COMPRESS && \
689         len >= p[1] && \
690         p[0] == opt) { \
691         len -= p[1]; \
692         INCPTR(2, p); \
693         GETSHORT(cishort, p); \
694         /* Check rejected value. */  \
695         if (cishort != val) \
696             goto bad; \
697         try.neg = 0; \
698      }
699
700     REJCIIFACEID(CI_IFACEID, neg_ifaceid, go->ourid);
701
702     REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol);
703
704     /*
705      * If there are any remaining CIs, then this packet is bad.
706      */
707     if (len != 0)
708         goto bad;
709     /*
710      * Now we can update state.
711      */
712     if (f->state != OPENED)
713         *go = try;
714     return 1;
715
716 bad:
717     IPV6CPDEBUG(("ipv6cp_rejci: received bad Reject!"));
718     return 0;
719 }
720
721
722 /*
723  * ipv6cp_reqci - Check the peer's requested CIs and send appropriate response.
724  *
725  * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
726  * appropriately.  If reject_if_disagree is non-zero, doesn't return
727  * CONFNAK; returns CONFREJ if it can't return CONFACK.
728  */
729 static int
730 ipv6cp_reqci(f, inp, len, reject_if_disagree)
731     fsm *f;
732     u_char *inp;                /* Requested CIs */
733     int *len;                   /* Length of requested CIs */
734     int reject_if_disagree;
735 {
736     ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
737     ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
738     ipv6cp_options *ao = &ipv6cp_allowoptions[f->unit];
739     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
740     u_char *cip, *next;         /* Pointer to current and next CIs */
741     u_short cilen, citype;      /* Parsed len, type */
742     u_short cishort;            /* Parsed short value */
743     eui64_t ifaceid;            /* Parsed interface identifier */
744     int rc = CONFACK;           /* Final packet return code */
745     int orc;                    /* Individual option return code */
746     u_char *p;                  /* Pointer to next char to parse */
747     u_char *ucp = inp;          /* Pointer to current output char */
748     int l = *len;               /* Length left */
749
750     /*
751      * Reset all his options.
752      */
753     BZERO(ho, sizeof(*ho));
754     
755     /*
756      * Process all his options.
757      */
758     next = inp;
759     while (l) {
760         orc = CONFACK;                  /* Assume success */
761         cip = p = next;                 /* Remember begining of CI */
762         if (l < 2 ||                    /* Not enough data for CI header or */
763             p[1] < 2 ||                 /*  CI length too small or */
764             p[1] > l) {                 /*  CI length too big? */
765             IPV6CPDEBUG(("ipv6cp_reqci: bad CI length!"));
766             orc = CONFREJ;              /* Reject bad CI */
767             cilen = l;                  /* Reject till end of packet */
768             l = 0;                      /* Don't loop again */
769             goto endswitch;
770         }
771         GETCHAR(citype, p);             /* Parse CI type */
772         GETCHAR(cilen, p);              /* Parse CI length */
773         l -= cilen;                     /* Adjust remaining length */
774         next += cilen;                  /* Step to next CI */
775
776         switch (citype) {               /* Check CI type */
777         case CI_IFACEID:
778             IPV6CPDEBUG(("ipv6cp: received interface identifier "));
779
780             if (!ao->neg_ifaceid ||
781                 cilen != CILEN_IFACEID) {       /* Check CI length */
782                 orc = CONFREJ;          /* Reject CI */
783                 break;
784             }
785
786             /*
787              * If he has no interface identifier, or if we both have same 
788              * identifier then NAK it with new idea.
789              * In particular, if we don't know his identifier, but he does,
790              * then accept it.
791              */
792             eui64_get(ifaceid, p);
793             IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid)));
794             if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) {
795                 orc = CONFREJ;          /* Reject CI */
796                 break;
797             }
798             if (!eui64_iszero(wo->hisid) && 
799                 !eui64_equals(ifaceid, wo->hisid) && 
800                 eui64_iszero(go->hisid)) {
801                     
802                 orc = CONFNAK;
803                 ifaceid = wo->hisid;
804                 go->hisid = ifaceid;
805                 DECPTR(sizeof(ifaceid), p);
806                 eui64_put(ifaceid, p);
807             } else
808             if (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->ourid)) {
809                 orc = CONFNAK;
810                 if (eui64_iszero(go->hisid))    /* first time, try option */
811                     ifaceid = wo->hisid;
812                 while (eui64_iszero(ifaceid) || 
813                        eui64_equals(ifaceid, go->ourid)) /* bad luck */
814                     eui64_magic(ifaceid);
815                 go->hisid = ifaceid;
816                 DECPTR(sizeof(ifaceid), p);
817                 eui64_put(ifaceid, p);
818             }
819
820             ho->neg_ifaceid = 1;
821             ho->hisid = ifaceid;
822             break;
823
824         case CI_COMPRESSTYPE:
825             IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE "));
826             if (!ao->neg_vj ||
827                 (cilen != CILEN_COMPRESS)) {
828                 orc = CONFREJ;
829                 break;
830             }
831             GETSHORT(cishort, p);
832             IPV6CPDEBUG(("(%d)", cishort));
833
834 #ifdef IPV6CP_COMP
835             if (!(cishort == IPV6CP_COMP)) {
836                 orc = CONFREJ;
837                 break;
838             }
839 #else
840             orc = CONFREJ;
841             break;
842 #endif
843
844             ho->neg_vj = 1;
845             ho->vj_protocol = cishort;
846             break;
847
848         default:
849             orc = CONFREJ;
850             break;
851         }
852
853 endswitch:
854         IPV6CPDEBUG((" (%s)\n", CODENAME(orc)));
855
856         if (orc == CONFACK &&           /* Good CI */
857             rc != CONFACK)              /*  but prior CI wasnt? */
858             continue;                   /* Don't send this one */
859
860         if (orc == CONFNAK) {           /* Nak this CI? */
861             if (reject_if_disagree)     /* Getting fed up with sending NAKs? */
862                 orc = CONFREJ;          /* Get tough if so */
863             else {
864                 if (rc == CONFREJ)      /* Rejecting prior CI? */
865                     continue;           /* Don't send this one */
866                 if (rc == CONFACK) {    /* Ack'd all prior CIs? */
867                     rc = CONFNAK;       /* Not anymore... */
868                     ucp = inp;          /* Backup */
869                 }
870             }
871         }
872
873         if (orc == CONFREJ &&           /* Reject this CI */
874             rc != CONFREJ) {            /*  but no prior ones? */
875             rc = CONFREJ;
876             ucp = inp;                  /* Backup */
877         }
878
879         /* Need to move CI? */
880         if (ucp != cip)
881             BCOPY(cip, ucp, cilen);     /* Move it */
882
883         /* Update output pointer */
884         INCPTR(cilen, ucp);
885     }
886
887     /*
888      * If we aren't rejecting this packet, and we want to negotiate
889      * their identifier and they didn't send their identifier, then we
890      * send a NAK with a CI_IFACEID option appended.  We assume the
891      * input buffer is long enough that we can append the extra
892      * option safely.
893      */
894     if (rc != CONFREJ && !ho->neg_ifaceid &&
895         wo->req_ifaceid && !reject_if_disagree) {
896         if (rc == CONFACK) {
897             rc = CONFNAK;
898             ucp = inp;                          /* reset pointer */
899             wo->req_ifaceid = 0;                /* don't ask again */
900         }
901         PUTCHAR(CI_IFACEID, ucp);
902         PUTCHAR(CILEN_IFACEID, ucp);
903         eui64_put(wo->hisid, ucp);
904     }
905
906     *len = ucp - inp;                   /* Compute output length */
907     IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(rc)));
908     return (rc);                        /* Return final code */
909 }
910
911
912 /*
913  * ipv6_check_options - check that any IP-related options are OK,
914  * and assign appropriate defaults.
915  */
916 static void
917 ipv6_check_options()
918 {
919     ipv6cp_options *wo = &ipv6cp_wantoptions[0];
920
921     if (!wo->opt_local) {       /* init interface identifier */
922         if (wo->use_ip && eui64_iszero(wo->ourid)) {
923             eui64_setlo32(wo->ourid, ntohl(ipcp_wantoptions[0].ouraddr));
924             if (!eui64_iszero(wo->ourid))
925                 wo->opt_local = 1;
926         }
927         
928         while (eui64_iszero(wo->ourid))
929             eui64_magic(wo->ourid);
930     }
931
932     if (!wo->opt_remote) {
933         if (wo->use_ip && eui64_iszero(wo->hisid)) {
934             eui64_setlo32(wo->hisid, ntohl(ipcp_wantoptions[0].hisaddr));
935             if (!eui64_iszero(wo->hisid))
936                 wo->opt_remote = 1;
937         }
938     }
939
940     if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) {
941         option_error("local/remote LL address required for demand-dialling\n");
942         exit(1);
943     }
944 }
945
946
947 /*
948  * ipv6_demand_conf - configure the interface as though
949  * IPV6CP were up, for use with dial-on-demand.
950  */
951 static int
952 ipv6_demand_conf(u)
953     int u;
954 {
955     ipv6cp_options *wo = &ipv6cp_wantoptions[u];
956
957 #if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
958     if (!sifup(u, PPP_IPV6))
959         return 0;
960 #endif    
961     if (!sif6addr(u, wo->ourid, wo->hisid))
962         return 0;
963 #if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
964     if (!sifup(u, PPP_IPV6))
965         return 0;
966 #endif
967     if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE))
968         return 0;
969
970     notice("ipv6_demand_conf");
971     notice("local  LL address %s", llv6_ntoa(wo->ourid));
972     notice("remote LL address %s", llv6_ntoa(wo->hisid));
973
974     return 1;
975 }
976
977
978 /*
979  * ipv6cp_up - IPV6CP has come UP.
980  *
981  * Configure the IPv6 network interface appropriately and bring it up.
982  */
983 static void
984 ipv6cp_up(f)
985     fsm *f;
986 {
987     ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
988     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
989     ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
990
991     IPV6CPDEBUG(("ipv6cp: up"));
992
993     /*
994      * We must have a non-zero LL address for both ends of the link.
995      */
996     if (!ho->neg_ifaceid)
997         ho->hisid = wo->hisid;
998
999     if(!no_ifaceid_neg) {
1000         if (eui64_iszero(ho->hisid)) {
1001             error("Could not determine remote LL address");
1002             ipv6cp_close(f->unit, "Could not determine remote LL address");
1003             return;
1004         }
1005         if (eui64_iszero(go->ourid)) {
1006             error("Could not determine local LL address");
1007             ipv6cp_close(f->unit, "Could not determine local LL address");
1008             return;
1009         }
1010         if (eui64_equals(go->ourid, ho->hisid)) {
1011             error("local and remote LL addresses are equal");
1012             ipv6cp_close(f->unit, "local and remote LL addresses are equal");
1013             return;
1014         }
1015     }
1016     script_setenv("LLLOCAL", llv6_ntoa(go->ourid));
1017     script_setenv("LLREMOTE", llv6_ntoa(ho->hisid));
1018
1019 #ifdef IPV6CP_COMP
1020     /* set tcp compression */
1021     sif6comp(f->unit, ho->neg_vj);
1022 #endif
1023
1024     /*
1025      * If we are doing dial-on-demand, the interface is already
1026      * configured, so we put out any saved-up packets, then set the
1027      * interface to pass IPv6 packets.
1028      */
1029     if (demand) {
1030         if (! eui64_equals(go->ourid, wo->ourid) || 
1031             ! eui64_equals(ho->hisid, wo->hisid)) {
1032             if (! eui64_equals(go->ourid, wo->ourid))
1033                 warn("Local LL address changed to %s", 
1034                      llv6_ntoa(go->ourid));
1035             if (! eui64_equals(ho->hisid, wo->hisid))
1036                 warn("Remote LL address changed to %s", 
1037                      llv6_ntoa(ho->hisid));
1038             ipv6cp_clear_addrs(f->unit);
1039
1040             /* Set the interface to the new addresses */
1041             if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
1042                 if (debug)
1043                     warn("sif6addr failed");
1044                 ipv6cp_close(f->unit, "Interface configuration failed");
1045                 return;
1046             }
1047
1048         }
1049         demand_rexmit(PPP_IPV6);
1050         sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
1051
1052     } else {
1053         /*
1054          * Set LL addresses
1055          */
1056 #if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
1057         if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
1058             if (debug)
1059                 warn("sif6addr failed");
1060             ipv6cp_close(f->unit, "Interface configuration failed");
1061             return;
1062         }
1063 #endif
1064
1065         /* bring the interface up for IPv6 */
1066         if (!sifup(f->unit, PPP_IPV6)) {
1067             if (debug)
1068                 warn("sif6up failed");
1069             ipv6cp_close(f->unit, "Interface configuration failed");
1070             return;
1071         }
1072
1073 #if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
1074         if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
1075             if (debug)
1076                 warn("sif6addr failed");
1077             ipv6cp_close(f->unit, "Interface configuration failed");
1078             return;
1079         }
1080 #endif
1081         sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
1082
1083         notice("local  LL address %s", llv6_ntoa(go->ourid));
1084         notice("remote LL address %s", llv6_ntoa(ho->hisid));
1085     }
1086
1087     np_up(f->unit, PPP_IPV6);
1088     ipv6cp_is_up = 1;
1089
1090     /*
1091      * Execute the ipv6-up script, like this:
1092      *  /etc/ppp/ipv6-up interface tty speed local-LL remote-LL
1093      */
1094     if (ipv6cp_script_state == s_down && ipv6cp_script_pid == 0) {
1095         ipv6cp_script_state = s_up;
1096         ipv6cp_script(_PATH_IPV6UP);
1097     }
1098 }
1099
1100
1101 /*
1102  * ipv6cp_down - IPV6CP has gone DOWN.
1103  *
1104  * Take the IPv6 network interface down, clear its addresses
1105  * and delete routes through it.
1106  */
1107 static void
1108 ipv6cp_down(f)
1109     fsm *f;
1110 {
1111     IPV6CPDEBUG(("ipv6cp: down"));
1112     update_link_stats(f->unit);
1113     if (ipv6cp_is_up) {
1114         ipv6cp_is_up = 0;
1115         np_down(f->unit, PPP_IPV6);
1116     }
1117 #ifdef IPV6CP_COMP
1118     sif6comp(f->unit, 0);
1119 #endif
1120
1121     /*
1122      * If we are doing dial-on-demand, set the interface
1123      * to queue up outgoing packets (for now).
1124      */
1125     if (demand) {
1126         sifnpmode(f->unit, PPP_IPV6, NPMODE_QUEUE);
1127     } else {
1128 #if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC)))
1129         sifdown(f->unit, PPP_IPV6);
1130 #endif
1131         ipv6cp_clear_addrs(f->unit);
1132 #if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC)))
1133         sifdown(f->unit, PPP_IPV6);
1134 #endif
1135     }
1136
1137     /* Execute the ipv6-down script */
1138     if (ipv6cp_script_state == s_up && ipv6cp_script_pid == 0) {
1139         ipv6cp_script_state = s_down;
1140         ipv6cp_script(_PATH_IPV6DOWN);
1141     }
1142 }
1143
1144
1145 /*
1146  * ipv6cp_clear_addrs() - clear the interface addresses, routes,
1147  * proxy arp entries, etc.
1148  */
1149 static void
1150 ipv6cp_clear_addrs(unit)
1151     int unit;
1152 {
1153     eui64_t ourid, hisid;
1154
1155     ourid = ipv6cp_gotoptions[unit].ourid;
1156     hisid = ipv6cp_hisoptions[unit].hisid;
1157     cif6addr(unit, ourid, hisid);
1158 }
1159
1160
1161 /*
1162  * ipv6cp_finished - possibly shut down the lower layers.
1163  */
1164 static void
1165 ipv6cp_finished(f)
1166     fsm *f;
1167 {
1168     np_finished(f->unit, PPP_IPV6);
1169 }
1170
1171
1172 /*
1173  * ipv6cp_script_done - called when the ipv6-up or ipv6-down script
1174  * has finished.
1175  */
1176 static void
1177 ipv6cp_script_done(arg)
1178     void *arg;
1179 {
1180     ipv6cp_script_pid = 0;
1181     switch (ipv6cp_script_state) {
1182     case s_up:
1183         if (ipv6cp_fsm[0].state != OPENED) {
1184             ipv6cp_script_state = s_down;
1185             ipv6cp_script(_PATH_IPV6DOWN);
1186         }
1187         break;
1188     case s_down:
1189         if (ipv6cp_fsm[0].state == OPENED) {
1190             ipv6cp_script_state = s_up;
1191             ipv6cp_script(_PATH_IPV6UP);
1192         }
1193         break;
1194     }
1195 }
1196
1197
1198 /*
1199  * ipv6cp_script - Execute a script with arguments
1200  * interface-name tty-name speed local-LL remote-LL.
1201  */
1202 static void
1203 ipv6cp_script(script)
1204     char *script;
1205 {
1206     char strspeed[32], strlocal[32], strremote[32];
1207     char *argv[8];
1208
1209     sprintf(strspeed, "%d", baud_rate);
1210     strcpy(strlocal, llv6_ntoa(ipv6cp_gotoptions[0].ourid));
1211     strcpy(strremote, llv6_ntoa(ipv6cp_hisoptions[0].hisid));
1212
1213     argv[0] = script;
1214     argv[1] = ifname;
1215     argv[2] = devnam;
1216     argv[3] = strspeed;
1217     argv[4] = strlocal;
1218     argv[5] = strremote;
1219     argv[6] = ipparam;
1220     argv[7] = NULL;
1221
1222     ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done, NULL);
1223 }
1224
1225 /*
1226  * ipv6cp_printpkt - print the contents of an IPV6CP packet.
1227  */
1228 static char *ipv6cp_codenames[] = {
1229     "ConfReq", "ConfAck", "ConfNak", "ConfRej",
1230     "TermReq", "TermAck", "CodeRej"
1231 };
1232
1233 static int
1234 ipv6cp_printpkt(p, plen, printer, arg)
1235     u_char *p;
1236     int plen;
1237     void (*printer) __P((void *, char *, ...));
1238     void *arg;
1239 {
1240     int code, id, len, olen;
1241     u_char *pstart, *optend;
1242     u_short cishort;
1243     eui64_t ifaceid;
1244
1245     if (plen < HEADERLEN)
1246         return 0;
1247     pstart = p;
1248     GETCHAR(code, p);
1249     GETCHAR(id, p);
1250     GETSHORT(len, p);
1251     if (len < HEADERLEN || len > plen)
1252         return 0;
1253
1254     if (code >= 1 && code <= sizeof(ipv6cp_codenames) / sizeof(char *))
1255         printer(arg, " %s", ipv6cp_codenames[code-1]);
1256     else
1257         printer(arg, " code=0x%x", code);
1258     printer(arg, " id=0x%x", id);
1259     len -= HEADERLEN;
1260     switch (code) {
1261     case CONFREQ:
1262     case CONFACK:
1263     case CONFNAK:
1264     case CONFREJ:
1265         /* print option list */
1266         while (len >= 2) {
1267             GETCHAR(code, p);
1268             GETCHAR(olen, p);
1269             p -= 2;
1270             if (olen < 2 || olen > len) {
1271                 break;
1272             }
1273             printer(arg, " <");
1274             len -= olen;
1275             optend = p + olen;
1276             switch (code) {
1277             case CI_COMPRESSTYPE:
1278                 if (olen >= CILEN_COMPRESS) {
1279                     p += 2;
1280                     GETSHORT(cishort, p);
1281                     printer(arg, "compress ");
1282                     printer(arg, "0x%x", cishort);
1283                 }
1284                 break;
1285             case CI_IFACEID:
1286                 if (olen == CILEN_IFACEID) {
1287                     p += 2;
1288                     eui64_get(ifaceid, p);
1289                     printer(arg, "addr %s", llv6_ntoa(ifaceid));
1290                 }
1291                 break;
1292             }
1293             while (p < optend) {
1294                 GETCHAR(code, p);
1295                 printer(arg, " %.2x", code);
1296             }
1297             printer(arg, ">");
1298         }
1299         break;
1300
1301     case TERMACK:
1302     case TERMREQ:
1303         if (len > 0 && *p >= ' ' && *p < 0x7f) {
1304             printer(arg, " ");
1305             print_string(p, len, printer, arg);
1306             p += len;
1307             len = 0;
1308         }
1309         break;
1310     }
1311
1312     /* print the rest of the bytes in the packet */
1313     for (; len > 0; --len) {
1314         GETCHAR(code, p);
1315         printer(arg, " %.2x", code);
1316     }
1317
1318     return p - pstart;
1319 }
1320
1321 /*
1322  * ipv6_active_pkt - see if this IP packet is worth bringing the link up for.
1323  * We don't bring the link up for IP fragments or for TCP FIN packets
1324  * with no data.
1325  */
1326 #define IP6_HDRLEN      40      /* bytes */
1327 #define IP6_NHDR_FRAG   44      /* fragment IPv6 header */
1328 #define IPPROTO_TCP     6
1329 #define TCP_HDRLEN      20
1330 #define TH_FIN          0x01
1331
1332 /*
1333  * We use these macros because the IP header may be at an odd address,
1334  * and some compilers might use word loads to get th_off or ip_hl.
1335  */
1336
1337 #define get_ip6nh(x)    (((unsigned char *)(x))[6])
1338 #define get_tcpoff(x)   (((unsigned char *)(x))[12] >> 4)
1339 #define get_tcpflags(x) (((unsigned char *)(x))[13])
1340
1341 static int
1342 ipv6_active_pkt(pkt, len)
1343     u_char *pkt;
1344     int len;
1345 {
1346     u_char *tcp;
1347
1348     len -= PPP_HDRLEN;
1349     pkt += PPP_HDRLEN;
1350     if (len < IP6_HDRLEN)
1351         return 0;
1352     if (get_ip6nh(pkt) == IP6_NHDR_FRAG)
1353         return 0;
1354     if (get_ip6nh(pkt) != IPPROTO_TCP)
1355         return 1;
1356     if (len < IP6_HDRLEN + TCP_HDRLEN)
1357         return 0;
1358     tcp = pkt + IP6_HDRLEN;
1359     if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == IP6_HDRLEN + get_tcpoff(tcp) * 4)
1360         return 0;
1361     return 1;
1362 }