aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHajimu UMEMOTO <ume@FreeBSD.org>2006-11-12 17:13:45 +0000
committerHajimu UMEMOTO <ume@FreeBSD.org>2006-11-12 17:13:45 +0000
commit1b1804f84b43e281dee48aeeb674959fd83d421e (patch)
treee3b3046f87b4a57d3e4812e0b2e8943f131b90bb
parentcbe7f651a6c2c5a59166b660fe0826ef1e895bcc (diff)
downloadsrc-vendor/pppd.tar.gz
src-vendor/pppd.zip
Import eui64.[ch] and ipv6cp.[ch] of ppp-2.3.11vendor/pppd
Notes
Notes: svn path=/vendor/pppd/dist/; revision=164219
-rw-r--r--usr.sbin/pppd/eui64.c40
-rw-r--r--usr.sbin/pppd/eui64.h97
-rw-r--r--usr.sbin/pppd/ipv6cp.c1478
-rw-r--r--usr.sbin/pppd/ipv6cp.h126
4 files changed, 1741 insertions, 0 deletions
diff --git a/usr.sbin/pppd/eui64.c b/usr.sbin/pppd/eui64.c
new file mode 100644
index 000000000000..a59186d8d565
--- /dev/null
+++ b/usr.sbin/pppd/eui64.c
@@ -0,0 +1,40 @@
+/*
+ eui64.c - EUI64 routines for IPv6CP.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms and that any documentation,
+ advertising materials, and other materials related to such
+ distribution and use acknowledge that the software was developed
+ by Tommi Komulainen. The name of the author may not be used
+ to endorse or promote products derived from this software without
+ specific prior written permission.
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+
+ $Id: eui64.c,v 1.3 1999/08/25 04:15:51 paulus Exp $
+*/
+
+#define RCSID "$Id: eui64.c,v 1.3 1999/08/25 04:15:51 paulus Exp $"
+
+#include "pppd.h"
+
+static const char rcsid[] = RCSID;
+
+/*
+ * eui64_ntoa - Make an ascii representation of an interface identifier
+ */
+char *
+eui64_ntoa(e)
+ eui64_t e;
+{
+ static char buf[32];
+
+ snprintf(buf, 32, "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+ e.e8[0], e.e8[1], e.e8[2], e.e8[3],
+ e.e8[4], e.e8[5], e.e8[6], e.e8[7]);
+ return buf;
+}
diff --git a/usr.sbin/pppd/eui64.h b/usr.sbin/pppd/eui64.h
new file mode 100644
index 000000000000..efdf7441f4ba
--- /dev/null
+++ b/usr.sbin/pppd/eui64.h
@@ -0,0 +1,97 @@
+/*
+ eui64.h - EUI64 routines for IPv6CP.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms and that any documentation,
+ advertising materials, and other materials related to such
+ distribution and use acknowledge that the software was developed
+ by Tommi Komulainen. The name of the author may not be used
+ to endorse or promote products derived from this software without
+ specific prior written permission.
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+
+ $Id: eui64.h,v 1.3 1999/09/30 19:56:37 masputra Exp $
+*/
+
+#ifndef __EUI64_H__
+#define __EUI64_H__
+
+#if !defined(INET6)
+#error "this file should only be included when INET6 is defined"
+#endif /* not defined(INET6) */
+
+#if defined(SOL2)
+#include <netinet/in.h>
+
+typedef union {
+ uint8_t e8[8]; /* lower 64-bit IPv6 address */
+ uint32_t e32[2]; /* lower 64-bit IPv6 address */
+} eui64_t;
+
+/*
+ * Declare the two below, since in.h only defines them when _KERNEL
+ * is declared - which shouldn't be true when dealing with user-land programs
+ */
+#define s6_addr8 _S6_un._S6_u8
+#define s6_addr32 _S6_un._S6_u32
+
+#else /* else if not defined(SOL2) */
+
+/*
+ * TODO:
+ *
+ * Maybe this should be done by processing struct in6_addr directly...
+ */
+typedef union
+{
+ u_int8_t e8[8];
+ u_int16_t e16[4];
+ u_int32_t e32[2];
+} eui64_t;
+
+#endif /* defined(SOL2) */
+
+#define eui64_iszero(e) (((e).e32[0] | (e).e32[1]) == 0)
+#define eui64_equals(e, o) (((e).e32[0] == (o).e32[0]) && \
+ ((e).e32[1] == (o).e32[1]))
+#define eui64_zero(e) (e).e32[0] = (e).e32[1] = 0;
+
+#define eui64_copy(s, d) memcpy(&(d), &(s), sizeof(eui64_t))
+
+#define eui64_magic(e) do { \
+ (e).e32[0] = magic(); \
+ (e).e32[1] = magic(); \
+ (e).e8[0] &= ~2; \
+ } while (0)
+#define eui64_magic_nz(x) do { \
+ eui64_magic(x); \
+ } while (eui64_iszero(x))
+#define eui64_magic_ne(x, y) do { \
+ eui64_magic(x); \
+ } while (eui64_equals(x, y))
+
+#define eui64_get(ll, cp) do { \
+ eui64_copy((*cp), (ll)); \
+ (cp) += sizeof(eui64_t); \
+ } while (0)
+
+#define eui64_put(ll, cp) do { \
+ eui64_copy((ll), (*cp)); \
+ (cp) += sizeof(eui64_t); \
+ } while (0)
+
+#define eui64_set32(e, l) do { \
+ (e).e32[0] = 0; \
+ (e).e32[1] = htonl(l); \
+ } while (0)
+#define eui64_setlo32(e, l) eui64_set32(e, l)
+
+char *eui64_ntoa __P((eui64_t)); /* Returns ascii representation of id */
+
+#endif /* __EUI64_H__ */
+
diff --git a/usr.sbin/pppd/ipv6cp.c b/usr.sbin/pppd/ipv6cp.c
new file mode 100644
index 000000000000..326ff65d572c
--- /dev/null
+++ b/usr.sbin/pppd/ipv6cp.c
@@ -0,0 +1,1478 @@
+/*
+ ipv6cp.c - PPP IPV6 Control Protocol.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms. The name of the author may not be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+/* Original version, based on RFC2023 :
+
+ Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt
+ Économique ayant pour membres BULL S.A. et l'INRIA).
+
+ Ce logiciel informatique est disponible aux conditions
+ usuelles dans la recherche, c'est-à-dire qu'il peut
+ être utilisé, copié, modifié, distribué à l'unique
+ condition que ce texte soit conservé afin que
+ l'origine de ce logiciel soit reconnue.
+
+ Le nom de l'Institut National de Recherche en Informatique
+ et en Automatique (INRIA), de l'IMAG, ou d'une personne morale
+ ou physique ayant participé à l'élaboration de ce logiciel ne peut
+ être utilisé sans son accord préalable explicite.
+
+ Ce logiciel est fourni tel quel sans aucune garantie,
+ support ou responsabilité d'aucune sorte.
+ Ce logiciel est dérivé de sources d'origine
+ "University of California at Berkeley" et
+ "Digital Equipment Corporation" couvertes par des copyrights.
+
+ L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG)
+ est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National
+ Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant
+ sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR).
+
+ This work has been done in the context of GIE DYADE (joint R & D venture
+ between BULL S.A. and INRIA).
+
+ This software is available with usual "research" terms
+ with the aim of retain credits of the software.
+ Permission to use, copy, modify and distribute this software for any
+ purpose and without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies,
+ and the name of INRIA, IMAG, or any contributor not be used in advertising
+ or publicity pertaining to this material without the prior explicit
+ permission. The software is provided "as is" without any
+ warranties, support or liabilities of any kind.
+ This software is derived from source code from
+ "University of California at Berkeley" and
+ "Digital Equipment Corporation" protected by copyrights.
+
+ Grenoble's Institute of Computer Science and Applied Mathematics (IMAG)
+ is a federation of seven research units funded by the CNRS, National
+ Polytechnic Institute of Grenoble and University Joseph Fourier.
+ The research unit in Software, Systems, Networks (LSR) is member of IMAG.
+*/
+
+/*
+ * Derived from :
+ *
+ *
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ipv6cp.c,v 1.7 1999/10/08 01:08:18 masputra Exp $
+ */
+
+#define RCSID "$Id: ipv6cp.c,v 1.7 1999/10/08 01:08:18 masputra Exp $"
+
+/*
+ * TODO:
+ *
+ * Proxy Neighbour Discovery.
+ *
+ * Better defines for selecting the ordering of
+ * interface up / set address. (currently checks for __linux__,
+ * since SVR4 && (SNI || __USLC__) didn't work properly)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "magic.h"
+#include "pathnames.h"
+
+static const char rcsid[] = RCSID;
+
+/* global vars */
+ipv6cp_options ipv6cp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipv6cp_options ipv6cp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipv6cp_options ipv6cp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipv6cp_options ipv6cp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+int no_ifaceid_neg = 0;
+
+/* local vars */
+static int ipv6cp_is_up;
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipv6cp_resetci __P((fsm *)); /* Reset our CI */
+static int ipv6cp_cilen __P((fsm *)); /* Return length of our CI */
+static void ipv6cp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
+static int ipv6cp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int ipv6cp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int ipv6cp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int ipv6cp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipv6cp_up __P((fsm *)); /* We're UP */
+static void ipv6cp_down __P((fsm *)); /* We're DOWN */
+static void ipv6cp_finished __P((fsm *)); /* Don't need lower layer */
+
+fsm ipv6cp_fsm[NUM_PPP]; /* IPV6CP fsm structure */
+
+static fsm_callbacks ipv6cp_callbacks = { /* IPV6CP callback routines */
+ ipv6cp_resetci, /* Reset our Configuration Information */
+ ipv6cp_cilen, /* Length of our Configuration Information */
+ ipv6cp_addci, /* Add our Configuration Information */
+ ipv6cp_ackci, /* ACK our Configuration Information */
+ ipv6cp_nakci, /* NAK our Configuration Information */
+ ipv6cp_rejci, /* Reject our Configuration Information */
+ ipv6cp_reqci, /* Request peer's Configuration Information */
+ ipv6cp_up, /* Called when fsm reaches OPENED state */
+ ipv6cp_down, /* Called when fsm leaves OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ ipv6cp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPV6CP" /* String name of protocol */
+};
+
+/*
+ * Command-line options.
+ */
+static int setifaceid __P((char **arg));
+
+static option_t ipv6cp_option_list[] = {
+ { "ipv6", o_special, setifaceid,
+ "Set interface identifiers for IPV6" },
+ { "noipv6", o_bool, &ipv6cp_protent.enabled_flag,
+ "Disable IPv6 and IPv6CP" },
+ { "-ipv6", o_bool, &ipv6cp_protent.enabled_flag,
+ "Disable IPv6 and IPv6CP" },
+ { "+ipv6", o_bool, &ipv6cp_protent.enabled_flag,
+ "Enable IPv6 and IPv6CP", 1 },
+
+ { "ipv6cp-accept-local", o_bool, &ipv6cp_allowoptions[0].accept_local,
+ "Accept peer's interface identifier for us", 1 },
+ { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip,
+ "Use (default) IPv4 address as interface identifier", 0 },
+#if defined(SOL2)
+ { "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent,
+ "Use uniquely-available persistent value for link local address", 1 },
+#endif /* defined(SOL2) */
+ { "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime,
+ "Set timeout for IPv6CP" },
+ { "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits,
+ "Set max #xmits for term-reqs" },
+ { "ipv6cp-max-configure", o_int, &ipv6cp_fsm[0].maxconfreqtransmits,
+ "Set max #xmits for conf-reqs" },
+ { "ipv6cp-max-failure", o_int, &ipv6cp_fsm[0].maxnakloops,
+ "Set max #conf-naks for IPv6CP" },
+
+ { NULL }
+};
+
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipv6cp_init __P((int));
+static void ipv6cp_open __P((int));
+static void ipv6cp_close __P((int, char *));
+static void ipv6cp_lowerup __P((int));
+static void ipv6cp_lowerdown __P((int));
+static void ipv6cp_input __P((int, u_char *, int));
+static void ipv6cp_protrej __P((int));
+static int ipv6cp_printpkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+static void ipv6_check_options __P((void));
+static int ipv6_demand_conf __P((int));
+static int ipv6_active_pkt __P((u_char *, int));
+
+struct protent ipv6cp_protent = {
+ PPP_IPV6CP,
+ ipv6cp_init,
+ ipv6cp_input,
+ ipv6cp_protrej,
+ ipv6cp_lowerup,
+ ipv6cp_lowerdown,
+ ipv6cp_open,
+ ipv6cp_close,
+ ipv6cp_printpkt,
+ NULL,
+ 0,
+ "IPV6CP",
+ "IPV6",
+ ipv6cp_option_list,
+ ipv6_check_options,
+ ipv6_demand_conf,
+ ipv6_active_pkt
+};
+
+static void ipv6cp_clear_addrs __P((int, eui64_t, eui64_t));
+static void ipv6cp_script __P((char *));
+static void ipv6cp_script_done __P((void *));
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID 2
+#define CILEN_COMPRESS 4 /* length for RFC2023 compress opt. */
+#define CILEN_IFACEID 10 /* RFC2472, interface identifier */
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+/*
+ * This state variable is used to ensure that we don't
+ * run an ipcp-up/down script while one is already running.
+ */
+static enum script_state {
+ s_down,
+ s_up,
+} ipv6cp_script_state;
+static pid_t ipv6cp_script_pid;
+
+/*
+ * setifaceid - set the interface identifiers manually
+ */
+static int
+setifaceid(argv)
+ char **argv;
+{
+ char *comma, *arg;
+ ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+ struct in6_addr addr;
+
+#define VALIDID(a) ( (((a).s6_addr32[0] == 0) && ((a).s6_addr32[1] == 0)) && \
+ (((a).s6_addr32[2] != 0) || ((a).s6_addr32[3] != 0)) )
+
+ arg = *argv;
+ if ((comma = strchr(arg, ',')) == NULL)
+ comma = arg + strlen(arg);
+
+ /*
+ * If comma first character, then no local identifier
+ */
+ if (comma != arg) {
+ *comma = '\0';
+
+ if (inet_pton(AF_INET6, arg, &addr) == 0 || !VALIDID(addr)) {
+ option_error("Illegal interface identifier (local): %s", arg);
+ return 0;
+ }
+
+ eui64_copy(addr.s6_addr32[2], wo->ourid);
+ wo->opt_local = 1;
+ *comma = ',';
+ }
+
+ /*
+ * If comma last character, the no remote identifier
+ */
+ if (*comma != 0 && *++comma != '\0') {
+ if (inet_pton(AF_INET6, comma, &addr) == 0 || !VALIDID(addr)) {
+ option_error("Illegal interface identifier (remote): %s", comma);
+ return 0;
+ }
+ eui64_copy(addr.s6_addr32[2], wo->hisid);
+ wo->opt_remote = 1;
+ }
+
+ ipv6cp_protent.enabled_flag = 1;
+ return 1;
+}
+
+/*
+ * Make a string representation of a network address.
+ */
+char *
+llv6_ntoa(ifaceid)
+ eui64_t ifaceid;
+{
+ static char b[64];
+
+ sprintf(b, "fe80::%s", eui64_ntoa(ifaceid));
+ return b;
+}
+
+
+/*
+ * ipv6cp_init - Initialize IPV6CP.
+ */
+static void
+ipv6cp_init(unit)
+ int unit;
+{
+ fsm *f = &ipv6cp_fsm[unit];
+ ipv6cp_options *wo = &ipv6cp_wantoptions[unit];
+ ipv6cp_options *ao = &ipv6cp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPV6CP;
+ f->callbacks = &ipv6cp_callbacks;
+ fsm_init(&ipv6cp_fsm[unit]);
+
+ memset(wo, 0, sizeof(*wo));
+ memset(ao, 0, sizeof(*ao));
+
+ wo->accept_local = 1;
+ wo->neg_ifaceid = 1;
+ ao->neg_ifaceid = 1;
+
+#ifdef IPV6CP_COMP
+ wo->neg_vj = 1;
+ ao->neg_vj = 1;
+ wo->vj_protocol = IPV6CP_COMP;
+#endif
+
+}
+
+
+/*
+ * ipv6cp_open - IPV6CP is allowed to come up.
+ */
+static void
+ipv6cp_open(unit)
+ int unit;
+{
+ fsm_open(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_close - Take IPV6CP down.
+ */
+static void
+ipv6cp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm_close(&ipv6cp_fsm[unit], reason);
+}
+
+
+/*
+ * ipv6cp_lowerup - The lower layer is up.
+ */
+static void
+ipv6cp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_lowerdown - The lower layer is down.
+ */
+static void
+ipv6cp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_input - Input IPV6CP packet.
+ */
+static void
+ipv6cp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm_input(&ipv6cp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipv6cp_protrej - A Protocol-Reject was received for IPV6CP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipv6cp_protrej(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_resetci - Reset our CI.
+ */
+static void
+ipv6cp_resetci(f)
+ fsm *f;
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+
+ wo->req_ifaceid = wo->neg_ifaceid && ipv6cp_allowoptions[f->unit].neg_ifaceid;
+
+ if (!wo->opt_local) {
+ eui64_magic_nz(wo->ourid);
+ }
+
+ *go = *wo;
+ eui64_zero(go->hisid); /* last proposed interface identifier */
+}
+
+
+/*
+ * ipv6cp_cilen - Return length of our CI.
+ */
+static int
+ipv6cp_cilen(f)
+ fsm *f;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+
+#define LENCIVJ(neg) (neg ? CILEN_COMPRESS : 0)
+#define LENCIIFACEID(neg) (neg ? CILEN_IFACEID : 0)
+
+ return (LENCIIFACEID(go->neg_ifaceid) +
+ LENCIVJ(go->neg_vj));
+}
+
+
+/*
+ * ipv6cp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipv6cp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ int len = *lenp;
+
+#define ADDCIVJ(opt, neg, val) \
+ if (neg) { \
+ int vjlen = CILEN_COMPRESS; \
+ if (len >= vjlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(vjlen, ucp); \
+ PUTSHORT(val, ucp); \
+ len -= vjlen; \
+ } else \
+ neg = 0; \
+ }
+
+#define ADDCIIFACEID(opt, neg, val1) \
+ if (neg) { \
+ int idlen = CILEN_IFACEID; \
+ if (len >= idlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(idlen, ucp); \
+ eui64_put(val1, ucp); \
+ len -= idlen; \
+ } else \
+ neg = 0; \
+ }
+
+ ADDCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
+
+ ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
+
+ *lenp -= len;
+}
+
+
+/*
+ * ipv6cp_ackci - Ack our CIs.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipv6cp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_short cilen, citype, cishort;
+ eui64_t ifaceid;
+
+ /*
+ * CIs must be in exactly the same order that we sent...
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+
+#define ACKCIVJ(opt, neg, val) \
+ if (neg) { \
+ int vjlen = CILEN_COMPRESS; \
+ if ((len -= vjlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != vjlen || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ }
+
+#define ACKCIIFACEID(opt, neg, val1) \
+ if (neg) { \
+ int idlen = CILEN_IFACEID; \
+ if ((len -= idlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != idlen || \
+ citype != opt) \
+ goto bad; \
+ eui64_get(ifaceid, p); \
+ if (! eui64_equals(val1, ifaceid)) \
+ goto bad; \
+ }
+
+ ACKCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
+
+ ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_ackci: received bad Ack!"));
+ return (0);
+}
+
+/*
+ * ipv6cp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPV6CP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+ipv6cp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_char citype, cilen, *next;
+ u_short cishort;
+ eui64_t ifaceid;
+ ipv6cp_options no; /* options we've seen Naks for */
+ ipv6cp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIIFACEID(opt, neg, code) \
+ if (go->neg && \
+ len >= (cilen = CILEN_IFACEID) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ eui64_get(ifaceid, p); \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIVJ(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_COMPRESS) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * Accept the peer's idea of {our,his} interface identifier, if different
+ * from our idea, only if the accept_{local,remote} flag is set.
+ */
+ NAKCIIFACEID(CI_IFACEID, neg_ifaceid,
+ if (go->accept_local) {
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->hisid)) /* bad luck */
+ eui64_magic(ifaceid);
+ try.ourid = ifaceid;
+ IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid)));
+ }
+ );
+
+#ifdef IPV6CP_COMP
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ {
+ if (cishort == IPV6CP_COMP) {
+ try.vj_protocol = cishort;
+ } else {
+ try.neg_vj = 0;
+ }
+ }
+ );
+#else
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ {
+ try.neg_vj = 0;
+ }
+ );
+#endif
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If they want to negotiate about interface identifier, we comply.
+ * If they want us to ask for compression, we refuse.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if( (len -= cilen) < 0 )
+ goto bad;
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_COMPRESSTYPE:
+ if (go->neg_vj || no.neg_vj ||
+ (cilen != CILEN_COMPRESS))
+ goto bad;
+ no.neg_vj = 1;
+ break;
+ case CI_IFACEID:
+ if (go->neg_ifaceid || no.neg_ifaceid || cilen != CILEN_IFACEID)
+ goto bad;
+ try.neg_ifaceid = 1;
+ eui64_get(ifaceid, p);
+ if (go->accept_local) {
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->hisid)) /* bad luck */
+ eui64_magic(ifaceid);
+ try.ourid = ifaceid;
+ }
+ no.neg_ifaceid = 1;
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0)
+ goto bad;
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_nakci: received bad Nak!"));
+ return 0;
+}
+
+
+/*
+ * ipv6cp_rejci - Reject some of our CIs.
+ */
+static int
+ipv6cp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_char cilen;
+ u_short cishort;
+ eui64_t ifaceid;
+ ipv6cp_options try; /* options to request next time */
+
+ try = *go;
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIIFACEID(opt, neg, val1) \
+ if (go->neg && \
+ len >= (cilen = CILEN_IFACEID) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ eui64_get(ifaceid, p); \
+ /* Check rejected value. */ \
+ if (! eui64_equals(ifaceid, val1)) \
+ goto bad; \
+ try.neg = 0; \
+ }
+
+#define REJCIVJ(opt, neg, val) \
+ if (go->neg && \
+ p[1] == CILEN_COMPRESS && \
+ len >= p[1] && \
+ p[0] == opt) { \
+ len -= p[1]; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) \
+ goto bad; \
+ try.neg = 0; \
+ }
+
+ REJCIIFACEID(CI_IFACEID, neg_ifaceid, go->ourid);
+
+ REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_rejci: received bad Reject!"));
+ return 0;
+}
+
+
+/*
+ * ipv6cp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipv6cp_reqci(f, inp, len, reject_if_disagree)
+ fsm *f;
+ u_char *inp; /* Requested CIs */
+ int *len; /* Length of requested CIs */
+ int reject_if_disagree;
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
+ ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
+ ipv6cp_options *ao = &ipv6cp_allowoptions[f->unit];
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+ u_short cishort; /* Parsed short value */
+ eui64_t ifaceid; /* Parsed interface identifier */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *ucp = inp; /* Pointer to current output char */
+ int l = *len; /* Length left */
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ IPV6CPDEBUG(("ipv6cp_reqci: bad CI length!"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+ case CI_IFACEID:
+ IPV6CPDEBUG(("ipv6cp: received interface identifier "));
+
+ if (!ao->neg_ifaceid ||
+ cilen != CILEN_IFACEID) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no interface identifier, or if we both have same
+ * identifier then NAK it with new idea.
+ * In particular, if we don't know his identifier, but he does,
+ * then accept it.
+ */
+ eui64_get(ifaceid, p);
+ IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid)));
+ if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) {
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ if (!eui64_iszero(wo->hisid) &&
+ !eui64_equals(ifaceid, wo->hisid) &&
+ eui64_iszero(go->hisid)) {
+
+ orc = CONFNAK;
+ ifaceid = wo->hisid;
+ go->hisid = ifaceid;
+ DECPTR(sizeof(ifaceid), p);
+ eui64_put(ifaceid, p);
+ } else
+ if (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->ourid)) {
+ orc = CONFNAK;
+ if (eui64_iszero(go->hisid)) /* first time, try option */
+ ifaceid = wo->hisid;
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->ourid)) /* bad luck */
+ eui64_magic(ifaceid);
+ go->hisid = ifaceid;
+ DECPTR(sizeof(ifaceid), p);
+ eui64_put(ifaceid, p);
+ }
+
+ ho->neg_ifaceid = 1;
+ ho->hisid = ifaceid;
+ break;
+
+ case CI_COMPRESSTYPE:
+ IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE "));
+ if (!ao->neg_vj ||
+ (cilen != CILEN_COMPRESS)) {
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+ IPV6CPDEBUG(("(%d)", cishort));
+
+#ifdef IPV6CP_COMP
+ if (!(cishort == IPV6CP_COMP)) {
+ orc = CONFREJ;
+ break;
+ }
+#else
+ orc = CONFREJ;
+ break;
+#endif
+
+ ho->neg_vj = 1;
+ ho->vj_protocol = cishort;
+ break;
+
+ default:
+ orc = CONFREJ;
+ break;
+ }
+
+endswitch:
+ IPV6CPDEBUG((" (%s)\n", CODENAME(orc)));
+
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree) /* Getting fed up with sending NAKs? */
+ orc = CONFREJ; /* Get tough if so */
+ else {
+ if (rc == CONFREJ) /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ if (rc == CONFACK) { /* Ack'd all prior CIs? */
+ rc = CONFNAK; /* Not anymore... */
+ ucp = inp; /* Backup */
+ }
+ }
+ }
+
+ if (orc == CONFREJ && /* Reject this CI */
+ rc != CONFREJ) { /* but no prior ones? */
+ rc = CONFREJ;
+ ucp = inp; /* Backup */
+ }
+
+ /* Need to move CI? */
+ if (ucp != cip)
+ BCOPY(cip, ucp, cilen); /* Move it */
+
+ /* Update output pointer */
+ INCPTR(cilen, ucp);
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their identifier and they didn't send their identifier, then we
+ * send a NAK with a CI_IFACEID option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+ if (rc != CONFREJ && !ho->neg_ifaceid &&
+ wo->req_ifaceid && !reject_if_disagree) {
+ if (rc == CONFACK) {
+ rc = CONFNAK;
+ ucp = inp; /* reset pointer */
+ wo->req_ifaceid = 0; /* don't ask again */
+ }
+ PUTCHAR(CI_IFACEID, ucp);
+ PUTCHAR(CILEN_IFACEID, ucp);
+ eui64_put(wo->hisid, ucp);
+ }
+
+ *len = ucp - inp; /* Compute output length */
+ IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+/*
+ * ipv6_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ipv6_check_options()
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+
+#if defined(SOL2)
+ /*
+ * Persistent link-local id is only used when user has not explicitly
+ * configure/hard-code the id
+ */
+ if ((wo->use_persistent) && (!wo->opt_local) && (!wo->opt_remote)) {
+
+ /*
+ * On systems where there are no Ethernet interfaces used, there
+ * may be other ways to obtain a persistent id. Right now, it
+ * will fall back to using magic [see eui64_magic] below when
+ * an EUI-48 from MAC address can't be obtained. Other possibilities
+ * include obtaining EEPROM serial numbers, or some other unique
+ * yet persistent number. On Sparc platforms, this is possible,
+ * but too bad there's no standards yet for x86 machines.
+ */
+ if (ether_to_eui64(&wo->ourid)) {
+ wo->opt_local = 1;
+ }
+ }
+#endif
+
+ if (!wo->opt_local) { /* init interface identifier */
+ if (wo->use_ip && eui64_iszero(wo->ourid)) {
+ eui64_setlo32(wo->ourid, ntohl(ipcp_wantoptions[0].ouraddr));
+ if (!eui64_iszero(wo->ourid))
+ wo->opt_local = 1;
+ }
+
+ while (eui64_iszero(wo->ourid))
+ eui64_magic(wo->ourid);
+ }
+
+ if (!wo->opt_remote) {
+ if (wo->use_ip && eui64_iszero(wo->hisid)) {
+ eui64_setlo32(wo->hisid, ntohl(ipcp_wantoptions[0].hisaddr));
+ if (!eui64_iszero(wo->hisid))
+ wo->opt_remote = 1;
+ }
+ }
+
+ if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) {
+ option_error("local/remote LL address required for demand-dialling\n");
+ exit(1);
+ }
+}
+
+
+/*
+ * ipv6_demand_conf - configure the interface as though
+ * IPV6CP were up, for use with dial-on-demand.
+ */
+static int
+ipv6_demand_conf(u)
+ int u;
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[u];
+
+#if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+#if defined(SOL2)
+ if (!sif6up(u))
+ return 0;
+#else
+ if (!sifup(u))
+ return 0;
+#endif /* defined(SOL2) */
+#endif
+ if (!sif6addr(u, wo->ourid, wo->hisid))
+ return 0;
+#if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sifup(u))
+ return 0;
+#endif
+ if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE))
+ return 0;
+
+ notice("ipv6_demand_conf");
+ notice("local LL address %s", llv6_ntoa(wo->ourid));
+ notice("remote LL address %s", llv6_ntoa(wo->hisid));
+
+ return 1;
+}
+
+
+/*
+ * ipv6cp_up - IPV6CP has come UP.
+ *
+ * Configure the IPv6 network interface appropriately and bring it up.
+ */
+static void
+ipv6cp_up(f)
+ fsm *f;
+{
+ ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
+
+ IPV6CPDEBUG(("ipv6cp: up"));
+
+ /*
+ * We must have a non-zero LL address for both ends of the link.
+ */
+ if (!ho->neg_ifaceid)
+ ho->hisid = wo->hisid;
+
+ if(!no_ifaceid_neg) {
+ if (eui64_iszero(ho->hisid)) {
+ error("Could not determine remote LL address");
+ ipv6cp_close(f->unit, "Could not determine remote LL address");
+ return;
+ }
+ if (eui64_iszero(go->ourid)) {
+ error("Could not determine local LL address");
+ ipv6cp_close(f->unit, "Could not determine local LL address");
+ return;
+ }
+ if (eui64_equals(go->ourid, ho->hisid)) {
+ error("local and remote LL addresses are equal");
+ ipv6cp_close(f->unit, "local and remote LL addresses are equal");
+ return;
+ }
+ }
+ script_setenv("LLLOCAL", llv6_ntoa(go->ourid));
+ script_setenv("LLREMOTE", llv6_ntoa(ho->hisid));
+
+#ifdef IPV6CP_COMP
+ /* set tcp compression */
+ sif6comp(f->unit, ho->neg_vj);
+#endif
+
+ /*
+ * If we are doing dial-on-demand, the interface is already
+ * configured, so we put out any saved-up packets, then set the
+ * interface to pass IPv6 packets.
+ */
+ if (demand) {
+ if (! eui64_equals(go->ourid, wo->ourid) ||
+ ! eui64_equals(ho->hisid, wo->hisid)) {
+ if (! eui64_equals(go->ourid, wo->ourid))
+ warn("Local LL address changed to %s",
+ llv6_ntoa(go->ourid));
+ if (! eui64_equals(ho->hisid, wo->hisid))
+ warn("Remote LL address changed to %s",
+ llv6_ntoa(ho->hisid));
+ ipv6cp_clear_addrs(f->unit, go->ourid, ho->hisid);
+
+ /* Set the interface to the new addresses */
+ if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
+ if (debug)
+ warn("sif6addr failed");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ }
+ demand_rexmit(PPP_IPV6);
+ sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
+
+ } else {
+ /*
+ * Set LL addresses
+ */
+#if !defined(__linux__) && !defined(SOL2) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
+ if (debug)
+ warn("sif6addr failed");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+
+ /* bring the interface up for IPv6 */
+#if defined(SOL2)
+ if (!sif6up(f->unit)) {
+ if (debug)
+ warn("sifup failed (IPV6)");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#else
+ if (!sifup(f->unit)) {
+ if (debug)
+ warn("sifup failed (IPV6)");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif /* defined(SOL2) */
+
+#if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
+ if (debug)
+ warn("sif6addr failed");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+ sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
+
+ notice("local LL address %s", llv6_ntoa(go->ourid));
+ notice("remote LL address %s", llv6_ntoa(ho->hisid));
+ }
+
+ np_up(f->unit, PPP_IPV6);
+ ipv6cp_is_up = 1;
+
+ /*
+ * Execute the ipv6-up script, like this:
+ * /etc/ppp/ipv6-up interface tty speed local-LL remote-LL
+ */
+ if (ipv6cp_script_state == s_down && ipv6cp_script_pid == 0) {
+ ipv6cp_script_state = s_up;
+ ipv6cp_script(_PATH_IPV6UP);
+ }
+}
+
+
+/*
+ * ipv6cp_down - IPV6CP has gone DOWN.
+ *
+ * Take the IPv6 network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void
+ipv6cp_down(f)
+ fsm *f;
+{
+ IPV6CPDEBUG(("ipv6cp: down"));
+ update_link_stats(f->unit);
+ if (ipv6cp_is_up) {
+ ipv6cp_is_up = 0;
+ np_down(f->unit, PPP_IPV6);
+ }
+#ifdef IPV6CP_COMP
+ sif6comp(f->unit, 0);
+#endif
+
+ /*
+ * If we are doing dial-on-demand, set the interface
+ * to queue up outgoing packets (for now).
+ */
+ if (demand) {
+ sifnpmode(f->unit, PPP_IPV6, NPMODE_QUEUE);
+ } else {
+ sifnpmode(f->unit, PPP_IPV6, NPMODE_DROP);
+#if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC)))
+#if defined(SOL2)
+ sif6down(f->unit);
+#else
+ sifdown(f->unit);
+#endif /* defined(SOL2) */
+#endif
+ ipv6cp_clear_addrs(f->unit,
+ ipv6cp_gotoptions[f->unit].ourid,
+ ipv6cp_hisoptions[f->unit].hisid);
+#if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC)))
+ sifdown(f->unit);
+#endif
+ }
+
+ /* Execute the ipv6-down script */
+ if (ipv6cp_script_state == s_up && ipv6cp_script_pid == 0) {
+ ipv6cp_script_state = s_down;
+ ipv6cp_script(_PATH_IPV6DOWN);
+ }
+}
+
+
+/*
+ * ipv6cp_clear_addrs() - clear the interface addresses, routes,
+ * proxy neighbour discovery entries, etc.
+ */
+static void
+ipv6cp_clear_addrs(unit, ourid, hisid)
+ int unit;
+ eui64_t ourid;
+ eui64_t hisid;
+{
+ cif6addr(unit, ourid, hisid);
+}
+
+
+/*
+ * ipv6cp_finished - possibly shut down the lower layers.
+ */
+static void
+ipv6cp_finished(f)
+ fsm *f;
+{
+ np_finished(f->unit, PPP_IPV6);
+}
+
+
+/*
+ * ipv6cp_script_done - called when the ipv6-up or ipv6-down script
+ * has finished.
+ */
+static void
+ipv6cp_script_done(arg)
+ void *arg;
+{
+ ipv6cp_script_pid = 0;
+ switch (ipv6cp_script_state) {
+ case s_up:
+ if (ipv6cp_fsm[0].state != OPENED) {
+ ipv6cp_script_state = s_down;
+ ipv6cp_script(_PATH_IPV6DOWN);
+ }
+ break;
+ case s_down:
+ if (ipv6cp_fsm[0].state == OPENED) {
+ ipv6cp_script_state = s_up;
+ ipv6cp_script(_PATH_IPV6UP);
+ }
+ break;
+ }
+}
+
+
+/*
+ * ipv6cp_script - Execute a script with arguments
+ * interface-name tty-name speed local-LL remote-LL.
+ */
+static void
+ipv6cp_script(script)
+ char *script;
+{
+ char strspeed[32], strlocal[32], strremote[32];
+ char *argv[8];
+
+ sprintf(strspeed, "%d", baud_rate);
+ strcpy(strlocal, llv6_ntoa(ipv6cp_gotoptions[0].ourid));
+ strcpy(strremote, llv6_ntoa(ipv6cp_hisoptions[0].hisid));
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = devnam;
+ argv[3] = strspeed;
+ argv[4] = strlocal;
+ argv[5] = strremote;
+ argv[6] = ipparam;
+ argv[7] = NULL;
+
+ ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done, NULL);
+}
+
+/*
+ * ipv6cp_printpkt - print the contents of an IPV6CP packet.
+ */
+static char *ipv6cp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej"
+};
+
+static int
+ipv6cp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ eui64_t ifaceid;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ipv6cp_codenames) / sizeof(char *))
+ printer(arg, " %s", ipv6cp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < 2 || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case CI_COMPRESSTYPE:
+ if (olen >= CILEN_COMPRESS) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "compress ");
+ printer(arg, "0x%x", cishort);
+ }
+ break;
+ case CI_IFACEID:
+ if (olen == CILEN_IFACEID) {
+ p += 2;
+ eui64_get(ifaceid, p);
+ printer(arg, "addr %s", llv6_ntoa(ifaceid));
+ }
+ break;
+ }
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string(p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+
+/*
+ * ipv6_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+#define IP6_HDRLEN 40 /* bytes */
+#define IP6_NHDR_FRAG 44 /* fragment IPv6 header */
+#define IPPROTO_TCP 6
+#define TCP_HDRLEN 20
+#define TH_FIN 0x01
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define get_ip6nh(x) (((unsigned char *)(x))[6])
+#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x) (((unsigned char *)(x))[13])
+
+static int
+ipv6_active_pkt(pkt, len)
+ u_char *pkt;
+ int len;
+{
+ u_char *tcp;
+
+ len -= PPP_HDRLEN;
+ pkt += PPP_HDRLEN;
+ if (len < IP6_HDRLEN)
+ return 0;
+ if (get_ip6nh(pkt) == IP6_NHDR_FRAG)
+ return 0;
+ if (get_ip6nh(pkt) != IPPROTO_TCP)
+ return 1;
+ if (len < IP6_HDRLEN + TCP_HDRLEN)
+ return 0;
+ tcp = pkt + IP6_HDRLEN;
+ if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == IP6_HDRLEN + get_tcpoff(tcp) * 4)
+ return 0;
+ return 1;
+}
diff --git a/usr.sbin/pppd/ipv6cp.h b/usr.sbin/pppd/ipv6cp.h
new file mode 100644
index 000000000000..d800d5d5da4b
--- /dev/null
+++ b/usr.sbin/pppd/ipv6cp.h
@@ -0,0 +1,126 @@
+/*
+ ipv6cp.h - PPP IPV6 Control Protocol.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms. The name of the author may not be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+/* Original version, based on RFC2023 :
+
+ Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt
+ Économique ayant pour membres BULL S.A. et l'INRIA).
+
+ Ce logiciel informatique est disponible aux conditions
+ usuelles dans la recherche, c'est-à-dire qu'il peut
+ être utilisé, copié, modifié, distribué à l'unique
+ condition que ce texte soit conservé afin que
+ l'origine de ce logiciel soit reconnue.
+
+ Le nom de l'Institut National de Recherche en Informatique
+ et en Automatique (INRIA), de l'IMAG, ou d'une personne morale
+ ou physique ayant participé à l'élaboration de ce logiciel ne peut
+ être utilisé sans son accord préalable explicite.
+
+ Ce logiciel est fourni tel quel sans aucune garantie,
+ support ou responsabilité d'aucune sorte.
+ Ce logiciel est dérivé de sources d'origine
+ "University of California at Berkeley" et
+ "Digital Equipment Corporation" couvertes par des copyrights.
+
+ L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG)
+ est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National
+ Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant
+ sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR).
+
+ This work has been done in the context of GIE DYADE (joint R & D venture
+ between BULL S.A. and INRIA).
+
+ This software is available with usual "research" terms
+ with the aim of retain credits of the software.
+ Permission to use, copy, modify and distribute this software for any
+ purpose and without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies,
+ and the name of INRIA, IMAG, or any contributor not be used in advertising
+ or publicity pertaining to this material without the prior explicit
+ permission. The software is provided "as is" without any
+ warranties, support or liabilities of any kind.
+ This software is derived from source code from
+ "University of California at Berkeley" and
+ "Digital Equipment Corporation" protected by copyrights.
+
+ Grenoble's Institute of Computer Science and Applied Mathematics (IMAG)
+ is a federation of seven research units funded by the CNRS, National
+ Polytechnic Institute of Grenoble and University Joseph Fourier.
+ The research unit in Software, Systems, Networks (LSR) is member of IMAG.
+*/
+
+/*
+ * Derived from :
+ *
+ *
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ipv6cp.h,v 1.3 1999/09/30 19:57:45 masputra Exp $
+ */
+
+/*
+ * Options.
+ */
+#define CI_IFACEID 1 /* Interface Identifier */
+#define CI_COMPRESSTYPE 2 /* Compression Type */
+
+/* No compression types yet defined.
+ *#define IPV6CP_COMP 0x004f
+ */
+typedef struct ipv6cp_options {
+ int neg_ifaceid; /* Negotiate interface identifier? */
+ int req_ifaceid; /* Ask peer to send interface identifier? */
+ int accept_local; /* accept peer's value for iface id? */
+ int opt_local; /* ourtoken set by option */
+ int opt_remote; /* histoken set by option */
+ int use_ip; /* use IP as interface identifier */
+#if defined(SOL2)
+ int use_persistent; /* use uniquely persistent value for address */
+#endif /* defined(SOL2) */
+ int neg_vj; /* Van Jacobson Compression? */
+ u_short vj_protocol; /* protocol value to use in VJ option */
+ eui64_t ourid, hisid; /* Interface identifiers */
+} ipv6cp_options;
+
+extern fsm ipv6cp_fsm[];
+extern ipv6cp_options ipv6cp_wantoptions[];
+extern ipv6cp_options ipv6cp_gotoptions[];
+extern ipv6cp_options ipv6cp_allowoptions[];
+extern ipv6cp_options ipv6cp_hisoptions[];
+
+extern struct protent ipv6cp_protent;