mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-17 10:26:15 +00:00
b07fbc17e9
Skinny is the protocol used by Cisco IP phones to talk to Cisco Call Managers. With this code, one can use a Cisco IP phone behind a FreeBSD NAT gateway. Currently, having the Call Manager behind the NAT gateway is not supported. More information on enabling Skinny support in libalias, natd, and ppp can be found in those applications' manpages. PR: 55843 Reviewed by: ru Approved by: ru MFC after: 30 days
595 lines
15 KiB
C
595 lines
15 KiB
C
/*-
|
|
* 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.
|
|
*
|
|
* $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
|
|
#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
|
|
|
|
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;
|
|
|
|
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 };
|