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