radius.c revision 58032
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 58032 2000-03-14 01:46:44Z 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
3943313Sbrian#include <errno.h>
4043313Sbrian#include <radlib.h>
4143313Sbrian#include <stdio.h>
4243313Sbrian#include <stdlib.h>
4343313Sbrian#include <string.h>
4443693Sbrian#include <sys/time.h>
4543313Sbrian#include <termios.h>
4650840Sbrian#include <ttyent.h>
4750840Sbrian#include <unistd.h>
4850840Sbrian#include <netdb.h>
4943313Sbrian
5046686Sbrian#include "layer.h"
5143313Sbrian#include "defs.h"
5243313Sbrian#include "log.h"
5343313Sbrian#include "descriptor.h"
5443313Sbrian#include "prompt.h"
5543313Sbrian#include "timer.h"
5643313Sbrian#include "fsm.h"
5743313Sbrian#include "iplist.h"
5843313Sbrian#include "slcompress.h"
5943313Sbrian#include "throughput.h"
6043313Sbrian#include "lqr.h"
6143313Sbrian#include "hdlc.h"
6243313Sbrian#include "mbuf.h"
6343313Sbrian#include "ipcp.h"
6443313Sbrian#include "route.h"
6543313Sbrian#include "command.h"
6643313Sbrian#include "filter.h"
6743313Sbrian#include "lcp.h"
6843313Sbrian#include "ccp.h"
6943313Sbrian#include "link.h"
7043313Sbrian#include "mp.h"
7143313Sbrian#include "radius.h"
7243693Sbrian#include "auth.h"
7343693Sbrian#include "async.h"
7443693Sbrian#include "physical.h"
7543693Sbrian#include "chat.h"
7643693Sbrian#include "cbcp.h"
7743693Sbrian#include "chap.h"
7843693Sbrian#include "datalink.h"
7943313Sbrian#include "bundle.h"
8043313Sbrian
8143693Sbrian/*
8243693Sbrian * rad_continue_send_request() has given us `got' (non-zero).  Deal with it.
8343693Sbrian */
8443693Sbrianstatic void
8543693Sbrianradius_Process(struct radius *r, int got)
8643313Sbrian{
8743313Sbrian  char *argv[MAXARGS], *nuke;
8843693Sbrian  struct bundle *bundle;
8945910Sbrian  int argc, addrs;
9045910Sbrian  size_t len;
9143313Sbrian  struct in_range dest;
9243313Sbrian  struct in_addr gw;
9343693Sbrian  const void *data;
9443313Sbrian
9543693Sbrian  r->cx.fd = -1;		/* Stop select()ing */
9643313Sbrian
9743313Sbrian  switch (got) {
9843313Sbrian    case RAD_ACCESS_ACCEPT:
9943693Sbrian      log_Printf(LogPHASE, "Radius: ACCEPT received\n");
10043313Sbrian      break;
10143313Sbrian
10243693Sbrian    case RAD_ACCESS_REJECT:
10343693Sbrian      log_Printf(LogPHASE, "Radius: REJECT received\n");
10443693Sbrian      auth_Failure(r->cx.auth);
10543693Sbrian      rad_close(r->cx.rad);
10643693Sbrian      return;
10743693Sbrian
10843313Sbrian    case RAD_ACCESS_CHALLENGE:
10943313Sbrian      /* we can't deal with this (for now) ! */
11043693Sbrian      log_Printf(LogPHASE, "Radius: CHALLENGE received (can't handle yet)\n");
11143693Sbrian      auth_Failure(r->cx.auth);
11243693Sbrian      rad_close(r->cx.rad);
11343693Sbrian      return;
11443313Sbrian
11543313Sbrian    case -1:
11643693Sbrian      log_Printf(LogPHASE, "radius: %s\n", rad_strerror(r->cx.rad));
11743693Sbrian      auth_Failure(r->cx.auth);
11843693Sbrian      rad_close(r->cx.rad);
11943693Sbrian      return;
12043313Sbrian
12143313Sbrian    default:
12243313Sbrian      log_Printf(LogERROR, "rad_send_request: Failed %d: %s\n",
12343693Sbrian                 got, rad_strerror(r->cx.rad));
12443693Sbrian      auth_Failure(r->cx.auth);
12543693Sbrian      rad_close(r->cx.rad);
12643693Sbrian      return;
12743313Sbrian  }
12843313Sbrian
12943313Sbrian  /* So we've been accepted !  Let's see what we've got in our reply :-I */
13043313Sbrian  r->ip.s_addr = r->mask.s_addr = INADDR_NONE;
13143313Sbrian  r->mtu = 0;
13243313Sbrian  r->vj = 0;
13343693Sbrian  while ((got = rad_get_attr(r->cx.rad, &data, &len)) > 0) {
13443313Sbrian    switch (got) {
13543313Sbrian      case RAD_FRAMED_IP_ADDRESS:
13643313Sbrian        r->ip = rad_cvt_addr(data);
13743693Sbrian        log_Printf(LogPHASE, "        IP %s\n", inet_ntoa(r->ip));
13843313Sbrian        break;
13943313Sbrian
14043313Sbrian      case RAD_FRAMED_IP_NETMASK:
14143313Sbrian        r->mask = rad_cvt_addr(data);
14243693Sbrian        log_Printf(LogPHASE, "        Netmask %s\n", inet_ntoa(r->mask));
14343313Sbrian        break;
14443313Sbrian
14543313Sbrian      case RAD_FRAMED_MTU:
14643313Sbrian        r->mtu = rad_cvt_int(data);
14743693Sbrian        log_Printf(LogPHASE, "        MTU %lu\n", r->mtu);
14843313Sbrian        break;
14943313Sbrian
15043313Sbrian      case RAD_FRAMED_ROUTING:
15143313Sbrian        /* Disabled for now - should we automatically set up some filters ? */
15243313Sbrian        /* rad_cvt_int(data); */
15343313Sbrian        /* bit 1 = Send routing packets */
15443313Sbrian        /* bit 2 = Receive routing packets */
15543313Sbrian        break;
15643313Sbrian
15743313Sbrian      case RAD_FRAMED_COMPRESSION:
15843313Sbrian        r->vj = rad_cvt_int(data) == 1 ? 1 : 0;
15943693Sbrian        log_Printf(LogPHASE, "        VJ %sabled\n", r->vj ? "en" : "dis");
16043313Sbrian        break;
16143313Sbrian
16243313Sbrian      case RAD_FRAMED_ROUTE:
16343313Sbrian        /*
16443313Sbrian         * We expect a string of the format ``dest[/bits] gw [metrics]''
16543313Sbrian         * Any specified metrics are ignored.  MYADDR and HISADDR are
16643313Sbrian         * understood for ``dest'' and ``gw'' and ``0.0.0.0'' is the same
16743313Sbrian         * as ``HISADDR''.
16843313Sbrian         */
16943313Sbrian
17043313Sbrian        if ((nuke = rad_cvt_string(data, len)) == NULL) {
17143693Sbrian          log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
17243693Sbrian          rad_close(r->cx.rad);
17343693Sbrian          return;
17443313Sbrian        }
17543313Sbrian
17643693Sbrian        log_Printf(LogPHASE, "        Route: %s\n", nuke);
17743693Sbrian        bundle = r->cx.auth->physical->dl->bundle;
17843313Sbrian        dest.ipaddr.s_addr = dest.mask.s_addr = INADDR_ANY;
17943313Sbrian        dest.width = 0;
18043313Sbrian        argc = command_Interpret(nuke, strlen(nuke), argv);
18154914Sbrian        if (argc < 0)
18254914Sbrian          log_Printf(LogWARN, "radius: %s: Syntax error\n",
18354914Sbrian                     argc == 1 ? argv[0] : "\"\"");
18454914Sbrian        else if (argc < 2)
18543313Sbrian          log_Printf(LogWARN, "radius: %s: Invalid route\n",
18643313Sbrian                     argc == 1 ? argv[0] : "\"\"");
18743313Sbrian        else if ((strcasecmp(argv[0], "default") != 0 &&
18843313Sbrian                  !ParseAddr(&bundle->ncp.ipcp, argv[0], &dest.ipaddr,
18943313Sbrian                             &dest.mask, &dest.width)) ||
19043313Sbrian                 !ParseAddr(&bundle->ncp.ipcp, argv[1], &gw, NULL, NULL))
19143313Sbrian          log_Printf(LogWARN, "radius: %s %s: Invalid route\n",
19243313Sbrian                     argv[0], argv[1]);
19343313Sbrian        else {
19443313Sbrian          if (dest.width == 32 && strchr(argv[0], '/') == NULL)
19543313Sbrian            /* No mask specified - use the natural mask */
19644455Sbrian            dest.mask = addr2mask(dest.ipaddr);
19743313Sbrian          addrs = 0;
19843313Sbrian
19943313Sbrian          if (!strncasecmp(argv[0], "HISADDR", 7))
20043313Sbrian            addrs = ROUTE_DSTHISADDR;
20143313Sbrian          else if (!strncasecmp(argv[0], "MYADDR", 6))
20243313Sbrian            addrs = ROUTE_DSTMYADDR;
20343313Sbrian
20443313Sbrian          if (gw.s_addr == INADDR_ANY) {
20543313Sbrian            addrs |= ROUTE_GWHISADDR;
20643313Sbrian            gw = bundle->ncp.ipcp.peer_ip;
20743313Sbrian          } else if (strcasecmp(argv[1], "HISADDR") == 0)
20843313Sbrian            addrs |= ROUTE_GWHISADDR;
20943313Sbrian
21043313Sbrian          route_Add(&r->routes, addrs, dest.ipaddr, dest.mask, gw);
21143313Sbrian        }
21243313Sbrian        free(nuke);
21343313Sbrian        break;
21443313Sbrian    }
21543313Sbrian  }
21643313Sbrian
21743313Sbrian  if (got == -1) {
21843693Sbrian    log_Printf(LogERROR, "rad_get_attr: %s (failing!)\n",
21943693Sbrian               rad_strerror(r->cx.rad));
22043693Sbrian    auth_Failure(r->cx.auth);
22143693Sbrian    rad_close(r->cx.rad);
22243693Sbrian  } else {
22343693Sbrian    r->valid = 1;
22443693Sbrian    auth_Success(r->cx.auth);
22543693Sbrian    rad_close(r->cx.rad);
22643313Sbrian  }
22743693Sbrian}
22843313Sbrian
22943693Sbrian/*
23058028Sbrian * We've either timed out or select()ed on the read fdescriptor
23143693Sbrian */
23243693Sbrianstatic void
23343693Sbrianradius_Continue(struct radius *r, int sel)
23443693Sbrian{
23543693Sbrian  struct timeval tv;
23643693Sbrian  int got;
23743313Sbrian
23843693Sbrian  timer_Stop(&r->cx.timer);
23943693Sbrian  if ((got = rad_continue_send_request(r->cx.rad, sel, &r->cx.fd, &tv)) == 0) {
24043693Sbrian    log_Printf(LogPHASE, "Radius: Request re-sent\n");
24143693Sbrian    r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
24243693Sbrian    timer_Start(&r->cx.timer);
24343693Sbrian    return;
24443693Sbrian  }
24543693Sbrian
24643693Sbrian  radius_Process(r, got);
24743313Sbrian}
24843313Sbrian
24943693Sbrian/*
25043693Sbrian * Time to call rad_continue_send_request() - timed out.
25143693Sbrian */
25243693Sbrianstatic void
25343693Sbrianradius_Timeout(void *v)
25443693Sbrian{
25543693Sbrian  radius_Continue((struct radius *)v, 0);
25643693Sbrian}
25743693Sbrian
25843693Sbrian/*
25943693Sbrian * Time to call rad_continue_send_request() - something to read.
26043693Sbrian */
26143693Sbrianstatic void
26258028Sbrianradius_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
26343693Sbrian{
26443693Sbrian  radius_Continue(descriptor2radius(d), 1);
26543693Sbrian}
26643693Sbrian
26743693Sbrian/*
26858028Sbrian * Behave as a struct fdescriptor (fdescriptor.h)
26943693Sbrian */
27043693Sbrianstatic int
27158028Sbrianradius_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
27243693Sbrian{
27343693Sbrian  struct radius *rad = descriptor2radius(d);
27443693Sbrian
27543693Sbrian  if (r && rad->cx.fd != -1) {
27643693Sbrian    FD_SET(rad->cx.fd, r);
27743693Sbrian    if (*n < rad->cx.fd + 1)
27843693Sbrian      *n = rad->cx.fd + 1;
27943693Sbrian    log_Printf(LogTIMER, "Radius: fdset(r) %d\n", rad->cx.fd);
28043693Sbrian    return 1;
28143693Sbrian  }
28243693Sbrian
28343693Sbrian  return 0;
28443693Sbrian}
28543693Sbrian
28643693Sbrian/*
28758028Sbrian * Behave as a struct fdescriptor (fdescriptor.h)
28843693Sbrian */
28943693Sbrianstatic int
29058028Sbrianradius_IsSet(struct fdescriptor *d, const fd_set *fdset)
29143693Sbrian{
29243693Sbrian  struct radius *r = descriptor2radius(d);
29343693Sbrian
29443693Sbrian  return r && r->cx.fd != -1 && FD_ISSET(r->cx.fd, fdset);
29543693Sbrian}
29643693Sbrian
29743693Sbrian/*
29858028Sbrian * Behave as a struct fdescriptor (fdescriptor.h)
29943693Sbrian */
30043693Sbrianstatic int
30158028Sbrianradius_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
30243693Sbrian{
30343693Sbrian  /* We never want to write here ! */
30443693Sbrian  log_Printf(LogALERT, "radius_Write: Internal error: Bad call !\n");
30543693Sbrian  return 0;
30643693Sbrian}
30743693Sbrian
30843693Sbrian/*
30943693Sbrian * Initialise ourselves
31043693Sbrian */
31143313Sbrianvoid
31243693Sbrianradius_Init(struct radius *r)
31343693Sbrian{
31443693Sbrian  r->valid = 0;
31543693Sbrian  r->cx.fd = -1;
31643693Sbrian  *r->cfg.file = '\0';;
31743693Sbrian  r->desc.type = RADIUS_DESCRIPTOR;
31843693Sbrian  r->desc.UpdateSet = radius_UpdateSet;
31943693Sbrian  r->desc.IsSet = radius_IsSet;
32043693Sbrian  r->desc.Read = radius_Read;
32143693Sbrian  r->desc.Write = radius_Write;
32243693Sbrian  memset(&r->cx.timer, '\0', sizeof r->cx.timer);
32343693Sbrian}
32443693Sbrian
32543693Sbrian/*
32643693Sbrian * Forget everything and go back to initialised state.
32743693Sbrian */
32843693Sbrianvoid
32943693Sbrianradius_Destroy(struct radius *r)
33043693Sbrian{
33143693Sbrian  r->valid = 0;
33243693Sbrian  timer_Stop(&r->cx.timer);
33343693Sbrian  route_DeleteAll(&r->routes);
33443693Sbrian  if (r->cx.fd != -1) {
33543693Sbrian    r->cx.fd = -1;
33643693Sbrian    rad_close(r->cx.rad);
33743693Sbrian  }
33843693Sbrian}
33943693Sbrian
34043693Sbrian/*
34143693Sbrian * Start an authentication request to the RADIUS server.
34243693Sbrian */
34343693Sbrianvoid
34443693Sbrianradius_Authenticate(struct radius *r, struct authinfo *authp, const char *name,
34543693Sbrian                    const char *key, const char *challenge)
34643693Sbrian{
34750840Sbrian  struct ttyent *ttyp;
34843693Sbrian  struct timeval tv;
34950840Sbrian  int got, slot;
35050840Sbrian  char hostname[MAXHOSTNAMELEN];
35150840Sbrian  struct hostent *hp;
35250840Sbrian  struct in_addr hostaddr;
35343693Sbrian
35443693Sbrian  if (!*r->cfg.file)
35543693Sbrian    return;
35643693Sbrian
35743693Sbrian  if (r->cx.fd != -1)
35843693Sbrian    /*
35943693Sbrian     * We assume that our name/key/challenge is the same as last time,
36043693Sbrian     * and just continue to wait for the RADIUS server(s).
36143693Sbrian     */
36243693Sbrian    return;
36343693Sbrian
36443693Sbrian  radius_Destroy(r);
36543693Sbrian
36643693Sbrian  if ((r->cx.rad = rad_open()) == NULL) {
36743693Sbrian    log_Printf(LogERROR, "rad_open: %s\n", strerror(errno));
36843693Sbrian    return;
36943693Sbrian  }
37043693Sbrian
37143693Sbrian  if (rad_config(r->cx.rad, r->cfg.file) != 0) {
37243693Sbrian    log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad));
37343693Sbrian    rad_close(r->cx.rad);
37443693Sbrian    return;
37543693Sbrian  }
37643693Sbrian
37743693Sbrian  if (rad_create_request(r->cx.rad, RAD_ACCESS_REQUEST) != 0) {
37843693Sbrian    log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad));
37943693Sbrian    rad_close(r->cx.rad);
38043693Sbrian    return;
38143693Sbrian  }
38243693Sbrian
38343693Sbrian  if (rad_put_string(r->cx.rad, RAD_USER_NAME, name) != 0 ||
38443693Sbrian      rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 ||
38543693Sbrian      rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0) {
38643693Sbrian    log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
38743693Sbrian    rad_close(r->cx.rad);
38843693Sbrian    return;
38943693Sbrian  }
39043693Sbrian
39143693Sbrian  if (challenge != NULL) {
39243693Sbrian    /* We're talking CHAP */
39343693Sbrian    if (rad_put_string(r->cx.rad, RAD_CHAP_PASSWORD, key) != 0 ||
39443693Sbrian        rad_put_string(r->cx.rad, RAD_CHAP_CHALLENGE, challenge) != 0) {
39543693Sbrian      log_Printf(LogERROR, "CHAP: rad_put_string: %s\n",
39643693Sbrian                 rad_strerror(r->cx.rad));
39743693Sbrian      rad_close(r->cx.rad);
39843693Sbrian      return;
39943693Sbrian    }
40043693Sbrian  } else if (rad_put_string(r->cx.rad, RAD_USER_PASSWORD, key) != 0) {
40143693Sbrian    /* We're talking PAP */
40243693Sbrian    log_Printf(LogERROR, "PAP: rad_put_string: %s\n", rad_strerror(r->cx.rad));
40343693Sbrian    rad_close(r->cx.rad);
40443693Sbrian    return;
40543693Sbrian  }
40643693Sbrian
40750840Sbrian  if (gethostname(hostname, sizeof hostname) != 0)
40850840Sbrian    log_Printf(LogERROR, "rad_put: gethostname(): %s\n", strerror(errno));
40950840Sbrian  else {
41050840Sbrian    if ((hp = gethostbyname(hostname)) != NULL) {
41150840Sbrian      hostaddr.s_addr = *(u_long *)hp->h_addr;
41250840Sbrian      if (rad_put_addr(r->cx.rad, RAD_NAS_IP_ADDRESS, hostaddr) != 0) {
41350840Sbrian        log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
41450840Sbrian                   rad_strerror(r->cx.rad));
41550840Sbrian        rad_close(r->cx.rad);
41650840Sbrian        return;
41750840Sbrian      }
41850840Sbrian    }
41950840Sbrian    if (rad_put_string(r->cx.rad, RAD_NAS_IDENTIFIER, hostname) != 0) {
42050840Sbrian      log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
42150840Sbrian                 rad_strerror(r->cx.rad));
42250840Sbrian      rad_close(r->cx.rad);
42350840Sbrian      return;
42450840Sbrian    }
42550840Sbrian  }
42650840Sbrian
42750840Sbrian  if (authp->physical->handler &&
42850840Sbrian      authp->physical->handler->type == TTY_DEVICE) {
42950840Sbrian    setttyent();
43050840Sbrian    for (slot = 1; (ttyp = getttyent()); ++slot)
43150840Sbrian      if (!strcmp(ttyp->ty_name, authp->physical->name.base)) {
43250840Sbrian        if(rad_put_int(r->cx.rad, RAD_NAS_PORT, slot) != 0) {
43350840Sbrian          log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
43450840Sbrian                      rad_strerror(r->cx.rad));
43550840Sbrian          rad_close(r->cx.rad);
43650840Sbrian          endttyent();
43750840Sbrian          return;
43850840Sbrian        }
43950840Sbrian        break;
44050840Sbrian      }
44150840Sbrian    endttyent();
44250840Sbrian  }
44350840Sbrian
44450840Sbrian
44543693Sbrian  if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv)))
44643693Sbrian    radius_Process(r, got);
44743693Sbrian  else {
44843693Sbrian    log_Printf(LogPHASE, "Radius: Request sent\n");
44943693Sbrian    log_Printf(LogDEBUG, "Using radius_Timeout [%p]\n", radius_Timeout);
45043693Sbrian    r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
45143693Sbrian    r->cx.timer.func = radius_Timeout;
45243693Sbrian    r->cx.timer.name = "radius";
45343693Sbrian    r->cx.timer.arg = r;
45443693Sbrian    r->cx.auth = authp;
45543693Sbrian    timer_Start(&r->cx.timer);
45643693Sbrian  }
45743693Sbrian}
45843693Sbrian
45943693Sbrian/*
46043693Sbrian * How do things look at the moment ?
46143693Sbrian */
46243693Sbrianvoid
46343313Sbrianradius_Show(struct radius *r, struct prompt *p)
46443313Sbrian{
46543313Sbrian  prompt_Printf(p, " Radius config: %s", *r->cfg.file ? r->cfg.file : "none");
46643313Sbrian  if (r->valid) {
46743313Sbrian    prompt_Printf(p, "\n            IP: %s\n", inet_ntoa(r->ip));
46843313Sbrian    prompt_Printf(p, "       Netmask: %s\n", inet_ntoa(r->mask));
46943313Sbrian    prompt_Printf(p, "           MTU: %lu\n", r->mtu);
47043313Sbrian    prompt_Printf(p, "            VJ: %sabled\n", r->vj ? "en" : "dis");
47143313Sbrian    if (r->routes)
47243313Sbrian      route_ShowSticky(p, r->routes, "        Routes", 16);
47343313Sbrian  } else
47443313Sbrian    prompt_Printf(p, " (not authenticated)\n");
47543313Sbrian}
476