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