freebsd-skq/usr.sbin/ppp/nat_cmd.c

595 lines
15 KiB
C
Raw Normal View History

1997-12-21 12:11:13 +00:00
/*-
* Copyright (c) 2001 Charles Mott <cm@linktel.net>
* Brian Somers <brian@Awfulhak.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
1997-12-21 12:11:13 +00:00
*
1999-08-28 01:35:59 +00:00
* $FreeBSD$
*/
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#ifdef LOCALNAT
1999-04-26 08:54:34 +00:00
#include "alias.h"
#else
#include <alias.h>
1998-09-17 00:45:27 +00:00
#endif
#include "layer.h"
#include "proto.h"
#include "defs.h"
#include "command.h"
#include "log.h"
#include "nat_cmd.h"
#include "descriptor.h"
#include "prompt.h"
#include "timer.h"
#include "fsm.h"
#include "slcompress.h"
#include "throughput.h"
#include "iplist.h"
#include "mbuf.h"
#include "lqr.h"
#include "hdlc.h"
#include "ncpaddr.h"
#include "ip.h"
#include "ipcp.h"
#include "ipv6cp.h"
#include "lcp.h"
#include "ccp.h"
#include "link.h"
#include "mp.h"
#include "filter.h"
#ifndef NORADIUS
#include "radius.h"
#endif
#include "ncp.h"
#include "bundle.h"
#define NAT_EXTRABUF (13)
static int StrToAddr(const char *, struct in_addr *);
1999-03-25 23:36:25 +00:00
static int StrToPortRange(const char *, u_short *, u_short *, const char *);
static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
u_short *, const char *);
static void
lowhigh(u_short *a, u_short *b)
{
if (a > b) {
u_short c;
c = *b;
*b = *a;
*a = c;
}
}
int
nat_RedirectPort(struct cmdargs const *arg)
{
if (!arg->bundle->NatEnabled) {
prompt_Printf(arg->prompt, "Alias not enabled\n");
return 1;
} else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
char proto_constant;
const char *proto;
struct in_addr localaddr;
u_short hlocalport, llocalport;
struct in_addr aliasaddr;
u_short haliasport, laliasport;
struct in_addr remoteaddr;
u_short hremoteport, lremoteport;
struct alias_link *link;
int error;
proto = arg->argv[arg->argn];
if (strcmp(proto, "tcp") == 0) {
proto_constant = IPPROTO_TCP;
} else if (strcmp(proto, "udp") == 0) {
proto_constant = IPPROTO_UDP;
} else {
prompt_Printf(arg->prompt, "port redirect: protocol must be"
" tcp or udp\n");
1999-03-25 23:36:25 +00:00
return -1;
}
error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
1999-03-25 23:36:25 +00:00
&hlocalport, proto);
if (error) {
prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
1999-03-25 23:36:25 +00:00
return -1;
}
1999-03-25 23:36:25 +00:00
error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
proto);
if (error) {
prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
1999-03-25 23:36:25 +00:00
return -1;
}
aliasaddr.s_addr = INADDR_ANY;
if (arg->argc == arg->argn + 4) {
error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
&lremoteport, &hremoteport, proto);
if (error) {
prompt_Printf(arg->prompt, "nat port: error reading "
"remoteaddr:port\n");
return -1;
}
} else {
remoteaddr.s_addr = INADDR_ANY;
lremoteport = hremoteport = 0;
1999-03-25 23:36:25 +00:00
}
lowhigh(&llocalport, &hlocalport);
lowhigh(&laliasport, &haliasport);
lowhigh(&lremoteport, &hremoteport);
1999-03-25 23:36:25 +00:00
if (haliasport - laliasport != hlocalport - llocalport) {
prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
"are not equal\n");
1999-03-25 23:36:25 +00:00
return -1;
}
if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
"are not equal\n");
return -1;
}
while (laliasport <= haliasport) {
link = PacketAliasRedirectPort(localaddr, htons(llocalport),
remoteaddr, htons(lremoteport),
aliasaddr, htons(laliasport),
1999-03-25 23:36:25 +00:00
proto_constant);
if (link == NULL) {
prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
error);
1999-03-25 23:36:25 +00:00
return 1;
}
llocalport++;
laliasport++;
if (hremoteport)
lremoteport++;
1999-03-25 23:36:25 +00:00
}
return 0;
}
return -1;
}
int
nat_RedirectAddr(struct cmdargs const *arg)
{
if (!arg->bundle->NatEnabled) {
prompt_Printf(arg->prompt, "nat not enabled\n");
return 1;
} else if (arg->argc == arg->argn+2) {
int error;
struct in_addr localaddr, aliasaddr;
struct alias_link *link;
error = StrToAddr(arg->argv[arg->argn], &localaddr);
if (error) {
prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
return 1;
}
error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr);
if (error) {
prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
arg->cmd->syntax);
return 1;
}
link = PacketAliasRedirectAddr(localaddr, aliasaddr);
if (link == NULL) {
prompt_Printf(arg->prompt, "address redirect: packet aliasing"
" engine error\n");
prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
arg->cmd->syntax);
}
} else
return -1;
return 0;
}
int
nat_RedirectProto(struct cmdargs const *arg)
{
if (!arg->bundle->NatEnabled) {
prompt_Printf(arg->prompt, "nat not enabled\n");
return 1;
} else if (arg->argc >= arg->argn + 2 && arg->argc <= arg->argn + 4) {
struct in_addr localIP, publicIP, remoteIP;
struct alias_link *link;
struct protoent *pe;
int error, len;
len = strlen(arg->argv[arg->argn]);
if (len == 0) {
prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
return 1;
}
if (strspn(arg->argv[arg->argn], "01234567") == len)
pe = getprotobynumber(atoi(arg->argv[arg->argn]));
else
pe = getprotobyname(arg->argv[arg->argn]);
if (pe == NULL) {
prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
return 1;
}
error = StrToAddr(arg->argv[arg->argn + 1], &localIP);
if (error) {
prompt_Printf(arg->prompt, "proto redirect: invalid src address\n");
return 1;
}
if (arg->argc >= arg->argn + 3) {
error = StrToAddr(arg->argv[arg->argn + 2], &publicIP);
if (error) {
prompt_Printf(arg->prompt, "proto redirect: invalid alias address\n");
prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
arg->cmd->syntax);
return 1;
}
} else
publicIP.s_addr = INADDR_ANY;
if (arg->argc == arg->argn + 4) {
error = StrToAddr(arg->argv[arg->argn + 2], &remoteIP);
if (error) {
prompt_Printf(arg->prompt, "proto redirect: invalid dst address\n");
prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
arg->cmd->syntax);
return 1;
}
} else
remoteIP.s_addr = INADDR_ANY;
link = PacketAliasRedirectProto(localIP, remoteIP, publicIP, pe->p_proto);
if (link == NULL) {
prompt_Printf(arg->prompt, "proto redirect: packet aliasing"
" engine error\n");
prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
arg->cmd->syntax);
}
} else
return -1;
return 0;
}
static int
StrToAddr(const char *str, struct in_addr *addr)
{
struct hostent *hp;
if (inet_aton(str, addr))
return 0;
hp = gethostbyname(str);
if (!hp) {
log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
return -1;
}
*addr = *((struct in_addr *) hp->h_addr);
return 0;
}
static int
StrToPort(const char *str, u_short *port, const char *proto)
{
struct servent *sp;
char *end;
1999-03-25 23:36:25 +00:00
*port = strtol(str, &end, 10);
if (*end != '\0') {
sp = getservbyname(str, proto);
if (sp == NULL) {
log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
str, proto);
return -1;
}
*port = ntohs(sp->s_port);
}
1999-03-25 23:36:25 +00:00
return 0;
}
1999-03-25 23:36:25 +00:00
static int
StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
{
char *minus;
int res;
minus = strchr(str, '-');
if (minus)
*minus = '\0'; /* Cheat the const-ness ! */
res = StrToPort(str, low, proto);
if (minus)
*minus = '-'; /* Cheat the const-ness ! */
if (res == 0) {
if (minus)
res = StrToPort(minus + 1, high, proto);
else
*high = *low;
}
return res;
}
static int
1999-03-25 23:36:25 +00:00
StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
u_short *high, const char *proto)
{
char *colon;
int res;
colon = strchr(str, ':');
if (!colon) {
log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
return -1;
}
*colon = '\0'; /* Cheat the const-ness ! */
res = StrToAddr(str, addr);
*colon = ':'; /* Cheat the const-ness ! */
if (res != 0)
return -1;
1999-03-25 23:36:25 +00:00
return StrToPortRange(colon + 1, low, high, proto);
}
int
nat_ProxyRule(struct cmdargs const *arg)
{
char cmd[LINE_LEN];
int f, pos;
size_t len;
if (arg->argn >= arg->argc)
return -1;
for (f = arg->argn, pos = 0; f < arg->argc; f++) {
len = strlen(arg->argv[f]);
if (sizeof cmd - pos < len + (len ? 1 : 0))
break;
if (len)
cmd[pos++] = ' ';
strcpy(cmd + pos, arg->argv[f]);
pos += len;
}
return PacketAliasProxyRule(cmd);
}
2000-03-31 14:26:23 +00:00
int
nat_SetTarget(struct cmdargs const *arg)
{
struct in_addr addr;
if (arg->argc == arg->argn) {
addr.s_addr = INADDR_ANY;
2000-03-31 14:26:23 +00:00
PacketAliasSetTarget(addr);
return 0;
}
if (arg->argc != arg->argn + 1)
return -1;
2000-05-11 07:59:21 +00:00
if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) {
addr.s_addr = INADDR_ANY;
PacketAliasSetTarget(addr);
return 0;
}
2000-03-31 14:26:23 +00:00
addr = GetIpAddr(arg->argv[arg->argn]);
if (addr.s_addr == INADDR_NONE) {
log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
return 1;
}
PacketAliasSetTarget(addr);
return 0;
}
#ifndef NO_FW_PUNCH
int
nat_PunchFW(struct cmdargs const *arg)
{
char *end;
long base, count;
if (arg->argc == arg->argn) {
PacketAliasSetMode(0, PKT_ALIAS_PUNCH_FW);
return 0;
}
if (arg->argc != arg->argn + 2)
return -1;
base = strtol(arg->argv[arg->argn], &end, 10);
if (*end != '\0' || base < 0)
return -1;
count = strtol(arg->argv[arg->argn + 1], &end, 10);
if (*end != '\0' || count < 0)
return -1;
PacketAliasSetFWBase(base, count);
PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
return 0;
}
#endif
int
nat_SkinnyPort(struct cmdargs const *arg)
{
char *end;
long port;
if (arg->argc == arg->argn) {
PacketAliasSetSkinnyPort(0);
return 0;
}
if (arg->argc != arg->argn + 1)
return -1;
port = strtol(arg->argv[arg->argn], &end, 10);
if (*end != '\0' || port < 0)
return -1;
PacketAliasSetSkinnyPort(port);
return 0;
}
static struct mbuf *
nat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
int pri, u_short *proto)
{
if (!bundle->NatEnabled || *proto != PROTO_IP)
return bp;
Allow ``host:port/udp'' devices and support ``host:port/tcp'' as being the same as the previous (still supported) ``host:port'' syntax for tcp socket devices. A udp device uses synchronous ppp rather than async, and avoids the double-retransmit overhead that comes with ppp over tcp (it's usually a bad idea to transport IP over a reliable transport that itself is using an unreliable transport). PPP over UDP provides througput of ** 1.5Mb per second ** with all compression disabled, maxing out a PPro/200 when running ppp twice, back-to-back. This proves that PPPoE is plausable in userland.... This change adds a few more handler functions to struct device and allows derivations of struct device (which may contain their own data etc) to pass themselves through the unix domain socket for MP. ** At last **, struct physical has lost all the tty crud ! iov2physical() is now smart enough to restore the correct stack of layers so that MP servers will work again. The version number has bumped as our MP link transfer contents have changed (they now may contain a `struct device'). Don't extract the protocol twice in MP mode (resulting in protocol rejects for every MP packet). This was broken with my original layering changes. Add ``Physical'' and ``Sync'' log levels for logging the relevent raw packets and add protocol-tracking LogDEBUG stuff in various LayerPush & LayerPull functions. Assign our physical device name for incoming tcp connections by calling getpeername(). Assign our physical device name for incoming udp connections from the address retrieved by the first recvfrom().
1999-05-12 09:49:12 +00:00
log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
m_settype(bp, MB_NATOUT);
/* Ensure there's a bit of extra buffer for the NAT code... */
bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
PacketAliasOut(MBUF_CTOP(bp), bp->m_len);
bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
return bp;
}
static struct mbuf *
nat_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
u_short *proto)
{
static int gfrags;
int ret, len, nfrags;
struct mbuf **last;
char *fptr;
if (!bundle->NatEnabled || *proto != PROTO_IP)
return bp;
log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
m_settype(bp, MB_NATIN);
/* Ensure there's a bit of extra buffer for the NAT code... */
bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
ret = PacketAliasIn(MBUF_CTOP(bp), bp->m_len);
bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
if (bp->m_len > MAX_MRU) {
2000-03-14 01:47:19 +00:00
log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
(unsigned long)bp->m_len);
m_freem(bp);
return NULL;
}
switch (ret) {
case PKT_ALIAS_OK:
break;
case PKT_ALIAS_UNRESOLVED_FRAGMENT:
/* Save the data for later */
fptr = malloc(bp->m_len);
bp = mbuf_Read(bp, fptr, bp->m_len);
PacketAliasSaveFragment(fptr);
log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
(unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
break;
case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
/* Fetch all the saved fragments and chain them on the end of `bp' */
last = &bp->m_nextpkt;
nfrags = 0;
while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
nfrags++;
PacketAliasFragmentIn(MBUF_CTOP(bp), fptr);
len = ntohs(((struct ip *)fptr)->ip_len);
*last = m_get(len, MB_NATIN);
memcpy(MBUF_CTOP(*last), fptr, len);
free(fptr);
last = &(*last)->m_nextpkt;
}
gfrags -= nfrags;
log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
"w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
nfrags, gfrags);
break;
case PKT_ALIAS_IGNORED:
if (PacketAliasSetMode(0, 0) & PKT_ALIAS_DENY_INCOMING) {
log_Printf(LogTCPIP, "NAT engine denied data:\n");
m_freem(bp);
bp = NULL;
} else if (log_IsKept(LogTCPIP)) {
log_Printf(LogTCPIP, "NAT engine ignored data:\n");
PacketCheck(bundle, AF_INET, MBUF_CTOP(bp), bp->m_len, NULL,
NULL, NULL);
}
break;
default:
log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
m_freem(bp);
bp = NULL;
break;
}
return bp;
}
struct layer natlayer =
{ LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };