ume 2006-11-19 07:27:43 UTC FreeBSD src repository Modified files: (Branch: RELENG_6) usr.sbin/pppd Makefile main.c options.c pathnames.h pppd.8 pppd.h sys-bsd.c Added files: (Branch: RELENG_6) usr.sbin/pppd eui64.c eui64.h ipv6cp.c ipv6cp.h Log: MFC: Teach an IPV6CP to pppd(8). The eui64.[ch] and ipv6cp.[ch] were taken from ppp-2.3.11. However, our stock pppd(8) doesn't provide option_t nor some utility functions. So, I made some hacks to adjust to our stock pppd(8). The sys_bsd.c part was taken from NetBSD with some modifications to adjust to our stock pppd(8). usr.sbin/pppd/Makefile: 1.31 usr.sbin/pppd/eui64.c: 1.2 usr.sbin/pppd/eui64.h: 1.1 usr.sbin/pppd/ipv6cp.c: 1.2 usr.sbin/pppd/ipv6cp.h: 1.2 usr.sbin/pppd/main.c: 1.22 usr.sbin/pppd/options.c: 1.25 usr.sbin/pppd/pathnames.h: 1.11 usr.sbin/pppd/pppd.8: 1.27 usr.sbin/pppd/pppd.h: 1.14 usr.sbin/pppd/sys-bsd.c: 1.21 Revision Changes Path 1.29.2.1 +5 -0 src/usr.sbin/pppd/Makefile 1.2.2.1 +46 -0 src/usr.sbin/pppd/eui64.c (new) 1.1.1.1.2.1 +98 -0 src/usr.sbin/pppd/eui64.h (new) 1.2.2.1 +1421 -0 src/usr.sbin/pppd/ipv6cp.c (new) 1.2.2.1 +129 -0 src/usr.sbin/pppd/ipv6cp.h (new) 1.21.12.1 +6 -0 src/usr.sbin/pppd/main.c 1.24.14.1 +113 -0 src/usr.sbin/pppd/options.c 1.10.14.1 +6 -1 src/usr.sbin/pppd/pathnames.h 1.25.8.2 +47 -2 src/usr.sbin/pppd/pppd.8 1.13.34.1 +16 -0 src/usr.sbin/pppd/pppd.h 1.20.8.1 +129 -1 src/usr.sbin/pppd/sys-bsd.c ume 2006-11-12 17:13:45 UTC FreeBSD src repository src/usr.sbin/pppd - Imported sources Update of /home/ncvs/src/usr.sbin/pppd In directory repoman.freebsd.org:/tmp/cvs-serv42719 Log Message: Import eui64.[ch] and ipv6cp.[ch] of ppp-2.3.11 Status: Vendor Tag: mackerras Release Tags: ppp_2_3_11 N src/usr.sbin/pppd/eui64.c N src/usr.sbin/pppd/eui64.h N src/usr.sbin/pppd/ipv6cp.c N src/usr.sbin/pppd/ipv6cp.h No conflicts created by this import ume 2006-11-12 17:36:58 UTC FreeBSD src repository Modified files: usr.sbin/pppd Makefile eui64.c ipv6cp.c ipv6cp.h main.c options.c pathnames.h pppd.8 pppd.h sys-bsd.c Log: Teach an IPV6CP to pppd(8). The eui64.[ch] and ipv6cp.[ch] were taken from ppp-2.3.11. However, our stock pppd(8) doesn't provide option_t nor some utility functions. So, I made some hacks to adjust to our stock pppd(8). The sys_bsd.c part was taken from NetBSD with some modifications to adjust to our stock pppd(8). MFC after: 1 week Revision Changes Path 1.31 +5 -0 src/usr.sbin/pppd/Makefile 1.2 +6 -0 src/usr.sbin/pppd/eui64.c 1.2 +21 -78 src/usr.sbin/pppd/ipv6cp.c 1.2 +3 -0 src/usr.sbin/pppd/ipv6cp.h 1.22 +6 -0 src/usr.sbin/pppd/main.c 1.25 +113 -0 src/usr.sbin/pppd/options.c 1.11 +6 -1 src/usr.sbin/pppd/pathnames.h 1.27 +47 -2 src/usr.sbin/pppd/pppd.8 1.14 +16 -0 src/usr.sbin/pppd/pppd.h 1.21 +129 -1 src/usr.sbin/pppd/sys-bsd.c Index: usr.sbin/pppd/Makefile diff -u usr.sbin/pppd/Makefile.orig usr.sbin/pppd/Makefile --- usr.sbin/pppd/Makefile.orig Tue Dec 21 19:16:04 2004 +++ usr.sbin/pppd/Makefile Sun Nov 12 13:04:52 2006 @@ -37,6 +37,11 @@ DPADD+= ${LIBCRYPTO} .endif +.if ${MK_INET6_SUPPORT} != "no" +CFLAGS+=-DINET6 +SRCS+= eui64.c ipv6cp.c +.endif + .if defined(RELEASE_CRUNCH) # We must create these objects because crunchgen will link them, # and we don't want any unused symbols to spoil the final link. Index: usr.sbin/pppd/eui64.c diff -u -p /dev/null usr.sbin/pppd/eui64.c --- /dev/null Mon Nov 13 02:00:01 2006 +++ usr.sbin/pppd/eui64.c Mon Nov 13 00:11:44 2006 @@ -0,0 +1,46 @@ +/* + eui64.c - EUI64 routines for IPv6CP. + Copyright (C) 1999 Tommi Komulainen + + 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 $ +*/ + +#ifndef lint +#define RCSID "$Id: eui64.c,v 1.3 1999/08/25 04:15:51 paulus Exp $" +#endif +#include +__FBSDID("$FreeBSD$"); + +#include "pppd.h" + +#ifdef RCSID +static const char rcsid[] = RCSID; +#endif + +/* + * 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; +} Index: usr.sbin/pppd/eui64.h diff -u /dev/null usr.sbin/pppd/eui64.h --- /dev/null Mon Nov 13 02:00:01 2006 +++ usr.sbin/pppd/eui64.h Sat Aug 5 13:40:02 2000 @@ -0,0 +1,97 @@ +/* + eui64.h - EUI64 routines for IPv6CP. + Copyright (C) 1999 Tommi Komulainen + + 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 + +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__ */ + Index: usr.sbin/pppd/ipv6cp.c diff -u -p /dev/null usr.sbin/pppd/ipv6cp.c --- /dev/null Mon Nov 13 02:00:01 2006 +++ usr.sbin/pppd/ipv6cp.c Mon Nov 13 00:01:36 2006 @@ -0,0 +1,1421 @@ +/* + ipv6cp.c - PPP IPV6 Control Protocol. + Copyright (C) 1999 Tommi Komulainen + + 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 $ + */ + +#ifndef lint +#define RCSID "$Id: ipv6cp.c,v 1.7 1999/10/08 01:08:18 masputra Exp $" +#endif +#include +__FBSDID("$FreeBSD$"); + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pppd.h" +#include "fsm.h" +#include "ipcp.h" +#include "ipv6cp.h" +#include "magic.h" +#include "pathnames.h" + +#define s6_addr32 __u6_addr.__u6_addr32 + +#ifdef RCSID +static const char rcsid[] = RCSID; +#endif + +/* 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 */ +}; + + +/* + * 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_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 *)); + +/* + * 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; + +/* + * setifaceid - set the interface identifiers manually + */ +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; + + syslog(LOG_NOTICE, "ipv6_demand_conf"); + syslog(LOG_NOTICE, "local LL address %s", llv6_ntoa(wo->ourid)); + syslog(LOG_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)) { + syslog(LOG_ERR, "Could not determine remote LL address"); + ipv6cp_close(f->unit, "Could not determine remote LL address"); + return; + } + if (eui64_iszero(go->ourid)) { + syslog(LOG_ERR, "Could not determine local LL address"); + ipv6cp_close(f->unit, "Could not determine local LL address"); + return; + } + if (eui64_equals(go->ourid, ho->hisid)) { + syslog(LOG_ERR, "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); + + syslog(LOG_NOTICE, "local LL address %s", llv6_ntoa(go->ourid)); + syslog(LOG_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_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")); + 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_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 - 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; + + run_program(script, argv, 0); +} + +/* + * 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; +} Index: usr.sbin/pppd/ipv6cp.h diff -u /dev/null usr.sbin/pppd/ipv6cp.h --- /dev/null Mon Nov 13 02:00:01 2006 +++ usr.sbin/pppd/ipv6cp.h Mon Nov 13 02:02:34 2006 @@ -0,0 +1,129 @@ +/* + ipv6cp.h - PPP IPV6 Control Protocol. + Copyright (C) 1999 Tommi Komulainen + + 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 $ + * $FreeBSD$ + */ + +/* + * 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; + +extern int setifaceid __P((char **arg)); Index: usr.sbin/pppd/main.c diff -u -p usr.sbin/pppd/main.c.orig usr.sbin/pppd/main.c --- usr.sbin/pppd/main.c.orig Tue May 13 03:51:31 2003 +++ usr.sbin/pppd/main.c Sun Nov 12 13:06:21 2006 @@ -46,6 +46,9 @@ static char rcsid[] = "$FreeBSD: src/usr #include "fsm.h" #include "lcp.h" #include "ipcp.h" +#ifdef INET6 +#include "ipv6cp.h" +#endif #include "upap.h" #include "chap.h" #include "ccp.h" @@ -152,6 +155,9 @@ struct protent *protocols[] = { &cbcp_protent, #endif &ipcp_protent, +#ifdef INET6 + &ipv6cp_protent, +#endif &ccp_protent, #ifdef IPX_CHANGE &ipxcp_protent, Index: usr.sbin/pppd/options.c diff -u -p usr.sbin/pppd/options.c.orig usr.sbin/pppd/options.c --- usr.sbin/pppd/options.c.orig Mon May 6 17:39:43 2002 +++ usr.sbin/pppd/options.c Mon Nov 13 00:01:04 2006 @@ -55,6 +55,10 @@ static char rcsid[] = "$FreeBSD: src/usr #include "cbcp.h" #endif +#ifdef INET6 +#include "ipv6cp.h" +#endif + #ifdef IPX_CHANGE #include "ipxcp.h" #endif /* IPX_CHANGE */ @@ -227,6 +231,7 @@ static int setpapcrypt __P((char **)); static int setidle __P((char **)); static int setholdoff __P((char **)); static int setdnsaddr __P((char **)); +static int resetipv6proto __P((char **)); static int resetipxproto __P((char **)); static int setwinsaddr __P((char **)); static int showversion __P((char **)); @@ -238,6 +243,19 @@ static int setpassfilter __P((char **)); static int setactivefilter __P((char **)); #endif +#ifdef INET6 +static int setipv6cp_accept_local __P((char **)); +static int setipv6cp_use_ip __P((char **)); +#if defined(SOL2) +static int setipv6cp_use_persistent __P((char **)); +#endif +static int setipv6cptimeout __P((char **)); +static int setipv6cpterm __P((char **)); +static int setipv6cpconf __P((char **)); +static int setipv6cpfails __P((char **)); +static int setipv6proto __P((char **)); +#endif /* INET6 */ + #ifdef IPX_CHANGE static int setipxproto __P((char **)); static int setipxanet __P((char **)); @@ -388,6 +406,8 @@ static struct cmd { /* end compat hack */ {"ms-dns", 1, setdnsaddr}, /* DNS address for the peer's use */ {"ms-wins", 1, setwinsaddr}, /* Nameserver for SMB over TCP/IP for peer */ + {"noipv6", 0, resetipv6proto}, /* Disable IPv6 and IPv6CP */ + {"-ipv6", 0, resetipv6proto}, /* Disable IPv6 and IPv6CP */ {"noipx", 0, resetipxproto}, /* Disable IPXCP (and IPX) */ {"-ipx", 0, resetipxproto}, /* Disable IPXCP (and IPX) */ {"--version", 0, showversion}, /* Show version number */ @@ -400,6 +420,20 @@ static struct cmd { {"active-filter", 1, setactivefilter}, /* set filter for active pkts */ #endif +#ifdef INET6 + {"ipv6", 1, setifaceid}, /* Set interface id for IPV6" */ + {"+ipv6", 0, setipv6proto}, /* Enable IPv6 and IPv6CP */ + {"ipv6cp-accept-local", 0, setipv6cp_accept_local}, /* Accept peer's iface id for us */ + {"ipv6cp-use-ipaddr", 0, setipv6cp_use_ip}, /* Use IPv4 addr as iface id */ +#if defined(SOL2) + {"ipv6cp-use-persistent", 0, setipv6cp_use_persistent}, /* Use uniquely-available persistent value for link local addr */ +#endif + {"ipv6cp-restart", 1, setipv6cptimeout}, /* Set timeout for IPv6CP */ + {"ipv6cp-max-terminate", 1, setipv6cpterm}, /* max #xmits for term-reqs */ + {"ipv6cp-max-configure", 1, setipv6cpconf}, /* max #xmits for conf-reqs */ + {"ipv6cp-max-failure", 1, setipv6cpfails}, /* max #conf-naks for IPv6CP */ +#endif + #ifdef IPX_CHANGE {"ipx-network", 1, setipxnetwork}, /* IPX network number */ {"ipxcp-accept-network", 0, setipxanet}, /* Accept peer netowrk */ @@ -2373,6 +2407,85 @@ setwinsaddr(argv) return (1); } + +#ifdef INET6 +static int +setipv6cp_accept_local(argv) + char **argv; +{ + ipv6cp_allowoptions[0].accept_local = 1; + return 1; +} + +static int +setipv6cp_use_ip(argv) + char **argv; +{ + ipv6cp_allowoptions[0].use_ip = 1; + return 1; +} + +#if defined(SOL2) +static int +setipv6cp_use_persistent(argv) + char **argv; +{ + ipv6cp_wantoptions[0].use_persistent = 1; + return 1; +} +#endif + +static int +setipv6cptimeout(argv) + char **argv; +{ + return int_option(*argv, &ipv6cp_fsm[0].timeouttime); +} + +static int +setipv6cpterm(argv) + char **argv; +{ + return int_option(*argv, &ipv6cp_fsm[0].maxtermtransmits); +} + +static int +setipv6cpconf(argv) + char **argv; +{ + return int_option(*argv, &ipv6cp_fsm[0].maxconfreqtransmits); +} + +static int +setipv6cpfails(argv) + char **argv; +{ + return int_option(*argv, &ipv6cp_fsm[0].maxnakloops); +} + +static int +setipv6proto(argv) + char **argv; +{ + ipv6cp_protent.enabled_flag = 1; + return 1; +} + +static int +resetipv6proto(argv) + char **argv; +{ + ipv6cp_protent.enabled_flag = 0; + return 1; +} +#else +static int +resetipv6proto(argv) + char **argv; +{ + return 1; +} +#endif /* INET6 */ #ifdef IPX_CHANGE static int Index: usr.sbin/pppd/pathnames.h diff -u usr.sbin/pppd/pathnames.h.orig usr.sbin/pppd/pathnames.h --- usr.sbin/pppd/pathnames.h.orig Wed Aug 1 01:09:29 2001 +++ usr.sbin/pppd/pathnames.h Sun Nov 12 13:07:28 2006 @@ -26,6 +26,11 @@ #define _PATH_PPPDENY "/etc/ppp/ppp.deny" #define _PATH_PPPSHELLS "/etc/ppp/ppp.shells" +#ifdef INET6 +#define _PATH_IPV6UP "/etc/ppp/ipv6-up" +#define _PATH_IPV6DOWN "/etc/ppp/ipv6-down" +#endif + #ifdef IPX_CHANGE #define _PATH_IPXUP "/etc/ppp/ipx-up" #define _PATH_IPXDOWN "/etc/ppp/ipx-down" Index: usr.sbin/pppd/pppd.8 diff -u usr.sbin/pppd/pppd.8.orig usr.sbin/pppd/pppd.8 --- usr.sbin/pppd/pppd.8.orig Sun Aug 21 00:38:38 2005 +++ usr.sbin/pppd/pppd.8 Sun Nov 12 23:40:00 2006 @@ -142,13 +142,14 @@ will ask the peer to send packets of no more than \fIn\fR bytes. The minimum MRU value is 128. The default MRU value is 1500. A value of 296 is recommended for slow links (40 bytes for TCP/IP header + 256 -bytes of data). +bytes of data). (Note that for IPv6 MRU must be at least 1280) .TP .B mtu \fIn Set the MTU [Maximum Transmit Unit] value to \fIn\fR. Unless the peer requests a smaller value via MRU negotiation, pppd will request that the kernel networking code send data packets of no more -than \fIn\fR bytes through the PPP network interface. +than \fIn\fR bytes through the PPP network interface. (Note that for +IPv6 MTU must be at least 1280) .TP .B passive Enables the "passive" option in the LCP. With this option, pppd will @@ -171,6 +172,17 @@ negotiation, unless the \fIipcp-accept-local\fR and/or \fIipcp-accept-remote\fR options are given, respectively. .TP +.B ipv6 \fI\fR,\fI +Set the local and/or remote 64-bit interface identifier. Either one may be +omitted. The identifier must be specified in standard ascii notation of +IPv6 addresses (e.g. ::dead:beef). If the +\fIipv6cp-use-ipaddr\fR +option is given, the local identifier is the local IPv4 address (see above). +On systems which supports a unique persistent id, such as EUI-48 derived +from the Ethernet MAC address, \fIipv6cp-use-persistent\fR option can be +used to replace the \fIipv6 ,\fR option. Otherwise the +identifier is randomized. +.TP .B bsdcomp \fInr,nt Request that the peer compress packets that it sends, using the BSD-Compress scheme, with a maximum code size of \fInr\fR bits, and @@ -300,6 +312,22 @@ option is given, the \fIstring\fR supplied is given as the 6th parameter to those scripts. .TP +.B ipv6cp-max-configure \fIn +Set the maximum number of IPv6CP configure-request transmissions to +\fIn\fR (default 10). +.TP +.B ipv6cp-max-failure \fIn +Set the maximum number of IPv6CP configure-NAKs returned before starting +to send configure-Rejects instead to \fIn\fR (default 10). +.TP +.B ipv6cp-max-terminate \fIn +Set the maximum number of IPv6CP terminate-request transmissions to +\fIn\fR (default 3). +.TP +.B ipv6cp-restart \fIn +Set the IPv6CP restart interval (retransmission timeout) to \fIn\fR +seconds (default 3). +.TP .B ipx Enable the IPXCP and IPX protocols. This option is presently only supported under Linux, and only if your kernel has been configured to @@ -526,6 +554,11 @@ only be required if the peer is buggy and gets confused by requests from pppd for IPCP negotiation. .TP +.B noipv6 +Disable IPv6CP negotiation and IPv6 communication. This option should +only be required if the peer is buggy and gets confused by requests +from pppd for IPv6CP negotiation. +.TP .B noipdefault Disables the default behaviour when no local IP address is specified, which is to determine (if possible) the local IP address from the @@ -1062,6 +1095,18 @@ used for undoing the effects of the /etc/ppp/ip-up script. It is invoked in the same manner and with the same parameters as the ip-up script. +.TP +.B /etc/ppp/ipv6-up +Like /etc/ppp/ip-up, except that it is executed when the link is available +for sending and receiving IPv6 packets. It is executed with the parameters +.IP +\fIinterface-name tty-device speed local-link-local-address +remote-link-local-address ipparam\fR +.TP +.B /etc/ppp/ipv6-down +Similar to /etc/ppp/ip-down, but it is executed when IPv6 packets can no +longer be transmitted on the link. It is executed with the same parameters +as the ipv6-up script. .TP .B /etc/ppp/ipx-up A program or script which is executed when the link is available for Index: usr.sbin/pppd/pppd.h diff -u usr.sbin/pppd/pppd.h.orig usr.sbin/pppd/pppd.h --- usr.sbin/pppd/pppd.h.orig Sat Aug 28 10:19:08 1999 +++ usr.sbin/pppd/pppd.h Sun Nov 12 20:39:16 2006 @@ -41,6 +41,10 @@ #define const #endif +#ifdef INET6 +#include "eui64.h" +#endif + /* * Limits. */ @@ -285,6 +289,12 @@ /* Configure IP addresses for i/f */ int cifaddr __P((int, u_int32_t, u_int32_t)); /* Reset i/f IP addresses */ +#ifdef INET6 +int sif6addr __P((int, eui64_t, eui64_t)); + /* Configure IPv6 addresses for i/f */ +int cif6addr __P((int, eui64_t, eui64_t)); + /* Remove an IPv6 address from i/f */ +#endif int sifdefaultroute __P((int, u_int32_t, u_int32_t)); /* Create default route through i/f */ int cifdefaultroute __P((int, u_int32_t, u_int32_t)); @@ -455,6 +465,12 @@ #define IPCPDEBUG(x) if (debug) syslog x #else #define IPCPDEBUG(x) +#endif + +#ifdef DEBUGIPV6CP +#define IPV6CPDEBUG(x) if (debug) syslog x +#else +#define IPV6CPDEBUG(x) #endif #ifdef DEBUGUPAP Index: usr.sbin/pppd/sys-bsd.c diff -u -p usr.sbin/pppd/sys-bsd.c.orig usr.sbin/pppd/sys-bsd.c --- usr.sbin/pppd/sys-bsd.c.orig Sat Apr 10 01:59:05 2004 +++ usr.sbin/pppd/sys-bsd.c Sun Nov 12 21:07:35 2006 @@ -58,6 +58,10 @@ static char rcsid[] = "$FreeBSD: src/usr #include #include #include +#include +#include +#include +#include #ifdef IPX_CHANGE #include @@ -100,6 +104,9 @@ static char loop_name[20]; static unsigned char inbuf[512]; /* buffer for chars read from loopback */ static int sockfd; /* socket for doing interface ioctls */ +#ifdef INET6 +static int sock6_fd = -1; /* socket for doing ipv6 interface ioctls */ +#endif /* INET6 */ static int if_is_up; /* the interface is currently up */ static u_int32_t ifaddrs[2]; /* local and remote addresses we set */ @@ -122,6 +129,13 @@ sys_init() syslog(LOG_ERR, "Couldn't create IP socket: %m"); die(1); } + +#ifdef INET6 + if ((sock6_fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + /* check it at runtime */ + sock6_fd = -1; + } +#endif } /* @@ -156,7 +170,12 @@ sys_cleanup() void sys_close() { - close(sockfd); + if (sockfd >= 0) + close(sockfd); +#ifdef INET6 + if (sock6_fd >= 0) + close(sock6_fd); +#endif if (loop_slave >= 0) { close(loop_slave); close(loop_master); @@ -476,6 +495,115 @@ int fd, on; ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits); } +#ifdef INET6 +/* + * sif6addr - Config the interface with an IPv6 link-local address + */ +int +sif6addr(int unit, eui64_t our_eui64, eui64_t his_eui64) +{ + int ifindex; + struct in6_aliasreq addreq6; + + if (sock6_fd < 0) { + syslog(LOG_ERR, "No IPv6 socket available"); + die(1); + /*NOTREACHED*/ + } + + /* actually, this part is not kame local - RFC2553 conformant */ + ifindex = if_nametoindex(ifname); + if (ifindex == 0) { + syslog(LOG_ERR, "sifaddr6: no interface %s", ifname); + return 0; + } + + memset(&addreq6, 0, sizeof(addreq6)); + strlcpy(addreq6.ifra_name, ifname, sizeof(addreq6.ifra_name)); + + /* my addr */ + addreq6.ifra_addr.sin6_family = AF_INET6; + addreq6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); + addreq6.ifra_addr.sin6_addr.s6_addr[0] = 0xfe; + addreq6.ifra_addr.sin6_addr.s6_addr[1] = 0x80; + memcpy(&addreq6.ifra_addr.sin6_addr.s6_addr[8], &our_eui64, + sizeof(our_eui64)); + /* KAME ifindex hack */ + *(u_int16_t *)&addreq6.ifra_addr.sin6_addr.s6_addr[2] = htons(ifindex); + + /* his addr */ + addreq6.ifra_dstaddr.sin6_family = AF_INET6; + addreq6.ifra_dstaddr.sin6_len = sizeof(struct sockaddr_in6); + addreq6.ifra_dstaddr.sin6_addr.s6_addr[0] = 0xfe; + addreq6.ifra_dstaddr.sin6_addr.s6_addr[1] = 0x80; + memcpy(&addreq6.ifra_dstaddr.sin6_addr.s6_addr[8], &his_eui64, + sizeof(our_eui64)); + /* KAME ifindex hack */ + *(u_int16_t *)&addreq6.ifra_dstaddr.sin6_addr.s6_addr[2] = htons(ifindex); + + /* prefix mask: 128bit */ + addreq6.ifra_prefixmask.sin6_family = AF_INET6; + addreq6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + memset(&addreq6.ifra_prefixmask.sin6_addr, 0xff, + sizeof(addreq6.ifra_prefixmask.sin6_addr)); + + /* address lifetime (infty) */ + addreq6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; + addreq6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; + + if (ioctl(sock6_fd, SIOCAIFADDR_IN6, &addreq6) < 0) { + syslog(LOG_ERR, "sif6addr: ioctl(SIOCAIFADDR_IN6): %m"); + return 0; + } + + return 1; +} + + +/* + * cif6addr - Remove IPv6 address from interface + */ +int +cif6addr(int unit, eui64_t our_eui64, eui64_t his_eui64) +{ + int ifindex; + struct in6_ifreq delreq6; + + if (sock6_fd < 0) { + syslog(LOG_ERR, "No IPv6 socket available"); + die(1); + /*NOTREACHED*/ + } + + /* actually, this part is not kame local - RFC2553 conformant */ + ifindex = if_nametoindex(ifname); + if (ifindex == 0) { + syslog(LOG_ERR, "cifaddr6: no interface %s", ifname); + return 0; + } + + memset(&delreq6, 0, sizeof(delreq6)); + strlcpy(delreq6.ifr_name, ifname, sizeof(delreq6.ifr_name)); + + /* my addr */ + delreq6.ifr_ifru.ifru_addr.sin6_family = AF_INET6; + delreq6.ifr_ifru.ifru_addr.sin6_len = sizeof(struct sockaddr_in6); + delreq6.ifr_ifru.ifru_addr.sin6_addr.s6_addr[0] = 0xfe; + delreq6.ifr_ifru.ifru_addr.sin6_addr.s6_addr[1] = 0x80; + memcpy(&delreq6.ifr_ifru.ifru_addr.sin6_addr.s6_addr[8], &our_eui64, + sizeof(our_eui64)); + /* KAME ifindex hack */ + *(u_int16_t *)&delreq6.ifr_ifru.ifru_addr.sin6_addr.s6_addr[2] = + htons(ifindex); + + if (ioctl(sock6_fd, SIOCDIFADDR_IN6, &delreq6) < 0) { + syslog(LOG_ERR, "cif6addr: ioctl(SIOCDIFADDR_IN6): %m"); + return 0; + } + + return 1; +} +#endif /* INET6 */ /* * open_ppp_loopback - open the device we use for getting