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