ipv6cp.c revision 112613
1/*-
2 * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/usr.sbin/ppp/ipv6cp.c 112613 2003-03-25 15:59:27Z ume $
27 */
28
29#include <sys/param.h>
30#include <netinet/in_systm.h>
31#include <netinet/in.h>
32#include <netinet/ip.h>
33#include <sys/socket.h>
34#include <net/route.h>
35#include <net/if.h>
36#include <net/if_types.h>
37#include <net/if_dl.h>
38#include <sys/un.h>
39
40#include <stdarg.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <termios.h>
45#include <ifaddrs.h>
46
47#include "layer.h"
48#include "defs.h"
49#include "mbuf.h"
50#include "timer.h"
51#include "fsm.h"
52#include "iplist.h"
53#include "throughput.h"
54#include "slcompress.h"
55#include "lqr.h"
56#include "hdlc.h"
57#include "lcp.h"
58#include "ncpaddr.h"
59#include "ip.h"
60#include "ipcp.h"
61#include "ipv6cp.h"
62#include "filter.h"
63#include "descriptor.h"
64#include "ccp.h"
65#include "link.h"
66#include "mp.h"
67#ifndef NORADIUS
68#include "radius.h"
69#endif
70#include "ncp.h"
71#include "bundle.h"
72#include "route.h"
73#include "iface.h"
74#include "log.h"
75#include "proto.h"
76#include "command.h"
77#include "prompt.h"
78#include "async.h"
79#include "physical.h"
80#include "probe.h"
81
82
83#ifndef NOINET6
84static int ipv6cp_LayerUp(struct fsm *);
85static void ipv6cp_LayerDown(struct fsm *);
86static void ipv6cp_LayerStart(struct fsm *);
87static void ipv6cp_LayerFinish(struct fsm *);
88static void ipv6cp_InitRestartCounter(struct fsm *, int);
89static void ipv6cp_SendConfigReq(struct fsm *);
90static void ipv6cp_SentTerminateReq(struct fsm *);
91static void ipv6cp_SendTerminateAck(struct fsm *, u_char);
92static void ipv6cp_DecodeConfig(struct fsm *, u_char *, u_char *, int,
93                                struct fsm_decode *);
94
95static struct fsm_callbacks ipv6cp_Callbacks = {
96  ipv6cp_LayerUp,
97  ipv6cp_LayerDown,
98  ipv6cp_LayerStart,
99  ipv6cp_LayerFinish,
100  ipv6cp_InitRestartCounter,
101  ipv6cp_SendConfigReq,
102  ipv6cp_SentTerminateReq,
103  ipv6cp_SendTerminateAck,
104  ipv6cp_DecodeConfig,
105  fsm_NullRecvResetReq,
106  fsm_NullRecvResetAck
107};
108
109static void
110SetInterfaceID(u_char *ifid, int userandom)
111{
112  struct ifaddrs *ifa, *ifap = NULL;
113  struct sockaddr_dl *sdl;
114  const u_long i32_max = 0xffffffff;
115  u_long r1, r2;
116
117  /* configure an interface ID based on Section 4.1 of RFC 2472 */
118  memset(ifid, 0, IPV6CP_IFIDLEN);
119
120  /*
121   * 1) If an IEEE global identifier (EUI-48 or EUI-64) is
122   * available anywhere on the node, it should be used to construct
123   * the tentative Interface-Identifier due to its uniqueness
124   * properties.
125   */
126  if (userandom)
127    goto randomid;
128  if (getifaddrs(&ifap) < 0)
129    goto randomid;
130
131  for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
132    char *cp;
133
134    if (ifa->ifa_addr->sa_family != AF_LINK)
135      continue;
136
137    sdl = (struct sockaddr_dl *)ifa->ifa_addr;
138    if (sdl->sdl_alen < 6)
139      continue;
140    /* we're only interested in IEEE hardware addresses */
141    switch(sdl->sdl_type) {
142    case IFT_ETHER:
143    case IFT_FDDI:
144      /* XXX need more cases? */
145      break;
146    default:
147      continue;
148    }
149
150    cp = (char *)(sdl->sdl_data + sdl->sdl_nlen);
151    ifid[0] = cp[0];
152    ifid[0] ^= 0x02; /* reverse the u/l bit*/
153    ifid[1] = cp[1];
154    ifid[2] = cp[2];
155    ifid[3] = 0xff;
156    ifid[4] = 0xfe;
157    ifid[5] = cp[3];
158    ifid[6] = cp[4];
159    ifid[7] = cp[5];
160
161    freeifaddrs(ifap);
162    return;
163  }
164
165  freeifaddrs(ifap);
166
167  /*
168   * 2) If an IEEE global identifier is not available a different source
169   * of uniqueness should be used.
170   * XXX: we skip this case.
171   */
172
173  /*
174   * 3) If a good source of uniqueness cannot be found, it is
175   * recommended that a random number be generated.  In this case the
176   * "u" bit of the interface identifier MUST be set to zero (0).
177   */
178 randomid:
179  randinit();
180  r1 = (((u_long)random()) % i32_max) + 1;
181  r2 = (((u_long)random()) % i32_max) + 1;
182  memcpy(ifid, &r1, sizeof(r1));
183  memcpy(ifid + 4, &r2, sizeof(r2));
184  ifid[0] &= 0xfd;
185  return;
186}
187
188static int
189ipcp_SetIPv6address(struct ipv6cp *ipv6cp, u_char *myifid, u_char *hisifid)
190{
191  struct bundle *bundle = ipv6cp->fsm.bundle;
192  struct in6_addr myaddr, hisaddr;
193  struct ncprange myrange;
194  struct sockaddr_storage ssdst, ssgw, ssmask;
195  struct sockaddr *sadst, *sagw, *samask;
196
197  sadst = (struct sockaddr *)&ssdst;
198  sagw = (struct sockaddr *)&ssgw;
199  samask = (struct sockaddr *)&ssmask;
200
201  memset(&myaddr, '\0', sizeof myaddr);
202  memset(&hisaddr, '\0', sizeof hisaddr);
203
204  myaddr.s6_addr[0] = 0xfe;
205  myaddr.s6_addr[1] = 0x80;
206  memcpy(&myaddr.s6_addr[8], myifid, IPV6CP_IFIDLEN);
207#if 0
208  myaddr.s6_addr[8] |= 0x02;	/* set 'universal' bit */
209#endif
210
211  hisaddr.s6_addr[0] = 0xfe;
212  hisaddr.s6_addr[1] = 0x80;
213  memcpy(&hisaddr.s6_addr[8], hisifid, IPV6CP_IFIDLEN);
214#if 0
215  hisaddr.s6_addr[8] |= 0x02;	/* set 'universal' bit */
216#endif
217
218  ncpaddr_setip6(&ipv6cp->myaddr, &myaddr);
219  ncpaddr_setip6(&ipv6cp->hisaddr, &hisaddr);
220  ncprange_sethost(&myrange, &ipv6cp->myaddr);
221
222  if (!iface_Add(bundle->iface, &bundle->ncp, &myrange, &ipv6cp->hisaddr,
223                 IFACE_ADD_FIRST|IFACE_FORCE_ADD|IFACE_SYSTEM))
224    return 0;
225
226  if (!Enabled(bundle, OPT_IFACEALIAS))
227    iface_Clear(bundle->iface, &bundle->ncp, AF_INET6,
228                IFACE_CLEAR_ALIASES|IFACE_SYSTEM);
229
230  if (bundle->ncp.cfg.sendpipe > 0 || bundle->ncp.cfg.recvpipe > 0) {
231    ncprange_getsa(&myrange, &ssgw, &ssmask);
232    if (ncpaddr_isset(&ipv6cp->hisaddr))
233      ncpaddr_getsa(&ipv6cp->hisaddr, &ssdst);
234    else
235      sadst = NULL;
236    rt_Update(bundle, sadst, sagw, samask);
237  }
238
239  if (Enabled(bundle, OPT_SROUTES))
240    route_Change(bundle, bundle->ncp.route, &ipv6cp->myaddr, &ipv6cp->hisaddr);
241
242#ifndef NORADIUS
243  if (bundle->radius.valid)
244    route_Change(bundle, bundle->radius.routes, &ipv6cp->myaddr,
245                 &ipv6cp->hisaddr);
246#endif
247
248  return 1;	/* Ok */
249}
250
251void
252ipv6cp_Init(struct ipv6cp *ipv6cp, struct bundle *bundle, struct link *l,
253                 const struct fsm_parent *parent)
254{
255  static const char * const timer_names[] =
256    {"IPV6CP restart", "IPV6CP openmode", "IPV6CP stopped"};
257  int n;
258
259  fsm_Init(&ipv6cp->fsm, "IPV6CP", PROTO_IPV6CP, 1, IPV6CP_MAXCODE, LogIPV6CP,
260           bundle, l, parent, &ipv6cp_Callbacks, timer_names);
261
262  ipv6cp->cfg.fsm.timeout = DEF_FSMRETRY;
263  ipv6cp->cfg.fsm.maxreq = DEF_FSMTRIES;
264  ipv6cp->cfg.fsm.maxtrm = DEF_FSMTRIES;
265
266  SetInterfaceID(ipv6cp->my_ifid, 0);
267  do {
268    SetInterfaceID(ipv6cp->his_ifid, 1);
269  } while (memcmp(ipv6cp->his_ifid, ipv6cp->my_ifid, IPV6CP_IFIDLEN) == 0);
270
271  if (probe.ipv6_available) {
272    n = 100;
273    while (n &&
274           !ipcp_SetIPv6address(ipv6cp, ipv6cp->my_ifid, ipv6cp->his_ifid)) {
275      do {
276	n--;
277    	SetInterfaceID(ipv6cp->my_ifid, 1);
278      } while (n
279	&& memcmp(ipv6cp->his_ifid, ipv6cp->my_ifid, IPV6CP_IFIDLEN) == 0);
280    }
281  }
282
283  throughput_init(&ipv6cp->throughput, SAMPLE_PERIOD);
284  memset(ipv6cp->Queue, '\0', sizeof ipv6cp->Queue);
285  ipv6cp_Setup(ipv6cp);
286}
287
288void
289ipv6cp_Destroy(struct ipv6cp *ipv6cp)
290{
291  throughput_destroy(&ipv6cp->throughput);
292}
293
294void
295ipv6cp_Setup(struct ipv6cp *ipv6cp)
296{
297  ncpaddr_init(&ipv6cp->myaddr);
298  ncpaddr_init(&ipv6cp->hisaddr);
299
300  ipv6cp->his_reject = 0;
301  ipv6cp->my_reject = 0;
302}
303
304void
305ipv6cp_SetLink(struct ipv6cp *ipv6cp, struct link *l)
306{
307  ipv6cp->fsm.link = l;
308}
309
310int
311ipv6cp_Show(struct cmdargs const *arg)
312{
313  struct ipv6cp *ipv6cp = &arg->bundle->ncp.ipv6cp;
314
315  prompt_Printf(arg->prompt, "%s [%s]\n", ipv6cp->fsm.name,
316                State2Nam(ipv6cp->fsm.state));
317  if (ipv6cp->fsm.state == ST_OPENED) {
318    prompt_Printf(arg->prompt, " His side:        %s\n",
319                  ncpaddr_ntoa(&ipv6cp->hisaddr));
320    prompt_Printf(arg->prompt, " My side:         %s\n",
321                  ncpaddr_ntoa(&ipv6cp->myaddr));
322    prompt_Printf(arg->prompt, " Queued packets:  %lu\n",
323                  (unsigned long)ipv6cp_QueueLen(ipv6cp));
324  }
325
326  prompt_Printf(arg->prompt, "\nDefaults:\n");
327  prompt_Printf(arg->prompt, "  FSM retry = %us, max %u Config"
328                " REQ%s, %u Term REQ%s\n\n", ipv6cp->cfg.fsm.timeout,
329                ipv6cp->cfg.fsm.maxreq, ipv6cp->cfg.fsm.maxreq == 1 ? "" : "s",
330                ipv6cp->cfg.fsm.maxtrm, ipv6cp->cfg.fsm.maxtrm == 1 ? "" : "s");
331
332  throughput_disp(&ipv6cp->throughput, arg->prompt);
333
334  return 0;
335}
336
337struct mbuf *
338ipv6cp_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
339{
340  /* Got PROTO_IPV6CP from link */
341  m_settype(bp, MB_IPV6CPIN);
342  if (bundle_Phase(bundle) == PHASE_NETWORK)
343    fsm_Input(&bundle->ncp.ipv6cp.fsm, bp);
344  else {
345    if (bundle_Phase(bundle) < PHASE_NETWORK)
346      log_Printf(LogIPV6CP, "%s: Error: Unexpected IPV6CP in phase %s"
347                 " (ignored)\n", l->name, bundle_PhaseName(bundle));
348    m_freem(bp);
349  }
350  return NULL;
351}
352
353void
354ipv6cp_AddInOctets(struct ipv6cp *ipv6cp, int n)
355{
356  throughput_addin(&ipv6cp->throughput, n);
357}
358
359void
360ipv6cp_AddOutOctets(struct ipv6cp *ipv6cp, int n)
361{
362  throughput_addout(&ipv6cp->throughput, n);
363}
364
365void
366ipv6cp_IfaceAddrAdded(struct ipv6cp *ipv6cp, const struct iface_addr *addr)
367{
368}
369
370void
371ipv6cp_IfaceAddrDeleted(struct ipv6cp *ipv6cp, const struct iface_addr *addr)
372{
373}
374
375int
376ipv6cp_InterfaceUp(struct ipv6cp *ipv6cp)
377{
378  if (!ipcp_SetIPv6address(ipv6cp, ipv6cp->my_ifid, ipv6cp->his_ifid)) {
379    log_Printf(LogERROR, "ipv6cp_InterfaceUp: unable to set ipv6 address\n");
380    return 0;
381  }
382
383  if (!iface_SetFlags(ipv6cp->fsm.bundle->iface->name, IFF_UP)) {
384    log_Printf(LogERROR, "ipv6cp_InterfaceUp: Can't set the IFF_UP"
385               " flag on %s\n", ipv6cp->fsm.bundle->iface->name);
386    return 0;
387  }
388
389  return 1;
390}
391
392size_t
393ipv6cp_QueueLen(struct ipv6cp *ipv6cp)
394{
395  struct mqueue *q;
396  size_t result;
397
398  result = 0;
399  for (q = ipv6cp->Queue; q < ipv6cp->Queue + IPV6CP_QUEUES(ipv6cp); q++)
400    result += q->len;
401
402  return result;
403}
404
405int
406ipv6cp_PushPacket(struct ipv6cp *ipv6cp, struct link *l)
407{
408  struct bundle *bundle = ipv6cp->fsm.bundle;
409  struct mqueue *queue;
410  struct mbuf *bp;
411  int m_len;
412  u_int32_t secs = 0;
413  unsigned alivesecs = 0;
414
415  if (ipv6cp->fsm.state != ST_OPENED)
416    return 0;
417
418  /*
419   * If ccp is not open but is required, do nothing.
420   */
421  if (l->ccp.fsm.state != ST_OPENED && ccp_Required(&l->ccp)) {
422    log_Printf(LogPHASE, "%s: Not transmitting... waiting for CCP\n", l->name);
423    return 0;
424  }
425
426  queue = ipv6cp->Queue + IPV6CP_QUEUES(ipv6cp) - 1;
427  do {
428    if (queue->top) {
429      bp = m_dequeue(queue);
430      bp = mbuf_Read(bp, &secs, sizeof secs);
431      bp = m_pullup(bp);
432      m_len = m_length(bp);
433      if (!FilterCheck(MBUF_CTOP(bp), AF_INET6, &bundle->filter.alive,
434                       &alivesecs)) {
435        if (secs == 0)
436          secs = alivesecs;
437        bundle_StartIdleTimer(bundle, secs);
438      }
439      link_PushPacket(l, bp, bundle, 0, PROTO_IPV6);
440      ipv6cp_AddOutOctets(ipv6cp, m_len);
441      return 1;
442    }
443  } while (queue-- != ipv6cp->Queue);
444
445  return 0;
446}
447
448static int
449ipv6cp_LayerUp(struct fsm *fp)
450{
451  /* We're now up */
452  struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
453  char tbuff[40];
454
455  log_Printf(LogIPV6CP, "%s: LayerUp.\n", fp->link->name);
456  if (!ipv6cp_InterfaceUp(ipv6cp))
457    return 0;
458
459  snprintf(tbuff, sizeof tbuff, "%s", ncpaddr_ntoa(&ipv6cp->myaddr));
460  log_Printf(LogIPV6CP, "myaddr %s hisaddr = %s\n",
461             tbuff, ncpaddr_ntoa(&ipv6cp->hisaddr));
462
463  /* XXX: Call radius_Account() */
464
465  if (!Enabled(fp->bundle, OPT_IPCP)) {
466    /*
467     * XXX this stuff should really live in the FSM.  Our config should
468     * associate executable sections in files with events.
469     */
470    if (system_Select(fp->bundle, tbuff, LINKUPFILE, NULL, NULL) < 0) {
471      if (bundle_GetLabel(fp->bundle)) {
472	if (system_Select(fp->bundle, bundle_GetLabel(fp->bundle),
473			  LINKUPFILE, NULL, NULL) < 0)
474	  system_Select(fp->bundle, "MYADDR6", LINKUPFILE, NULL, NULL);
475      } else
476	system_Select(fp->bundle, "MYADDR6", LINKUPFILE, NULL, NULL);
477    }
478  }
479
480  fp->more.reqs = fp->more.naks = fp->more.rejs = ipv6cp->cfg.fsm.maxreq * 3;
481  log_DisplayPrompts();
482
483  return 1;
484}
485
486static void
487ipv6cp_LayerDown(struct fsm *fp)
488{
489  /* About to come down */
490  struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
491  static int recursing;
492  char addr[40];
493
494  if (!recursing++) {
495    snprintf(addr, sizeof addr, "%s", ncpaddr_ntoa(&ipv6cp->myaddr));
496    log_Printf(LogIPV6CP, "%s: LayerDown: %s\n", fp->link->name, addr);
497
498    /* XXX: Call radius_Account() */
499
500    if (!Enabled(fp->bundle, OPT_IPCP)) {
501      /*
502       * XXX this stuff should really live in the FSM.  Our config should
503       * associate executable sections in files with events.
504       */
505      if (system_Select(fp->bundle, addr, LINKDOWNFILE, NULL, NULL) < 0) {
506	if (bundle_GetLabel(fp->bundle)) {
507	  if (system_Select(fp->bundle, bundle_GetLabel(fp->bundle),
508			    LINKDOWNFILE, NULL, NULL) < 0)
509	    system_Select(fp->bundle, "MYADDR6", LINKDOWNFILE, NULL, NULL);
510	} else
511	  system_Select(fp->bundle, "MYADDR6", LINKDOWNFILE, NULL, NULL);
512      }
513    }
514
515    ipv6cp_Setup(ipv6cp);
516  }
517  recursing--;
518}
519
520static void
521ipv6cp_LayerStart(struct fsm *fp)
522{
523  /* We're about to start up ! */
524  struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
525
526  log_Printf(LogIPV6CP, "%s: LayerStart.\n", fp->link->name);
527  throughput_start(&ipv6cp->throughput, "IPV6CP throughput",
528                   Enabled(fp->bundle, OPT_THROUGHPUT));
529  fp->more.reqs = fp->more.naks = fp->more.rejs = ipv6cp->cfg.fsm.maxreq * 3;
530  ipv6cp->peer_tokenreq = 0;
531}
532
533static void
534ipv6cp_LayerFinish(struct fsm *fp)
535{
536  /* We're now down */
537  struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
538
539  log_Printf(LogIPV6CP, "%s: LayerFinish.\n", fp->link->name);
540  throughput_stop(&ipv6cp->throughput);
541  throughput_log(&ipv6cp->throughput, LogIPV6CP, NULL);
542}
543
544static void
545ipv6cp_InitRestartCounter(struct fsm *fp, int what)
546{
547  /* Set fsm timer load */
548  struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
549
550  fp->FsmTimer.load = ipv6cp->cfg.fsm.timeout * SECTICKS;
551  switch (what) {
552    case FSM_REQ_TIMER:
553      fp->restart = ipv6cp->cfg.fsm.maxreq;
554      break;
555    case FSM_TRM_TIMER:
556      fp->restart = ipv6cp->cfg.fsm.maxtrm;
557      break;
558    default:
559      fp->restart = 1;
560      break;
561  }
562}
563
564static void
565ipv6cp_SendConfigReq(struct fsm *fp)
566{
567  /* Send config REQ please */
568  struct physical *p = link2physical(fp->link);
569  struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
570  u_char buff[IPV6CP_IFIDLEN+2];
571  struct fsm_opt *o;
572
573  o = (struct fsm_opt *)buff;
574
575  if ((p && !physical_IsSync(p)) || !REJECTED(ipv6cp, TY_TOKEN)) {
576    memcpy(o->data, ipv6cp->my_ifid, IPV6CP_IFIDLEN);
577    INC_FSM_OPT(TY_TOKEN, IPV6CP_IFIDLEN + 2, o);
578  }
579
580  fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, (u_char *)o - buff,
581             MB_IPV6CPOUT);
582}
583
584static void
585ipv6cp_SentTerminateReq(struct fsm *fp)
586{
587  /* Term REQ just sent by FSM */
588}
589
590static void
591ipv6cp_SendTerminateAck(struct fsm *fp, u_char id)
592{
593  /* Send Term ACK please */
594  fsm_Output(fp, CODE_TERMACK, id, NULL, 0, MB_IPV6CPOUT);
595}
596
597static const char *
598protoname(int proto)
599{
600  static const char *cftypes[] = { "IFACEID", "COMPPROTO" };
601
602  if (proto > 0 && proto <= sizeof cftypes / sizeof *cftypes)
603    return cftypes[proto - 1];
604
605  return NumStr(proto, NULL, 0);
606}
607
608static void
609ipv6cp_ValidateInterfaceID(struct ipv6cp *ipv6cp, u_char *ifid,
610			   struct fsm_decode *dec)
611{
612  struct fsm_opt opt;
613  u_char zero[IPV6CP_IFIDLEN];
614
615  memset(zero, 0, IPV6CP_IFIDLEN);
616
617  if (memcmp(ifid, zero, IPV6CP_IFIDLEN) != 0
618      && memcmp(ifid, ipv6cp->my_ifid, IPV6CP_IFIDLEN) != 0)
619    memcpy(ipv6cp->his_ifid, ifid, IPV6CP_IFIDLEN);
620
621  opt.hdr.id = TY_TOKEN;
622  opt.hdr.len = IPV6CP_IFIDLEN + 2;
623  memcpy(opt.data, &ipv6cp->his_ifid, IPV6CP_IFIDLEN);
624  if (memcmp(ifid, ipv6cp->his_ifid, IPV6CP_IFIDLEN) == 0)
625    fsm_ack(dec, &opt);
626  else
627    fsm_nak(dec, &opt);
628}
629
630static void
631ipv6cp_DecodeConfig(struct fsm *fp, u_char *cp, u_char *end, int mode_type,
632                    struct fsm_decode *dec)
633{
634  /* Deal with incoming PROTO_IPV6CP */
635  struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
636  int n;
637  char tbuff[100];
638  u_char ifid[IPV6CP_IFIDLEN], zero[IPV6CP_IFIDLEN];
639  struct fsm_opt *opt;
640
641  memset(zero, 0, IPV6CP_IFIDLEN);
642
643  while (end - cp >= sizeof(opt->hdr)) {
644    if ((opt = fsm_readopt(&cp)) == NULL)
645      break;
646
647    snprintf(tbuff, sizeof tbuff, " %s[%d]", protoname(opt->hdr.id),
648             opt->hdr.len);
649
650    switch (opt->hdr.id) {
651    case TY_TOKEN:
652      memcpy(ifid, opt->data, IPV6CP_IFIDLEN);
653      log_Printf(LogIPV6CP, "%s 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", tbuff,
654		 ifid[0], ifid[1], ifid[2], ifid[3], ifid[4], ifid[5], ifid[6], ifid[7]);
655
656      switch (mode_type) {
657      case MODE_REQ:
658        ipv6cp->peer_tokenreq = 1;
659        ipv6cp_ValidateInterfaceID(ipv6cp, ifid, dec);
660        break;
661
662      case MODE_NAK:
663        if (memcmp(ifid, zero, IPV6CP_IFIDLEN) == 0) {
664          log_Printf(log_IsKept(LogIPV6CP) ? LogIPV6CP : LogPHASE,
665		     "0x0000000000000000: Unacceptable IntefaceID!\n");
666          fsm_Close(&ipv6cp->fsm);
667        } else if (memcmp(ifid, ipv6cp->his_ifid, IPV6CP_IFIDLEN) == 0) {
668          log_Printf(log_IsKept(LogIPV6CP) ? LogIPV6CP : LogPHASE,
669		     "0x%02x%02x%02x%02x%02x%02x%02x%02x: "
670		     "Unacceptable IntefaceID!\n",
671		     ifid[0], ifid[1], ifid[2], ifid[3],
672		     ifid[4], ifid[5], ifid[6], ifid[7]);
673        } else if (memcmp(ifid, ipv6cp->my_ifid, IPV6CP_IFIDLEN) != 0) {
674          n = 100;
675	  while (n && !ipcp_SetIPv6address(ipv6cp, ifid, ipv6cp->his_ifid)) {
676	    do {
677	      n--;
678	      SetInterfaceID(ifid, 1);
679	    } while (n && memcmp(ifid, ipv6cp->his_ifid, IPV6CP_IFIDLEN) == 0);
680	  }
681
682          if (n == 0) {
683            log_Printf(log_IsKept(LogIPV6CP) ? LogIPV6CP : LogPHASE,
684                       "0x0000000000000000: Unacceptable IntefaceID!\n");
685            fsm_Close(&ipv6cp->fsm);
686          } else {
687	    log_Printf(LogIPV6CP, "%s changing IntefaceID: "
688		       "0x%02x%02x%02x%02x%02x%02x%02x%02x "
689		       "--> 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", tbuff,
690		       ipv6cp->my_ifid[0], ipv6cp->my_ifid[1],
691		       ipv6cp->my_ifid[2], ipv6cp->my_ifid[3],
692		       ipv6cp->my_ifid[4], ipv6cp->my_ifid[5],
693		       ipv6cp->my_ifid[6], ipv6cp->my_ifid[7],
694		       ifid[0], ifid[1], ifid[2], ifid[3],
695		       ifid[4], ifid[5], ifid[6], ifid[7]);
696            memcpy(ipv6cp->my_ifid, ifid, IPV6CP_IFIDLEN);
697            bundle_AdjustFilters(fp->bundle, &ipv6cp->myaddr, NULL);
698          }
699        }
700        break;
701
702      case MODE_REJ:
703        ipv6cp->his_reject |= (1 << opt->hdr.id);
704        break;
705      }
706      break;
707
708    default:
709      if (mode_type != MODE_NOP) {
710        ipv6cp->my_reject |= (1 << opt->hdr.id);
711        fsm_rej(dec, opt);
712      }
713      break;
714    }
715  }
716
717  if (mode_type != MODE_NOP) {
718    if (mode_type == MODE_REQ && !ipv6cp->peer_tokenreq) {
719      if (dec->rejend == dec->rej && dec->nakend == dec->nak) {
720        /*
721         * Pretend the peer has requested a TOKEN.
722         * We do this to ensure that we only send one NAK if the only
723         * reason for the NAK is because the peer isn't sending a
724         * TY_TOKEN REQ.  This stops us from repeatedly trying to tell
725         * the peer that we have to have an IP address on their end.
726         */
727        ipv6cp->peer_tokenreq = 1;
728      }
729      memset(ifid, 0, IPV6CP_IFIDLEN);
730      ipv6cp_ValidateInterfaceID(ipv6cp, ifid, dec);
731    }
732    fsm_opt_normalise(dec);
733  }
734}
735#endif
736