radius.c revision 58038
143313Sbrian/*
243313Sbrian * Copyright 1999 Internet Business Solutions Ltd., Switzerland
343313Sbrian * All rights reserved.
443313Sbrian *
543313Sbrian * Redistribution and use in source and binary forms, with or without
643313Sbrian * modification, are permitted provided that the following conditions
743313Sbrian * are met:
843313Sbrian * 1. Redistributions of source code must retain the above copyright
943313Sbrian *    notice, this list of conditions and the following disclaimer.
1043313Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1143313Sbrian *    notice, this list of conditions and the following disclaimer in the
1243313Sbrian *    documentation and/or other materials provided with the distribution.
1343313Sbrian *
1443313Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1543313Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1643313Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1743313Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1843313Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1943313Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2043313Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2143313Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2243313Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2343313Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2443313Sbrian * SUCH DAMAGE.
2543313Sbrian *
2650479Speter * $FreeBSD: head/usr.sbin/ppp/radius.c 58038 2000-03-14 01:47:07Z brian $
2743313Sbrian *
2843313Sbrian */
2943313Sbrian
3043313Sbrian#include <sys/param.h>
3158032Sbrian#include <sys/socket.h>
3243313Sbrian#include <netinet/in_systm.h>
3343313Sbrian#include <netinet/in.h>
3443313Sbrian#include <netinet/ip.h>
3543313Sbrian#include <arpa/inet.h>
3643313Sbrian#include <sys/un.h>
3758032Sbrian#include <net/route.h>
3843313Sbrian
3958037Sbrian#ifdef LOCALRAD
4058037Sbrian#include "radlib.h"
4158037Sbrian#else
4258037Sbrian#include <radlib.h>
4358037Sbrian#endif
4458037Sbrian
4543313Sbrian#include <errno.h>
4643313Sbrian#include <stdio.h>
4743313Sbrian#include <stdlib.h>
4843313Sbrian#include <string.h>
4943693Sbrian#include <sys/time.h>
5043313Sbrian#include <termios.h>
5150840Sbrian#include <ttyent.h>
5250840Sbrian#include <unistd.h>
5350840Sbrian#include <netdb.h>
5443313Sbrian
5546686Sbrian#include "layer.h"
5643313Sbrian#include "defs.h"
5743313Sbrian#include "log.h"
5843313Sbrian#include "descriptor.h"
5943313Sbrian#include "prompt.h"
6043313Sbrian#include "timer.h"
6143313Sbrian#include "fsm.h"
6243313Sbrian#include "iplist.h"
6343313Sbrian#include "slcompress.h"
6443313Sbrian#include "throughput.h"
6543313Sbrian#include "lqr.h"
6643313Sbrian#include "hdlc.h"
6743313Sbrian#include "mbuf.h"
6843313Sbrian#include "ipcp.h"
6943313Sbrian#include "route.h"
7043313Sbrian#include "command.h"
7143313Sbrian#include "filter.h"
7243313Sbrian#include "lcp.h"
7343313Sbrian#include "ccp.h"
7443313Sbrian#include "link.h"
7543313Sbrian#include "mp.h"
7643313Sbrian#include "radius.h"
7743693Sbrian#include "auth.h"
7843693Sbrian#include "async.h"
7943693Sbrian#include "physical.h"
8043693Sbrian#include "chat.h"
8143693Sbrian#include "cbcp.h"
8243693Sbrian#include "chap.h"
8343693Sbrian#include "datalink.h"
8443313Sbrian#include "bundle.h"
8543313Sbrian
8643693Sbrian/*
8743693Sbrian * rad_continue_send_request() has given us `got' (non-zero).  Deal with it.
8843693Sbrian */
8943693Sbrianstatic void
9043693Sbrianradius_Process(struct radius *r, int got)
9143313Sbrian{
9243313Sbrian  char *argv[MAXARGS], *nuke;
9343693Sbrian  struct bundle *bundle;
9445910Sbrian  int argc, addrs;
9545910Sbrian  size_t len;
9643313Sbrian  struct in_range dest;
9743313Sbrian  struct in_addr gw;
9843693Sbrian  const void *data;
9943313Sbrian
10043693Sbrian  r->cx.fd = -1;		/* Stop select()ing */
10143313Sbrian
10243313Sbrian  switch (got) {
10343313Sbrian    case RAD_ACCESS_ACCEPT:
10443693Sbrian      log_Printf(LogPHASE, "Radius: ACCEPT received\n");
10543313Sbrian      break;
10643313Sbrian
10743693Sbrian    case RAD_ACCESS_REJECT:
10843693Sbrian      log_Printf(LogPHASE, "Radius: REJECT received\n");
10943693Sbrian      auth_Failure(r->cx.auth);
11043693Sbrian      rad_close(r->cx.rad);
11143693Sbrian      return;
11243693Sbrian
11343313Sbrian    case RAD_ACCESS_CHALLENGE:
11443313Sbrian      /* we can't deal with this (for now) ! */
11543693Sbrian      log_Printf(LogPHASE, "Radius: CHALLENGE received (can't handle yet)\n");
11643693Sbrian      auth_Failure(r->cx.auth);
11743693Sbrian      rad_close(r->cx.rad);
11843693Sbrian      return;
11943313Sbrian
12043313Sbrian    case -1:
12143693Sbrian      log_Printf(LogPHASE, "radius: %s\n", rad_strerror(r->cx.rad));
12243693Sbrian      auth_Failure(r->cx.auth);
12343693Sbrian      rad_close(r->cx.rad);
12443693Sbrian      return;
12543313Sbrian
12643313Sbrian    default:
12743313Sbrian      log_Printf(LogERROR, "rad_send_request: Failed %d: %s\n",
12843693Sbrian                 got, rad_strerror(r->cx.rad));
12943693Sbrian      auth_Failure(r->cx.auth);
13043693Sbrian      rad_close(r->cx.rad);
13143693Sbrian      return;
13243313Sbrian  }
13343313Sbrian
13443313Sbrian  /* So we've been accepted !  Let's see what we've got in our reply :-I */
13543313Sbrian  r->ip.s_addr = r->mask.s_addr = INADDR_NONE;
13643313Sbrian  r->mtu = 0;
13743313Sbrian  r->vj = 0;
13843693Sbrian  while ((got = rad_get_attr(r->cx.rad, &data, &len)) > 0) {
13943313Sbrian    switch (got) {
14043313Sbrian      case RAD_FRAMED_IP_ADDRESS:
14143313Sbrian        r->ip = rad_cvt_addr(data);
14243693Sbrian        log_Printf(LogPHASE, "        IP %s\n", inet_ntoa(r->ip));
14343313Sbrian        break;
14443313Sbrian
14543313Sbrian      case RAD_FRAMED_IP_NETMASK:
14643313Sbrian        r->mask = rad_cvt_addr(data);
14743693Sbrian        log_Printf(LogPHASE, "        Netmask %s\n", inet_ntoa(r->mask));
14843313Sbrian        break;
14943313Sbrian
15043313Sbrian      case RAD_FRAMED_MTU:
15143313Sbrian        r->mtu = rad_cvt_int(data);
15243693Sbrian        log_Printf(LogPHASE, "        MTU %lu\n", r->mtu);
15343313Sbrian        break;
15443313Sbrian
15543313Sbrian      case RAD_FRAMED_ROUTING:
15643313Sbrian        /* Disabled for now - should we automatically set up some filters ? */
15743313Sbrian        /* rad_cvt_int(data); */
15843313Sbrian        /* bit 1 = Send routing packets */
15943313Sbrian        /* bit 2 = Receive routing packets */
16043313Sbrian        break;
16143313Sbrian
16243313Sbrian      case RAD_FRAMED_COMPRESSION:
16343313Sbrian        r->vj = rad_cvt_int(data) == 1 ? 1 : 0;
16443693Sbrian        log_Printf(LogPHASE, "        VJ %sabled\n", r->vj ? "en" : "dis");
16543313Sbrian        break;
16643313Sbrian
16743313Sbrian      case RAD_FRAMED_ROUTE:
16843313Sbrian        /*
16943313Sbrian         * We expect a string of the format ``dest[/bits] gw [metrics]''
17043313Sbrian         * Any specified metrics are ignored.  MYADDR and HISADDR are
17143313Sbrian         * understood for ``dest'' and ``gw'' and ``0.0.0.0'' is the same
17243313Sbrian         * as ``HISADDR''.
17343313Sbrian         */
17443313Sbrian
17543313Sbrian        if ((nuke = rad_cvt_string(data, len)) == NULL) {
17643693Sbrian          log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
17743693Sbrian          rad_close(r->cx.rad);
17843693Sbrian          return;
17943313Sbrian        }
18043313Sbrian
18143693Sbrian        log_Printf(LogPHASE, "        Route: %s\n", nuke);
18243693Sbrian        bundle = r->cx.auth->physical->dl->bundle;
18343313Sbrian        dest.ipaddr.s_addr = dest.mask.s_addr = INADDR_ANY;
18443313Sbrian        dest.width = 0;
18543313Sbrian        argc = command_Interpret(nuke, strlen(nuke), argv);
18654914Sbrian        if (argc < 0)
18754914Sbrian          log_Printf(LogWARN, "radius: %s: Syntax error\n",
18854914Sbrian                     argc == 1 ? argv[0] : "\"\"");
18954914Sbrian        else if (argc < 2)
19043313Sbrian          log_Printf(LogWARN, "radius: %s: Invalid route\n",
19143313Sbrian                     argc == 1 ? argv[0] : "\"\"");
19243313Sbrian        else if ((strcasecmp(argv[0], "default") != 0 &&
19343313Sbrian                  !ParseAddr(&bundle->ncp.ipcp, argv[0], &dest.ipaddr,
19443313Sbrian                             &dest.mask, &dest.width)) ||
19543313Sbrian                 !ParseAddr(&bundle->ncp.ipcp, argv[1], &gw, NULL, NULL))
19643313Sbrian          log_Printf(LogWARN, "radius: %s %s: Invalid route\n",
19743313Sbrian                     argv[0], argv[1]);
19843313Sbrian        else {
19943313Sbrian          if (dest.width == 32 && strchr(argv[0], '/') == NULL)
20043313Sbrian            /* No mask specified - use the natural mask */
20144455Sbrian            dest.mask = addr2mask(dest.ipaddr);
20243313Sbrian          addrs = 0;
20343313Sbrian
20443313Sbrian          if (!strncasecmp(argv[0], "HISADDR", 7))
20543313Sbrian            addrs = ROUTE_DSTHISADDR;
20643313Sbrian          else if (!strncasecmp(argv[0], "MYADDR", 6))
20743313Sbrian            addrs = ROUTE_DSTMYADDR;
20843313Sbrian
20943313Sbrian          if (gw.s_addr == INADDR_ANY) {
21043313Sbrian            addrs |= ROUTE_GWHISADDR;
21143313Sbrian            gw = bundle->ncp.ipcp.peer_ip;
21243313Sbrian          } else if (strcasecmp(argv[1], "HISADDR") == 0)
21343313Sbrian            addrs |= ROUTE_GWHISADDR;
21443313Sbrian
21543313Sbrian          route_Add(&r->routes, addrs, dest.ipaddr, dest.mask, gw);
21643313Sbrian        }
21743313Sbrian        free(nuke);
21843313Sbrian        break;
21943313Sbrian    }
22043313Sbrian  }
22143313Sbrian
22243313Sbrian  if (got == -1) {
22343693Sbrian    log_Printf(LogERROR, "rad_get_attr: %s (failing!)\n",
22443693Sbrian               rad_strerror(r->cx.rad));
22543693Sbrian    auth_Failure(r->cx.auth);
22643693Sbrian    rad_close(r->cx.rad);
22743693Sbrian  } else {
22843693Sbrian    r->valid = 1;
22943693Sbrian    auth_Success(r->cx.auth);
23043693Sbrian    rad_close(r->cx.rad);
23143313Sbrian  }
23243693Sbrian}
23343313Sbrian
23443693Sbrian/*
23558038Sbrian * We've either timed out or select()ed on the read descriptor
23643693Sbrian */
23743693Sbrianstatic void
23843693Sbrianradius_Continue(struct radius *r, int sel)
23943693Sbrian{
24043693Sbrian  struct timeval tv;
24143693Sbrian  int got;
24243313Sbrian
24343693Sbrian  timer_Stop(&r->cx.timer);
24443693Sbrian  if ((got = rad_continue_send_request(r->cx.rad, sel, &r->cx.fd, &tv)) == 0) {
24543693Sbrian    log_Printf(LogPHASE, "Radius: Request re-sent\n");
24643693Sbrian    r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
24743693Sbrian    timer_Start(&r->cx.timer);
24843693Sbrian    return;
24943693Sbrian  }
25043693Sbrian
25143693Sbrian  radius_Process(r, got);
25243313Sbrian}
25343313Sbrian
25443693Sbrian/*
25543693Sbrian * Time to call rad_continue_send_request() - timed out.
25643693Sbrian */
25743693Sbrianstatic void
25843693Sbrianradius_Timeout(void *v)
25943693Sbrian{
26043693Sbrian  radius_Continue((struct radius *)v, 0);
26143693Sbrian}
26243693Sbrian
26343693Sbrian/*
26443693Sbrian * Time to call rad_continue_send_request() - something to read.
26543693Sbrian */
26643693Sbrianstatic void
26758028Sbrianradius_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
26843693Sbrian{
26943693Sbrian  radius_Continue(descriptor2radius(d), 1);
27043693Sbrian}
27143693Sbrian
27243693Sbrian/*
27358038Sbrian * Behave as a struct fdescriptor (descriptor.h)
27443693Sbrian */
27543693Sbrianstatic int
27658028Sbrianradius_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
27743693Sbrian{
27843693Sbrian  struct radius *rad = descriptor2radius(d);
27943693Sbrian
28043693Sbrian  if (r && rad->cx.fd != -1) {
28143693Sbrian    FD_SET(rad->cx.fd, r);
28243693Sbrian    if (*n < rad->cx.fd + 1)
28343693Sbrian      *n = rad->cx.fd + 1;
28443693Sbrian    log_Printf(LogTIMER, "Radius: fdset(r) %d\n", rad->cx.fd);
28543693Sbrian    return 1;
28643693Sbrian  }
28743693Sbrian
28843693Sbrian  return 0;
28943693Sbrian}
29043693Sbrian
29143693Sbrian/*
29258038Sbrian * Behave as a struct fdescriptor (descriptor.h)
29343693Sbrian */
29443693Sbrianstatic int
29558028Sbrianradius_IsSet(struct fdescriptor *d, const fd_set *fdset)
29643693Sbrian{
29743693Sbrian  struct radius *r = descriptor2radius(d);
29843693Sbrian
29943693Sbrian  return r && r->cx.fd != -1 && FD_ISSET(r->cx.fd, fdset);
30043693Sbrian}
30143693Sbrian
30243693Sbrian/*
30358038Sbrian * Behave as a struct fdescriptor (descriptor.h)
30443693Sbrian */
30543693Sbrianstatic int
30658028Sbrianradius_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
30743693Sbrian{
30843693Sbrian  /* We never want to write here ! */
30943693Sbrian  log_Printf(LogALERT, "radius_Write: Internal error: Bad call !\n");
31043693Sbrian  return 0;
31143693Sbrian}
31243693Sbrian
31343693Sbrian/*
31443693Sbrian * Initialise ourselves
31543693Sbrian */
31643313Sbrianvoid
31743693Sbrianradius_Init(struct radius *r)
31843693Sbrian{
31943693Sbrian  r->valid = 0;
32043693Sbrian  r->cx.fd = -1;
32143693Sbrian  *r->cfg.file = '\0';;
32243693Sbrian  r->desc.type = RADIUS_DESCRIPTOR;
32343693Sbrian  r->desc.UpdateSet = radius_UpdateSet;
32443693Sbrian  r->desc.IsSet = radius_IsSet;
32543693Sbrian  r->desc.Read = radius_Read;
32643693Sbrian  r->desc.Write = radius_Write;
32743693Sbrian  memset(&r->cx.timer, '\0', sizeof r->cx.timer);
32843693Sbrian}
32943693Sbrian
33043693Sbrian/*
33143693Sbrian * Forget everything and go back to initialised state.
33243693Sbrian */
33343693Sbrianvoid
33443693Sbrianradius_Destroy(struct radius *r)
33543693Sbrian{
33643693Sbrian  r->valid = 0;
33743693Sbrian  timer_Stop(&r->cx.timer);
33843693Sbrian  route_DeleteAll(&r->routes);
33943693Sbrian  if (r->cx.fd != -1) {
34043693Sbrian    r->cx.fd = -1;
34143693Sbrian    rad_close(r->cx.rad);
34243693Sbrian  }
34343693Sbrian}
34443693Sbrian
34543693Sbrian/*
34643693Sbrian * Start an authentication request to the RADIUS server.
34743693Sbrian */
34843693Sbrianvoid
34943693Sbrianradius_Authenticate(struct radius *r, struct authinfo *authp, const char *name,
35043693Sbrian                    const char *key, const char *challenge)
35143693Sbrian{
35250840Sbrian  struct ttyent *ttyp;
35343693Sbrian  struct timeval tv;
35450840Sbrian  int got, slot;
35550840Sbrian  char hostname[MAXHOSTNAMELEN];
35650840Sbrian  struct hostent *hp;
35750840Sbrian  struct in_addr hostaddr;
35843693Sbrian
35943693Sbrian  if (!*r->cfg.file)
36043693Sbrian    return;
36143693Sbrian
36243693Sbrian  if (r->cx.fd != -1)
36343693Sbrian    /*
36443693Sbrian     * We assume that our name/key/challenge is the same as last time,
36543693Sbrian     * and just continue to wait for the RADIUS server(s).
36643693Sbrian     */
36743693Sbrian    return;
36843693Sbrian
36943693Sbrian  radius_Destroy(r);
37043693Sbrian
37143693Sbrian  if ((r->cx.rad = rad_open()) == NULL) {
37243693Sbrian    log_Printf(LogERROR, "rad_open: %s\n", strerror(errno));
37343693Sbrian    return;
37443693Sbrian  }
37543693Sbrian
37643693Sbrian  if (rad_config(r->cx.rad, r->cfg.file) != 0) {
37743693Sbrian    log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad));
37843693Sbrian    rad_close(r->cx.rad);
37943693Sbrian    return;
38043693Sbrian  }
38143693Sbrian
38243693Sbrian  if (rad_create_request(r->cx.rad, RAD_ACCESS_REQUEST) != 0) {
38343693Sbrian    log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad));
38443693Sbrian    rad_close(r->cx.rad);
38543693Sbrian    return;
38643693Sbrian  }
38743693Sbrian
38843693Sbrian  if (rad_put_string(r->cx.rad, RAD_USER_NAME, name) != 0 ||
38943693Sbrian      rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 ||
39043693Sbrian      rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0) {
39143693Sbrian    log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
39243693Sbrian    rad_close(r->cx.rad);
39343693Sbrian    return;
39443693Sbrian  }
39543693Sbrian
39643693Sbrian  if (challenge != NULL) {
39743693Sbrian    /* We're talking CHAP */
39843693Sbrian    if (rad_put_string(r->cx.rad, RAD_CHAP_PASSWORD, key) != 0 ||
39943693Sbrian        rad_put_string(r->cx.rad, RAD_CHAP_CHALLENGE, challenge) != 0) {
40043693Sbrian      log_Printf(LogERROR, "CHAP: rad_put_string: %s\n",
40143693Sbrian                 rad_strerror(r->cx.rad));
40243693Sbrian      rad_close(r->cx.rad);
40343693Sbrian      return;
40443693Sbrian    }
40543693Sbrian  } else if (rad_put_string(r->cx.rad, RAD_USER_PASSWORD, key) != 0) {
40643693Sbrian    /* We're talking PAP */
40743693Sbrian    log_Printf(LogERROR, "PAP: rad_put_string: %s\n", rad_strerror(r->cx.rad));
40843693Sbrian    rad_close(r->cx.rad);
40943693Sbrian    return;
41043693Sbrian  }
41143693Sbrian
41250840Sbrian  if (gethostname(hostname, sizeof hostname) != 0)
41350840Sbrian    log_Printf(LogERROR, "rad_put: gethostname(): %s\n", strerror(errno));
41450840Sbrian  else {
41550840Sbrian    if ((hp = gethostbyname(hostname)) != NULL) {
41650840Sbrian      hostaddr.s_addr = *(u_long *)hp->h_addr;
41750840Sbrian      if (rad_put_addr(r->cx.rad, RAD_NAS_IP_ADDRESS, hostaddr) != 0) {
41850840Sbrian        log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
41950840Sbrian                   rad_strerror(r->cx.rad));
42050840Sbrian        rad_close(r->cx.rad);
42150840Sbrian        return;
42250840Sbrian      }
42350840Sbrian    }
42450840Sbrian    if (rad_put_string(r->cx.rad, RAD_NAS_IDENTIFIER, hostname) != 0) {
42550840Sbrian      log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
42650840Sbrian                 rad_strerror(r->cx.rad));
42750840Sbrian      rad_close(r->cx.rad);
42850840Sbrian      return;
42950840Sbrian    }
43050840Sbrian  }
43150840Sbrian
43250840Sbrian  if (authp->physical->handler &&
43350840Sbrian      authp->physical->handler->type == TTY_DEVICE) {
43450840Sbrian    setttyent();
43550840Sbrian    for (slot = 1; (ttyp = getttyent()); ++slot)
43650840Sbrian      if (!strcmp(ttyp->ty_name, authp->physical->name.base)) {
43750840Sbrian        if(rad_put_int(r->cx.rad, RAD_NAS_PORT, slot) != 0) {
43850840Sbrian          log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
43950840Sbrian                      rad_strerror(r->cx.rad));
44050840Sbrian          rad_close(r->cx.rad);
44150840Sbrian          endttyent();
44250840Sbrian          return;
44350840Sbrian        }
44450840Sbrian        break;
44550840Sbrian      }
44650840Sbrian    endttyent();
44750840Sbrian  }
44850840Sbrian
44950840Sbrian
45043693Sbrian  if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv)))
45143693Sbrian    radius_Process(r, got);
45243693Sbrian  else {
45343693Sbrian    log_Printf(LogPHASE, "Radius: Request sent\n");
45443693Sbrian    log_Printf(LogDEBUG, "Using radius_Timeout [%p]\n", radius_Timeout);
45543693Sbrian    r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
45643693Sbrian    r->cx.timer.func = radius_Timeout;
45743693Sbrian    r->cx.timer.name = "radius";
45843693Sbrian    r->cx.timer.arg = r;
45943693Sbrian    r->cx.auth = authp;
46043693Sbrian    timer_Start(&r->cx.timer);
46143693Sbrian  }
46243693Sbrian}
46343693Sbrian
46443693Sbrian/*
46543693Sbrian * How do things look at the moment ?
46643693Sbrian */
46743693Sbrianvoid
46843313Sbrianradius_Show(struct radius *r, struct prompt *p)
46943313Sbrian{
47043313Sbrian  prompt_Printf(p, " Radius config: %s", *r->cfg.file ? r->cfg.file : "none");
47143313Sbrian  if (r->valid) {
47243313Sbrian    prompt_Printf(p, "\n            IP: %s\n", inet_ntoa(r->ip));
47343313Sbrian    prompt_Printf(p, "       Netmask: %s\n", inet_ntoa(r->mask));
47443313Sbrian    prompt_Printf(p, "           MTU: %lu\n", r->mtu);
47543313Sbrian    prompt_Printf(p, "            VJ: %sabled\n", r->vj ? "en" : "dis");
47643313Sbrian    if (r->routes)
47743313Sbrian      route_ShowSticky(p, r->routes, "        Routes", 16);
47843313Sbrian  } else
47943313Sbrian    prompt_Printf(p, " (not authenticated)\n");
48043313Sbrian}
481