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