X-Git-Url: https://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=pppd%2Fipv6cp.c;h=a36b1d942be4d530215078aebba7699156ebc75a;hp=38cb22bbb5f60bf7585d5cd1c3dcfaebfc79578a;hb=HEAD;hpb=2e53641535da26bf8c3c424172758ed81d908581 diff --git a/pppd/ipv6cp.c b/pppd/ipv6cp.c index 38cb22b..a36b1d9 100644 --- a/pppd/ipv6cp.c +++ b/pppd/ipv6cp.c @@ -1,45 +1,159 @@ /* * ipv6cp.c - PPP IPV6 Control Protocol. * + * Copyright (c) 1999 Tommi Komulainen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Tommi Komulainen + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/* 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. + * Copyright (c) 1984-2000 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. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: * - * $Id: ipv6cp.c,v 1.3 1999/08/24 05:31:09 paulus Exp $ + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. * - * Original version by Inria (www.inria.fr) - * Modified to match RFC2472 by Tommi Komulainen + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define RCSID "$Id: ipv6cp.c,v 1.3 1999/08/24 05:31:09 paulus 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) */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include +#include #include +#include #include #include #include @@ -48,14 +162,15 @@ #include #include -#include "pppd.h" +#include "pppd-private.h" +#include "options.h" #include "fsm.h" +#include "eui64.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 */ @@ -65,21 +180,30 @@ ipv6cp_options ipv6cp_hisoptions[NUM_PPP]; /* Options that we ack'd */ int no_ifaceid_neg = 0; /* local vars */ +static int default_route_set[NUM_PPP]; /* Have set up a default route */ static int ipv6cp_is_up; +static bool ipv6cp_noremote; + +ipv6_up_hook_fn *ipv6_up_hook = NULL; +ipv6_down_hook_fn *ipv6_down_hook = NULL; + +/* Notifiers for when IPCPv6 goes up and down */ +struct notifier *ipv6_up_notifier = NULL; +struct notifier *ipv6_down_notifier = NULL; /* * 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 */ +static void ipv6cp_resetci (fsm *); /* Reset our CI */ +static int ipv6cp_cilen (fsm *); /* Return length of our CI */ +static void ipv6cp_addci (fsm *, u_char *, int *); /* Add our CI */ +static int ipv6cp_ackci (fsm *, u_char *, int); /* Peer ack'd our CI */ +static int ipv6cp_nakci (fsm *, u_char *, int, int);/* Peer nak'd our CI */ +static int ipv6cp_rejci (fsm *, u_char *, int); /* Peer rej'd our CI */ +static int ipv6cp_reqci (fsm *, u_char *, int *, int); /* Rcv CI */ +static void ipv6cp_up (fsm *); /* We're UP */ +static void ipv6cp_down (fsm *); /* We're DOWN */ +static void ipv6cp_finished (fsm *); /* Don't need lower layer */ fsm ipv6cp_fsm[NUM_PPP]; /* IPV6CP fsm structure */ @@ -104,29 +228,58 @@ static fsm_callbacks ipv6cp_callbacks = { /* IPV6CP callback routines */ /* * Command-line options. */ -static int setifaceid __P((char *arg)); +static int setifaceid (char **arg); +static void printifaceid (struct option *, + void (*)(void *, char *, ...), void *); + +static struct option ipv6cp_option_list[] = { + { "ipv6", o_special, (void *)setifaceid, + "Set interface identifiers for IPV6", + OPT_A2PRINTER, (void *)printifaceid }, -static option_t ipv6cp_option_list[] = { - { "ipv6", o_special, setifaceid, - "Set interface identifiers for IPV6" }, + { "+ipv6", o_bool, &ipv6cp_protent.enabled_flag, + "Enable IPv6 and IPv6CP", OPT_PRIO | 1 }, { "noipv6", o_bool, &ipv6cp_protent.enabled_flag, - "Disable IPv6 and IPv6CP" }, + "Disable IPv6 and IPv6CP", OPT_PRIOSUB }, { "-ipv6", o_bool, &ipv6cp_protent.enabled_flag, - "Disable IPv6 and IPv6CP" }, + "Disable IPv6 and IPv6CP", OPT_PRIOSUB | OPT_ALIAS }, - { "ipv6cp-accept-local", o_bool, &ipv6cp_allowoptions[0].accept_local, + { "ipv6cp-accept-local", o_bool, &ipv6cp_wantoptions[0].accept_local, "Accept peer's interface identifier for us", 1 }, + { "ipv6cp-accept-remote", o_bool, &ipv6cp_wantoptions[0].accept_remote, + "Accept peer's interface identifier for itself", 1 }, + + { "defaultroute6", o_bool, &ipv6cp_wantoptions[0].default_route, + "Add default IPv6 route", OPT_ENABLE|1, &ipv6cp_allowoptions[0].default_route }, + { "nodefaultroute6", o_bool, &ipv6cp_allowoptions[0].default_route, + "disable defaultroute6 option", OPT_A2CLR, + &ipv6cp_wantoptions[0].default_route }, + { "-defaultroute6", o_bool, &ipv6cp_allowoptions[0].default_route, + "disable defaultroute6 option", OPT_ALIAS | OPT_A2CLR, + &ipv6cp_wantoptions[0].default_route }, + { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip, - "Use (default) IPv4 address as interface identifier", 0 }, + "Use (default) IPv4 addresses for both local and remote interface identifiers", 1 }, + { "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent, + "Use uniquely-available persistent value for local interface identifier", 1 }, + { "ipv6cp-use-remotenumber", o_bool, &ipv6cp_wantoptions[0].use_remotenumber, + "Use remotenumber value for remote interface identifier", 1 }, + +#ifdef __linux__ + { "ipv6cp-noremote", o_bool, &ipv6cp_noremote, + "Allow peer to have no interface identifier", 1 }, +#endif + { "ipv6cp-nosend", o_bool, &ipv6cp_wantoptions[0].neg_ifaceid, + "Don't send local interface identifier to peer", OPT_A2CLR }, { "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime, - "Set timeout for IPv6CP" }, + "Set timeout for IPv6CP", OPT_PRIO }, { "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits, - "Set max #xmits for term-reqs" }, + "Set max #xmits for term-reqs", OPT_PRIO }, { "ipv6cp-max-configure", o_int, &ipv6cp_fsm[0].maxconfreqtransmits, - "Set max #xmits for conf-reqs" }, + "Set max #xmits for conf-reqs", OPT_PRIO }, { "ipv6cp-max-failure", o_int, &ipv6cp_fsm[0].maxnakloops, - "Set max #conf-naks for IPv6CP" }, + "Set max #conf-naks for IPv6CP", OPT_PRIO }, { NULL } }; @@ -135,18 +288,18 @@ static option_t ipv6cp_option_list[] = { /* * 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)); +static void ipv6cp_init (int); +static void ipv6cp_open (int); +static void ipv6cp_close (int, char *); +static void ipv6cp_lowerup (int); +static void ipv6cp_lowerdown (int); +static void ipv6cp_input (int, u_char *, int); +static void ipv6cp_protrej (int); +static int ipv6cp_printpkt (u_char *, int, + void (*) (void *, char *, ...), void *); +static void ipv6_check_options (void); +static int ipv6_demand_conf (int); +static int ipv6_active_pkt (u_char *, int); struct protent ipv6cp_protent = { PPP_IPV6CP, @@ -159,7 +312,7 @@ struct protent ipv6cp_protent = { ipv6cp_close, ipv6cp_printpkt, NULL, - 0, + 1, "IPV6CP", "IPV6", ipv6cp_option_list, @@ -168,9 +321,9 @@ struct protent ipv6cp_protent = { ipv6_active_pkt }; -static void ipv6cp_clear_addrs __P((int)); -static void ipv6cp_script __P((char *)); -static void ipv6cp_script_done __P((void *)); +static void ipv6cp_clear_addrs (int, eui64_t, eui64_t); +static void ipv6cp_script (char *); +static void ipv6cp_script_done (void *); /* * Lengths of configuration options. @@ -196,16 +349,17 @@ static pid_t ipv6cp_script_pid; * setifaceid - set the interface identifiers manually */ static int -setifaceid(arg) - char *arg; +setifaceid(char **argv) { - char *comma; + char *comma, *arg, c; ipv6cp_options *wo = &ipv6cp_wantoptions[0]; struct in6_addr addr; - + static int prio_local, prio_remote; + #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); @@ -213,16 +367,20 @@ setifaceid(arg) * If comma first character, then no local identifier */ if (comma != arg) { + c = *comma; *comma = '\0'; if (inet_pton(AF_INET6, arg, &addr) == 0 || !VALIDID(addr)) { - option_error("Illegal interface identifier: %s", arg); + ppp_option_error("Illegal interface identifier (local): %s", arg); return 0; } - - eui64_copy(addr.s6_addr32[2], wo->ourid); - wo->opt_local = 1; - *comma = ','; + + if (option_priority >= prio_local) { + eui64_copy(addr.s6_addr32[2], wo->ourid); + wo->opt_local = 1; + prio_local = option_priority; + } + *comma = c; } /* @@ -230,22 +388,40 @@ setifaceid(arg) */ if (*comma != 0 && *++comma != '\0') { if (inet_pton(AF_INET6, comma, &addr) == 0 || !VALIDID(addr)) { - option_error("Illegal interface identifier: %s", comma); + ppp_option_error("Illegal interface identifier (remote): %s", comma); return 0; } - eui64_copy(addr.s6_addr32[2], wo->hisid); - wo->opt_remote = 1; + if (option_priority >= prio_remote) { + eui64_copy(addr.s6_addr32[2], wo->hisid); + wo->opt_remote = 1; + prio_remote = option_priority; + } } + if (override_value("+ipv6", option_priority, option_source)) + ipv6cp_protent.enabled_flag = 1; return 1; } +char *llv6_ntoa(eui64_t ifaceid); + +static void +printifaceid(struct option *opt, void (*printer) (void *, char *, ...), void *arg) +{ + ipv6cp_options *wo = &ipv6cp_wantoptions[0]; + + if (wo->opt_local) + printer(arg, "%s", llv6_ntoa(wo->ourid)); + printer(arg, ","); + if (wo->opt_remote) + printer(arg, "%s", llv6_ntoa(wo->hisid)); +} + /* * Make a string representation of a network address. */ char * -llv6_ntoa(ifaceid) - eui64_t ifaceid; +llv6_ntoa(eui64_t ifaceid) { static char b[64]; @@ -258,8 +434,7 @@ llv6_ntoa(ifaceid) * ipv6cp_init - Initialize IPV6CP. */ static void -ipv6cp_init(unit) - int unit; +ipv6cp_init(int unit) { fsm *f = &ipv6cp_fsm[unit]; ipv6cp_options *wo = &ipv6cp_wantoptions[unit]; @@ -273,7 +448,8 @@ ipv6cp_init(unit) memset(wo, 0, sizeof(*wo)); memset(ao, 0, sizeof(*ao)); - wo->accept_local = 1; + wo->accept_local = 0; + wo->accept_remote = 0; wo->neg_ifaceid = 1; ao->neg_ifaceid = 1; @@ -283,6 +459,10 @@ ipv6cp_init(unit) wo->vj_protocol = IPV6CP_COMP; #endif + /* + * XXX This controls whether the user may use the defaultroute option. + */ + ao->default_route = 1; } @@ -290,8 +470,7 @@ ipv6cp_init(unit) * ipv6cp_open - IPV6CP is allowed to come up. */ static void -ipv6cp_open(unit) - int unit; +ipv6cp_open(int unit) { fsm_open(&ipv6cp_fsm[unit]); } @@ -301,9 +480,7 @@ ipv6cp_open(unit) * ipv6cp_close - Take IPV6CP down. */ static void -ipv6cp_close(unit, reason) - int unit; - char *reason; +ipv6cp_close(int unit, char *reason) { fsm_close(&ipv6cp_fsm[unit], reason); } @@ -313,8 +490,7 @@ ipv6cp_close(unit, reason) * ipv6cp_lowerup - The lower layer is up. */ static void -ipv6cp_lowerup(unit) - int unit; +ipv6cp_lowerup(int unit) { fsm_lowerup(&ipv6cp_fsm[unit]); } @@ -324,8 +500,7 @@ ipv6cp_lowerup(unit) * ipv6cp_lowerdown - The lower layer is down. */ static void -ipv6cp_lowerdown(unit) - int unit; +ipv6cp_lowerdown(int unit) { fsm_lowerdown(&ipv6cp_fsm[unit]); } @@ -335,10 +510,7 @@ ipv6cp_lowerdown(unit) * ipv6cp_input - Input IPV6CP packet. */ static void -ipv6cp_input(unit, p, len) - int unit; - u_char *p; - int len; +ipv6cp_input(int unit, u_char *p, int len) { fsm_input(&ipv6cp_fsm[unit], p, len); } @@ -350,8 +522,7 @@ ipv6cp_input(unit, p, len) * Pretend the lower layer went down, so we shut up. */ static void -ipv6cp_protrej(unit) - int unit; +ipv6cp_protrej(int unit) { fsm_lowerdown(&ipv6cp_fsm[unit]); } @@ -361,8 +532,7 @@ ipv6cp_protrej(unit) * ipv6cp_resetci - Reset our CI. */ static void -ipv6cp_resetci(f) - fsm *f; +ipv6cp_resetci(fsm *f) { ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit]; ipv6cp_options *go = &ipv6cp_gotoptions[f->unit]; @@ -370,8 +540,12 @@ ipv6cp_resetci(f) wo->req_ifaceid = wo->neg_ifaceid && ipv6cp_allowoptions[f->unit].neg_ifaceid; if (!wo->opt_local) { - eui64_magic_nz(wo->ourid); + wo->accept_local = 1; + if (!demand) + eui64_magic_nz(wo->ourid); } + if (!wo->opt_remote) + wo->accept_remote = 1; *go = *wo; eui64_zero(go->hisid); /* last proposed interface identifier */ @@ -382,8 +556,7 @@ ipv6cp_resetci(f) * ipv6cp_cilen - Return length of our CI. */ static int -ipv6cp_cilen(f) - fsm *f; +ipv6cp_cilen(fsm *f) { ipv6cp_options *go = &ipv6cp_gotoptions[f->unit]; @@ -399,10 +572,7 @@ ipv6cp_cilen(f) * ipv6cp_addci - Add our desired CIs to a packet. */ static void -ipv6cp_addci(f, ucp, lenp) - fsm *f; - u_char *ucp; - int *lenp; +ipv6cp_addci(fsm *f, u_char *ucp, int *lenp) { ipv6cp_options *go = &ipv6cp_gotoptions[f->unit]; int len = *lenp; @@ -447,10 +617,7 @@ ipv6cp_addci(f, ucp, lenp) * 1 - Ack was good. */ static int -ipv6cp_ackci(f, p, len) - fsm *f; - u_char *p; - int len; +ipv6cp_ackci(fsm *f, u_char *p, int len) { ipv6cp_options *go = &ipv6cp_gotoptions[f->unit]; u_short cilen, citype, cishort; @@ -518,11 +685,9 @@ bad: * 1 - Nak was good. */ static int -ipv6cp_nakci(f, p, len) - fsm *f; - u_char *p; - int len; +ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) { + ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit]; ipv6cp_options *go = &ipv6cp_gotoptions[f->unit]; u_char citype, cilen, *next; u_short cishort; @@ -567,19 +732,23 @@ ipv6cp_nakci(f, p, len) * 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))); - } - ); + if (treat_as_reject) { + try.neg_ifaceid = 0; + } else if (go->accept_local && !eui64_iszero(ifaceid) && !eui64_equals(ifaceid, go->hisid)) { + try.ourid = ifaceid; + } else if (eui64_iszero(ifaceid) && !go->opt_local && wo->neg_ifaceid) { + 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) { + if (cishort == IPV6CP_COMP && !treat_as_reject) { try.vj_protocol = cishort; } else { try.neg_vj = 0; @@ -600,10 +769,10 @@ ipv6cp_nakci(f, p, len) * If they want to negotiate about interface identifier, we comply. * If they want us to ask for compression, we refuse. */ - while (len > CILEN_VOID) { + while (len >= CILEN_VOID) { GETCHAR(citype, p); GETCHAR(cilen, p); - if( (len -= cilen) < 0 ) + if ( cilen < CILEN_VOID || (len -= cilen) < 0 ) goto bad; next = p + cilen - 2; @@ -619,11 +788,15 @@ ipv6cp_nakci(f, p, len) goto bad; try.neg_ifaceid = 1; eui64_get(ifaceid, p); - if (go->accept_local) { + if (go->accept_local && !eui64_iszero(ifaceid) && !eui64_equals(ifaceid, go->hisid)) { + try.ourid = ifaceid; + } else if (eui64_iszero(ifaceid) && !go->opt_local && wo->neg_ifaceid) { while (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->hisid)) /* bad luck */ eui64_magic(ifaceid); try.ourid = ifaceid; + } else { + try.neg_ifaceid = 0; } no.neg_ifaceid = 1; break; @@ -653,10 +826,7 @@ bad: * ipv6cp_rejci - Reject some of our CIs. */ static int -ipv6cp_rejci(f, p, len) - fsm *f; - u_char *p; - int len; +ipv6cp_rejci(fsm *f, u_char *p, int len) { ipv6cp_options *go = &ipv6cp_gotoptions[f->unit]; u_char cilen; @@ -728,11 +898,7 @@ bad: * 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_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree) { ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit]; ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit]; @@ -796,9 +962,8 @@ ipv6cp_reqci(f, inp, len, reject_if_disagree) orc = CONFREJ; /* Reject CI */ break; } - if (!eui64_iszero(wo->hisid) && - !eui64_equals(ifaceid, wo->hisid) && - eui64_iszero(go->hisid)) { + if (!eui64_iszero(wo->hisid) && !wo->accept_remote && + !eui64_equals(ifaceid, wo->hisid)) { orc = CONFNAK; ifaceid = wo->hisid; @@ -810,9 +975,17 @@ ipv6cp_reqci(f, inp, len, reject_if_disagree) 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); + if (eui64_equals(ifaceid, go->ourid)) /* bad luck */ + eui64_zero(ifaceid); + if (eui64_iszero(ifaceid)) { + if (wo->opt_remote) + ifaceid = wo->hisid; + else { + 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); @@ -837,14 +1010,14 @@ ipv6cp_reqci(f, inp, len, reject_if_disagree) orc = CONFREJ; break; } -#else - orc = CONFREJ; - break; -#endif ho->neg_vj = 1; ho->vj_protocol = cishort; break; +#else + orc = CONFREJ; + break; +#endif default: orc = CONFREJ; @@ -910,15 +1083,119 @@ endswitch: } +/* + * eui48_to_eui64 - Convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1] + */ +static void +eui48_to_eui64(eui64_t *p_eui64, const u_char addr[6]) +{ + p_eui64->e8[0] = addr[0] | 0x02; + p_eui64->e8[1] = addr[1]; + p_eui64->e8[2] = addr[2]; + p_eui64->e8[3] = 0xFF; + p_eui64->e8[4] = 0xFE; + p_eui64->e8[5] = addr[3]; + p_eui64->e8[6] = addr[4]; + p_eui64->e8[7] = addr[5]; +} + + +/* + * ether_to_eui64 - Convert 48-bit Ethernet address into 64-bit EUI + * + * walks the list of valid ethernet interfaces, starting with devnam + * (for PPPoE it is ethernet interface), and convert the first + * found 48-bit MAC address into EUI 64. caller also assumes that + * the system has a properly configured Ethernet interface for this + * function to return non-zero. + */ +static int +ether_to_eui64(eui64_t *p_eui64) +{ + u_char addr[6]; + + if (get_if_hwaddr(addr, devnam) < 0 && get_first_ether_hwaddr(addr) < 0) { + error("ipv6cp: no persistent id can be found"); + return 0; + } + + eui48_to_eui64(p_eui64, addr); + return 1; +} + + /* * ipv6_check_options - check that any IP-related options are OK, * and assign appropriate defaults. */ static void -ipv6_check_options() +ipv6_check_options(void) { ipv6cp_options *wo = &ipv6cp_wantoptions[0]; + if (!ipv6cp_protent.enabled_flag) + return; + + /* + * Persistent link-local id is only used when user has not explicitly + * configure/hard-code the id + */ + if ((wo->use_persistent) && (!wo->opt_local)) { + + /* + * 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; + } + } + + if (!wo->opt_remote && wo->use_remotenumber && *remote_number) { + /* remote number can be either MAC address, IPv4 address, IPv6 address or telephone number */ + struct in_addr addr; + struct in6_addr addr6; + unsigned long long tel; + unsigned char mac[6]; + const char *str; + char *endptr; + if (sscanf(remote_number, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &mac[0], &mac[1], &mac[2], + &mac[3], &mac[4], &mac[5]) == 6) { + eui48_to_eui64(&wo->hisid, mac); + } else if (inet_pton(AF_INET, remote_number, &addr) == 1) { + eui64_setlo32(wo->hisid, ntohl(addr.s_addr)); + } else if (inet_pton(AF_INET6, remote_number, &addr6) == 1) { + /* use low 64 bits of IPv6 address for interface identifier */ + wo->hisid.e8[0] = addr6.s6_addr[8]; + wo->hisid.e8[1] = addr6.s6_addr[9]; + wo->hisid.e8[2] = addr6.s6_addr[10]; + wo->hisid.e8[3] = addr6.s6_addr[11]; + wo->hisid.e8[4] = addr6.s6_addr[12]; + wo->hisid.e8[5] = addr6.s6_addr[13]; + wo->hisid.e8[6] = addr6.s6_addr[14]; + wo->hisid.e8[7] = addr6.s6_addr[15]; + } else { + str = remote_number; + /* telephone number may start with leading '+' sign, so skip it */ + if (str[0] == '+') + str++; + errno = 0; + tel = strtoull(str, &endptr, 10); + if (!errno && *str && !*endptr && tel) { + wo->hisid.e32[0] = htonl(tel >> 32); + wo->hisid.e32[1] = htonl(tel & 0xFFFFFFFF); + } + } + if (!eui64_iszero(wo->hisid)) + wo->opt_remote = 1; + } + if (!wo->opt_local) { /* init interface identifier */ if (wo->use_ip && eui64_iszero(wo->ourid)) { eui64_setlo32(wo->ourid, ntohl(ipcp_wantoptions[0].ouraddr)); @@ -937,11 +1214,6 @@ ipv6_check_options() 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); - } } @@ -950,15 +1222,21 @@ ipv6_check_options() * IPV6CP were up, for use with dial-on-demand. */ static int -ipv6_demand_conf(u) - int u; +ipv6_demand_conf(int u) { ipv6cp_options *wo = &ipv6cp_wantoptions[u]; -#if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC__))) - if (!sifup(u)) + if (eui64_iszero(wo->hisid) && !ipv6cp_noremote) { + /* make up an arbitrary identifier for the peer */ + eui64_magic_nz(wo->hisid); + } + if (eui64_iszero(wo->ourid)) { + /* make up an arbitrary identifier for us */ + eui64_magic_nz(wo->ourid); + } + + if (!sif6up(u)) return 0; -#endif if (!sif6addr(u, wo->ourid, wo->hisid)) return 0; #if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__))) @@ -967,10 +1245,13 @@ ipv6_demand_conf(u) #endif if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE)) return 0; + if (wo->default_route) + if (sif6defaultroute(u, wo->ourid, wo->hisid)) + default_route_set[u] = 1; - notice("ipv6_demand_conf"); notice("local LL address %s", llv6_ntoa(wo->ourid)); - notice("remote LL address %s", llv6_ntoa(wo->hisid)); + if (!eui64_iszero(wo->hisid)) + notice("remote LL address %s", llv6_ntoa(wo->hisid)); return 1; } @@ -982,8 +1263,7 @@ ipv6_demand_conf(u) * Configure the IPv6 network interface appropriately and bring it up. */ static void -ipv6cp_up(f) - fsm *f; +ipv6cp_up(fsm *f) { ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit]; ipv6cp_options *go = &ipv6cp_gotoptions[f->unit]; @@ -994,11 +1274,17 @@ ipv6cp_up(f) /* * We must have a non-zero LL address for both ends of the link. */ + + if (!eui64_iszero(wo->hisid) && !wo->accept_remote && (!ho->neg_ifaceid || !eui64_equals(ho->hisid, wo->hisid))) { + error("Peer refused to agree to his interface identifier"); + ipv6cp_close(f->unit, "Refused his interface identifier"); + return; + } if (!ho->neg_ifaceid) ho->hisid = wo->hisid; if(!no_ifaceid_neg) { - if (eui64_iszero(ho->hisid)) { + if (eui64_iszero(ho->hisid) && !ipv6cp_noremote) { error("Could not determine remote LL address"); ipv6cp_close(f->unit, "Could not determine remote LL address"); return; @@ -1014,8 +1300,9 @@ ipv6cp_up(f) return; } } - script_setenv("LLLOCAL", llv6_ntoa(go->ourid)); - script_setenv("LLREMOTE", llv6_ntoa(ho->hisid)); + ppp_script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0); + if (!eui64_iszero(ho->hisid)) + ppp_script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0); #ifdef IPV6CP_COMP /* set tcp compression */ @@ -1036,7 +1323,7 @@ ipv6cp_up(f) if (! eui64_equals(ho->hisid, wo->hisid)) warn("Remote LL address changed to %s", llv6_ntoa(ho->hisid)); - ipv6cp_clear_addrs(f->unit); + ipv6cp_clear_addrs(f->unit, wo->ourid, wo->hisid); /* Set the interface to the new addresses */ if (!sif6addr(f->unit, go->ourid, ho->hisid)) { @@ -1046,55 +1333,55 @@ ipv6cp_up(f) return; } + /* assign a default route through the interface if required */ + if (ipv6cp_wantoptions[f->unit].default_route) + if (sif6defaultroute(f->unit, go->ourid, ho->hisid)) + default_route_set[f->unit] = 1; } demand_rexmit(PPP_IPV6); sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS); } else { - /* - * Set LL addresses - */ -#if !defined(__linux__) && !(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 (!sifup(f->unit)) { + if (!sif6up(f->unit)) { if (debug) - warn("sif6up failed"); + warn("sif6up failed (IPV6)"); ipv6cp_close(f->unit, "Interface configuration failed"); return; } -#if defined(__linux__) || (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); + /* assign a default route through the interface if required */ + if (ipv6cp_wantoptions[f->unit].default_route) + if (sif6defaultroute(f->unit, go->ourid, ho->hisid)) + default_route_set[f->unit] = 1; + notice("local LL address %s", llv6_ntoa(go->ourid)); - notice("remote LL address %s", llv6_ntoa(ho->hisid)); + if (!eui64_iszero(ho->hisid)) + notice("remote LL address %s", llv6_ntoa(ho->hisid)); } np_up(f->unit, PPP_IPV6); ipv6cp_is_up = 1; + notify(ipv6_up_notifier, 0); + if (ipv6_up_hook) + ipv6_up_hook(); + /* * 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_script(path_ipv6up); } } @@ -1106,11 +1393,13 @@ ipv6cp_up(f) * and delete routes through it. */ static void -ipv6cp_down(f) - fsm *f; +ipv6cp_down(fsm *f) { IPV6CPDEBUG(("ipv6cp: down")); - update_link_stats(f->unit); + ppp_get_link_stats(NULL); + notify(ipv6_down_notifier, 0); + if (ipv6_down_hook) + ipv6_down_hook(); if (ipv6cp_is_up) { ipv6cp_is_up = 0; np_down(f->unit, PPP_IPV6); @@ -1126,12 +1415,16 @@ ipv6cp_down(f) if (demand) { sifnpmode(f->unit, PPP_IPV6, NPMODE_QUEUE); } else { -#if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC))) sifnpmode(f->unit, PPP_IPV6, NPMODE_DROP); - sifdown(f->unit); +#if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC))) + sif6down(f->unit); #endif - ipv6cp_clear_addrs(f->unit); -#if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC))) + ipv6cp_clear_addrs(f->unit, + ipv6cp_gotoptions[f->unit].ourid, + ipv6cp_hisoptions[f->unit].hisid); +#if defined(__linux__) + sif6down(f->unit); +#elif defined(SVR4) && (defined(SNI) || defined(__USLC)) sifdown(f->unit); #endif } @@ -1139,23 +1432,18 @@ ipv6cp_down(f) /* 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_script(path_ipv6down); } } /* * ipv6cp_clear_addrs() - clear the interface addresses, routes, - * proxy arp entries, etc. + * proxy neighbour discovery entries, etc. */ static void -ipv6cp_clear_addrs(unit) - int unit; +ipv6cp_clear_addrs(int unit, eui64_t ourid, eui64_t hisid) { - eui64_t ourid, hisid; - - ourid = ipv6cp_gotoptions[unit].ourid; - hisid = ipv6cp_hisoptions[unit].hisid; cif6addr(unit, ourid, hisid); } @@ -1164,8 +1452,7 @@ ipv6cp_clear_addrs(unit) * ipv6cp_finished - possibly shut down the lower layers. */ static void -ipv6cp_finished(f) - fsm *f; +ipv6cp_finished(fsm *f) { np_finished(f->unit, PPP_IPV6); } @@ -1176,21 +1463,20 @@ ipv6cp_finished(f) * has finished. */ static void -ipv6cp_script_done(arg) - void *arg; +ipv6cp_script_done(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); + ipv6cp_script(path_ipv6down); } break; case s_down: if (ipv6cp_fsm[0].state == OPENED) { ipv6cp_script_state = s_up; - ipv6cp_script(_PATH_IPV6UP); + ipv6cp_script(path_ipv6up); } break; } @@ -1202,10 +1488,9 @@ ipv6cp_script_done(arg) * interface-name tty-name speed local-LL remote-LL. */ static void -ipv6cp_script(script) - char *script; +ipv6cp_script(char *script) { - char strspeed[32], strlocal[32], strremote[32]; + char strspeed[32], strlocal[64], strremote[64]; char *argv[8]; sprintf(strspeed, "%d", baud_rate); @@ -1221,7 +1506,8 @@ ipv6cp_script(script) argv[6] = ipparam; argv[7] = NULL; - ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done, NULL); + ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done, + NULL, 0); } /* @@ -1233,11 +1519,8 @@ static char *ipv6cp_codenames[] = { }; static int -ipv6cp_printpkt(p, plen, printer, arg) - u_char *p; - int plen; - void (*printer) __P((void *, char *, ...)); - void *arg; +ipv6cp_printpkt(u_char *p, int plen, + void (*printer) (void *, char *, ...), void *arg) { int code, id, len, olen; u_char *pstart, *optend; @@ -1304,7 +1587,7 @@ ipv6cp_printpkt(p, plen, printer, arg) case TERMREQ: if (len > 0 && *p >= ' ' && *p < 0x7f) { printer(arg, " "); - print_string(p, len, printer, arg); + print_string((char *)p, len, printer, arg); p += len; len = 0; } @@ -1327,7 +1610,6 @@ ipv6cp_printpkt(p, plen, printer, arg) */ #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 @@ -1341,9 +1623,7 @@ ipv6cp_printpkt(p, plen, printer, arg) #define get_tcpflags(x) (((unsigned char *)(x))[13]) static int -ipv6_active_pkt(pkt, len) - u_char *pkt; - int len; +ipv6_active_pkt(u_char *pkt, int len) { u_char *tcp;