radius.c revision 54914
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 54914 1999-12-20 20:30:02Z brian $
2743313Sbrian *
2843313Sbrian */
2943313Sbrian
3043313Sbrian#include <sys/param.h>
3143313Sbrian#include <netinet/in_systm.h>
3243313Sbrian#include <netinet/in.h>
3343313Sbrian#include <netinet/ip.h>
3443313Sbrian#include <arpa/inet.h>
3543313Sbrian#include <sys/un.h>
3643313Sbrian
3743313Sbrian#include <errno.h>
3843313Sbrian#include <radlib.h>
3943313Sbrian#include <stdio.h>
4043313Sbrian#include <stdlib.h>
4143313Sbrian#include <string.h>
4243693Sbrian#include <sys/time.h>
4343313Sbrian#include <termios.h>
4450840Sbrian#include <ttyent.h>
4550840Sbrian#include <unistd.h>
4650840Sbrian#include <netdb.h>
4743313Sbrian
4846686Sbrian#include "layer.h"
4943313Sbrian#include "defs.h"
5043313Sbrian#include "log.h"
5143313Sbrian#include "descriptor.h"
5243313Sbrian#include "prompt.h"
5343313Sbrian#include "timer.h"
5443313Sbrian#include "fsm.h"
5543313Sbrian#include "iplist.h"
5643313Sbrian#include "slcompress.h"
5743313Sbrian#include "throughput.h"
5843313Sbrian#include "lqr.h"
5943313Sbrian#include "hdlc.h"
6043313Sbrian#include "mbuf.h"
6143313Sbrian#include "ipcp.h"
6243313Sbrian#include "route.h"
6343313Sbrian#include "command.h"
6443313Sbrian#include "filter.h"
6543313Sbrian#include "lcp.h"
6643313Sbrian#include "ccp.h"
6743313Sbrian#include "link.h"
6843313Sbrian#include "mp.h"
6943313Sbrian#include "radius.h"
7043693Sbrian#include "auth.h"
7143693Sbrian#include "async.h"
7243693Sbrian#include "physical.h"
7343693Sbrian#include "chat.h"
7443693Sbrian#include "cbcp.h"
7543693Sbrian#include "chap.h"
7643693Sbrian#include "datalink.h"
7743313Sbrian#include "bundle.h"
7843313Sbrian
7943693Sbrian/*
8043693Sbrian * rad_continue_send_request() has given us `got' (non-zero).  Deal with it.
8143693Sbrian */
8243693Sbrianstatic void
8343693Sbrianradius_Process(struct radius *r, int got)
8443313Sbrian{
8543313Sbrian  char *argv[MAXARGS], *nuke;
8643693Sbrian  struct bundle *bundle;
8745910Sbrian  int argc, addrs;
8845910Sbrian  size_t len;
8943313Sbrian  struct in_range dest;
9043313Sbrian  struct in_addr gw;
9143693Sbrian  const void *data;
9243313Sbrian
9343693Sbrian  r->cx.fd = -1;		/* Stop select()ing */
9443313Sbrian
9543313Sbrian  switch (got) {
9643313Sbrian    case RAD_ACCESS_ACCEPT:
9743693Sbrian      log_Printf(LogPHASE, "Radius: ACCEPT received\n");
9843313Sbrian      break;
9943313Sbrian
10043693Sbrian    case RAD_ACCESS_REJECT:
10143693Sbrian      log_Printf(LogPHASE, "Radius: REJECT received\n");
10243693Sbrian      auth_Failure(r->cx.auth);
10343693Sbrian      rad_close(r->cx.rad);
10443693Sbrian      return;
10543693Sbrian
10643313Sbrian    case RAD_ACCESS_CHALLENGE:
10743313Sbrian      /* we can't deal with this (for now) ! */
10843693Sbrian      log_Printf(LogPHASE, "Radius: CHALLENGE received (can't handle yet)\n");
10943693Sbrian      auth_Failure(r->cx.auth);
11043693Sbrian      rad_close(r->cx.rad);
11143693Sbrian      return;
11243313Sbrian
11343313Sbrian    case -1:
11443693Sbrian      log_Printf(LogPHASE, "radius: %s\n", rad_strerror(r->cx.rad));
11543693Sbrian      auth_Failure(r->cx.auth);
11643693Sbrian      rad_close(r->cx.rad);
11743693Sbrian      return;
11843313Sbrian
11943313Sbrian    default:
12043313Sbrian      log_Printf(LogERROR, "rad_send_request: Failed %d: %s\n",
12143693Sbrian                 got, rad_strerror(r->cx.rad));
12243693Sbrian      auth_Failure(r->cx.auth);
12343693Sbrian      rad_close(r->cx.rad);
12443693Sbrian      return;
12543313Sbrian  }
12643313Sbrian
12743313Sbrian  /* So we've been accepted !  Let's see what we've got in our reply :-I */
12843313Sbrian  r->ip.s_addr = r->mask.s_addr = INADDR_NONE;
12943313Sbrian  r->mtu = 0;
13043313Sbrian  r->vj = 0;
13143693Sbrian  while ((got = rad_get_attr(r->cx.rad, &data, &len)) > 0) {
13243313Sbrian    switch (got) {
13343313Sbrian      case RAD_FRAMED_IP_ADDRESS:
13443313Sbrian        r->ip = rad_cvt_addr(data);
13543693Sbrian        log_Printf(LogPHASE, "        IP %s\n", inet_ntoa(r->ip));
13643313Sbrian        break;
13743313Sbrian
13843313Sbrian      case RAD_FRAMED_IP_NETMASK:
13943313Sbrian        r->mask = rad_cvt_addr(data);
14043693Sbrian        log_Printf(LogPHASE, "        Netmask %s\n", inet_ntoa(r->mask));
14143313Sbrian        break;
14243313Sbrian
14343313Sbrian      case RAD_FRAMED_MTU:
14443313Sbrian        r->mtu = rad_cvt_int(data);
14543693Sbrian        log_Printf(LogPHASE, "        MTU %lu\n", r->mtu);
14643313Sbrian        break;
14743313Sbrian
14843313Sbrian      case RAD_FRAMED_ROUTING:
14943313Sbrian        /* Disabled for now - should we automatically set up some filters ? */
15043313Sbrian        /* rad_cvt_int(data); */
15143313Sbrian        /* bit 1 = Send routing packets */
15243313Sbrian        /* bit 2 = Receive routing packets */
15343313Sbrian        break;
15443313Sbrian
15543313Sbrian      case RAD_FRAMED_COMPRESSION:
15643313Sbrian        r->vj = rad_cvt_int(data) == 1 ? 1 : 0;
15743693Sbrian        log_Printf(LogPHASE, "        VJ %sabled\n", r->vj ? "en" : "dis");
15843313Sbrian        break;
15943313Sbrian
16043313Sbrian      case RAD_FRAMED_ROUTE:
16143313Sbrian        /*
16243313Sbrian         * We expect a string of the format ``dest[/bits] gw [metrics]''
16343313Sbrian         * Any specified metrics are ignored.  MYADDR and HISADDR are
16443313Sbrian         * understood for ``dest'' and ``gw'' and ``0.0.0.0'' is the same
16543313Sbrian         * as ``HISADDR''.
16643313Sbrian         */
16743313Sbrian
16843313Sbrian        if ((nuke = rad_cvt_string(data, len)) == NULL) {
16943693Sbrian          log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
17043693Sbrian          rad_close(r->cx.rad);
17143693Sbrian          return;
17243313Sbrian        }
17343313Sbrian
17443693Sbrian        log_Printf(LogPHASE, "        Route: %s\n", nuke);
17543693Sbrian        bundle = r->cx.auth->physical->dl->bundle;
17643313Sbrian        dest.ipaddr.s_addr = dest.mask.s_addr = INADDR_ANY;
17743313Sbrian        dest.width = 0;
17843313Sbrian        argc = command_Interpret(nuke, strlen(nuke), argv);
17954914Sbrian        if (argc < 0)
18054914Sbrian          log_Printf(LogWARN, "radius: %s: Syntax error\n",
18154914Sbrian                     argc == 1 ? argv[0] : "\"\"");
18254914Sbrian        else if (argc < 2)
18343313Sbrian          log_Printf(LogWARN, "radius: %s: Invalid route\n",
18443313Sbrian                     argc == 1 ? argv[0] : "\"\"");
18543313Sbrian        else if ((strcasecmp(argv[0], "default") != 0 &&
18643313Sbrian                  !ParseAddr(&bundle->ncp.ipcp, argv[0], &dest.ipaddr,
18743313Sbrian                             &dest.mask, &dest.width)) ||
18843313Sbrian                 !ParseAddr(&bundle->ncp.ipcp, argv[1], &gw, NULL, NULL))
18943313Sbrian          log_Printf(LogWARN, "radius: %s %s: Invalid route\n",
19043313Sbrian                     argv[0], argv[1]);
19143313Sbrian        else {
19243313Sbrian          if (dest.width == 32 && strchr(argv[0], '/') == NULL)
19343313Sbrian            /* No mask specified - use the natural mask */
19444455Sbrian            dest.mask = addr2mask(dest.ipaddr);
19543313Sbrian          addrs = 0;
19643313Sbrian
19743313Sbrian          if (!strncasecmp(argv[0], "HISADDR", 7))
19843313Sbrian            addrs = ROUTE_DSTHISADDR;
19943313Sbrian          else if (!strncasecmp(argv[0], "MYADDR", 6))
20043313Sbrian            addrs = ROUTE_DSTMYADDR;
20143313Sbrian
20243313Sbrian          if (gw.s_addr == INADDR_ANY) {
20343313Sbrian            addrs |= ROUTE_GWHISADDR;
20443313Sbrian            gw = bundle->ncp.ipcp.peer_ip;
20543313Sbrian          } else if (strcasecmp(argv[1], "HISADDR") == 0)
20643313Sbrian            addrs |= ROUTE_GWHISADDR;
20743313Sbrian
20843313Sbrian          route_Add(&r->routes, addrs, dest.ipaddr, dest.mask, gw);
20943313Sbrian        }
21043313Sbrian        free(nuke);
21143313Sbrian        break;
21243313Sbrian    }
21343313Sbrian  }
21443313Sbrian
21543313Sbrian  if (got == -1) {
21643693Sbrian    log_Printf(LogERROR, "rad_get_attr: %s (failing!)\n",
21743693Sbrian               rad_strerror(r->cx.rad));
21843693Sbrian    auth_Failure(r->cx.auth);
21943693Sbrian    rad_close(r->cx.rad);
22043693Sbrian  } else {
22143693Sbrian    r->valid = 1;
22243693Sbrian    auth_Success(r->cx.auth);
22343693Sbrian    rad_close(r->cx.rad);
22443313Sbrian  }
22543693Sbrian}
22643313Sbrian
22743693Sbrian/*
22843693Sbrian * We've either timed out or select()ed on the read descriptor
22943693Sbrian */
23043693Sbrianstatic void
23143693Sbrianradius_Continue(struct radius *r, int sel)
23243693Sbrian{
23343693Sbrian  struct timeval tv;
23443693Sbrian  int got;
23543313Sbrian
23643693Sbrian  timer_Stop(&r->cx.timer);
23743693Sbrian  if ((got = rad_continue_send_request(r->cx.rad, sel, &r->cx.fd, &tv)) == 0) {
23843693Sbrian    log_Printf(LogPHASE, "Radius: Request re-sent\n");
23943693Sbrian    r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
24043693Sbrian    timer_Start(&r->cx.timer);
24143693Sbrian    return;
24243693Sbrian  }
24343693Sbrian
24443693Sbrian  radius_Process(r, got);
24543313Sbrian}
24643313Sbrian
24743693Sbrian/*
24843693Sbrian * Time to call rad_continue_send_request() - timed out.
24943693Sbrian */
25043693Sbrianstatic void
25143693Sbrianradius_Timeout(void *v)
25243693Sbrian{
25343693Sbrian  radius_Continue((struct radius *)v, 0);
25443693Sbrian}
25543693Sbrian
25643693Sbrian/*
25743693Sbrian * Time to call rad_continue_send_request() - something to read.
25843693Sbrian */
25943693Sbrianstatic void
26043693Sbrianradius_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
26143693Sbrian{
26243693Sbrian  radius_Continue(descriptor2radius(d), 1);
26343693Sbrian}
26443693Sbrian
26543693Sbrian/*
26643693Sbrian * Behave as a struct descriptor (descriptor.h)
26743693Sbrian */
26843693Sbrianstatic int
26943693Sbrianradius_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
27043693Sbrian{
27143693Sbrian  struct radius *rad = descriptor2radius(d);
27243693Sbrian
27343693Sbrian  if (r && rad->cx.fd != -1) {
27443693Sbrian    FD_SET(rad->cx.fd, r);
27543693Sbrian    if (*n < rad->cx.fd + 1)
27643693Sbrian      *n = rad->cx.fd + 1;
27743693Sbrian    log_Printf(LogTIMER, "Radius: fdset(r) %d\n", rad->cx.fd);
27843693Sbrian    return 1;
27943693Sbrian  }
28043693Sbrian
28143693Sbrian  return 0;
28243693Sbrian}
28343693Sbrian
28443693Sbrian/*
28543693Sbrian * Behave as a struct descriptor (descriptor.h)
28643693Sbrian */
28743693Sbrianstatic int
28843693Sbrianradius_IsSet(struct descriptor *d, const fd_set *fdset)
28943693Sbrian{
29043693Sbrian  struct radius *r = descriptor2radius(d);
29143693Sbrian
29243693Sbrian  return r && r->cx.fd != -1 && FD_ISSET(r->cx.fd, fdset);
29343693Sbrian}
29443693Sbrian
29543693Sbrian/*
29643693Sbrian * Behave as a struct descriptor (descriptor.h)
29743693Sbrian */
29843693Sbrianstatic int
29943693Sbrianradius_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
30043693Sbrian{
30143693Sbrian  /* We never want to write here ! */
30243693Sbrian  log_Printf(LogALERT, "radius_Write: Internal error: Bad call !\n");
30343693Sbrian  return 0;
30443693Sbrian}
30543693Sbrian
30643693Sbrian/*
30743693Sbrian * Initialise ourselves
30843693Sbrian */
30943313Sbrianvoid
31043693Sbrianradius_Init(struct radius *r)
31143693Sbrian{
31243693Sbrian  r->valid = 0;
31343693Sbrian  r->cx.fd = -1;
31443693Sbrian  *r->cfg.file = '\0';;
31543693Sbrian  r->desc.type = RADIUS_DESCRIPTOR;
31643693Sbrian  r->desc.UpdateSet = radius_UpdateSet;
31743693Sbrian  r->desc.IsSet = radius_IsSet;
31843693Sbrian  r->desc.Read = radius_Read;
31943693Sbrian  r->desc.Write = radius_Write;
32043693Sbrian  memset(&r->cx.timer, '\0', sizeof r->cx.timer);
32143693Sbrian}
32243693Sbrian
32343693Sbrian/*
32443693Sbrian * Forget everything and go back to initialised state.
32543693Sbrian */
32643693Sbrianvoid
32743693Sbrianradius_Destroy(struct radius *r)
32843693Sbrian{
32943693Sbrian  r->valid = 0;
33043693Sbrian  timer_Stop(&r->cx.timer);
33143693Sbrian  route_DeleteAll(&r->routes);
33243693Sbrian  if (r->cx.fd != -1) {
33343693Sbrian    r->cx.fd = -1;
33443693Sbrian    rad_close(r->cx.rad);
33543693Sbrian  }
33643693Sbrian}
33743693Sbrian
33843693Sbrian/*
33943693Sbrian * Start an authentication request to the RADIUS server.
34043693Sbrian */
34143693Sbrianvoid
34243693Sbrianradius_Authenticate(struct radius *r, struct authinfo *authp, const char *name,
34343693Sbrian                    const char *key, const char *challenge)
34443693Sbrian{
34550840Sbrian  struct ttyent *ttyp;
34643693Sbrian  struct timeval tv;
34750840Sbrian  int got, slot;
34850840Sbrian  char hostname[MAXHOSTNAMELEN];
34950840Sbrian  struct hostent *hp;
35050840Sbrian  struct in_addr hostaddr;
35143693Sbrian
35243693Sbrian  if (!*r->cfg.file)
35343693Sbrian    return;
35443693Sbrian
35543693Sbrian  if (r->cx.fd != -1)
35643693Sbrian    /*
35743693Sbrian     * We assume that our name/key/challenge is the same as last time,
35843693Sbrian     * and just continue to wait for the RADIUS server(s).
35943693Sbrian     */
36043693Sbrian    return;
36143693Sbrian
36243693Sbrian  radius_Destroy(r);
36343693Sbrian
36443693Sbrian  if ((r->cx.rad = rad_open()) == NULL) {
36543693Sbrian    log_Printf(LogERROR, "rad_open: %s\n", strerror(errno));
36643693Sbrian    return;
36743693Sbrian  }
36843693Sbrian
36943693Sbrian  if (rad_config(r->cx.rad, r->cfg.file) != 0) {
37043693Sbrian    log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad));
37143693Sbrian    rad_close(r->cx.rad);
37243693Sbrian    return;
37343693Sbrian  }
37443693Sbrian
37543693Sbrian  if (rad_create_request(r->cx.rad, RAD_ACCESS_REQUEST) != 0) {
37643693Sbrian    log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad));
37743693Sbrian    rad_close(r->cx.rad);
37843693Sbrian    return;
37943693Sbrian  }
38043693Sbrian
38143693Sbrian  if (rad_put_string(r->cx.rad, RAD_USER_NAME, name) != 0 ||
38243693Sbrian      rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 ||
38343693Sbrian      rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0) {
38443693Sbrian    log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
38543693Sbrian    rad_close(r->cx.rad);
38643693Sbrian    return;
38743693Sbrian  }
38843693Sbrian
38943693Sbrian  if (challenge != NULL) {
39043693Sbrian    /* We're talking CHAP */
39143693Sbrian    if (rad_put_string(r->cx.rad, RAD_CHAP_PASSWORD, key) != 0 ||
39243693Sbrian        rad_put_string(r->cx.rad, RAD_CHAP_CHALLENGE, challenge) != 0) {
39343693Sbrian      log_Printf(LogERROR, "CHAP: rad_put_string: %s\n",
39443693Sbrian                 rad_strerror(r->cx.rad));
39543693Sbrian      rad_close(r->cx.rad);
39643693Sbrian      return;
39743693Sbrian    }
39843693Sbrian  } else if (rad_put_string(r->cx.rad, RAD_USER_PASSWORD, key) != 0) {
39943693Sbrian    /* We're talking PAP */
40043693Sbrian    log_Printf(LogERROR, "PAP: rad_put_string: %s\n", rad_strerror(r->cx.rad));
40143693Sbrian    rad_close(r->cx.rad);
40243693Sbrian    return;
40343693Sbrian  }
40443693Sbrian
40550840Sbrian  if (gethostname(hostname, sizeof hostname) != 0)
40650840Sbrian    log_Printf(LogERROR, "rad_put: gethostname(): %s\n", strerror(errno));
40750840Sbrian  else {
40850840Sbrian    if ((hp = gethostbyname(hostname)) != NULL) {
40950840Sbrian      hostaddr.s_addr = *(u_long *)hp->h_addr;
41050840Sbrian      if (rad_put_addr(r->cx.rad, RAD_NAS_IP_ADDRESS, hostaddr) != 0) {
41150840Sbrian        log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
41250840Sbrian                   rad_strerror(r->cx.rad));
41350840Sbrian        rad_close(r->cx.rad);
41450840Sbrian        return;
41550840Sbrian      }
41650840Sbrian    }
41750840Sbrian    if (rad_put_string(r->cx.rad, RAD_NAS_IDENTIFIER, hostname) != 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
42550840Sbrian  if (authp->physical->handler &&
42650840Sbrian      authp->physical->handler->type == TTY_DEVICE) {
42750840Sbrian    setttyent();
42850840Sbrian    for (slot = 1; (ttyp = getttyent()); ++slot)
42950840Sbrian      if (!strcmp(ttyp->ty_name, authp->physical->name.base)) {
43050840Sbrian        if(rad_put_int(r->cx.rad, RAD_NAS_PORT, slot) != 0) {
43150840Sbrian          log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
43250840Sbrian                      rad_strerror(r->cx.rad));
43350840Sbrian          rad_close(r->cx.rad);
43450840Sbrian          endttyent();
43550840Sbrian          return;
43650840Sbrian        }
43750840Sbrian        break;
43850840Sbrian      }
43950840Sbrian    endttyent();
44050840Sbrian  }
44150840Sbrian
44250840Sbrian
44343693Sbrian  if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv)))
44443693Sbrian    radius_Process(r, got);
44543693Sbrian  else {
44643693Sbrian    log_Printf(LogPHASE, "Radius: Request sent\n");
44743693Sbrian    log_Printf(LogDEBUG, "Using radius_Timeout [%p]\n", radius_Timeout);
44843693Sbrian    r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
44943693Sbrian    r->cx.timer.func = radius_Timeout;
45043693Sbrian    r->cx.timer.name = "radius";
45143693Sbrian    r->cx.timer.arg = r;
45243693Sbrian    r->cx.auth = authp;
45343693Sbrian    timer_Start(&r->cx.timer);
45443693Sbrian  }
45543693Sbrian}
45643693Sbrian
45743693Sbrian/*
45843693Sbrian * How do things look at the moment ?
45943693Sbrian */
46043693Sbrianvoid
46143313Sbrianradius_Show(struct radius *r, struct prompt *p)
46243313Sbrian{
46343313Sbrian  prompt_Printf(p, " Radius config: %s", *r->cfg.file ? r->cfg.file : "none");
46443313Sbrian  if (r->valid) {
46543313Sbrian    prompt_Printf(p, "\n            IP: %s\n", inet_ntoa(r->ip));
46643313Sbrian    prompt_Printf(p, "       Netmask: %s\n", inet_ntoa(r->mask));
46743313Sbrian    prompt_Printf(p, "           MTU: %lu\n", r->mtu);
46843313Sbrian    prompt_Printf(p, "            VJ: %sabled\n", r->vj ? "en" : "dis");
46943313Sbrian    if (r->routes)
47043313Sbrian      route_ShowSticky(p, r->routes, "        Routes", 16);
47143313Sbrian  } else
47243313Sbrian    prompt_Printf(p, " (not authenticated)\n");
47343313Sbrian}
474