diff options
Diffstat (limited to 'src/nat_cmd.c')
-rw-r--r-- | src/nat_cmd.c | 600 |
1 files changed, 600 insertions, 0 deletions
diff --git a/src/nat_cmd.c b/src/nat_cmd.c new file mode 100644 index 0000000..d94a59a --- /dev/null +++ b/src/nat_cmd.c @@ -0,0 +1,600 @@ +/*- + * 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: src/usr.sbin/ppp/nat_cmd.c,v 1.62.10.1.4.1 2010/12/21 17:10:29 kensmith Exp $ + */ + +#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; + } + + do { + 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++; + if (hremoteport) + lremoteport++; + } while (laliasport++ < haliasport); + + 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; + unsigned 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 __unused, struct mbuf *bp, + int pri __unused, 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 __unused, 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 */ + if ((fptr = malloc(bp->m_len)) == NULL) { + log_Printf(LogWARN, "nat_LayerPull: Dropped unresolved fragment -" + " out of memory!\n"); + m_freem(bp); + bp = NULL; + } else { + 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 }; |