summaryrefslogtreecommitdiff
path: root/src/nat_cmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nat_cmd.c')
-rw-r--r--src/nat_cmd.c600
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 };