30949fd4b5
structures (well, they're treated as opaque). It's now possible to manage IPv6 interface addresses and routing table entries and to filter IPV6 traffic whether encapsulated or not. IPV6CP support is crude for now, and hasn't been tested against any other implementations. RADIUS and IPv6 are independent of eachother for now. ppp.linkup/ppp.linkdown aren't currently used by IPV6CP o Understand all protocols(5) in filter rules rather than only a select few. o Allow a mask specification for the ``delete'' command. It's now possible to specifically delete one of two conflicting routes. o When creating and deleting proxy arp entries, do it for all IPv4 interface addresses rather than doing it just for the ``current'' peer address. o When iface-alias isn't in effect, don't blow away manually (via ``iface add'') added interface addresses. o When listening on a tcp server (diagnostic) socket, bind so that a tcp46 socket is created -- allowing both IPv4 and IPv6 connections. o When displaying ICMP traffic, don't display the icmp type twice. When display traffic, display at least some information about unrecognised traffic. o Bump version Inspired after filtering work by: Makoto MATSUSHITA <matusita@jp.FreeBSD.org>
571 lines
15 KiB
C
571 lines
15 KiB
C
/*-
|
|
* Copyright (c) 2001 Charles Mott <cmott@scientech.com>
|
|
* 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.
|
|
*
|
|
* $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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <termios.h>
|
|
|
|
#ifdef LOCALNAT
|
|
#include "alias.h"
|
|
#else
|
|
#include <alias.h>
|
|
#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 *);
|
|
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");
|
|
return -1;
|
|
}
|
|
|
|
error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
|
|
&hlocalport, proto);
|
|
if (error) {
|
|
prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
|
|
return -1;
|
|
}
|
|
|
|
error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
|
|
proto);
|
|
if (error) {
|
|
prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
|
|
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;
|
|
}
|
|
|
|
lowhigh(&llocalport, &hlocalport);
|
|
lowhigh(&laliasport, &haliasport);
|
|
lowhigh(&lremoteport, &hremoteport);
|
|
|
|
if (haliasport - laliasport != hlocalport - llocalport) {
|
|
prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
|
|
"are not equal\n");
|
|
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),
|
|
proto_constant);
|
|
|
|
if (link == NULL) {
|
|
prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
|
|
error);
|
|
return 1;
|
|
}
|
|
llocalport++;
|
|
laliasport++;
|
|
if (hremoteport)
|
|
lremoteport++;
|
|
}
|
|
|
|
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;
|
|
|
|
*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);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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
|
|
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;
|
|
|
|
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);
|
|
}
|
|
|
|
int
|
|
nat_SetTarget(struct cmdargs const *arg)
|
|
{
|
|
struct in_addr addr;
|
|
|
|
if (arg->argc == arg->argn) {
|
|
addr.s_addr = INADDR_ANY;
|
|
PacketAliasSetTarget(addr);
|
|
return 0;
|
|
}
|
|
|
|
if (arg->argc != arg->argn + 1)
|
|
return -1;
|
|
|
|
if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) {
|
|
addr.s_addr = INADDR_ANY;
|
|
PacketAliasSetTarget(addr);
|
|
return 0;
|
|
}
|
|
|
|
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
|
|
|
|
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;
|
|
|
|
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) {
|
|
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 };
|