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