xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/pppd/ipv6cp.c (revision d1aea6f139360e9e7f1504facb24f8521047b15c)
1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  * Copyright (c) 2016 by Delphix. All rights reserved.
5  *
6  *
7     ipv6cp.c - PPP IPV6 Control Protocol.
8     Copyright (C) 1999  Tommi Komulainen <Tommi.Komulainen@iki.fi>
9 
10     Redistribution and use in source and binary forms are permitted
11     provided that the above copyright notice and this paragraph are
12     duplicated in all such forms.  The name of the author may not be
13     used to endorse or promote products derived from this software
14     without specific prior written permission.
15     THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16     IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17     WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18 */
19 
20 /*  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 <stdlib.h>
116 #include <unistd.h>
117 #include <netdb.h>
118 #include <sys/param.h>
119 #include <sys/types.h>
120 #include <sys/socket.h>
121 #include <netinet/in.h>
122 #include <arpa/inet.h>
123 
124 #include "pppd.h"
125 #include "eui64.h"
126 #include "fsm.h"
127 #include "ipcp.h"
128 #include "ipv6cp.h"
129 #include "magic.h"
130 #include "pathnames.h"
131 
132 #if !defined(lint) && !defined(_lint)
133 static const char rcsid[] = RCSID;
134 #endif
135 
136 /* global vars */
137 ipv6cp_options ipv6cp_wantoptions[NUM_PPP];     /* Options that we want to request */
138 ipv6cp_options ipv6cp_gotoptions[NUM_PPP];	/* Options that peer ack'd */
139 ipv6cp_options ipv6cp_allowoptions[NUM_PPP];	/* Options we allow peer to request */
140 ipv6cp_options ipv6cp_hisoptions[NUM_PPP];	/* Options that we ack'd */
141 int no_ifaceid_neg = 0;
142 
143 /* local vars */
144 static bool ipv6cp_is_up;
145 
146 /*
147  * Callbacks for fsm code.  (CI = Configuration Information)
148  */
149 static void ipv6cp_resetci __P((fsm *));	/* Reset our CI */
150 static int  ipv6cp_cilen __P((fsm *));	        /* Return length of our CI */
151 static void ipv6cp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
152 static int  ipv6cp_ackci __P((fsm *, u_char *, int));	/* Peer ack'd our CI */
153 static int  ipv6cp_nakci __P((fsm *, u_char *, int));	/* Peer nak'd our CI */
154 static int  ipv6cp_rejci __P((fsm *, u_char *, int));	/* Peer rej'd our CI */
155 static int  ipv6cp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
156 static void ipv6cp_up __P((fsm *));		/* We're UP */
157 static void ipv6cp_down __P((fsm *));		/* We're DOWN */
158 static void ipv6cp_finished __P((fsm *));	/* Don't need lower layer */
159 
160 fsm ipv6cp_fsm[NUM_PPP];		/* IPV6CP fsm structure */
161 
162 static fsm_callbacks ipv6cp_callbacks = { /* IPV6CP callback routines */
163     ipv6cp_resetci,		/* Reset our Configuration Information */
164     ipv6cp_cilen,		/* Length of our Configuration Information */
165     ipv6cp_addci,		/* Add our Configuration Information */
166     ipv6cp_ackci,		/* ACK our Configuration Information */
167     ipv6cp_nakci,		/* NAK our Configuration Information */
168     ipv6cp_rejci,		/* Reject our Configuration Information */
169     ipv6cp_reqci,		/* Request peer's Configuration Information */
170     ipv6cp_up,			/* Called when fsm reaches OPENED state */
171     ipv6cp_down,		/* Called when fsm leaves OPENED state */
172     NULL,			/* Called when we want the lower layer up */
173     ipv6cp_finished,		/* Called when we want the lower layer down */
174     NULL,			/* Retransmission is necessary */
175     NULL,			/* Called to handle protocol-specific codes */
176     "IPV6CP",			/* String name of protocol */
177     NULL			/* Peer rejected a code number */
178 };
179 
180 static int setifaceid __P((char **arg, option_t *));
181 
182 /*
183  * Command-line options.
184  */
185 static option_t ipv6cp_option_list[] = {
186     { "ipv6", o_special, (void *)setifaceid,
187       "Set interface identifiers for IPV6" },
188     { "noipv6", o_bool, &ipv6cp_protent.enabled_flag,
189       "Disable IPv6 and IPv6CP" },
190     { "-ipv6", o_bool, &ipv6cp_protent.enabled_flag,
191       "Disable IPv6 and IPv6CP" },
192     { "+ipv6", o_bool, &ipv6cp_protent.enabled_flag,
193       "Enable IPv6 and IPv6CP", 1 },
194     { "ipv6cp-accept-local", o_bool, &ipv6cp_wantoptions[0].accept_local,
195       "Accept peer's interface identifier for us", 1 },
196     { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_wantoptions[0].use_ip,
197       "Use (default) IPv4 address as interface identifier", 1 },
198 #if defined(SOL2)
199     { "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent,
200       "Use unique persistent value for link local address", 1 },
201 #endif /* defined(SOL2) */
202     { "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime,
203       "Set timeout for IPv6CP" },
204     { "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits,
205       "Maximum number of IPV6CP Terminate-Request" },
206     { "ipv6cp-max-configure", o_int, &ipv6cp_fsm[0].maxconfreqtransmits,
207       "Maximum number of IPV6CP Configure-Request" },
208     { "ipv6cp-max-failure", o_int, &ipv6cp_fsm[0].maxnakloops,
209       "Maximum number of IPV6CP Configure-Nak" },
210     { NULL }
211 };
212 
213 
214 /*
215  * Protocol entry points from main code.
216  */
217 static void ipv6cp_init __P((int));
218 static void ipv6cp_open __P((int));
219 static void ipv6cp_close __P((int, char *));
220 static void ipv6cp_lowerup __P((int));
221 static void ipv6cp_lowerdown __P((int));
222 static void ipv6cp_input __P((int, u_char *, int));
223 static void ipv6cp_protrej __P((int));
224 static int  ipv6cp_printpkt __P((u_char *, int,
225     void (*) __P((void *, const char *, ...)), void *));
226 static void ipv6_check_options __P((void));
227 static int  ipv6_demand_conf __P((int));
228 static int  ipv6_active_pkt __P((u_char *, int));
229 
230 struct protent ipv6cp_protent = {
231     PPP_IPV6CP,			/* Protocol Number for IPV6CP */
232     ipv6cp_init,		/* Initializes IPV6CP */
233     ipv6cp_input,		/* Processes a received IPV6CP packet */
234     ipv6cp_protrej,		/* Process a received Protocol-reject */
235     ipv6cp_lowerup,		/* Called when LCP is brought up */
236     ipv6cp_lowerdown,		/* Called when LCP has gone down */
237     ipv6cp_open,		/* Called when link is established */
238     ipv6cp_close,		/* Called when link has gone down */
239     ipv6cp_printpkt,		/* Print a packet in human readable form */
240     NULL,			/* Process a received data packet */
241     0,				/* IPV6CP is disabled by default */
242     "IPV6CP",			/* Name of the protocol */
243     "IPV6",			/* Name of the corresponding data protocol */
244     ipv6cp_option_list,		/* List of IPV6CP command-line options */
245     ipv6_check_options,		/* Assigns default values for options */
246     ipv6_demand_conf,		/* Configures demand-dial */
247     ipv6_active_pkt		/* Bring up the link for this packet? */
248 };
249 
250 /*
251  * Local forward function declarations.
252  */
253 static void ipv6cp_clear_addrs __P((int, eui64_t, eui64_t));
254 static void ipv6cp_script __P((char *));
255 static void ipv6cp_script_done __P((void *, int));
256 
257 /*
258  * Lengths of configuration options.
259  */
260 #define CILEN_VOID	2
261 #define CILEN_COMPRESS	4	/* length for RFC2023 compress opt. */
262 #define CILEN_IFACEID   10	/* RFC2472, interface identifier    */
263 
264 #define CODENAME(x)	((x) == CODE_CONFACK ? "ACK" : \
265 			 (x) == CODE_CONFNAK ? "NAK" : "REJ")
266 
267 /*
268  * This state variable is used to ensure that we don't
269  * run an ipcp-up/down script while one is already running.
270  */
271 static enum script_state {
272     s_down,
273     s_up
274 } ipv6cp_script_state;
275 static pid_t ipv6cp_script_pid;
276 
277 /*
278  * setifaceid - set the interface identifiers manually
279  */
280 /*ARGSUSED*/
281 static int
282 setifaceid(argv, opt)
283     char **argv;
284     option_t *opt;
285 {
286     char *comma, *arg;
287     ipv6cp_options *wo = &ipv6cp_wantoptions[0];
288     struct in6_addr addr;
289 
290 #define VALIDID(a) ( (((a).s6_addr32[0] == 0) && ((a).s6_addr32[1] == 0)) && \
291 			(((a).s6_addr32[2] != 0) || ((a).s6_addr32[3] != 0)) )
292 
293 
294     arg = *argv;
295 
296     comma = strchr(arg, ',');
297 
298     /*
299      * If comma first character, then no local identifier
300      */
301     if (comma != arg) {
302 	if (comma != NULL)
303 	    *comma = '\0';
304 
305 	if (inet_pton(AF_INET6, arg, &addr) != 1 || !VALIDID(addr)) {
306 	    option_error("Illegal interface identifier (local): %s", arg);
307 	    return 0;
308 	}
309 
310 	eui64_copy(addr.s6_addr32[2], wo->ourid);
311 	wo->opt_local = 1;
312     }
313 
314     /*
315      * If comma last character, then no remote identifier
316      */
317     if (comma != NULL && *++comma != '\0') {
318 	if (inet_pton(AF_INET6, comma, &addr) != 1 || !VALIDID(addr)) {
319 	    option_error("Illegal interface identifier (remote): %s", comma);
320 	    return 0;
321 	}
322 	eui64_copy(addr.s6_addr32[2], wo->hisid);
323 	wo->opt_remote = 1;
324     }
325 
326     ipv6cp_protent.enabled_flag = 1;
327     return 1;
328 }
329 
330 /*
331  * Given an interface identifier, return a string representation of the
332  * link local address associated with that identifier.
333  * string will be at most 26 characters (including null terminator).
334  */
335 static char *
336 llv6_ntoa(ifaceid)
337     eui64_t ifaceid;
338 {
339     struct in6_addr addr;
340     static char addrstr[26];
341 
342     BZERO(&addr, sizeof (addr));
343     addr.s6_addr[0] = 0xfe;
344     addr.s6_addr[1] = 0x80;
345     eui64_copy(ifaceid, addr.s6_addr[8]);
346 
347     (void) inet_ntop(AF_INET6, &addr, addrstr, 26);
348 
349     return addrstr;
350 }
351 
352 
353 /*
354  * ipv6cp_init - Initialize IPV6CP.
355  */
356 static void
357 ipv6cp_init(unit)
358     int unit;
359 {
360     fsm *f = &ipv6cp_fsm[unit];
361     ipv6cp_options *wo = &ipv6cp_wantoptions[unit];
362     ipv6cp_options *ao = &ipv6cp_allowoptions[unit];
363 
364     f->unit = unit;
365     f->protocol = PPP_IPV6CP;
366     f->callbacks = &ipv6cp_callbacks;
367     fsm_init(&ipv6cp_fsm[unit]);
368 
369     BZERO(wo, sizeof(*wo));
370     BZERO(ao, sizeof(*ao));
371 
372     wo->neg_ifaceid = 1;
373     ao->neg_ifaceid = 1;
374 
375 #ifdef IPV6CP_COMP
376     wo->neg_vj = 1;
377     ao->neg_vj = 1;
378     wo->vj_protocol = IPV6CP_COMP;
379 #endif
380 
381 }
382 
383 
384 /*
385  * ipv6cp_open - IPV6CP is allowed to come up.
386  */
387 static void
388 ipv6cp_open(unit)
389     int unit;
390 {
391     fsm_open(&ipv6cp_fsm[unit]);
392 }
393 
394 
395 /*
396  * ipv6cp_close - Take IPV6CP down.
397  */
398 static void
399 ipv6cp_close(unit, reason)
400     int unit;
401     char *reason;
402 {
403     fsm_close(&ipv6cp_fsm[unit], reason);
404 }
405 
406 
407 /*
408  * ipv6cp_lowerup - The lower layer is up.
409  */
410 static void
411 ipv6cp_lowerup(unit)
412     int unit;
413 {
414     fsm_lowerup(&ipv6cp_fsm[unit]);
415 }
416 
417 
418 /*
419  * ipv6cp_lowerdown - The lower layer is down.
420  */
421 static void
422 ipv6cp_lowerdown(unit)
423     int unit;
424 {
425     fsm_lowerdown(&ipv6cp_fsm[unit]);
426 }
427 
428 
429 /*
430  * ipv6cp_input - Input IPV6CP packet.
431  */
432 static void
433 ipv6cp_input(unit, p, len)
434     int unit;
435     u_char *p;
436     int len;
437 {
438     fsm_input(&ipv6cp_fsm[unit], p, len);
439 }
440 
441 
442 /*
443  * ipv6cp_protrej - A Protocol-Reject was received for IPV6CP.
444  */
445 static void
446 ipv6cp_protrej(unit)
447     int unit;
448 {
449     fsm_protreject(&ipv6cp_fsm[unit]);
450 }
451 
452 
453 /*
454  * ipv6cp_resetci - Reset our CI.
455  */
456 static void
457 ipv6cp_resetci(f)
458     fsm *f;
459 {
460     ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
461     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
462 
463     wo->req_ifaceid = wo->neg_ifaceid && ipv6cp_allowoptions[f->unit].neg_ifaceid;
464 
465     if (!wo->opt_local) {
466 	eui64_magic_nz(wo->ourid);
467     }
468 
469     *go = *wo;
470     eui64_zero(go->hisid);	/* last proposed interface identifier */
471 }
472 
473 
474 /*
475  * ipv6cp_cilen - Return length of our CI.
476  */
477 static int
478 ipv6cp_cilen(f)
479     fsm *f;
480 {
481     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
482 
483 #define LENCIVJ(neg)		(neg ? CILEN_COMPRESS : 0)
484 #define LENCIIFACEID(neg)	(neg ? CILEN_IFACEID : 0)
485 
486     return (LENCIIFACEID(go->neg_ifaceid) +
487 	    LENCIVJ(go->neg_vj));
488 }
489 
490 
491 /*
492  * ipv6cp_addci - Add our desired CIs to a packet.
493  */
494 static void
495 ipv6cp_addci(f, ucp, lenp)
496     fsm *f;
497     u_char *ucp;
498     int *lenp;
499 {
500     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
501     int len = *lenp;
502 
503 #define ADDCIVJ(opt, neg, val) \
504     if (neg) { \
505 	int vjlen = CILEN_COMPRESS; \
506 	if (len >= vjlen) { \
507 	    PUTCHAR(opt, ucp); \
508 	    PUTCHAR(vjlen, ucp); \
509 	    PUTSHORT(val, ucp); \
510 	    len -= vjlen; \
511 	} else \
512 	    neg = 0; \
513     }
514 
515 #define ADDCIIFACEID(opt, neg, val1) \
516     if (neg) { \
517 	int idlen = CILEN_IFACEID; \
518 	if (len >= idlen) { \
519 	    PUTCHAR(opt, ucp); \
520 	    PUTCHAR(idlen, ucp); \
521 	    eui64_put(val1, ucp); \
522 	    len -= idlen; \
523 	} else \
524 	    neg = 0; \
525     }
526 
527     ADDCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
528 
529     ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
530 
531     *lenp -= len;
532 }
533 
534 
535 /*
536  * ipv6cp_ackci - Ack our CIs.
537  *
538  * Returns:
539  *	0 - Ack was bad.
540  *	1 - Ack was good.
541  */
542 static int
543 ipv6cp_ackci(f, p, len)
544     fsm *f;
545     u_char *p;
546     int len;
547 {
548     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
549     u_short cilen, citype, cishort;
550     eui64_t ifaceid;
551 
552     /*
553      * CIs must be in exactly the same order that we sent...
554      * Check packet length and CI length at each step.
555      * If we find any deviations, then this packet is bad.
556      */
557 
558 #define ACKCIVJ(opt, neg, val) \
559     if (neg) { \
560 	int vjlen = CILEN_COMPRESS; \
561 	if ((len -= vjlen) < 0) \
562 	    goto bad; \
563 	GETCHAR(citype, p); \
564 	GETCHAR(cilen, p); \
565 	if (cilen != vjlen || \
566 	    citype != opt)  \
567 	    goto bad; \
568 	GETSHORT(cishort, p); \
569 	if (cishort != val) \
570 	    goto bad; \
571     }
572 
573 #define ACKCIIFACEID(opt, neg, val1) \
574     if (neg) { \
575 	int idlen = CILEN_IFACEID; \
576 	if ((len -= idlen) < 0) \
577 	    goto bad; \
578 	GETCHAR(citype, p); \
579 	GETCHAR(cilen, p); \
580 	if (cilen != idlen || \
581 	    citype != opt) \
582 	    goto bad; \
583 	eui64_get(ifaceid, p); \
584 	if (! eui64_equals(val1, ifaceid)) \
585 	    goto bad; \
586     }
587 
588     ACKCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
589 
590     ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
591 
592     /*
593      * If there are any remaining CIs, then this packet is bad.
594      */
595     if (len != 0)
596 	goto bad;
597     return (1);
598 
599 bad:
600     IPV6CPDEBUG(("ipv6cp_ackci: received bad Ack!"));
601     return (0);
602 }
603 
604 /*
605  * ipv6cp_nakci - Peer has sent a NAK for some of our CIs.
606  * This should not modify any state if the Nak is bad
607  * or if IPV6CP is in the OPENED state.
608  *
609  * Returns:
610  *	0 - Nak was bad.
611  *	1 - Nak was good.
612  */
613 static int
614 ipv6cp_nakci(f, p, len)
615     fsm *f;
616     u_char *p;
617     int len;
618 {
619     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
620     u_char citype, cilen, *next;
621     u_short cishort;
622     eui64_t ifaceid;
623     ipv6cp_options no;		/* options we've seen Naks for */
624     ipv6cp_options try;		/* options to request next time */
625 
626     BZERO(&no, sizeof(no));
627     try = *go;
628 
629     /*
630      * Any Nak'd CIs must be in exactly the same order that we sent.
631      * Check packet length and CI length at each step.
632      * If we find any deviations, then this packet is bad.
633      */
634 #define NAKCIIFACEID(opt, neg, code) \
635     if (go->neg && \
636 	len >= (cilen = CILEN_IFACEID) && \
637 	p[1] == cilen && \
638 	p[0] == opt) { \
639 	len -= cilen; \
640 	INCPTR(2, p); \
641 	eui64_get(ifaceid, p); \
642 	no.neg = 1; \
643 	code \
644     }
645 
646 #define NAKCIVJ(opt, neg, code) \
647     if (go->neg && \
648 	((cilen = p[1]) == CILEN_COMPRESS) && \
649 	len >= cilen && \
650 	p[0] == opt) { \
651 	len -= cilen; \
652 	INCPTR(2, p); \
653 	GETSHORT(cishort, p); \
654 	no.neg = 1; \
655         code \
656     }
657 
658     /*
659      * Accept the peer's idea of {our,its} interface identifier, if different
660      * from our idea, only if the accept_{local,remote} flag is set.
661      */
662     NAKCIIFACEID(CI_IFACEID, neg_ifaceid,
663 	      if (go->accept_local) {
664 		  while (eui64_iszero(ifaceid) ||
665 			 eui64_equals(ifaceid, go->hisid)) /* bad luck */
666 		      eui64_magic(ifaceid);
667 		  try.ourid = ifaceid;
668 		  IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid)));
669 	      }
670 	      );
671 
672 #ifdef IPV6CP_COMP
673     NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
674 	    {
675 		if (cishort == IPV6CP_COMP) {
676 		    try.vj_protocol = cishort;
677 		} else {
678 		    try.neg_vj = 0;
679 		}
680 	    }
681 	    );
682 #else
683     NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
684 	    {
685 		try.neg_vj = 0;
686 	    }
687 	    );
688 #endif
689 
690     /*
691      * There may be remaining CIs, if the peer is requesting negotiation
692      * on an option that we didn't include in our request packet.
693      * If they want to negotiate about interface identifier, we comply.
694      * If they want us to ask for compression, we refuse.
695      */
696     while (len > CILEN_VOID) {
697 	GETCHAR(citype, p);
698 	GETCHAR(cilen, p);
699 	if( (len -= cilen) < 0 )
700 	    goto bad;
701 	next = p + cilen - 2;
702 
703 	switch (citype) {
704 	case CI_COMPRESSTYPE:
705 	    if (go->neg_vj || no.neg_vj ||
706 		(cilen != CILEN_COMPRESS))
707 		goto bad;
708 	    no.neg_vj = 1;
709 	    break;
710 	case CI_IFACEID:
711 	    if (go->neg_ifaceid || no.neg_ifaceid || cilen != CILEN_IFACEID)
712 		goto bad;
713 	    try.neg_ifaceid = 1;
714 	    eui64_get(ifaceid, p);
715 	    if (go->accept_local) {
716 		while (eui64_iszero(ifaceid) ||
717 		       eui64_equals(ifaceid, go->hisid)) /* bad luck */
718 		    eui64_magic(ifaceid);
719 		try.ourid = ifaceid;
720 	    }
721 	    no.neg_ifaceid = 1;
722 	    break;
723 	}
724 	p = next;
725     }
726 
727     /* If there is still anything left, this packet is bad. */
728     if (len != 0)
729 	goto bad;
730 
731     /*
732      * OK, the Nak is good.  Now we can update state.
733      */
734     if (f->state != OPENED)
735 	*go = try;
736 
737     return 1;
738 
739 bad:
740     IPV6CPDEBUG(("ipv6cp_nakci: received bad Nak!"));
741     return 0;
742 }
743 
744 
745 /*
746  * ipv6cp_rejci - Reject some of our CIs.
747  */
748 static int
749 ipv6cp_rejci(f, p, len)
750     fsm *f;
751     u_char *p;
752     int len;
753 {
754     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
755     u_char cilen;
756     u_short cishort;
757     eui64_t ifaceid;
758     ipv6cp_options try;		/* options to request next time */
759 
760     try = *go;
761     /*
762      * Any Rejected CIs must be in exactly the same order that we sent.
763      * Check packet length and CI length at each step.
764      * If we find any deviations, then this packet is bad.
765      */
766 #define REJCIIFACEID(opt, neg, val1) \
767     if (go->neg && \
768 	len >= (cilen = CILEN_IFACEID) && \
769 	p[1] == cilen && \
770 	p[0] == opt) { \
771 	len -= cilen; \
772 	INCPTR(2, p); \
773 	eui64_get(ifaceid, p); \
774 	/* Check rejected value. */ \
775 	if (! eui64_equals(ifaceid, val1)) \
776 	    goto bad; \
777 	try.neg = 0; \
778     }
779 
780 #define REJCIVJ(opt, neg, val) \
781     if (go->neg && \
782 	p[1] == CILEN_COMPRESS && \
783 	len >= p[1] && \
784 	p[0] == opt) { \
785 	len -= p[1]; \
786 	INCPTR(2, p); \
787 	GETSHORT(cishort, p); \
788 	/* Check rejected value. */  \
789 	if (cishort != val) \
790 	    goto bad; \
791 	try.neg = 0; \
792      }
793 
794     REJCIIFACEID(CI_IFACEID, neg_ifaceid, go->ourid);
795 
796     REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol);
797 
798     /*
799      * If there are any remaining CIs, then this packet is bad.
800      */
801     if (len != 0)
802 	goto bad;
803     /*
804      * Now we can update state.
805      */
806     if (f->state != OPENED)
807 	*go = try;
808     return 1;
809 
810 bad:
811     IPV6CPDEBUG(("ipv6cp_rejci: received bad Reject!"));
812     return 0;
813 }
814 
815 
816 /*
817  * ipv6cp_reqci - Check the peer's requested CIs and send appropriate response.
818  *
819  * Returns: CODE_CONFACK, CODE_CONFNAK or CODE_CONFREJ and input packet modified
820  * appropriately.  If reject_if_disagree is non-zero, doesn't return
821  * CODE_CONFNAK; returns CODE_CONFREJ if it can't return CODE_CONFACK.
822  */
823 static int
824 ipv6cp_reqci(f, p, lenp, dont_nak)
825     fsm *f;
826     u_char *p;		/* Requested CIs */
827     int *lenp;			/* Length of requested CIs */
828     int dont_nak;
829 {
830     ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
831     ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
832     ipv6cp_options *ao = &ipv6cp_allowoptions[f->unit];
833     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
834     u_char *p0, *nakp, *rejp, *prev;
835     int ret, newret;
836     int len, cilen, type;
837     eui64_t ifaceid;
838     u_short cishort;
839 
840     ret = CODE_CONFACK;
841     rejp = p0 = p;
842     nakp = nak_buffer;
843 
844     /*
845      * Reset all its options.
846      */
847     BZERO(ho, sizeof(*ho));
848 
849     /*
850      * Process all its options.
851      */
852     for (len = *lenp; len > 0; len -= cilen, p = prev + cilen) {
853 	newret = CODE_CONFACK;
854 
855 	if ((len < 2) || p[1] > len) {
856 	    /*
857 	     * RFC 1661 page 40 -- if the option extends beyond the
858 	     * packet, then discard the entire packet.
859 	     */
860 	    return (0);
861 	}
862 
863 	prev = p;
864 	GETCHAR(type, p);
865 	GETCHAR(cilen, p);
866 
867 	switch (type) {		/* Check CI type */
868 	case CI_IFACEID:
869 	    IPV6CPDEBUG(("ipv6cp: received interface identifier "));
870 
871 	    if (!ao->neg_ifaceid) {
872 		newret = CODE_CONFREJ;
873 		break;
874 	    }
875 
876 	    if (cilen != CILEN_IFACEID) {
877 		/*
878 		 * rfc1661, page 40 -- a recongnized option with an
879 		 * invalid length should be Nak'ed.
880 		 */
881 		newret = CODE_CONFNAK;
882 		eui64_copy(wo->hisid, ifaceid);
883 	    } else {
884 
885 		/*
886 		 * If it has no interface identifier, or if we both
887 		 * have same identifier then NAK it with new idea.  In
888 		 * particular, if we don't know his identifier, but it
889 		 * does, then accept that identifier.
890 		 */
891 		eui64_get(ifaceid, p);
892 		IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid)));
893 		if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) {
894 		    newret = CODE_CONFREJ;		/* Reject CI */
895 		    break;
896 		}
897 		/* If we don't like its ID, then nak that ID. */
898 		if (!eui64_iszero(wo->hisid) &&
899 		    !eui64_equals(ifaceid, wo->hisid) &&
900 		    eui64_iszero(go->hisid)) {
901 		    newret = CODE_CONFNAK;
902 		    eui64_copy(wo->hisid, ifaceid);
903 		} else if (eui64_iszero(ifaceid) ||
904 		    eui64_equals(ifaceid, go->ourid)) {
905 		    newret = CODE_CONFNAK;
906 		    /* first time, try option */
907 		    if (eui64_iszero(go->hisid))
908 			eui64_copy(wo->hisid, ifaceid);
909 		    while (eui64_iszero(ifaceid) ||
910 			eui64_equals(ifaceid, go->ourid)) /* bad luck */
911 			eui64_magic(ifaceid);
912 		}
913 	    }
914 	    if (newret == CODE_CONFNAK) {
915 		PUTCHAR(type, nakp);
916 		PUTCHAR(CILEN_IFACEID, nakp);
917 		eui64_put(ifaceid, nakp);
918 	    }
919 
920 	    ho->neg_ifaceid = 1;
921 	    eui64_copy(ifaceid, ho->hisid);
922 	    break;
923 
924 	case CI_COMPRESSTYPE:
925 	    IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE "));
926 
927 	    if (!ao->neg_vj) {
928 		newret = CODE_CONFREJ;
929 		break;
930 	    }
931 
932 	    if (cilen != CILEN_COMPRESS) {
933 		newret = CODE_CONFNAK;
934 		cishort = ao->vj_protocol;
935 	    } else {
936 		GETSHORT(cishort, p);
937 		IPV6CPDEBUG(("(%d)", cishort));
938 
939 #ifdef IPV6CP_COMP
940 		if (cishort != IPV6CP_COMP) {
941 		    newret = CODE_CONFNAK;
942 		    cishort = IPV6CP_COMP;
943 		}
944 #else
945 		newret = CODE_CONFREJ;
946 		break;
947 #endif
948 	    }
949 
950 	    ho->neg_vj = 1;
951 	    ho->vj_protocol = cishort;
952 	    break;
953 
954 	default:
955 	    newret = CODE_CONFREJ;
956 	    break;
957 	}
958 
959 	IPV6CPDEBUG((" (%s)\n", CODENAME(newret)));
960 
961 	/* Cope with confused peers. */
962 	if (cilen < 2)
963 	    cilen = 2;
964 
965 	/*
966 	 * If this is an Ack'able CI, but we're sending back a Nak,
967 	 * don't include this CI.
968 	 */
969 	if (newret == CODE_CONFACK && ret != CODE_CONFACK)
970 	    continue;
971 
972 	if (newret == CODE_CONFNAK) {
973 	    if (dont_nak) {
974 		newret = CODE_CONFREJ;
975 	    } else {
976 		/* Ignore subsequent Nak'able things if rejecting. */
977 		if (ret == CODE_CONFREJ)
978 		    continue;
979 		ret = CODE_CONFNAK;
980 	    }
981 	}
982 
983 	if (newret == CODE_CONFREJ) {
984 	    ret = CODE_CONFREJ;
985 	    if (prev != rejp)
986 		(void) BCOPY(prev, rejp, cilen);
987 	    rejp += cilen;
988 	}
989     }
990 
991     /*
992      * If we aren't rejecting this packet, and we want to negotiate
993      * their identifier and they didn't send their identifier, then we
994      * send a NAK with a CI_IFACEID option appended.  We assume the
995      * input buffer is long enough that we can append the extra
996      * option safely.
997      */
998     if (ret != CODE_CONFREJ && !ho->neg_ifaceid &&
999 	wo->req_ifaceid && !dont_nak) {
1000 	if (ret == CODE_CONFACK)
1001 	    wo->req_ifaceid = 0;
1002 	ret = CODE_CONFNAK;
1003 	PUTCHAR(CI_IFACEID, nakp);
1004 	PUTCHAR(CILEN_IFACEID, nakp);
1005 	eui64_put(wo->hisid, nakp);
1006     }
1007 
1008     switch (ret) {
1009     case CODE_CONFACK:
1010 	*lenp = p - p0;
1011 	sys_block_proto(PPP_IPV6);
1012 	break;
1013     case CODE_CONFNAK:
1014 	*lenp = nakp - nak_buffer;
1015 	(void) BCOPY(nak_buffer, p0, *lenp);
1016 	break;
1017     case CODE_CONFREJ:
1018 	*lenp = rejp - p0;
1019 	break;
1020     }
1021 
1022     IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(ret)));
1023     return (ret);			/* Return final code */
1024 }
1025 
1026 
1027 /*
1028  * ipv6_check_options - check that any IP-related options are OK,
1029  * and assign appropriate defaults.
1030  */
1031 static void
1032 ipv6_check_options()
1033 {
1034     ipv6cp_options *wo = &ipv6cp_wantoptions[0];
1035 
1036 #if defined(SOL2)
1037     /*
1038      * Persistent link-local id is only used when user has not explicitly
1039      * configure/hard-code the id
1040      */
1041     if ((wo->use_persistent) && (!wo->opt_local) && (!wo->opt_remote)) {
1042 
1043 	/*
1044 	 * On systems where there are no Ethernet interfaces used, there
1045 	 * may be other ways to obtain a persistent id. Right now, it
1046 	 * will fall back to using magic [see eui64_magic] below when
1047 	 * an EUI-48 from MAC address can't be obtained. Other possibilities
1048 	 * include obtaining EEPROM serial numbers, or some other unique
1049 	 * yet persistent number. On Sparc platforms, this is possible,
1050 	 * but too bad there's no standards yet for x86 machines.
1051 	 */
1052 	if (ether_to_eui64(&wo->ourid)) {
1053 	    wo->opt_local = 1;
1054 	}
1055     }
1056 #endif
1057 
1058     /*
1059      * If ipv6cp-use-ipaddr is used, then both local and remote IPv4
1060      * addresses should be specified as options.  Otherwise, since
1061      * ipcp has yet to negotiate the IPv4 addresses, the interface
1062      * identifiers will be based on meaningless values.
1063      */
1064     if (wo->use_ip) {
1065 	if ((ipcp_wantoptions[0].accept_local ||
1066 	    ipcp_wantoptions[0].ouraddr == 0) && eui64_iszero(wo->ourid)) {
1067 	    warn("either IPv4 or IPv6 local address should be non-zero for ipv6cp-use-ipaddr");
1068 	}
1069 	if ((ipcp_wantoptions[0].accept_remote ||
1070 	    ipcp_wantoptions[0].hisaddr == 0) && eui64_iszero(wo->hisid)) {
1071 	    warn("either IPv4 or IPv6 remote address should be non-zero for ipv6cp-use-ipaddr");
1072 	}
1073     }
1074 
1075     if (!wo->opt_local) {	/* init interface identifier */
1076 	if (wo->use_ip && eui64_iszero(wo->ourid)) {
1077 	    eui64_setlo32(wo->ourid, ntohl(ipcp_wantoptions[0].ouraddr));
1078 	    if (!eui64_iszero(wo->ourid))
1079 		wo->opt_local = 1;
1080 	}
1081 
1082 	while (eui64_iszero(wo->ourid))
1083 	    eui64_magic(wo->ourid);
1084     }
1085 
1086     if (!wo->opt_remote) {
1087 	if (wo->use_ip && eui64_iszero(wo->hisid)) {
1088 	    eui64_setlo32(wo->hisid, ntohl(ipcp_wantoptions[0].hisaddr));
1089 	    if (!eui64_iszero(wo->hisid))
1090 		wo->opt_remote = 1;
1091 	}
1092     }
1093 
1094     if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) {
1095 	fatal("local/remote LL address required for demand-dialling\n");
1096     }
1097 }
1098 
1099 
1100 /*
1101  * ipv6_demand_conf - configure the interface as though
1102  * IPV6CP were up, for use with dial-on-demand.
1103  */
1104 static int
1105 ipv6_demand_conf(u)
1106     int u;
1107 {
1108     ipv6cp_options *wo = &ipv6cp_wantoptions[u];
1109 
1110 #if SIF6UPFIRST
1111     if (!sif6up(u))
1112 	return 0;
1113 #endif
1114     if (!sif6addr(u, wo->ourid, wo->hisid))
1115 	return 0;
1116 #if !SIF6UPFIRST
1117     if (!sif6up(u))
1118 	return 0;
1119 #endif
1120     if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE))
1121 	return 0;
1122 
1123     notice("local  LL address %s", llv6_ntoa(wo->ourid));
1124     notice("remote LL address %s", llv6_ntoa(wo->hisid));
1125 
1126     return 1;
1127 }
1128 
1129 
1130 /*
1131  * ipv6cp_up - IPV6CP has come UP.
1132  *
1133  * Configure the IPv6 network interface appropriately and bring it up.
1134  */
1135 static void
1136 ipv6cp_up(f)
1137     fsm *f;
1138 {
1139     ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
1140     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
1141     ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
1142 
1143     IPV6CPDEBUG(("ipv6cp: up"));
1144 
1145     /*
1146      * We must have a non-zero LL address for both ends of the link.
1147      */
1148     if (!ho->neg_ifaceid)
1149 	ho->hisid = wo->hisid;
1150 
1151     if(!no_ifaceid_neg) {
1152 	if (eui64_iszero(ho->hisid)) {
1153 	    error("Could not determine remote LL address");
1154 	    ipv6cp_close(f->unit, "Could not determine remote LL address");
1155 	    return;
1156 	}
1157 	if (eui64_iszero(go->ourid)) {
1158 	    error("Could not determine local LL address");
1159 	    ipv6cp_close(f->unit, "Could not determine local LL address");
1160 	    return;
1161 	}
1162 	if (eui64_equals(go->ourid, ho->hisid)) {
1163 	    error("local and remote LL addresses are equal");
1164 	    ipv6cp_close(f->unit, "local and remote LL addresses are equal");
1165 	    return;
1166 	}
1167     }
1168 
1169 #ifdef IPV6CP_COMP
1170     /* set tcp compression */
1171     if (sif6comp(f->unit, ho->neg_vj) != 1) {
1172 	ipv6cp_close(f->unit, "Could not enable TCP compression");
1173 	return;
1174     }
1175 #endif
1176 
1177     /*
1178      * If we are doing dial-on-demand, the interface is already
1179      * configured, so we put out any saved-up packets, then set the
1180      * interface to pass IPv6 packets.
1181      */
1182     if (demand) {
1183 	if (! eui64_equals(go->ourid, wo->ourid) ||
1184 	    ! eui64_equals(ho->hisid, wo->hisid)) {
1185 	    if (! eui64_equals(go->ourid, wo->ourid))
1186 		warn("Local LL address changed to %s",
1187 		     llv6_ntoa(go->ourid));
1188 	    if (! eui64_equals(ho->hisid, wo->hisid))
1189 		warn("Remote LL address changed to %s",
1190 		     llv6_ntoa(ho->hisid));
1191 	    ipv6cp_clear_addrs(f->unit, go->ourid, ho->hisid);
1192 
1193 	    /* Set the interface to the new addresses */
1194 	    if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
1195 		if (debug)
1196 		    warn("sif6addr failed");
1197 		ipv6cp_close(f->unit, "Interface configuration failed");
1198 		return;
1199 	    }
1200 
1201 	}
1202 	demand_rexmit(PPP_IPV6);
1203 	if (sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS) != 1) {
1204 	    ipv6cp_close(f->unit, "Interface configuration failed");
1205 	    return;
1206 	}
1207 
1208     } else {
1209 	/*
1210 	 * Set LL addresses
1211 	 */
1212 #if !SIF6UPFIRST
1213 	if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
1214 	    if (debug)
1215 		warn("sif6addr failed");
1216 	    ipv6cp_close(f->unit, "Interface configuration failed");
1217 	    return;
1218 	}
1219 #endif
1220 #if defined(SOL2)
1221 	/* bring the interface up for IPv6 */
1222 	if (!sif6up(f->unit)) {
1223 	    if (debug)
1224 		warn("sifup failed (IPV6)");
1225 	    ipv6cp_close(f->unit, "Interface configuration failed");
1226 	    return;
1227 	}
1228 #else
1229 	if (!sifup(f->unit)) {
1230 	    if (debug)
1231 		warn("sifup failed (IPV6)");
1232 	    ipv6cp_close(f->unit, "Interface configuration failed");
1233 	    return;
1234 	}
1235 #endif
1236 #if SIF6UPFIRST
1237 	if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
1238 	    if (debug)
1239 		warn("sif6addr failed");
1240 	    ipv6cp_close(f->unit, "Interface configuration failed");
1241 	    return;
1242 	}
1243 #endif
1244 	if (sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS) != 1) {
1245 	    ipv6cp_close(f->unit, "Interface configuration failed");
1246 	    return;
1247 	}
1248 
1249 	notice("local  LL address %s", llv6_ntoa(go->ourid));
1250 	notice("remote LL address %s", llv6_ntoa(ho->hisid));
1251     }
1252 
1253     np_up(f->unit, PPP_IPV6);
1254     ipv6cp_is_up = 1;
1255 
1256     /*
1257      * Execute the ipv6-up script, like this:
1258      *	/etc/ppp/ipv6-up interface tty speed local-LL remote-LL
1259      */
1260     script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0);
1261     script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0);
1262     if (ipv6cp_script_state == s_down && ipv6cp_script_pid == 0) {
1263 	ipv6cp_script_state = s_up;
1264 	ipv6cp_script(_PATH_IPV6UP);
1265     }
1266     sys_unblock_proto(PPP_IPV6);
1267 }
1268 
1269 
1270 /*
1271  * ipv6cp_down - IPV6CP has gone DOWN.
1272  *
1273  * Take the IPv6 network interface down, clear its addresses
1274  * and delete routes through it.
1275  */
1276 static void
1277 ipv6cp_down(f)
1278     fsm *f;
1279 {
1280     IPV6CPDEBUG(("ipv6cp: down"));
1281     update_link_stats(f->unit);
1282     if (ipv6cp_is_up) {
1283 	ipv6cp_is_up = 0;
1284 	np_down(f->unit, PPP_IPV6);
1285     }
1286 #ifdef IPV6CP_COMP
1287     if (sif6comp(f->unit, 0) != 1) {
1288 	if (debug)
1289 	    warn("Failed to disable TCP compression.");
1290     }
1291 #endif
1292 
1293     /*
1294      * If we are doing dial-on-demand, set the interface
1295      * to queue up outgoing packets (for now).
1296      */
1297     if (demand) {
1298 	if (sifnpmode(f->unit, PPP_IPV6, NPMODE_QUEUE) != 1) {
1299 	    if (debug)
1300 		warn("Failed to enable queueing on outgoing packets.");
1301 	}
1302     } else {
1303 	if (sifnpmode(f->unit, PPP_IPV6, NPMODE_ERROR) != 1) {
1304 	    if (debug)
1305 		warn("Could not set interface to drop packets.");
1306 	}
1307 #if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC)))
1308 #if defined(SOL2)
1309 	if (sif6down(f->unit) != 1)
1310 	    warn("Couldn not bring interface down.");
1311 #else
1312 	if (sifdown(f->unit) != 1)
1313 	    warn("Could not bring interface down.");
1314 #endif /* defined(SOL2) */
1315 #endif
1316 	ipv6cp_clear_addrs(f->unit,
1317 			   ipv6cp_gotoptions[f->unit].ourid,
1318 			   ipv6cp_hisoptions[f->unit].hisid);
1319 #if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC)))
1320 	if (sifdown(f->unit) != 1)
1321 	    warn("Could not bring interface down.");
1322 #endif
1323     }
1324 
1325     /* Execute the ipv6-down script */
1326     if (ipv6cp_script_state == s_up && ipv6cp_script_pid == 0) {
1327 	ipv6cp_script_state = s_down;
1328 	ipv6cp_script(_PATH_IPV6DOWN);
1329     }
1330 }
1331 
1332 
1333 /*
1334  * ipv6cp_clear_addrs() - clear the interface addresses, routes,
1335  * proxy neighbour discovery entries, etc.
1336  */
1337 static void
1338 ipv6cp_clear_addrs(unit, ourid, hisid)
1339     int unit;
1340     eui64_t ourid;
1341     eui64_t hisid;
1342 {
1343     if (cif6addr(unit, ourid, hisid) != 1)
1344 	warn("Could not clear addresses");
1345 }
1346 
1347 
1348 /*
1349  * ipv6cp_finished - possibly shut down the lower layers.
1350  */
1351 static void
1352 ipv6cp_finished(f)
1353     fsm *f;
1354 {
1355     np_finished(f->unit, PPP_IPV6);
1356 }
1357 
1358 
1359 /*
1360  * ipv6cp_script_done - called when the ipv6-up or ipv6-down script
1361  * has finished.
1362  */
1363 /*ARGSUSED*/
1364 static void
1365 ipv6cp_script_done(arg, status)
1366     void *arg;
1367     int status;
1368 {
1369     ipv6cp_script_pid = 0;
1370     switch (ipv6cp_script_state) {
1371     case s_up:
1372 	if (ipv6cp_fsm[0].state != OPENED) {
1373 	    ipv6cp_script_state = s_down;
1374 	    ipv6cp_script(_PATH_IPV6DOWN);
1375 	}
1376 	break;
1377     case s_down:
1378 	if (ipv6cp_fsm[0].state == OPENED) {
1379 	    ipv6cp_script_state = s_up;
1380 	    ipv6cp_script(_PATH_IPV6UP);
1381 	}
1382 	break;
1383     }
1384 }
1385 
1386 
1387 /*
1388  * ipv6cp_script - Execute a script with arguments
1389  * interface-name tty-name speed local-LL remote-LL.
1390  */
1391 static void
1392 ipv6cp_script(script)
1393     char *script;
1394 {
1395     char strspeed[32], strlocal[26], strremote[26];
1396     char *argv[8];
1397 
1398     (void) slprintf(strspeed, sizeof (strspeed), "%d", baud_rate);
1399     (void) strlcpy(strlocal, llv6_ntoa(ipv6cp_gotoptions[0].ourid),
1400 	sizeof (strlocal));
1401     (void) strlcpy(strremote, llv6_ntoa(ipv6cp_hisoptions[0].hisid),
1402 	sizeof (strremote));
1403 
1404     argv[0] = script;
1405     argv[1] = ifname;
1406     argv[2] = devnam;
1407     argv[3] = strspeed;
1408     argv[4] = strlocal;
1409     argv[5] = strremote;
1410     argv[6] = ipparam;
1411     argv[7] = NULL;
1412 
1413     ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done, NULL);
1414 }
1415 
1416 static int
1417 ipv6cp_printpkt(p, plen, printer, arg)
1418     u_char *p;
1419     int plen;
1420     void (*printer) __P((void *, const char *, ...));
1421     void *arg;
1422 {
1423     int code, id, len, olen;
1424     u_char *pstart, *optend;
1425     u_short cishort;
1426     eui64_t ifaceid;
1427 
1428     if (plen < HEADERLEN)
1429 	return 0;
1430     pstart = p;
1431     GETCHAR(code, p);
1432     GETCHAR(id, p);
1433     GETSHORT(len, p);
1434     if (len < HEADERLEN || len > plen)
1435 	return 0;
1436 
1437 
1438     printer(arg, " %s id=0x%x", code_name(code, 1), id);
1439     len -= HEADERLEN;
1440     switch (code) {
1441     case CODE_CONFREQ:
1442     case CODE_CONFACK:
1443     case CODE_CONFNAK:
1444     case CODE_CONFREJ:
1445 	/* print option list */
1446 	while (len >= 2) {
1447 	    GETCHAR(code, p);
1448 	    GETCHAR(olen, p);
1449 	    p -= 2;
1450 	    if (olen < 2 || olen > len) {
1451 		break;
1452 	    }
1453 	    printer(arg, " <");
1454 	    len -= olen;
1455 	    optend = p + olen;
1456 	    switch (code) {
1457 	    case CI_COMPRESSTYPE:
1458 		if (olen >= CILEN_COMPRESS) {
1459 		    p += 2;
1460 		    GETSHORT(cishort, p);
1461 		    printer(arg, "compress 0x%x", cishort);
1462 		}
1463 		break;
1464 	    case CI_IFACEID:
1465 		if (olen == CILEN_IFACEID) {
1466 		    p += 2;
1467 		    eui64_get(ifaceid, p);
1468 		    printer(arg, "addr %s", llv6_ntoa(ifaceid));
1469 		}
1470 		break;
1471 	    }
1472 	    printer(arg, "%8.*B>", optend-p, p);
1473 	    p = optend;
1474 	}
1475 	break;
1476 
1477     case CODE_TERMACK:
1478     case CODE_TERMREQ:
1479 	if (len > 0 && *p >= ' ' && *p < 0x7f) {
1480 	    printer(arg, " ");
1481 	    print_string((char *)p, len, printer, arg);
1482 	    p += len;
1483 	    len = 0;
1484 	}
1485 	break;
1486     }
1487 
1488     /* print the rest of the bytes in the packet */
1489     printer(arg, " %32.*B", len, p);
1490 
1491     return p - pstart;
1492 }
1493 
1494 /*
1495  * ipv6_active_pkt - see if this IP packet is worth bringing the link up for.
1496  * We don't bring the link up for IP fragments or for TCP FIN packets
1497  * with no data.
1498  */
1499 #define TCP_HDRLEN	20
1500 #define TH_FIN		0x01
1501 
1502 static int
1503 ipv6_active_pkt(pkt, len)
1504     u_char *pkt;
1505     int len;
1506 {
1507     u_char *tcp;
1508     struct in6_addr addr;
1509     char fromstr[26];
1510     char tostr[26];
1511 
1512     len -= PPP_HDRLEN;
1513     pkt += PPP_HDRLEN;
1514     if (len < IP6_HDRLEN) {
1515 	dbglog("IPv6 packet of length %d is not activity", len);
1516 	return 0;
1517     }
1518     (void) BCOPY(get_ip6src(pkt), &addr, sizeof (addr));
1519     (void) inet_ntop(AF_INET6, &addr, fromstr, 26);
1520     (void) BCOPY(get_ip6dst(pkt), &addr, sizeof (addr));
1521     (void) inet_ntop(AF_INET6, &addr, tostr, 26);
1522     if (get_ip6nh(pkt) == IPPROTO_FRAGMENT) {
1523 	dbglog("IPv6 fragment from %s->%s is not activity", fromstr, tostr);
1524 	return 0;
1525     }
1526     if (get_ip6nh(pkt) != IPPROTO_TCP) {
1527 	info("IPv6 proto %d from %s->%s is activity", get_ip6nh(pkt), fromstr,
1528 	    tostr);
1529 	return 1;
1530     }
1531     if (len < IP6_HDRLEN + TCP_HDRLEN) {
1532 	dbglog("Bad TCP length %d<%d+%d %s->%s is not activity", len,
1533 	    IP6_HDRLEN, TCP_HDRLEN, fromstr, tostr);
1534 	return 0;
1535     }
1536     tcp = pkt + IP6_HDRLEN;
1537     if ((get_tcpflags(tcp) & TH_FIN) != 0 &&
1538 	len == IP6_HDRLEN + get_tcpoff(tcp) * 4) {
1539 	dbglog("Empty TCP FIN %s->%s is not activity", fromstr, tostr);
1540 	return 0;
1541     }
1542     info("TCP %d data %s%s->%s is activity", len - IP6_HDRLEN - TCP_HDRLEN,
1543 	tcp_flag_decode(get_tcpflags(tcp)), fromstr, tostr);
1544     return 1;
1545 }
1546