diff options
Diffstat (limited to 'usr.sbin/ppp')
-rw-r--r-- | usr.sbin/ppp/arp.c | 52 | ||||
-rw-r--r-- | usr.sbin/ppp/bundle.c | 28 | ||||
-rw-r--r-- | usr.sbin/ppp/bundle.h | 19 | ||||
-rw-r--r-- | usr.sbin/ppp/command.c | 32 | ||||
-rw-r--r-- | usr.sbin/ppp/ipcp.c | 52 | ||||
-rw-r--r-- | usr.sbin/ppp/ipcp.h | 5 | ||||
-rw-r--r-- | usr.sbin/ppp/ppp.8 | 21 | ||||
-rw-r--r-- | usr.sbin/ppp/ppp.8.m4 | 21 |
8 files changed, 169 insertions, 61 deletions
diff --git a/usr.sbin/ppp/arp.c b/usr.sbin/ppp/arp.c index b819c9a..283d909 100644 --- a/usr.sbin/ppp/arp.c +++ b/usr.sbin/ppp/arp.c @@ -17,7 +17,7 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: arp.c,v 1.29 1998/06/16 19:40:34 brian Exp $ + * $Id: arp.c,v 1.30 1998/08/26 17:39:36 brian Exp $ * */ @@ -87,10 +87,8 @@ static struct { char extra[128]; } arpmsg; -static int arpmsg_valid; - -int -arp_SetProxy(struct bundle *bundle, struct in_addr addr, int s) +static int +arp_ProxySub(struct bundle *bundle, struct in_addr addr, int add, int s) { int routes; @@ -98,9 +96,11 @@ arp_SetProxy(struct bundle *bundle, struct in_addr addr, int s) * Get the hardware address of an interface on the same subnet as our local * address. */ + memset(&arpmsg, 0, sizeof arpmsg); if (!get_ether_addr(s, addr, &arpmsg.hwa)) { - log_Printf(LogWARN, "Cannot determine ethernet address for proxy ARP\n"); + log_Printf(LogWARN, "%s: Cannot determine ethernet address for proxy ARP\n", + inet_ntoa(addr)); return 0; } routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET); @@ -109,7 +109,7 @@ arp_SetProxy(struct bundle *bundle, struct in_addr addr, int s) strerror(errno)); return 0; } - arpmsg.hdr.rtm_type = RTM_ADD; + arpmsg.hdr.rtm_type = add ? RTM_ADD : RTM_DELETE; arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC; arpmsg.hdr.rtm_version = RTM_VERSION; arpmsg.hdr.rtm_seq = ++bundle->routing_seq; @@ -122,44 +122,34 @@ arp_SetProxy(struct bundle *bundle, struct in_addr addr, int s) arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg + arpmsg.hwa.sdl_len; - if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { - log_Printf(LogERROR, "Add proxy arp entry: %s\n", strerror(errno)); + + + if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0 && + !(!add && errno == ESRCH)) { + log_Printf(LogERROR, "%s proxy arp entry %s: %s\n", + add ? "Add" : "Delete", inet_ntoa(addr), strerror(errno)); close(routes); return 0; } close(routes); - arpmsg_valid = 1; return 1; } +int +arp_SetProxy(struct bundle *bundle, struct in_addr addr, int s) +{ + + return (arp_ProxySub(bundle, addr, 1, s)); +} + /* * arp_ClearProxy - Delete the proxy ARP entry for the peer. */ int arp_ClearProxy(struct bundle *bundle, struct in_addr addr, int s) { - int routes; - - if (!arpmsg_valid) - return 0; - arpmsg_valid = 0; - - arpmsg.hdr.rtm_type = RTM_DELETE; - arpmsg.hdr.rtm_seq = ++bundle->routing_seq; - routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET); - if (routes < 0) { - log_Printf(LogERROR, "arp_SetProxy: opening routing socket: %s\n", - strerror(errno)); - return 0; - } - if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { - log_Printf(LogERROR, "Delete proxy arp entry: %s\n", strerror(errno)); - close(routes); - return 0; - } - close(routes); - return 1; + return (arp_ProxySub(bundle, addr, 0, s)); } #else /* RTM_VERSION */ diff --git a/usr.sbin/ppp/bundle.c b/usr.sbin/ppp/bundle.c index 8bc26e4..263f6cc 100644 --- a/usr.sbin/ppp/bundle.c +++ b/usr.sbin/ppp/bundle.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: bundle.c,v 1.36 1998/10/22 02:32:48 brian Exp $ + * $Id: bundle.c,v 1.37 1998/10/24 01:08:45 brian Exp $ */ #include <sys/param.h> @@ -985,6 +985,17 @@ bundle_SetRoute(struct bundle *bundle, int cmd, struct in_addr dst, rtmes.m_rtm.rtm_pid = getpid(); rtmes.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; + if (cmd == RTM_ADD || cmd == RTM_CHANGE) { + if (bundle->ncp.ipcp.cfg.sendpipe > 0) { + rtmes.m_rtm.rtm_rmx.rmx_sendpipe = bundle->ncp.ipcp.cfg.sendpipe; + rtmes.m_rtm.rtm_inits |= RTV_SPIPE; + } + if (bundle->ncp.ipcp.cfg.recvpipe > 0) { + rtmes.m_rtm.rtm_rmx.rmx_recvpipe = bundle->ncp.ipcp.cfg.recvpipe; + rtmes.m_rtm.rtm_inits |= RTV_RPIPE; + } + } + memset(&rtdata, '\0', sizeof rtdata); rtdata.sin_len = sizeof rtdata; rtdata.sin_family = AF_INET; @@ -1032,7 +1043,7 @@ failed: (rtmes.m_rtm.rtm_errno == 0 && errno == EEXIST))) { if (!bang) { log_Printf(LogWARN, "Add route failed: %s already exists\n", - inet_ntoa(dst)); + dst.s_addr == 0 ? "default" : inet_ntoa(dst)); result = 0; /* Don't add to our dynamic list */ } else { rtmes.m_rtm.rtm_type = cmd = RTM_CHANGE; @@ -1230,6 +1241,17 @@ bundle_ShowStatus(struct cmdargs const *arg) else prompt_Printf(arg->prompt, "unspecified\n"); + prompt_Printf(arg->prompt, " sendpipe: "); + if (arg->bundle->ncp.ipcp.cfg.sendpipe > 0) + prompt_Printf(arg->prompt, "%ld\n", arg->bundle->ncp.ipcp.cfg.sendpipe); + else + prompt_Printf(arg->prompt, "unspecified\n"); + prompt_Printf(arg->prompt, " recvpipe: "); + if (arg->bundle->ncp.ipcp.cfg.recvpipe > 0) + prompt_Printf(arg->prompt, "%ld\n", arg->bundle->ncp.ipcp.cfg.recvpipe); + else + prompt_Printf(arg->prompt, "unspecified\n"); + prompt_Printf(arg->prompt, " Sticky Routes: %s\n", optval(arg->bundle, OPT_SROUTES)); prompt_Printf(arg->prompt, " ID check: %s\n", @@ -1240,6 +1262,8 @@ bundle_ShowStatus(struct cmdargs const *arg) optval(arg->bundle, OPT_PASSWDAUTH)); prompt_Printf(arg->prompt, " Proxy: %s\n", optval(arg->bundle, OPT_PROXY)); + prompt_Printf(arg->prompt, " Proxyall: %s\n", + optval(arg->bundle, OPT_PROXYALL)); prompt_Printf(arg->prompt, " Throughput: %s\n", optval(arg->bundle, OPT_THROUGHPUT)); prompt_Printf(arg->prompt, " Utmp Logging: %s\n", diff --git a/usr.sbin/ppp/bundle.h b/usr.sbin/ppp/bundle.h index 5096ae1..75722b1 100644 --- a/usr.sbin/ppp/bundle.h +++ b/usr.sbin/ppp/bundle.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: bundle.h,v 1.14 1998/10/22 02:32:48 brian Exp $ + * $Id: bundle.h,v 1.15 1998/10/24 01:08:45 brian Exp $ */ #define PHASE_DEAD 0 /* Link is dead */ @@ -33,14 +33,15 @@ #define PHASE_TERMINATE 4 /* Terminating link */ /* cfg.opt bit settings */ -#define OPT_IDCHECK 0x01 -#define OPT_LOOPBACK 0x02 -#define OPT_PASSWDAUTH 0x04 -#define OPT_PROXY 0x08 -#define OPT_SROUTES 0x10 -#define OPT_THROUGHPUT 0x20 -#define OPT_UTMP 0x40 -#define OPT_IFACEALIAS 0x80 +#define OPT_IDCHECK 0x0001 +#define OPT_LOOPBACK 0x0002 +#define OPT_PASSWDAUTH 0x0004 +#define OPT_PROXY 0x0008 +#define OPT_PROXYALL 0x0010 +#define OPT_SROUTES 0x0020 +#define OPT_THROUGHPUT 0x0040 +#define OPT_UTMP 0x0080 +#define OPT_IFACEALIAS 0x0100 #define MAX_ENDDISC_CLASS 5 diff --git a/usr.sbin/ppp/command.c b/usr.sbin/ppp/command.c index f474aca..a3f0d1d 100644 --- a/usr.sbin/ppp/command.c +++ b/usr.sbin/ppp/command.c @@ -17,7 +17,7 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: command.c,v 1.169 1998/10/24 01:08:45 brian Exp $ + * $Id: command.c,v 1.170 1998/10/26 19:07:36 brian Exp $ * */ #include <sys/types.h> @@ -113,6 +113,8 @@ #define VAR_CALLBACK 24 #define VAR_CBCP 25 #define VAR_CHOKED 26 +#define VAR_SENDPIPE 27 +#define VAR_RECVPIPE 28 /* ``accept|deny|disable|enable'' masks */ #define NEG_HISMASK (1) @@ -132,7 +134,7 @@ #define NEG_DNS 50 const char Version[] = "2.0"; -const char VersionDate[] = "$Date: 1998/10/24 01:08:45 $"; +const char VersionDate[] = "$Date: 1998/10/26 19:07:36 $"; static int ShowCommand(struct cmdargs const *); static int TerminalCommand(struct cmdargs const *); @@ -484,8 +486,9 @@ ShellCommand(struct cmdargs const *arg, int bg) execl(shell, shell, NULL); } - log_Printf(LogWARN, "exec() of %s failed\n", - arg->argc > arg->argn ? arg->argv[arg->argn] : shell); + log_Printf(LogWARN, "exec() of %s failed: %s\n", + arg->argc > arg->argn ? arg->argv[arg->argn] : shell, + strerror(errno)); exit(255); } @@ -1665,7 +1668,16 @@ SetVariable(struct cmdargs const *arg) if (arg->bundle->cfg.choked.timeout <= 0) arg->bundle->cfg.choked.timeout = CHOKED_TIMEOUT; break; - + + case VAR_SENDPIPE: + long_val = atol(argp); + arg->bundle->ncp.ipcp.cfg.sendpipe = long_val; + break; + + case VAR_RECVPIPE: + long_val = atol(argp); + arg->bundle->ncp.ipcp.cfg.recvpipe = long_val; + break; } return err ? 1 : 0; @@ -1763,8 +1775,12 @@ static struct cmdtab const SetCommands[] = { "set phone phone1[:phone2[...]]", (const void *)VAR_PHONE}, {"reconnect", NULL, datalink_SetReconnect, LOCAL_AUTH | LOCAL_CX, "Reconnect timeout", "set reconnect value ntries"}, + {"recvpipe", NULL, SetVariable, LOCAL_AUTH, + "RECVPIPE value", "set recvpipe value", (const void *)VAR_RECVPIPE}, {"redial", NULL, datalink_SetRedial, LOCAL_AUTH | LOCAL_CX, "Redial timeout", "set redial value|random[.value|random] [attempts]"}, + {"sendpipe", NULL, SetVariable, LOCAL_AUTH, + "SENDPIPE value", "set sendpipe value", (const void *)VAR_SENDPIPE}, {"server", "socket", SetServer, LOCAL_AUTH, "server port", "set server|socket TcpPort|LocalName|none password [mask]"}, {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX, @@ -2166,8 +2182,10 @@ static struct cmdtab const NegotiateCommands[] = { "disable|enable", (const void *)OPT_LOOPBACK}, {"passwdauth", NULL, OptSet, LOCAL_AUTH, "Use passwd file", "disable|enable", (const void *)OPT_PASSWDAUTH}, - {"proxy", NULL, OptSet, LOCAL_AUTH, "Create proxy ARP entry", + {"proxy", NULL, OptSet, LOCAL_AUTH, "Create a proxy ARP entry", "disable|enable", (const void *)OPT_PROXY}, + {"proxyall", NULL, OptSet, LOCAL_AUTH, "Proxy ARP for all remote hosts", + "disable|enable", (const void *)OPT_PROXYALL}, {"sroutes", NULL, OptSet, LOCAL_AUTH, "Use sticky routes", "disable|enable", (const void *)OPT_SROUTES}, {"throughput", NULL, OptSet, LOCAL_AUTH, "Rolling throughput", @@ -2178,7 +2196,7 @@ static struct cmdtab const NegotiateCommands[] = { "maintain interface addresses", "disable|enable", (const void *)OPT_IFACEALIAS}, -#define OPT_MAX 8 /* accept/deny allowed below and not above */ +#define OPT_MAX 9 /* accept/deny allowed below and not above */ {"acfcomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX, "Address & Control field compression", "accept|deny|disable|enable", diff --git a/usr.sbin/ppp/ipcp.c b/usr.sbin/ppp/ipcp.c index 3d1d3ee..eae5f4c 100644 --- a/usr.sbin/ppp/ipcp.c +++ b/usr.sbin/ppp/ipcp.c @@ -17,7 +17,7 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: ipcp.c,v 1.66 1998/09/17 00:45:26 brian Exp $ + * $Id: ipcp.c,v 1.67 1998/10/22 02:32:49 brian Exp $ * * TODO: * o More RFC1772 backward compatibility @@ -28,10 +28,12 @@ #include <netinet/ip.h> #include <arpa/inet.h> #include <sys/socket.h> +#include <net/route.h> #include <netdb.h> #include <net/if.h> #include <sys/sockio.h> #include <sys/un.h> +#include <arpa/nameser.h> #include <fcntl.h> #include <resolv.h> @@ -457,9 +459,39 @@ ipcp_Setup(struct ipcp *ipcp) } static int +ipcp_doproxyall(struct bundle *bundle, + int (*proxyfun)(struct bundle *, struct in_addr, int), int s) +{ + int n, ret; + struct sticky_route *rp; + struct in_addr addr; + struct ipcp *ipcp; + + ipcp = &bundle->ncp.ipcp; + for (rp = ipcp->route; rp != NULL; rp = rp->next) { + if (ntohl(rp->mask.s_addr) == INADDR_BROADCAST) + continue; + n = INADDR_BROADCAST - ntohl(rp->mask.s_addr) - 1; + if (n > 0 && n <= 254 && rp->dst.s_addr != INADDR_ANY) { + addr = rp->dst; + while (n--) { + addr.s_addr = htonl(ntohl(addr.s_addr) + 1); + log_Printf(LogDEBUG, "ipcp_doproxyall: %s\n", inet_ntoa(addr)); + ret = (*proxyfun)(bundle, addr, s); + if (!ret) + return ret; + } + } + } + + return 0; +} + +static int ipcp_SetIPaddress(struct bundle *bundle, struct in_addr myaddr, struct in_addr hisaddr, int silent) { + static struct in_addr none = { INADDR_ANY }; struct in_addr mask, oaddr; u_int32_t addr; @@ -486,16 +518,22 @@ ipcp_SetIPaddress(struct bundle *bundle, struct in_addr myaddr, /* Nuke the old one */ iface_inDelete(bundle->iface, oaddr); + if (bundle->ncp.ipcp.cfg.sendpipe > 0 || bundle->ncp.ipcp.cfg.recvpipe > 0) + bundle_SetRoute(bundle, RTM_CHANGE, hisaddr, myaddr, none, 0, 0); + if (Enabled(bundle, OPT_SROUTES)) route_Change(bundle, bundle->ncp.ipcp.route, myaddr, hisaddr); - if (Enabled(bundle, OPT_PROXY)) { + if (Enabled(bundle, OPT_PROXY) || Enabled(bundle, OPT_PROXYALL)) { int s = ID0socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) log_Printf(LogERROR, "ipcp_SetIPaddress: socket(): %s\n", strerror(errno)); else { - arp_SetProxy(bundle, hisaddr, s); + if (Enabled(bundle, OPT_PROXYALL)) + ipcp_doproxyall(bundle, arp_SetProxy, s); + else if (Enabled(bundle, OPT_PROXY)) + arp_SetProxy(bundle, hisaddr, s); close(s); } } @@ -623,13 +661,17 @@ ipcp_CleanInterface(struct ipcp *ipcp) route_Clean(ipcp->fsm.bundle, ipcp->route); - if (iface->in_addrs && Enabled(ipcp->fsm.bundle, OPT_PROXY)) { + if (iface->in_addrs && (Enabled(ipcp->fsm.bundle, OPT_PROXY) || + Enabled(ipcp->fsm.bundle, OPT_PROXYALL))) { int s = ID0socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) log_Printf(LogERROR, "ipcp_CleanInterface: socket: %s\n", strerror(errno)); else { - arp_ClearProxy(ipcp->fsm.bundle, iface->in_addr[0].brd, s); + if (Enabled(ipcp->fsm.bundle, OPT_PROXYALL)) + ipcp_doproxyall(ipcp->fsm.bundle, arp_ClearProxy, s); + else if (Enabled(ipcp->fsm.bundle, OPT_PROXY)) + arp_ClearProxy(ipcp->fsm.bundle, iface->in_addr[0].brd, s); close(s); } } diff --git a/usr.sbin/ppp/ipcp.h b/usr.sbin/ppp/ipcp.h index 39ea319..5b5fc9b 100644 --- a/usr.sbin/ppp/ipcp.h +++ b/usr.sbin/ppp/ipcp.h @@ -15,7 +15,7 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: ipcp.h,v 1.20 1998/08/26 17:39:37 brian Exp $ + * $Id: ipcp.h,v 1.21 1998/10/22 02:32:49 brian Exp $ * * TODO: */ @@ -57,6 +57,9 @@ struct ipcp { struct in_range peer_range; /* HISADDR spec */ struct iplist peer_list; /* Ranges of HISADDR values */ + u_long sendpipe; /* route sendpipe size */ + u_long recvpipe; /* route recvpipe size */ + struct in_addr TriggerAddress; /* Address to suggest in REQ */ unsigned HaveTriggerAddress : 1; /* Trigger address specified */ diff --git a/usr.sbin/ppp/ppp.8 b/usr.sbin/ppp/ppp.8 index 80e1832..03e5739 100644 --- a/usr.sbin/ppp/ppp.8 +++ b/usr.sbin/ppp/ppp.8 @@ -1,4 +1,4 @@ -.\" $Id: ppp.8,v 1.127 1998/10/22 02:32:49 brian Exp $ +.\" $Id: ppp.8,v 1.128 1998/10/26 19:07:36 brian Exp $ .Dd 20 September 1995 .Os FreeBSD .Dt PPP 8 @@ -2084,6 +2084,11 @@ as the client password in Default: Disabled. Enabling this option will tell .Nm to proxy ARP for the peer. +.It proxyall +Default: Disabled. Enabling this will tell +.Nm +to add proxy arp entries for every IP address in all class C or +smaller subnets routed via the tun interface. .It sroutes Default: Enabled. When the .Dq add @@ -3317,6 +3322,11 @@ defaults to zero. A value of for .Ar timeout will result in a variable pause, somewhere between 0 and 30 seconds. +.It set recvpipe Op Ar value +This sets the routing table RECVPIPE value. The optimum value is +just over twice the MTU value. If +.Ar value +is unspecified or zero, the default kernel controlled value is used. .It set redial Ar seconds[.nseconds] [attempts] .Nm Ppp can be instructed to attempt to redial @@ -3345,6 +3355,11 @@ should immediately follow the keyword. See the .Dq open description above for further details. +.It set sendpipe Op Ar value +This sets the routing table SENDPIPE value. The optimum value is +just over twice the MTU value. If +.Ar value +is unspecified or zero, the default kernel controlled value is used. .It set server|socket Ar TcpPort|LocalName|none password Op Ar mask This command tells .Nm @@ -3532,11 +3547,11 @@ Read the example configuration files. They are a good source of information. .It Use .Dq help , -.Dq show ? , .Dq alias ? , +.Dq enable ? , .Dq set ? and -.Dq set ? <var> +.Dq show ? to get online information about what's available. .It The following urls contain useful information: diff --git a/usr.sbin/ppp/ppp.8.m4 b/usr.sbin/ppp/ppp.8.m4 index 80e1832..03e5739 100644 --- a/usr.sbin/ppp/ppp.8.m4 +++ b/usr.sbin/ppp/ppp.8.m4 @@ -1,4 +1,4 @@ -.\" $Id: ppp.8,v 1.127 1998/10/22 02:32:49 brian Exp $ +.\" $Id: ppp.8,v 1.128 1998/10/26 19:07:36 brian Exp $ .Dd 20 September 1995 .Os FreeBSD .Dt PPP 8 @@ -2084,6 +2084,11 @@ as the client password in Default: Disabled. Enabling this option will tell .Nm to proxy ARP for the peer. +.It proxyall +Default: Disabled. Enabling this will tell +.Nm +to add proxy arp entries for every IP address in all class C or +smaller subnets routed via the tun interface. .It sroutes Default: Enabled. When the .Dq add @@ -3317,6 +3322,11 @@ defaults to zero. A value of for .Ar timeout will result in a variable pause, somewhere between 0 and 30 seconds. +.It set recvpipe Op Ar value +This sets the routing table RECVPIPE value. The optimum value is +just over twice the MTU value. If +.Ar value +is unspecified or zero, the default kernel controlled value is used. .It set redial Ar seconds[.nseconds] [attempts] .Nm Ppp can be instructed to attempt to redial @@ -3345,6 +3355,11 @@ should immediately follow the keyword. See the .Dq open description above for further details. +.It set sendpipe Op Ar value +This sets the routing table SENDPIPE value. The optimum value is +just over twice the MTU value. If +.Ar value +is unspecified or zero, the default kernel controlled value is used. .It set server|socket Ar TcpPort|LocalName|none password Op Ar mask This command tells .Nm @@ -3532,11 +3547,11 @@ Read the example configuration files. They are a good source of information. .It Use .Dq help , -.Dq show ? , .Dq alias ? , +.Dq enable ? , .Dq set ? and -.Dq set ? <var> +.Dq show ? to get online information about what's available. .It The following urls contain useful information: |