mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-31 04:45:39 +01:00
ipfw: add ability to run ipfw(8) binary with 15.0+ kernel module
After D46183 the KBI was changed and this made the upgrade procedure
to 15.0+ version a bit difficult, because the old binary can not load
firewall rules when the new kernel is loaded.
This commit adds the sbin/ipfw15 binary that uses new KBI, and then
original sbin/ipfw can detect new KBI and run the new binary instead.
PR: 291562
Reviewed by: jhb, glebius
Fixes: 4a77657cbc
MFC after: 3 days
Differential Revision: https://reviews.freebsd.org/D54763
This commit is contained in:
1 parent
30c04399f2
commit
704ec5e68c
23 files changed
+24049
No files matched your search
@@ -78,6 +78,7 @@ SUBDIR.${MK_HAST}+= hastd
|
||||
SUBDIR.${MK_INET6}+= rtsol
|
||||
SUBDIR.${MK_IPFILTER}+= ipf
|
||||
SUBDIR.${MK_IPFW}+= ipfw
|
||||
SUBDIR.${MK_IPFW}+= ipfw15
|
||||
SUBDIR.${MK_IPFW}+= natd
|
||||
SUBDIR.${MK_OPENSSL}+= decryptcore
|
||||
SUBDIR.${MK_PF}+= pfctl
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
* Command line interface for IP firewall facility
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
@@ -30,6 +31,8 @@
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include <osreldate.h>
|
||||
|
||||
#include "ipfw2.h"
|
||||
|
||||
static void
|
||||
@@ -690,6 +693,29 @@ main(int ac, char *av[])
|
||||
else
|
||||
g_co.prog = cmdline_prog_ipfw;
|
||||
|
||||
/*
|
||||
* KBI-incompatibility detected, check for availability of ipfw/dnctl15
|
||||
* binaries and run them instead
|
||||
*/
|
||||
if (getosreldate() >= 1500000) {
|
||||
const char *releng15_progname;
|
||||
int ret;
|
||||
|
||||
if (g_co.prog == cmdline_prog_ipfw)
|
||||
releng15_progname = "/sbin/ipfw15";
|
||||
else
|
||||
releng15_progname = "/sbin/dnctl15";
|
||||
|
||||
printf("WARNING! KBI incompatibility for ipfw is detected,"
|
||||
" trying to run %s.\n", releng15_progname);
|
||||
|
||||
if ((ret = execv(releng15_progname, av)) < 0) {
|
||||
printf("execv(%s) error: %s\n", releng15_progname,
|
||||
strerror(errno));
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the last argument is an absolute pathname, interpret it
|
||||
* as a file to be preprocessed.
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
.include <src.opts.mk>
|
||||
|
||||
PACKAGE=ipfw15
|
||||
PROG= ipfw15
|
||||
|
||||
CFLAGS+= -I ${.CURDIR}/include15
|
||||
|
||||
LINKS= ${BINDIR}/ipfw15 ${BINDIR}/dnctl15
|
||||
MK_MAN= no
|
||||
|
||||
SRCS= ipfw2.c dummynet.c ipv6.c main.c nat.c tables.c
|
||||
SRCS+= nat64clat.c nat64lsn.c nat64stl.c nptv6.c
|
||||
|
||||
.if ${MK_PF} != "no"
|
||||
SRCS+= altq.c
|
||||
CFLAGS+=-DPF
|
||||
.endif
|
||||
|
||||
LIBADD= jail util
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
||||
CWARNFLAGS+= -Wno-cast-align
|
||||
@@ -0,0 +1,19 @@
|
||||
# Autogenerated - do NOT edit!
|
||||
|
||||
DIRDEPS = \
|
||||
include \
|
||||
include/arpa \
|
||||
include/xlocale \
|
||||
lib/${CSU_DIR} \
|
||||
lib/libalias/libalias \
|
||||
lib/libc \
|
||||
lib/libcompiler_rt \
|
||||
lib/libjail \
|
||||
lib/libutil \
|
||||
|
||||
|
||||
.include <dirdeps.mk>
|
||||
|
||||
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
|
||||
# local dependencies - needed for -jN in clean tree
|
||||
.endif
|
||||
@@ -0,0 +1,152 @@
|
||||
/*-
|
||||
* Copyright (c) 2002-2003 Luigi Rizzo
|
||||
* Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
|
||||
* Copyright (c) 1994 Ugen J.S.Antsilevich
|
||||
*
|
||||
* Idea and grammar partially left from:
|
||||
* Copyright (c) 1993 Daniel Boulet
|
||||
*
|
||||
* Redistribution and use in source forms, with and without modification,
|
||||
* are permitted provided that this entire comment appears intact.
|
||||
*
|
||||
* Redistribution in binary form may occur without any restrictions.
|
||||
* Obviously, it would be nice if you gave credit where credit is due
|
||||
* but requiring it would be too onerous.
|
||||
*
|
||||
* This software is provided ``AS IS'' without any warranties of any kind.
|
||||
*
|
||||
* NEW command line interface for IP firewall facility
|
||||
*
|
||||
* altq interface
|
||||
*/
|
||||
|
||||
#define PFIOC_USE_LATEST
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
|
||||
#include "ipfw2.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <net/if.h> /* IFNAMSIZ */
|
||||
#include <net/pfvar.h>
|
||||
#include <netinet/in.h> /* in_addr */
|
||||
#include <netinet/ip_fw15.h>
|
||||
|
||||
/*
|
||||
* Map between current altq queue id numbers and names.
|
||||
*/
|
||||
static TAILQ_HEAD(, pf_altq) altq_entries =
|
||||
TAILQ_HEAD_INITIALIZER(altq_entries);
|
||||
|
||||
void
|
||||
altq_set_enabled(int enabled)
|
||||
{
|
||||
int pffd;
|
||||
|
||||
pffd = open("/dev/pf", O_RDWR);
|
||||
if (pffd == -1)
|
||||
err(EX_UNAVAILABLE,
|
||||
"altq support opening pf(4) control device");
|
||||
if (enabled) {
|
||||
if (ioctl(pffd, DIOCSTARTALTQ) != 0 && errno != EEXIST)
|
||||
err(EX_UNAVAILABLE, "enabling altq");
|
||||
} else {
|
||||
if (ioctl(pffd, DIOCSTOPALTQ) != 0 && errno != ENOENT)
|
||||
err(EX_UNAVAILABLE, "disabling altq");
|
||||
}
|
||||
close(pffd);
|
||||
}
|
||||
|
||||
static void
|
||||
altq_fetch(void)
|
||||
{
|
||||
struct pfioc_altq pfioc;
|
||||
struct pf_altq *altq;
|
||||
int pffd;
|
||||
unsigned int mnr;
|
||||
static int altq_fetched = 0;
|
||||
|
||||
if (altq_fetched)
|
||||
return;
|
||||
altq_fetched = 1;
|
||||
pffd = open("/dev/pf", O_RDONLY);
|
||||
if (pffd == -1) {
|
||||
warn("altq support opening pf(4) control device");
|
||||
return;
|
||||
}
|
||||
bzero(&pfioc, sizeof(pfioc));
|
||||
pfioc.version = PFIOC_ALTQ_VERSION;
|
||||
if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) {
|
||||
warn("altq support getting queue list");
|
||||
close(pffd);
|
||||
return;
|
||||
}
|
||||
mnr = pfioc.nr;
|
||||
for (pfioc.nr = 0; pfioc.nr < mnr; pfioc.nr++) {
|
||||
if (ioctl(pffd, DIOCGETALTQ, &pfioc) != 0) {
|
||||
if (errno == EBUSY)
|
||||
break;
|
||||
warn("altq support getting queue list");
|
||||
close(pffd);
|
||||
return;
|
||||
}
|
||||
if (pfioc.altq.qid == 0)
|
||||
continue;
|
||||
altq = safe_calloc(1, sizeof(*altq));
|
||||
*altq = pfioc.altq;
|
||||
TAILQ_INSERT_TAIL(&altq_entries, altq, entries);
|
||||
}
|
||||
close(pffd);
|
||||
}
|
||||
|
||||
u_int32_t
|
||||
altq_name_to_qid(const char *name)
|
||||
{
|
||||
struct pf_altq *altq;
|
||||
|
||||
altq_fetch();
|
||||
TAILQ_FOREACH(altq, &altq_entries, entries)
|
||||
if (strcmp(name, altq->qname) == 0)
|
||||
break;
|
||||
if (altq == NULL)
|
||||
errx(EX_DATAERR, "altq has no queue named `%s'", name);
|
||||
return altq->qid;
|
||||
}
|
||||
|
||||
static const char *
|
||||
altq_qid_to_name(u_int32_t qid)
|
||||
{
|
||||
struct pf_altq *altq;
|
||||
|
||||
altq_fetch();
|
||||
TAILQ_FOREACH(altq, &altq_entries, entries)
|
||||
if (qid == altq->qid)
|
||||
break;
|
||||
if (altq == NULL)
|
||||
return NULL;
|
||||
return altq->qname;
|
||||
}
|
||||
|
||||
void
|
||||
print_altq_cmd(struct buf_pr *bp, const ipfw_insn_altq *altqptr)
|
||||
{
|
||||
if (altqptr) {
|
||||
const char *qname;
|
||||
|
||||
qname = altq_qid_to_name(altqptr->qid);
|
||||
if (qname == NULL)
|
||||
bprintf(bp, " altq ?<%u>", altqptr->qid);
|
||||
else
|
||||
bprintf(bp, " altq %s", qname);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,259 @@
|
||||
/* lint -save -library Flexelint comment for external headers */
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2001 Charles Mott <cm@linktel.net>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Alias.h defines the outside world interfaces for the packet aliasing
|
||||
* software.
|
||||
*
|
||||
* This software is placed into the public domain with no restrictions on its
|
||||
* distribution.
|
||||
*/
|
||||
|
||||
#ifndef _ALIAS_H_
|
||||
#define _ALIAS_H_
|
||||
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
|
||||
#define LIBALIAS_BUF_SIZE 128
|
||||
#ifdef _KERNEL
|
||||
/*
|
||||
* The kernel version of libalias does not support these features.
|
||||
*/
|
||||
#define NO_FW_PUNCH
|
||||
#define NO_USE_SOCKETS
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The external interface to libalias, the packet aliasing engine.
|
||||
*
|
||||
* There are two sets of functions:
|
||||
*
|
||||
* PacketAlias*() the old API which doesn't take an instance pointer
|
||||
* and therefore can only have one packet engine at a time.
|
||||
*
|
||||
* LibAlias*() the new API which takes as first argument a pointer to
|
||||
* the instance of the packet aliasing engine.
|
||||
*
|
||||
* The functions otherwise correspond to each other one for one, except
|
||||
* for the LibAliasUnaliasOut()/PacketUnaliasOut() function which were
|
||||
* were misnamed in the old API.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The instance structure
|
||||
*/
|
||||
struct libalias;
|
||||
|
||||
/*
|
||||
* An anonymous structure, a pointer to which is returned from
|
||||
* PacketAliasRedirectAddr(), PacketAliasRedirectPort() or
|
||||
* PacketAliasRedirectProto(), passed to PacketAliasAddServer(),
|
||||
* and freed by PacketAliasRedirectDelete().
|
||||
*/
|
||||
struct alias_link;
|
||||
|
||||
/* Initialization and control functions. */
|
||||
struct libalias *LibAliasInit(struct libalias *);
|
||||
void LibAliasSetAddress(struct libalias *, struct in_addr _addr);
|
||||
void LibAliasSetAliasPortRange(struct libalias *la, u_short port_low, u_short port_hi);
|
||||
void LibAliasSetFWBase(struct libalias *, unsigned int _base, unsigned int _num);
|
||||
void LibAliasSetSkinnyPort(struct libalias *, unsigned int _port);
|
||||
unsigned int LibAliasSetMode(struct libalias *, unsigned int _flags, unsigned int _mask);
|
||||
void LibAliasUninit(struct libalias *);
|
||||
|
||||
/* Packet Handling functions. */
|
||||
int LibAliasIn (struct libalias *, void *_ptr, int _maxpacketsize);
|
||||
int LibAliasOut(struct libalias *, void *_ptr, int _maxpacketsize);
|
||||
int LibAliasOutTry(struct libalias *, void *_ptr, int _maxpacketsize, int _create);
|
||||
int LibAliasUnaliasOut(struct libalias *, void *_ptr, int _maxpacketsize);
|
||||
|
||||
/* Port and address redirection functions. */
|
||||
|
||||
int LibAliasAddServer(struct libalias *, struct alias_link *_lnk,
|
||||
struct in_addr _addr, unsigned short _port);
|
||||
struct alias_link * LibAliasRedirectAddr(struct libalias *, struct in_addr _src_addr,
|
||||
struct in_addr _alias_addr);
|
||||
int LibAliasRedirectDynamic(struct libalias *, struct alias_link *_lnk);
|
||||
void LibAliasRedirectDelete(struct libalias *, struct alias_link *_lnk);
|
||||
struct alias_link * LibAliasRedirectPort(struct libalias *, struct in_addr _src_addr,
|
||||
unsigned short _src_port, struct in_addr _dst_addr,
|
||||
unsigned short _dst_port, struct in_addr _alias_addr,
|
||||
unsigned short _alias_port, unsigned char _proto);
|
||||
struct alias_link * LibAliasRedirectProto(struct libalias *, struct in_addr _src_addr,
|
||||
struct in_addr _dst_addr, struct in_addr _alias_addr,
|
||||
unsigned char _proto);
|
||||
|
||||
/* Fragment Handling functions. */
|
||||
void LibAliasFragmentIn(struct libalias *, void *_ptr, void *_ptr_fragment);
|
||||
void *LibAliasGetFragment(struct libalias *, void *_ptr);
|
||||
int LibAliasSaveFragment(struct libalias *, void *_ptr);
|
||||
|
||||
/* Miscellaneous functions. */
|
||||
unsigned short LibAliasInternetChecksum(struct libalias *, unsigned short *_ptr, int _nbytes);
|
||||
void LibAliasSetTarget(struct libalias *, struct in_addr _target_addr);
|
||||
|
||||
/* Transparent proxying routines. */
|
||||
int LibAliasProxyRule(struct libalias *, const char *_cmd);
|
||||
|
||||
/* Module handling API */
|
||||
int LibAliasLoadModule(char *);
|
||||
int LibAliasUnLoadAllModule(void);
|
||||
int LibAliasRefreshModules(void);
|
||||
|
||||
/* Mbuf helper function. */
|
||||
struct mbuf *m_megapullup(struct mbuf *, int);
|
||||
|
||||
/*
|
||||
* Mode flags and other constants.
|
||||
*/
|
||||
|
||||
/* Mode flags, set using PacketAliasSetMode() */
|
||||
|
||||
/*
|
||||
* If PKT_ALIAS_LOG is set, a message will be printed to /var/log/alias.log
|
||||
* every time a link is created or deleted. This is useful for debugging.
|
||||
*/
|
||||
#define PKT_ALIAS_LOG 0x01
|
||||
|
||||
/*
|
||||
* If PKT_ALIAS_DENY_INCOMING is set, then incoming connections (e.g. to ftp,
|
||||
* telnet or web servers will be prevented by the aliasing mechanism.
|
||||
*/
|
||||
#define PKT_ALIAS_DENY_INCOMING 0x02
|
||||
|
||||
/*
|
||||
* If PKT_ALIAS_SAME_PORTS is set, packets will be attempted sent from the
|
||||
* same port as they originated on. This allows e.g. rsh to work *99% of the
|
||||
* time*, but _not_ 100% (it will be slightly flakey instead of not working
|
||||
* at all). This mode bit is set by PacketAliasInit(), so it is a default
|
||||
* mode of operation.
|
||||
*/
|
||||
#define PKT_ALIAS_SAME_PORTS 0x04
|
||||
|
||||
/*
|
||||
* If PKT_ALIAS_USE_SOCKETS is set, then when partially specified links (e.g.
|
||||
* destination port and/or address is zero), the packet aliasing engine will
|
||||
* attempt to allocate a socket for the aliasing port it chooses. This will
|
||||
* avoid interference with the host machine. Fully specified links do not
|
||||
* require this. This bit is set after a call to PacketAliasInit(), so it is
|
||||
* a default mode of operation.
|
||||
*/
|
||||
#ifndef NO_USE_SOCKETS
|
||||
#define PKT_ALIAS_USE_SOCKETS 0x08
|
||||
#endif
|
||||
/*-
|
||||
* If PKT_ALIAS_UNREGISTERED_ONLY is set, then only packets with
|
||||
* unregistered source addresses will be aliased. Private
|
||||
* addresses are those in the following ranges:
|
||||
*
|
||||
* 10.0.0.0 -> 10.255.255.255
|
||||
* 172.16.0.0 -> 172.31.255.255
|
||||
* 192.168.0.0 -> 192.168.255.255
|
||||
*/
|
||||
#define PKT_ALIAS_UNREGISTERED_ONLY 0x10
|
||||
|
||||
/*
|
||||
* If PKT_ALIAS_RESET_ON_ADDR_CHANGE is set, then the table of dynamic
|
||||
* aliasing links will be reset whenever PacketAliasSetAddress() changes the
|
||||
* default aliasing address. If the default aliasing address is left
|
||||
* unchanged by this function call, then the table of dynamic aliasing links
|
||||
* will be left intact. This bit is set after a call to PacketAliasInit().
|
||||
*/
|
||||
#define PKT_ALIAS_RESET_ON_ADDR_CHANGE 0x20
|
||||
|
||||
/*
|
||||
* If PKT_ALIAS_PROXY_ONLY is set, then NAT will be disabled and only
|
||||
* transparent proxying is performed.
|
||||
*/
|
||||
#define PKT_ALIAS_PROXY_ONLY 0x40
|
||||
|
||||
/*
|
||||
* If PKT_ALIAS_REVERSE is set, the actions of PacketAliasIn() and
|
||||
* PacketAliasOut() are reversed.
|
||||
*/
|
||||
#define PKT_ALIAS_REVERSE 0x80
|
||||
|
||||
#ifndef NO_FW_PUNCH
|
||||
/*
|
||||
* If PKT_ALIAS_PUNCH_FW is set, active FTP and IRC DCC connections will
|
||||
* create a 'hole' in the firewall to allow the transfers to work. The
|
||||
* ipfw rule number that the hole is created with is controlled by
|
||||
* PacketAliasSetFWBase(). The hole will be attached to that
|
||||
* particular alias_link, so when the link goes away the hole is deleted.
|
||||
*/
|
||||
#define PKT_ALIAS_PUNCH_FW 0x100
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If PKT_ALIAS_SKIP_GLOBAL is set, nat instance is not checked for matching
|
||||
* states in 'ipfw nat global' rule.
|
||||
*/
|
||||
#define PKT_ALIAS_SKIP_GLOBAL 0x200
|
||||
|
||||
/*
|
||||
* Like PKT_ALIAS_UNREGISTERED_ONLY, but includes the RFC 6598
|
||||
* (Carrier Grade NAT) address range as follows:
|
||||
*
|
||||
* 100.64.0.0 -> 100.127.255.255
|
||||
*/
|
||||
#define PKT_ALIAS_UNREGISTERED_CGN 0x400
|
||||
|
||||
/*
|
||||
* When this bit is set, UDP uses endpoint-independent mapping (EIM), as per
|
||||
* RFC 4787 ("full cone" NAT of RFC 3489). All packets from the same internal
|
||||
* address:port are mapped to the same NAT address:port, regardless of their
|
||||
* destination address:port. If filtering rules allow, and if
|
||||
* PKT_ALIAS_DENY_INCOMING is unset, any other external address:port can also
|
||||
* send to the internal address:port through its mapped NAT address:port. This
|
||||
* is more compatible with applications, and can reduce the need for port
|
||||
* forwarding, but less scalable as each NAT address:port can only be
|
||||
* concurrently used by at most one internal address:port.
|
||||
*
|
||||
* When this bit is unset, UDP packets use endpoint-dependent mapping (EDM)
|
||||
* ("symmetric" NAT). Each connection from a particular internal address:port
|
||||
* to different external addresses:ports is mapped to a random and
|
||||
* unpredictable NAT address:port. Two appplications behind EDM NATs can only
|
||||
* connect to each other by port forwarding on the NAT, or tunnelling through
|
||||
* an in-between server.
|
||||
*/
|
||||
#define PKT_ALIAS_UDP_EIM 0x800
|
||||
|
||||
/* Function return codes. */
|
||||
#define PKT_ALIAS_ERROR -1
|
||||
#define PKT_ALIAS_OK 1
|
||||
#define PKT_ALIAS_IGNORED 2
|
||||
#define PKT_ALIAS_UNRESOLVED_FRAGMENT 3
|
||||
#define PKT_ALIAS_FOUND_HEADER_FRAGMENT 4
|
||||
|
||||
#endif /* !_ALIAS_H_ */
|
||||
|
||||
/* lint -restore */
|
||||
@@ -0,0 +1,284 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 1998-2010 Luigi Rizzo, Universita` di Pisa
|
||||
* Portions Copyright (c) 2000 Akamba Corp.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _IP_DUMMYNET_H
|
||||
#define _IP_DUMMYNET_H
|
||||
#define NEW_AQM
|
||||
/*
|
||||
* Definition of the kernel-userland API for dummynet.
|
||||
*
|
||||
* Setsockopt() and getsockopt() pass a batch of objects, each
|
||||
* of them starting with a "struct dn_id" which should fully identify
|
||||
* the object and its relation with others in the sequence.
|
||||
* The first object in each request should have
|
||||
* type= DN_CMD_*, id = DN_API_VERSION.
|
||||
* For other objects, type and subtype specify the object, len indicates
|
||||
* the total length including the header, and 'id' identifies the specific
|
||||
* object.
|
||||
*
|
||||
* Most objects are numbered with an identifier in the range 1..65535.
|
||||
* DN_MAX_ID indicates the first value outside the range.
|
||||
*/
|
||||
|
||||
#define DN_API_VERSION 12500000
|
||||
#define DN_MAX_ID 0x10000
|
||||
|
||||
struct dn_id {
|
||||
uint16_t len; /* total obj len including this header */
|
||||
uint8_t type;
|
||||
uint8_t subtype;
|
||||
uint32_t id; /* generic id */
|
||||
};
|
||||
|
||||
/*
|
||||
* These values are in the type field of struct dn_id.
|
||||
* To preserve the ABI, never rearrange the list or delete
|
||||
* entries with the exception of DN_LAST
|
||||
*/
|
||||
enum {
|
||||
DN_NONE = 0,
|
||||
DN_LINK = 1,
|
||||
DN_FS,
|
||||
DN_SCH,
|
||||
DN_SCH_I,
|
||||
DN_QUEUE,
|
||||
DN_DELAY_LINE,
|
||||
DN_PROFILE,
|
||||
DN_FLOW, /* struct dn_flow */
|
||||
DN_TEXT, /* opaque text is the object */
|
||||
|
||||
DN_CMD_CONFIG = 0x80, /* objects follow */
|
||||
DN_CMD_DELETE, /* subtype + list of entries */
|
||||
DN_CMD_GET, /* subtype + list of entries */
|
||||
DN_CMD_FLUSH,
|
||||
/* for compatibility with FreeBSD 7.2/8 */
|
||||
DN_COMPAT_PIPE,
|
||||
DN_COMPAT_QUEUE,
|
||||
DN_GET_COMPAT,
|
||||
|
||||
/* special commands for emulation of sysctl variables */
|
||||
DN_SYSCTL_GET,
|
||||
DN_SYSCTL_SET,
|
||||
#ifdef NEW_AQM
|
||||
/* subtypes used for setting/getting extra parameters.
|
||||
* these subtypes used with IP_DUMMYNET3 command (get)
|
||||
* and DN_TEXT (set). */
|
||||
DN_AQM_PARAMS, /* AQM extra params */
|
||||
DN_SCH_PARAMS, /* scheduler extra params */
|
||||
#endif
|
||||
DN_LAST,
|
||||
};
|
||||
|
||||
enum { /* subtype for schedulers, flowset and the like */
|
||||
DN_SCHED_UNKNOWN = 0,
|
||||
DN_SCHED_FIFO = 1,
|
||||
DN_SCHED_WF2QP = 2,
|
||||
/* others are in individual modules */
|
||||
};
|
||||
|
||||
enum { /* user flags */
|
||||
DN_HAVE_MASK = 0x0001, /* fs or sched has a mask */
|
||||
DN_NOERROR = 0x0002, /* do not report errors */
|
||||
DN_QHT_HASH = 0x0004, /* qht is a hash table */
|
||||
DN_QSIZE_BYTES = 0x0008, /* queue size is in bytes */
|
||||
DN_HAS_PROFILE = 0x0010, /* a link has a profile */
|
||||
DN_IS_RED = 0x0020,
|
||||
DN_IS_GENTLE_RED= 0x0040,
|
||||
DN_IS_ECN = 0x0080,
|
||||
#ifdef NEW_AQM
|
||||
DN_IS_AQM = 0x0100, /* AQMs: e.g Codel & PIE */
|
||||
#endif
|
||||
DN_PIPE_CMD = 0x1000, /* pipe config... */
|
||||
};
|
||||
|
||||
/*
|
||||
* link template.
|
||||
*/
|
||||
struct dn_link {
|
||||
struct dn_id oid;
|
||||
|
||||
/*
|
||||
* Userland sets bw and delay in bits/s and milliseconds.
|
||||
* The kernel converts this back and forth to bits/tick and ticks.
|
||||
* XXX what about burst ?
|
||||
*/
|
||||
int32_t link_nr;
|
||||
uint32_t bandwidth; /* bit/s or bits/tick. */
|
||||
int delay; /* ms and ticks */
|
||||
uint64_t burst; /* scaled. bits*Hz XXX */
|
||||
};
|
||||
|
||||
/*
|
||||
* A flowset, which is a template for flows. Contains parameters
|
||||
* from the command line: id, target scheduler, queue sizes, plr,
|
||||
* flow masks, buckets for the flow hash, and possibly scheduler-
|
||||
* specific parameters (weight, quantum and so on).
|
||||
*/
|
||||
struct dn_fs {
|
||||
struct dn_id oid;
|
||||
uint32_t fs_nr; /* the flowset number */
|
||||
uint32_t flags; /* userland flags */
|
||||
int qsize; /* queue size in slots or bytes */
|
||||
int32_t pl_state; /* packet loss state */
|
||||
uint32_t buckets; /* buckets used for the queue hash table */
|
||||
|
||||
struct ipfw_flow_id flow_mask;
|
||||
uint32_t sched_nr; /* the scheduler we attach to */
|
||||
/* generic scheduler parameters. Leave them at -1 if unset.
|
||||
* Now we use 0: weight, 1: lmax, 2: priority
|
||||
*/
|
||||
int par[4];
|
||||
|
||||
/* RED/GRED parameters.
|
||||
* weight and probabilities are in the range 0..1 represented
|
||||
* in fixed point arithmetic with SCALE_RED decimal bits.
|
||||
*/
|
||||
#define SCALE_RED 16
|
||||
#define SCALE(x) ( (x) << SCALE_RED )
|
||||
#define SCALE_VAL(x) ( (x) >> SCALE_RED )
|
||||
#define SCALE_MUL(x,y) ( ( (x) * (y) ) >> SCALE_RED )
|
||||
int w_q ; /* queue weight (scaled) */
|
||||
int max_th ; /* maximum threshold for queue (scaled) */
|
||||
int min_th ; /* minimum threshold for queue (scaled) */
|
||||
int max_p ; /* maximum value for p_b (scaled) */
|
||||
|
||||
int32_t plr[4]; /* PLR, pkt loss rate (2^31-1 means 100%) */
|
||||
};
|
||||
|
||||
/*
|
||||
* dn_flow collects flow_id and stats for queues and scheduler
|
||||
* instances, and is used to pass these info to userland.
|
||||
* oid.type/oid.subtype describe the object, oid.id is number
|
||||
* of the parent object.
|
||||
*/
|
||||
struct dn_flow {
|
||||
struct dn_id oid;
|
||||
struct ipfw_flow_id fid;
|
||||
uint64_t tot_pkts; /* statistics counters */
|
||||
uint64_t tot_bytes;
|
||||
uint32_t length; /* Queue length, in packets */
|
||||
uint32_t len_bytes; /* Queue length, in bytes */
|
||||
uint32_t drops;
|
||||
};
|
||||
|
||||
/*
|
||||
* Scheduler template, mostly indicating the name, number,
|
||||
* sched_mask and buckets.
|
||||
*/
|
||||
struct dn_sch {
|
||||
struct dn_id oid;
|
||||
uint32_t sched_nr; /* N, scheduler number */
|
||||
uint32_t buckets; /* number of buckets for the instances */
|
||||
uint32_t flags; /* have_mask, ... */
|
||||
|
||||
char name[16]; /* null terminated */
|
||||
/* mask to select the appropriate scheduler instance */
|
||||
struct ipfw_flow_id sched_mask; /* M */
|
||||
};
|
||||
|
||||
/* A delay profile is attached to a link.
|
||||
* Note that a profile, as any other object, cannot be longer than 2^16
|
||||
*/
|
||||
#define ED_MAX_SAMPLES_NO 1024
|
||||
struct dn_profile {
|
||||
struct dn_id oid;
|
||||
/* fields to simulate a delay profile */
|
||||
#define ED_MAX_NAME_LEN 32
|
||||
char name[ED_MAX_NAME_LEN];
|
||||
int link_nr;
|
||||
int loss_level;
|
||||
uint32_t bandwidth; // XXX use link bandwidth?
|
||||
int samples_no; /* actual len of samples[] */
|
||||
int samples[ED_MAX_SAMPLES_NO]; /* may be shorter */
|
||||
};
|
||||
|
||||
#ifdef NEW_AQM
|
||||
/* Extra parameters for AQM and scheduler.
|
||||
* This struct is used to pass and retrieve parameters (configurations)
|
||||
* to/from AQM and Scheduler.
|
||||
*/
|
||||
struct dn_extra_parms {
|
||||
struct dn_id oid;
|
||||
char name[16];
|
||||
uint32_t nr;
|
||||
#define DN_MAX_EXTRA_PARM 10
|
||||
int64_t par[DN_MAX_EXTRA_PARM];
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Overall structure of dummynet
|
||||
|
||||
In dummynet, packets are selected with the firewall rules, and passed
|
||||
to two different objects: PIPE or QUEUE (bad name).
|
||||
|
||||
A QUEUE defines a classifier, which groups packets into flows
|
||||
according to a 'mask', puts them into independent queues (one
|
||||
per flow) with configurable size and queue management policy,
|
||||
and passes flows to a scheduler:
|
||||
|
||||
(flow_mask|sched_mask) sched_mask
|
||||
+---------+ weight Wx +-------------+
|
||||
| |->-[flow]-->--| |-+
|
||||
-->--| QUEUE x | ... | | |
|
||||
| |->-[flow]-->--| SCHEDuler N | |
|
||||
+---------+ | | |
|
||||
... | +--[LINK N]-->--
|
||||
+---------+ weight Wy | | +--[LINK N]-->--
|
||||
| |->-[flow]-->--| | |
|
||||
-->--| QUEUE y | ... | | |
|
||||
| |->-[flow]-->--| | |
|
||||
+---------+ +-------------+ |
|
||||
+-------------+
|
||||
|
||||
Many QUEUE objects can connect to the same scheduler, each
|
||||
QUEUE object can have its own set of parameters.
|
||||
|
||||
In turn, the SCHEDuler 'forks' multiple instances according
|
||||
to a 'sched_mask', each instance manages its own set of queues
|
||||
and transmits on a private instance of a configurable LINK.
|
||||
|
||||
A PIPE is a simplified version of the above, where there
|
||||
is no flow_mask, and each scheduler instance handles a single queue.
|
||||
|
||||
The following data structures (visible from userland) describe
|
||||
the objects used by dummynet:
|
||||
|
||||
+ dn_link, contains the main configuration parameters related
|
||||
to delay and bandwidth;
|
||||
+ dn_profile describes a delay profile;
|
||||
+ dn_flow describes the flow status (flow id, statistics)
|
||||
|
||||
+ dn_sch describes a scheduler
|
||||
+ dn_fs describes a flowset (msk, weight, queue parameters)
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
#endif /* _IP_DUMMYNET_H */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,212 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2015-2019 Yandex LLC
|
||||
* Copyright (c) 2015 Alexander V. Chernikov <melifaro@FreeBSD.org>
|
||||
* Copyright (c) 2015-2019 Andrey V. Elsukov <ae@FreeBSD.org>
|
||||
*
|
||||
* 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 ``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 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.
|
||||
*/
|
||||
|
||||
#ifndef _NETINET6_IP_FW_NAT64_H_
|
||||
#define _NETINET6_IP_FW_NAT64_H_
|
||||
|
||||
struct ipfw_nat64stl_stats {
|
||||
uint64_t opcnt64; /* 6to4 of packets translated */
|
||||
uint64_t opcnt46; /* 4to6 of packets translated */
|
||||
uint64_t ofrags; /* number of fragments generated */
|
||||
uint64_t ifrags; /* number of fragments received */
|
||||
uint64_t oerrors; /* number of output errors */
|
||||
uint64_t noroute4;
|
||||
uint64_t noroute6;
|
||||
uint64_t noproto; /* Protocol not supported */
|
||||
uint64_t nomem; /* mbuf allocation failed */
|
||||
uint64_t dropped; /* dropped due to some errors */
|
||||
};
|
||||
|
||||
struct ipfw_nat64clat_stats {
|
||||
uint64_t opcnt64; /* 6to4 of packets translated */
|
||||
uint64_t opcnt46; /* 4to6 of packets translated */
|
||||
uint64_t ofrags; /* number of fragments generated */
|
||||
uint64_t ifrags; /* number of fragments received */
|
||||
uint64_t oerrors; /* number of output errors */
|
||||
uint64_t noroute4;
|
||||
uint64_t noroute6;
|
||||
uint64_t noproto; /* Protocol not supported */
|
||||
uint64_t nomem; /* mbuf allocation failed */
|
||||
uint64_t dropped; /* dropped due to some errors */
|
||||
};
|
||||
|
||||
struct ipfw_nat64lsn_stats {
|
||||
uint64_t opcnt64; /* 6to4 of packets translated */
|
||||
uint64_t opcnt46; /* 4to6 of packets translated */
|
||||
uint64_t ofrags; /* number of fragments generated */
|
||||
uint64_t ifrags; /* number of fragments received */
|
||||
uint64_t oerrors; /* number of output errors */
|
||||
uint64_t noroute4;
|
||||
uint64_t noroute6;
|
||||
uint64_t noproto; /* Protocol not supported */
|
||||
uint64_t nomem; /* mbuf allocation failed */
|
||||
uint64_t dropped; /* dropped due to some errors */
|
||||
|
||||
uint64_t nomatch4; /* No addr/port match */
|
||||
uint64_t jcalls; /* Number of job handler calls */
|
||||
uint64_t jrequests; /* Number of job requests */
|
||||
uint64_t jhostsreq; /* Number of job host requests */
|
||||
uint64_t jportreq; /* Number of portgroup requests */
|
||||
uint64_t jhostfails; /* Number of failed host allocs */
|
||||
uint64_t jportfails; /* Number of failed portgroup allocs */
|
||||
uint64_t jreinjected; /* Number of packets reinjected to q */
|
||||
uint64_t jmaxlen; /* Max queue length reached */
|
||||
uint64_t jnomem; /* No memory to alloc queue item */
|
||||
|
||||
uint64_t screated; /* Number of states created */
|
||||
uint64_t sdeleted; /* Number of states deleted */
|
||||
uint64_t spgcreated; /* Number of portgroups created */
|
||||
uint64_t spgdeleted; /* Number of portgroups deleted */
|
||||
uint64_t hostcount; /* Number of hosts */
|
||||
uint64_t tcpchunks; /* Number of TCP portgroups */
|
||||
uint64_t udpchunks; /* Number of UDP portgroups */
|
||||
uint64_t icmpchunks; /* Number of ICMP portgroups */
|
||||
|
||||
uint64_t _reserved[4];
|
||||
};
|
||||
|
||||
#define NAT64_LOG 0x0001 /* Enable logging via BPF */
|
||||
#define NAT64_ALLOW_PRIVATE 0x0002 /* Allow private IPv4 address
|
||||
* translation
|
||||
*/
|
||||
#define NAT64LSN_ALLOW_SWAPCONF 0x0004 /* Allow configuration exchange
|
||||
* between NAT64LSN instances
|
||||
* during the sets swapping.
|
||||
*/
|
||||
typedef struct _ipfw_nat64stl_cfg {
|
||||
char name[64]; /* NAT name */
|
||||
ipfw_obj_ntlv ntlv6; /* object name tlv */
|
||||
ipfw_obj_ntlv ntlv4; /* object name tlv */
|
||||
struct in6_addr prefix6; /* NAT64 prefix */
|
||||
uint8_t plen6; /* Prefix length */
|
||||
uint8_t set; /* Named instance set [0..31] */
|
||||
uint8_t spare[2];
|
||||
uint32_t flags;
|
||||
} ipfw_nat64stl_cfg;
|
||||
|
||||
typedef struct _ipfw_nat64clat_cfg {
|
||||
char name[64]; /* NAT name */
|
||||
struct in6_addr plat_prefix; /* NAT64 (PLAT) prefix */
|
||||
struct in6_addr clat_prefix; /* Client (CLAT) prefix */
|
||||
uint8_t plat_plen; /* PLAT Prefix length */
|
||||
uint8_t clat_plen; /* CLAT Prefix length */
|
||||
uint8_t set; /* Named instance set [0..31] */
|
||||
uint8_t spare;
|
||||
uint32_t flags;
|
||||
} ipfw_nat64clat_cfg;
|
||||
|
||||
/*
|
||||
* NAT64LSN default configuration values
|
||||
*/
|
||||
#define NAT64LSN_MAX_PORTS 2048 /* Unused */
|
||||
#define NAT64LSN_JMAXLEN 2048 /* Max outstanding requests. */
|
||||
#define NAT64LSN_TCP_SYN_AGE 10 /* State's TTL after SYN received. */
|
||||
#define NAT64LSN_TCP_EST_AGE (2 * 3600) /* TTL for established connection */
|
||||
#define NAT64LSN_TCP_FIN_AGE 180 /* State's TTL after FIN/RST received */
|
||||
#define NAT64LSN_UDP_AGE 120 /* TTL for UDP states */
|
||||
#define NAT64LSN_ICMP_AGE 60 /* TTL for ICMP states */
|
||||
#define NAT64LSN_HOST_AGE 3600 /* TTL for stale host entry */
|
||||
#define NAT64LSN_PG_AGE 900 /* TTL for stale ports groups */
|
||||
|
||||
typedef struct _ipfw_nat64lsn_cfg {
|
||||
char name[64]; /* NAT name */
|
||||
uint32_t flags;
|
||||
|
||||
uint32_t max_ports; /* Unused */
|
||||
uint32_t agg_prefix_len; /* Unused */
|
||||
uint32_t agg_prefix_max; /* Unused */
|
||||
|
||||
struct in_addr prefix4;
|
||||
uint16_t plen4; /* Prefix length */
|
||||
uint16_t plen6; /* Prefix length */
|
||||
struct in6_addr prefix6; /* NAT64 prefix */
|
||||
uint32_t jmaxlen; /* Max jobqueue length */
|
||||
|
||||
uint16_t min_port; /* Unused */
|
||||
uint16_t max_port; /* Unused */
|
||||
|
||||
uint16_t nh_delete_delay;/* Stale host delete delay */
|
||||
uint16_t pg_delete_delay;/* Stale portgroup delete delay */
|
||||
uint16_t st_syn_ttl; /* TCP syn expire */
|
||||
uint16_t st_close_ttl; /* TCP fin expire */
|
||||
uint16_t st_estab_ttl; /* TCP established expire */
|
||||
uint16_t st_udp_ttl; /* UDP expire */
|
||||
uint16_t st_icmp_ttl; /* ICMP expire */
|
||||
uint8_t set; /* Named instance set [0..31] */
|
||||
uint8_t states_chunks; /* Number of states chunks per PG */
|
||||
} ipfw_nat64lsn_cfg;
|
||||
|
||||
typedef struct _ipfw_nat64lsn_state {
|
||||
struct in_addr daddr; /* Remote IPv4 address */
|
||||
uint16_t dport; /* Remote destination port */
|
||||
uint16_t aport; /* Local alias port */
|
||||
uint16_t sport; /* Source port */
|
||||
uint8_t flags; /* State flags */
|
||||
uint8_t spare[3];
|
||||
uint16_t idle; /* Last used time */
|
||||
} ipfw_nat64lsn_state;
|
||||
|
||||
typedef struct _ipfw_nat64lsn_stg {
|
||||
uint64_t next_idx; /* next state index */
|
||||
struct in_addr alias4; /* IPv4 alias address */
|
||||
uint8_t proto; /* protocol */
|
||||
uint8_t flags;
|
||||
uint16_t spare;
|
||||
struct in6_addr host6; /* Bound IPv6 host */
|
||||
uint32_t count; /* Number of states */
|
||||
uint32_t spare2;
|
||||
} ipfw_nat64lsn_stg;
|
||||
|
||||
typedef struct _ipfw_nat64lsn_state_v1 {
|
||||
struct in6_addr host6; /* Bound IPv6 host */
|
||||
struct in_addr daddr; /* Remote IPv4 address */
|
||||
uint16_t dport; /* Remote destination port */
|
||||
uint16_t aport; /* Local alias port */
|
||||
uint16_t sport; /* Source port */
|
||||
uint16_t spare;
|
||||
uint16_t idle; /* Last used time */
|
||||
uint8_t flags; /* State flags */
|
||||
uint8_t proto; /* protocol */
|
||||
} ipfw_nat64lsn_state_v1;
|
||||
|
||||
typedef struct _ipfw_nat64lsn_stg_v1 {
|
||||
union nat64lsn_pgidx {
|
||||
uint64_t index;
|
||||
struct {
|
||||
uint8_t chunk; /* states chunk */
|
||||
uint8_t proto; /* protocol */
|
||||
uint16_t port; /* base port */
|
||||
in_addr_t addr; /* alias address */
|
||||
};
|
||||
} next; /* next state index */
|
||||
struct in_addr alias4; /* IPv4 alias address */
|
||||
uint32_t count; /* Number of states */
|
||||
} ipfw_nat64lsn_stg_v1;
|
||||
|
||||
#endif /* _NETINET6_IP_FW_NAT64_H_ */
|
||||
@@ -0,0 +1,52 @@
|
||||
/*-
|
||||
* Copyright (c) 2016 Yandex LLC
|
||||
* Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.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 ``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 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.
|
||||
*/
|
||||
|
||||
#ifndef _NETINET6_IP_FW_NPTV6_H_
|
||||
#define _NETINET6_IP_FW_NPTV6_H_
|
||||
|
||||
struct ipfw_nptv6_stats {
|
||||
uint64_t in2ex; /* Int->Ext packets translated */
|
||||
uint64_t ex2in; /* Ext->Int packets translated */
|
||||
uint64_t dropped; /* dropped due to some errors */
|
||||
uint64_t reserved[5];
|
||||
};
|
||||
|
||||
typedef struct _ipfw_nptv6_cfg {
|
||||
char name[64]; /* NPTv6 instance name */
|
||||
struct in6_addr internal; /* NPTv6 internal prefix */
|
||||
union {
|
||||
struct in6_addr external; /* NPTv6 external prefix */
|
||||
char if_name[IF_NAMESIZE];
|
||||
};
|
||||
uint8_t plen; /* Prefix length */
|
||||
uint8_t set; /* Named instance set [0..31] */
|
||||
uint8_t spare[2];
|
||||
uint32_t flags;
|
||||
#define NPTV6_DYNAMIC_PREFIX 1 /* Use dynamic external prefix */
|
||||
} ipfw_nptv6_cfg;
|
||||
|
||||
#endif /* _NETINET6_IP_FW_NPTV6_H_ */
|
||||
File diff suppressed because it is too large.
Load diff
+5094
File diff suppressed because it is too large.
Load diff
+6129
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,470 @@
|
||||
/*-
|
||||
* Copyright (c) 2002-2003 Luigi Rizzo
|
||||
* Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
|
||||
* Copyright (c) 1994 Ugen J.S.Antsilevich
|
||||
*
|
||||
* Idea and grammar partially left from:
|
||||
* Copyright (c) 1993 Daniel Boulet
|
||||
*
|
||||
* Redistribution and use in source forms, with and without modification,
|
||||
* are permitted provided that this entire comment appears intact.
|
||||
*
|
||||
* Redistribution in binary form may occur without any restrictions.
|
||||
* Obviously, it would be nice if you gave credit where credit is due
|
||||
* but requiring it would be too onerous.
|
||||
*
|
||||
* This software is provided ``AS IS'' without any warranties of any kind.
|
||||
*
|
||||
* NEW command line interface for IP firewall facility
|
||||
*/
|
||||
|
||||
enum cmdline_prog {
|
||||
cmdline_prog_ipfw,
|
||||
cmdline_prog_dnctl
|
||||
};
|
||||
|
||||
/*
|
||||
* Options that can be set on the command line.
|
||||
* When reading commands from a file, a subset of the options can also
|
||||
* be applied globally by specifying them before the file name.
|
||||
* After that, each line can contain its own option that changes
|
||||
* the global value.
|
||||
* XXX The context is not restored after each line.
|
||||
*/
|
||||
|
||||
struct cmdline_opts {
|
||||
/* boolean options: */
|
||||
int do_value_as_ip; /* show table value as IP */
|
||||
int do_resolv; /* try to resolve all ip to names */
|
||||
int do_time; /* Show time stamps */
|
||||
int do_quiet; /* Be quiet in add and flush */
|
||||
int do_pipe; /* this cmd refers to a pipe/queue/sched */
|
||||
int do_nat; /* this cmd refers to a nat config */
|
||||
int do_compact; /* show rules in compact mode */
|
||||
int do_force; /* do not ask for confirmation */
|
||||
int show_sets; /* display the set each rule belongs to */
|
||||
int test_only; /* only check syntax */
|
||||
int comment_only; /* only print action and comment */
|
||||
int verbose; /* be verbose on some commands */
|
||||
int debug_only; /* output ioctl i/o on stdout */
|
||||
|
||||
/* The options below can have multiple values. */
|
||||
|
||||
int do_dynamic; /* 1 - display dynamic rules */
|
||||
/* 2 - display/delete only dynamic rules */
|
||||
int do_sort; /* field to sort results (0 = no) */
|
||||
/* valid fields are 1 and above */
|
||||
|
||||
uint32_t use_set; /* work with specified set number */
|
||||
/* 0 means all sets, otherwise apply to set use_set - 1 */
|
||||
|
||||
enum cmdline_prog prog; /* Are we ipfw or dnctl? */
|
||||
};
|
||||
|
||||
int is_ipfw(void);
|
||||
|
||||
enum {
|
||||
TIMESTAMP_NONE = 0,
|
||||
TIMESTAMP_STRING,
|
||||
TIMESTAMP_NUMERIC,
|
||||
};
|
||||
|
||||
extern struct cmdline_opts g_co;
|
||||
|
||||
/*
|
||||
* _s_x is a structure that stores a string <-> token pairs, used in
|
||||
* various places in the parser. Entries are stored in arrays,
|
||||
* with an entry with s=NULL as terminator.
|
||||
* The search routines are match_token() and match_value().
|
||||
* Often, an element with x=0 contains an error string.
|
||||
*
|
||||
*/
|
||||
struct _s_x {
|
||||
char const *s;
|
||||
int x;
|
||||
};
|
||||
|
||||
extern struct _s_x f_ipdscp[];
|
||||
|
||||
enum tokens {
|
||||
TOK_NULL=0,
|
||||
|
||||
TOK_OR,
|
||||
TOK_NOT,
|
||||
TOK_STARTBRACE,
|
||||
TOK_ENDBRACE,
|
||||
|
||||
TOK_ABORT6,
|
||||
TOK_ABORT,
|
||||
TOK_ACCEPT,
|
||||
TOK_COUNT,
|
||||
TOK_EACTION,
|
||||
TOK_PIPE,
|
||||
TOK_LINK,
|
||||
TOK_QUEUE,
|
||||
TOK_FLOWSET,
|
||||
TOK_SCHED,
|
||||
TOK_DIVERT,
|
||||
TOK_TEE,
|
||||
TOK_NETGRAPH,
|
||||
TOK_NGTEE,
|
||||
TOK_FORWARD,
|
||||
TOK_SKIPTO,
|
||||
TOK_DENY,
|
||||
TOK_REJECT,
|
||||
TOK_RESET,
|
||||
TOK_UNREACH,
|
||||
TOK_CHECKSTATE,
|
||||
TOK_NAT,
|
||||
TOK_REASS,
|
||||
TOK_CALL,
|
||||
TOK_RETURN,
|
||||
|
||||
TOK_ALTQ,
|
||||
TOK_LOG,
|
||||
TOK_TAG,
|
||||
TOK_UNTAG,
|
||||
|
||||
TOK_TAGGED,
|
||||
TOK_UID,
|
||||
TOK_GID,
|
||||
TOK_JAIL,
|
||||
TOK_IN,
|
||||
TOK_LIMIT,
|
||||
TOK_SETLIMIT,
|
||||
TOK_KEEPSTATE,
|
||||
TOK_RECORDSTATE,
|
||||
TOK_LAYER2,
|
||||
TOK_OUT,
|
||||
TOK_DIVERTED,
|
||||
TOK_DIVERTEDLOOPBACK,
|
||||
TOK_DIVERTEDOUTPUT,
|
||||
TOK_XMIT,
|
||||
TOK_RECV,
|
||||
TOK_VIA,
|
||||
TOK_FRAG,
|
||||
TOK_IPOPTS,
|
||||
TOK_IPLEN,
|
||||
TOK_IPID,
|
||||
TOK_IPPRECEDENCE,
|
||||
TOK_DSCP,
|
||||
TOK_IPTOS,
|
||||
TOK_IPTTL,
|
||||
TOK_IPVER,
|
||||
TOK_ESTAB,
|
||||
TOK_SETUP,
|
||||
TOK_TCPDATALEN,
|
||||
TOK_TCPFLAGS,
|
||||
TOK_TCPOPTS,
|
||||
TOK_TCPSEQ,
|
||||
TOK_TCPACK,
|
||||
TOK_TCPMSS,
|
||||
TOK_TCPWIN,
|
||||
TOK_ICMPTYPES,
|
||||
TOK_MAC,
|
||||
TOK_MACTYPE,
|
||||
TOK_VERREVPATH,
|
||||
TOK_VERSRCREACH,
|
||||
TOK_ANTISPOOF,
|
||||
TOK_IPSEC,
|
||||
TOK_COMMENT,
|
||||
|
||||
TOK_PLR,
|
||||
TOK_NOERROR,
|
||||
TOK_BUCKETS,
|
||||
TOK_DSTIP,
|
||||
TOK_SRCIP,
|
||||
TOK_DSTPORT,
|
||||
TOK_SRCPORT,
|
||||
TOK_DSTMAC,
|
||||
TOK_SRCMAC,
|
||||
TOK_ALL,
|
||||
TOK_MASK,
|
||||
TOK_FLOW_MASK,
|
||||
TOK_SCHED_MASK,
|
||||
TOK_BW,
|
||||
TOK_DELAY,
|
||||
TOK_PROFILE,
|
||||
TOK_BURST,
|
||||
TOK_RED,
|
||||
TOK_GRED,
|
||||
TOK_ECN,
|
||||
TOK_DROPTAIL,
|
||||
TOK_PROTO,
|
||||
#ifdef NEW_AQM
|
||||
/* AQM tokens*/
|
||||
TOK_NO_ECN,
|
||||
TOK_CODEL,
|
||||
TOK_FQ_CODEL,
|
||||
TOK_TARGET,
|
||||
TOK_INTERVAL,
|
||||
TOK_FLOWS,
|
||||
TOK_QUANTUM,
|
||||
|
||||
TOK_PIE,
|
||||
TOK_FQ_PIE,
|
||||
TOK_TUPDATE,
|
||||
TOK_MAX_BURST,
|
||||
TOK_MAX_ECNTH,
|
||||
TOK_ALPHA,
|
||||
TOK_BETA,
|
||||
TOK_CAPDROP,
|
||||
TOK_NO_CAPDROP,
|
||||
TOK_ONOFF,
|
||||
TOK_DRE,
|
||||
TOK_TS,
|
||||
TOK_DERAND,
|
||||
TOK_NO_DERAND,
|
||||
#endif
|
||||
/* dummynet tokens */
|
||||
TOK_WEIGHT,
|
||||
TOK_LMAX,
|
||||
TOK_PRI,
|
||||
TOK_TYPE,
|
||||
TOK_SLOTSIZE,
|
||||
|
||||
TOK_IP,
|
||||
TOK_IF,
|
||||
TOK_ALOG,
|
||||
TOK_DENY_INC,
|
||||
TOK_SAME_PORTS,
|
||||
TOK_UNREG_ONLY,
|
||||
TOK_UNREG_CGN,
|
||||
TOK_SKIP_GLOBAL,
|
||||
TOK_RESET_ADDR,
|
||||
TOK_ALIAS_REV,
|
||||
TOK_PROXY_ONLY,
|
||||
TOK_REDIR_ADDR,
|
||||
TOK_REDIR_PORT,
|
||||
TOK_REDIR_PROTO,
|
||||
|
||||
TOK_IPV6,
|
||||
TOK_FLOWID,
|
||||
TOK_ICMP6TYPES,
|
||||
TOK_EXT6HDR,
|
||||
TOK_DSTIP6,
|
||||
TOK_SRCIP6,
|
||||
|
||||
TOK_IPV4,
|
||||
TOK_UNREACH6,
|
||||
TOK_RESET6,
|
||||
|
||||
TOK_FIB,
|
||||
TOK_SETFIB,
|
||||
TOK_LOOKUP,
|
||||
TOK_SOCKARG,
|
||||
TOK_SETDSCP,
|
||||
TOK_FLOW,
|
||||
TOK_IFLIST,
|
||||
/* Table tokens */
|
||||
TOK_CREATE,
|
||||
TOK_DESTROY,
|
||||
TOK_LIST,
|
||||
TOK_INFO,
|
||||
TOK_DETAIL,
|
||||
TOK_MODIFY,
|
||||
TOK_FLUSH,
|
||||
TOK_SWAP,
|
||||
TOK_ADD,
|
||||
TOK_DEL,
|
||||
TOK_VALTYPE,
|
||||
TOK_ALGO,
|
||||
TOK_TALIST,
|
||||
TOK_ATOMIC,
|
||||
TOK_LOCK,
|
||||
TOK_UNLOCK,
|
||||
TOK_VLIST,
|
||||
TOK_OLIST,
|
||||
TOK_MONITOR,
|
||||
TOK_MISSING,
|
||||
TOK_ORFLUSH,
|
||||
|
||||
/* NAT64 tokens */
|
||||
TOK_NAT64STL,
|
||||
TOK_NAT64LSN,
|
||||
TOK_STATS,
|
||||
TOK_STATES,
|
||||
TOK_CONFIG,
|
||||
TOK_TABLE4,
|
||||
TOK_TABLE6,
|
||||
TOK_PREFIX4,
|
||||
TOK_PREFIX6,
|
||||
TOK_AGG_LEN,
|
||||
TOK_AGG_COUNT,
|
||||
TOK_MAX_PORTS,
|
||||
TOK_STATES_CHUNKS,
|
||||
TOK_JMAXLEN,
|
||||
TOK_PORT_RANGE,
|
||||
TOK_PORT_ALIAS,
|
||||
TOK_HOST_DEL_AGE,
|
||||
TOK_PG_DEL_AGE,
|
||||
TOK_TCP_SYN_AGE,
|
||||
TOK_TCP_CLOSE_AGE,
|
||||
TOK_TCP_EST_AGE,
|
||||
TOK_UDP_AGE,
|
||||
TOK_ICMP_AGE,
|
||||
TOK_LOGOFF,
|
||||
TOK_PRIVATE,
|
||||
TOK_PRIVATEOFF,
|
||||
TOK_SWAPCONF,
|
||||
TOK_SWAPCONFOFF,
|
||||
|
||||
/* NAT64 CLAT tokens */
|
||||
TOK_NAT64CLAT,
|
||||
TOK_PLAT_PREFIX,
|
||||
TOK_CLAT_PREFIX,
|
||||
|
||||
/* NPTv6 tokens */
|
||||
TOK_NPTV6,
|
||||
TOK_INTPREFIX,
|
||||
TOK_EXTPREFIX,
|
||||
TOK_PREFIXLEN,
|
||||
TOK_EXTIF,
|
||||
|
||||
TOK_TCPSETMSS,
|
||||
|
||||
TOK_MARK,
|
||||
TOK_SETMARK,
|
||||
|
||||
TOK_SKIPACTION,
|
||||
TOK_UDP_EIM,
|
||||
};
|
||||
|
||||
/*
|
||||
* the following macro returns an error message if we run out of
|
||||
* arguments.
|
||||
*/
|
||||
#define NEED(_p, msg) {if (!_p) errx(EX_USAGE, msg);}
|
||||
#define NEED1(msg) {if (!(*av)) errx(EX_USAGE, msg);}
|
||||
|
||||
struct buf_pr {
|
||||
char *buf; /* allocated buffer */
|
||||
char *ptr; /* current pointer */
|
||||
size_t size; /* total buffer size */
|
||||
size_t avail; /* available storage */
|
||||
size_t needed; /* length needed */
|
||||
};
|
||||
|
||||
int pr_u64(struct buf_pr *bp, void *pd, int width);
|
||||
int bp_alloc(struct buf_pr *b, size_t size);
|
||||
void bp_free(struct buf_pr *b);
|
||||
int bprintf(struct buf_pr *b, const char *format, ...) __printflike(2, 3);
|
||||
|
||||
|
||||
/* memory allocation support */
|
||||
void *safe_calloc(size_t number, size_t size);
|
||||
void *safe_realloc(void *ptr, size_t size);
|
||||
|
||||
/* string comparison functions used for historical compatibility */
|
||||
int _substrcmp(const char *str1, const char* str2);
|
||||
int _substrcmp2(const char *str1, const char* str2, const char* str3);
|
||||
int stringnum_cmp(const char *a, const char *b);
|
||||
|
||||
/* utility functions */
|
||||
int match_token(struct _s_x *table, const char *string);
|
||||
int match_token_relaxed(struct _s_x *table, const char *string);
|
||||
int get_token(struct _s_x *table, const char *string, const char *errbase);
|
||||
char const *match_value(struct _s_x *p, int value);
|
||||
size_t concat_tokens(char *buf, size_t bufsize, struct _s_x *table,
|
||||
const char *delimiter);
|
||||
int fill_flags(struct _s_x *flags, char *p, char **e, uint32_t *set,
|
||||
uint32_t *clear);
|
||||
void print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint32_t set);
|
||||
|
||||
struct _ip_fw3_opheader;
|
||||
int do_cmd(int optname, void *optval, uintptr_t optlen);
|
||||
int do_set3(int optname, struct _ip_fw3_opheader *op3, size_t optlen);
|
||||
int do_get3(int optname, struct _ip_fw3_opheader *op3, size_t *optlen);
|
||||
|
||||
struct in6_addr;
|
||||
void n2mask(struct in6_addr *mask, int n);
|
||||
int contigmask(const uint8_t *p, int len);
|
||||
|
||||
/*
|
||||
* Forward declarations to avoid include way too many headers.
|
||||
* C does not allow duplicated typedefs, so we use the base struct
|
||||
* that the typedef points to.
|
||||
* Should the typedefs use a different type, the compiler will
|
||||
* still detect the change when compiling the body of the
|
||||
* functions involved, so we do not lose error checking.
|
||||
*/
|
||||
struct _ipfw_insn;
|
||||
struct _ipfw_insn_altq;
|
||||
struct _ipfw_insn_u32;
|
||||
struct _ipfw_insn_ip6;
|
||||
struct _ipfw_insn_icmp6;
|
||||
|
||||
/*
|
||||
* The reserved set numer. This is a constant in ip_fw.h
|
||||
* but we store it in a variable so other files do not depend
|
||||
* in that header just for one constant.
|
||||
*/
|
||||
extern int resvd_set_number;
|
||||
|
||||
/* first-level command handlers */
|
||||
void ipfw_add(char *av[]);
|
||||
void ipfw_show_nat(int ac, char **av);
|
||||
int ipfw_delete_nat(int i);
|
||||
void ipfw_config_pipe(int ac, char **av);
|
||||
void ipfw_config_nat(int ac, char **av);
|
||||
void ipfw_sets_handler(char *av[]);
|
||||
void ipfw_table_handler(int ac, char *av[]);
|
||||
void ipfw_sysctl_handler(char *av[], int which);
|
||||
void ipfw_delete(char *av[]);
|
||||
void ipfw_flush(int force);
|
||||
void ipfw_zero(int ac, char *av[], int optname);
|
||||
void ipfw_list(int ac, char *av[], int show_counters);
|
||||
void ipfw_internal_handler(int ac, char *av[]);
|
||||
void ipfw_nat64clat_handler(int ac, char *av[]);
|
||||
void ipfw_nat64lsn_handler(int ac, char *av[]);
|
||||
void ipfw_nat64stl_handler(int ac, char *av[]);
|
||||
void ipfw_nptv6_handler(int ac, char *av[]);
|
||||
int ipfw_check_object_name(const char *name);
|
||||
int ipfw_check_nat64prefix(const struct in6_addr *prefix, int length);
|
||||
|
||||
#ifdef PF
|
||||
/* altq.c */
|
||||
void altq_set_enabled(int enabled);
|
||||
u_int32_t altq_name_to_qid(const char *name);
|
||||
void print_altq_cmd(struct buf_pr *bp, const struct _ipfw_insn_altq *altqptr);
|
||||
#else
|
||||
#define NO_ALTQ
|
||||
#endif
|
||||
|
||||
/* dummynet.c */
|
||||
void dummynet_list(int ac, char *av[], int show_counters);
|
||||
void dummynet_flush(void);
|
||||
int ipfw_delete_pipe(int pipe_or_queue, int n);
|
||||
|
||||
/* ipv6.c */
|
||||
void print_unreach6_code(struct buf_pr *bp, uint16_t code);
|
||||
void print_ip6(struct buf_pr *bp, const struct _ipfw_insn_ip6 *cmd);
|
||||
void print_flow6id(struct buf_pr *bp, const struct _ipfw_insn_u32 *cmd);
|
||||
void print_icmp6types(struct buf_pr *bp, const struct _ipfw_insn_u32 *cmd);
|
||||
void print_ext6hdr(struct buf_pr *bp, const struct _ipfw_insn *cmd);
|
||||
|
||||
struct tidx;
|
||||
struct _ipfw_insn *add_srcip6(struct _ipfw_insn *cmd, char *av, int cblen,
|
||||
struct tidx *tstate);
|
||||
struct _ipfw_insn *add_dstip6(struct _ipfw_insn *cmd, char *av, int cblen,
|
||||
struct tidx *tstate);
|
||||
|
||||
void fill_flow6(struct _ipfw_insn_u32 *cmd, char *av, int cblen);
|
||||
uint16_t get_unreach6_code(const char *str);
|
||||
void fill_icmp6types(struct _ipfw_insn_icmp6 *cmd, char *av, int cblen);
|
||||
int fill_ext6hdr(struct _ipfw_insn *cmd, char *av);
|
||||
|
||||
/* ipfw2.c */
|
||||
void bp_flush(struct buf_pr *b);
|
||||
void fill_table(struct _ipfw_insn *cmd, char *av, uint8_t opcode,
|
||||
struct tidx *tstate);
|
||||
|
||||
/* tables.c */
|
||||
struct _ipfw_obj_ctlv;
|
||||
struct _ipfw_obj_ntlv;
|
||||
int table_check_name(const char *tablename);
|
||||
void ipfw_list_ta(int ac, char *av[]);
|
||||
void ipfw_list_values(int ac, char *av[]);
|
||||
void table_fill_ntlv(struct _ipfw_obj_ntlv *ntlv, const char *name,
|
||||
uint8_t set, uint32_t uidx);
|
||||
|
||||
@@ -0,0 +1,519 @@
|
||||
/*-
|
||||
* Copyright (c) 2002-2003 Luigi Rizzo
|
||||
* Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
|
||||
* Copyright (c) 1994 Ugen J.S.Antsilevich
|
||||
*
|
||||
* Idea and grammar partially left from:
|
||||
* Copyright (c) 1993 Daniel Boulet
|
||||
*
|
||||
* Redistribution and use in source forms, with and without modification,
|
||||
* are permitted provided that this entire comment appears intact.
|
||||
*
|
||||
* Redistribution in binary form may occur without any restrictions.
|
||||
* Obviously, it would be nice if you gave credit where credit is due
|
||||
* but requiring it would be too onerous.
|
||||
*
|
||||
* This software is provided ``AS IS'' without any warranties of any kind.
|
||||
*
|
||||
* NEW command line interface for IP firewall facility
|
||||
*
|
||||
* ipv6 support
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "ipfw2.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/icmp6.h>
|
||||
#include <netinet/ip_fw15.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define CHECK_LENGTH(v, len) do { \
|
||||
if ((v) < (len)) \
|
||||
errx(EX_DATAERR, "Rule too long"); \
|
||||
} while (0)
|
||||
|
||||
static struct _s_x icmp6codes[] = {
|
||||
{ "no-route", ICMP6_DST_UNREACH_NOROUTE },
|
||||
{ "admin-prohib", ICMP6_DST_UNREACH_ADMIN },
|
||||
{ "address", ICMP6_DST_UNREACH_ADDR },
|
||||
{ "port", ICMP6_DST_UNREACH_NOPORT },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
uint16_t
|
||||
get_unreach6_code(const char *str)
|
||||
{
|
||||
int val;
|
||||
char *s;
|
||||
|
||||
val = strtoul(str, &s, 0);
|
||||
if (s == str || *s != '\0' || val >= 0x100)
|
||||
val = match_token(icmp6codes, str);
|
||||
if (val < 0)
|
||||
errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str);
|
||||
return (val);
|
||||
}
|
||||
|
||||
void
|
||||
print_unreach6_code(struct buf_pr *bp, uint16_t code)
|
||||
{
|
||||
char const *s = match_value(icmp6codes, code);
|
||||
|
||||
if (s != NULL)
|
||||
bprintf(bp, "unreach6 %s", s);
|
||||
else
|
||||
bprintf(bp, "unreach6 %u", code);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the ip address contained in a command.
|
||||
*/
|
||||
void
|
||||
print_ip6(struct buf_pr *bp, const ipfw_insn_ip6 *cmd)
|
||||
{
|
||||
char trad[255];
|
||||
struct hostent *he = NULL;
|
||||
const struct in6_addr *a = &(cmd->addr6);
|
||||
int len, mb;
|
||||
|
||||
len = F_LEN((const ipfw_insn *)cmd) - 1;
|
||||
if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
|
||||
bprintf(bp, " me6");
|
||||
return;
|
||||
}
|
||||
if (cmd->o.opcode == O_IP6) {
|
||||
bprintf(bp, " ip6");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* len == 4 indicates a single IP, whereas lists of 1 or more
|
||||
* addr/mask pairs have len = (2n+1). We convert len to n so we
|
||||
* use that to count the number of entries.
|
||||
*/
|
||||
bprintf(bp, " ");
|
||||
for (len = len / 4; len > 0; len -= 2, a += 2) {
|
||||
/* mask length */
|
||||
mb = (cmd->o.opcode == O_IP6_SRC ||
|
||||
cmd->o.opcode == O_IP6_DST) ? 128:
|
||||
contigmask((const uint8_t *)&(a[1]), 128);
|
||||
|
||||
if (mb == 128 && g_co.do_resolv)
|
||||
he = gethostbyaddr((const char *)a, sizeof(*a),
|
||||
AF_INET6);
|
||||
|
||||
if (he != NULL) /* resolved to name */
|
||||
bprintf(bp, "%s", he->h_name);
|
||||
else if (mb == 0) /* any */
|
||||
bprintf(bp, "any");
|
||||
else { /* numeric IP followed by some kind of mask */
|
||||
if (inet_ntop(AF_INET6, a, trad,
|
||||
sizeof(trad)) == NULL)
|
||||
bprintf(bp, "Error ntop in print_ip6\n");
|
||||
bprintf(bp, "%s", trad );
|
||||
if (mb < 0) /* mask not contiguous */
|
||||
bprintf(bp, "/%s", inet_ntop(AF_INET6, &a[1],
|
||||
trad, sizeof(trad)));
|
||||
else if (mb < 128)
|
||||
bprintf(bp, "/%d", mb);
|
||||
}
|
||||
if (len > 2)
|
||||
bprintf(bp, ",");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av, int cblen)
|
||||
{
|
||||
uint8_t type;
|
||||
|
||||
CHECK_LENGTH(cblen, (int)F_INSN_SIZE(ipfw_insn_icmp6));
|
||||
memset(cmd, 0, sizeof(*cmd));
|
||||
while (*av) {
|
||||
if (*av == ',')
|
||||
av++;
|
||||
type = strtoul(av, &av, 0);
|
||||
if (*av != ',' && *av != '\0')
|
||||
errx(EX_DATAERR, "invalid ICMP6 type");
|
||||
/*
|
||||
* XXX: shouldn't this be 0xFF? I can't see any reason why
|
||||
* we shouldn't be able to filter all possiable values
|
||||
* regardless of the ability of the rest of the kernel to do
|
||||
* anything useful with them.
|
||||
*/
|
||||
if (type > ICMP6_MAXTYPE)
|
||||
errx(EX_DATAERR, "ICMP6 type out of range");
|
||||
cmd->d[type / 32] |= ( 1 << (type % 32));
|
||||
}
|
||||
cmd->o.opcode = O_ICMP6TYPE;
|
||||
cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6);
|
||||
}
|
||||
|
||||
void
|
||||
print_icmp6types(struct buf_pr *bp, const ipfw_insn_u32 *cmd)
|
||||
{
|
||||
int i, j;
|
||||
char sep= ' ';
|
||||
|
||||
bprintf(bp, " icmp6types");
|
||||
for (i = 0; i < 7; i++)
|
||||
for (j=0; j < 32; ++j) {
|
||||
if ( (cmd->d[i] & (1 << (j))) == 0)
|
||||
continue;
|
||||
bprintf(bp, "%c%d", sep, (i*32 + j));
|
||||
sep = ',';
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_flow6id(struct buf_pr *bp, const ipfw_insn_u32 *cmd)
|
||||
{
|
||||
uint16_t i, limit = cmd->o.arg1;
|
||||
char sep = ',';
|
||||
|
||||
bprintf(bp, " flow-id ");
|
||||
for( i=0; i < limit; ++i) {
|
||||
if (i == limit - 1)
|
||||
sep = ' ';
|
||||
bprintf(bp, "%d%c", cmd->d[i], sep);
|
||||
}
|
||||
}
|
||||
|
||||
/* structure and define for the extension header in ipv6 */
|
||||
static struct _s_x ext6hdrcodes[] = {
|
||||
{ "frag", EXT_FRAGMENT },
|
||||
{ "hopopt", EXT_HOPOPTS },
|
||||
{ "route", EXT_ROUTING },
|
||||
{ "dstopt", EXT_DSTOPTS },
|
||||
{ "ah", EXT_AH },
|
||||
{ "esp", EXT_ESP },
|
||||
{ "rthdr0", EXT_RTHDR0 },
|
||||
{ "rthdr2", EXT_RTHDR2 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/* fills command for the extension header filtering */
|
||||
int
|
||||
fill_ext6hdr( ipfw_insn *cmd, char *av)
|
||||
{
|
||||
int tok;
|
||||
char *s = av;
|
||||
|
||||
cmd->arg1 = 0;
|
||||
while(s) {
|
||||
av = strsep( &s, ",") ;
|
||||
tok = match_token(ext6hdrcodes, av);
|
||||
switch (tok) {
|
||||
case EXT_FRAGMENT:
|
||||
cmd->arg1 |= EXT_FRAGMENT;
|
||||
break;
|
||||
case EXT_HOPOPTS:
|
||||
cmd->arg1 |= EXT_HOPOPTS;
|
||||
break;
|
||||
case EXT_ROUTING:
|
||||
cmd->arg1 |= EXT_ROUTING;
|
||||
break;
|
||||
case EXT_DSTOPTS:
|
||||
cmd->arg1 |= EXT_DSTOPTS;
|
||||
break;
|
||||
case EXT_AH:
|
||||
cmd->arg1 |= EXT_AH;
|
||||
break;
|
||||
case EXT_ESP:
|
||||
cmd->arg1 |= EXT_ESP;
|
||||
break;
|
||||
case EXT_RTHDR0:
|
||||
cmd->arg1 |= EXT_RTHDR0;
|
||||
break;
|
||||
case EXT_RTHDR2:
|
||||
cmd->arg1 |= EXT_RTHDR2;
|
||||
break;
|
||||
default:
|
||||
errx(EX_DATAERR,
|
||||
"invalid option for ipv6 exten header");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cmd->arg1 == 0)
|
||||
return (0);
|
||||
cmd->opcode = O_EXT_HDR;
|
||||
cmd->len |= F_INSN_SIZE(ipfw_insn);
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
print_ext6hdr(struct buf_pr *bp, const ipfw_insn *cmd )
|
||||
{
|
||||
char sep = ' ';
|
||||
|
||||
bprintf(bp, " extension header:");
|
||||
if (cmd->arg1 & EXT_FRAGMENT) {
|
||||
bprintf(bp, "%cfragmentation", sep);
|
||||
sep = ',';
|
||||
}
|
||||
if (cmd->arg1 & EXT_HOPOPTS) {
|
||||
bprintf(bp, "%chop options", sep);
|
||||
sep = ',';
|
||||
}
|
||||
if (cmd->arg1 & EXT_ROUTING) {
|
||||
bprintf(bp, "%crouting options", sep);
|
||||
sep = ',';
|
||||
}
|
||||
if (cmd->arg1 & EXT_RTHDR0) {
|
||||
bprintf(bp, "%crthdr0", sep);
|
||||
sep = ',';
|
||||
}
|
||||
if (cmd->arg1 & EXT_RTHDR2) {
|
||||
bprintf(bp, "%crthdr2", sep);
|
||||
sep = ',';
|
||||
}
|
||||
if (cmd->arg1 & EXT_DSTOPTS) {
|
||||
bprintf(bp, "%cdestination options", sep);
|
||||
sep = ',';
|
||||
}
|
||||
if (cmd->arg1 & EXT_AH) {
|
||||
bprintf(bp, "%cauthentication header", sep);
|
||||
sep = ',';
|
||||
}
|
||||
if (cmd->arg1 & EXT_ESP) {
|
||||
bprintf(bp, "%cencapsulating security payload", sep);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to find ipv6 address by hostname */
|
||||
static int
|
||||
lookup_host6 (char *host, struct in6_addr *ip6addr)
|
||||
{
|
||||
struct hostent *he;
|
||||
|
||||
if (!inet_pton(AF_INET6, host, ip6addr)) {
|
||||
if ((he = gethostbyname2(host, AF_INET6)) == NULL)
|
||||
return(-1);
|
||||
memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* fill the addr and mask fields in the instruction as appropriate from av.
|
||||
* Update length as appropriate.
|
||||
* The following formats are allowed:
|
||||
* any matches any IP6. Actually returns an empty instruction.
|
||||
* me returns O_IP6_*_ME
|
||||
*
|
||||
* 03f1::234:123:0342 single IP6 address
|
||||
* 03f1::234:123:0342/24 address/masklen
|
||||
* 03f1::234:123:0342/ffff::ffff:ffff address/mask
|
||||
* 03f1::234:123:0342/24,03f1::234:123:0343/ List of address
|
||||
*
|
||||
* Set of address (as in ipv6) not supported because ipv6 address
|
||||
* are typically random past the initial prefix.
|
||||
* Return 1 on success, 0 on failure.
|
||||
*/
|
||||
static int
|
||||
fill_ip6(ipfw_insn_ip6 *cmd, char *av, int cblen, struct tidx *tstate)
|
||||
{
|
||||
int len = 0;
|
||||
struct in6_addr *d = &(cmd->addr6);
|
||||
char *oav;
|
||||
/*
|
||||
* Needed for multiple address.
|
||||
* Note d[1] points to struct in6_add r mask6 of cmd
|
||||
*/
|
||||
|
||||
cmd->o.len &= ~F_LEN_MASK; /* zero len */
|
||||
|
||||
if (strcmp(av, "any") == 0)
|
||||
return (1);
|
||||
|
||||
/* Set the data for "me" opt */
|
||||
if (strcmp(av, "me") == 0 || strcmp(av, "me6") == 0) {
|
||||
cmd->o.len |= F_INSN_SIZE(ipfw_insn);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (strncmp(av, "table(", 6) == 0) {
|
||||
fill_table(&cmd->o, av, O_IP_DST_LOOKUP, tstate);
|
||||
return (1);
|
||||
}
|
||||
|
||||
oav = av = strdup(av);
|
||||
while (av) {
|
||||
/*
|
||||
* After the address we can have '/' indicating a mask,
|
||||
* or ',' indicating another address follows.
|
||||
*/
|
||||
|
||||
char *p, *q;
|
||||
int masklen;
|
||||
char md = '\0';
|
||||
|
||||
CHECK_LENGTH(cblen,
|
||||
1 + len + 2 * (int)F_INSN_SIZE(struct in6_addr));
|
||||
|
||||
if ((q = strchr(av, ',')) ) {
|
||||
*q = '\0';
|
||||
q++;
|
||||
}
|
||||
|
||||
if ((p = strchr(av, '/')) ) {
|
||||
md = *p; /* save the separator */
|
||||
*p = '\0'; /* terminate address string */
|
||||
p++; /* and skip past it */
|
||||
}
|
||||
/* now p points to NULL, mask or next entry */
|
||||
|
||||
/* lookup stores address in *d as a side effect */
|
||||
if (lookup_host6(av, d) != 0) {
|
||||
/* XXX: failed. Free memory and go */
|
||||
errx(EX_DATAERR, "bad address \"%s\"", av);
|
||||
}
|
||||
/* next, look at the mask, if any */
|
||||
if (md == '/' && strchr(p, ':')) {
|
||||
if (!inet_pton(AF_INET6, p, &d[1]))
|
||||
errx(EX_DATAERR, "bad mask \"%s\"", p);
|
||||
|
||||
masklen = contigmask((uint8_t *)&(d[1]), 128);
|
||||
} else {
|
||||
masklen = (md == '/') ? atoi(p) : 128;
|
||||
if (masklen > 128 || masklen < 0)
|
||||
errx(EX_DATAERR, "bad width \"%s\''", p);
|
||||
else
|
||||
n2mask(&d[1], masklen);
|
||||
}
|
||||
|
||||
APPLY_MASK(d, &d[1]); /* mask base address with mask */
|
||||
|
||||
av = q;
|
||||
|
||||
/* Check this entry */
|
||||
if (masklen == 0) {
|
||||
/*
|
||||
* 'any' turns the entire list into a NOP.
|
||||
* 'not any' never matches, so it is removed from the
|
||||
* list unless it is the only item, in which case we
|
||||
* report an error.
|
||||
*/
|
||||
if (cmd->o.len & F_NOT && av == NULL && len == 0)
|
||||
errx(EX_DATAERR, "not any never matches");
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* A single IP can be stored alone
|
||||
*/
|
||||
if (masklen == 128 && av == NULL && len == 0) {
|
||||
len = F_INSN_SIZE(struct in6_addr);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update length and pointer to arguments */
|
||||
len += F_INSN_SIZE(struct in6_addr)*2;
|
||||
d += 2;
|
||||
} /* end while */
|
||||
|
||||
/*
|
||||
* Total length of the command, remember that 1 is the size of
|
||||
* the base command.
|
||||
*/
|
||||
if (len + 1 > F_LEN_MASK)
|
||||
errx(EX_DATAERR, "address list too long");
|
||||
cmd->o.len |= len+1;
|
||||
free(oav);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* fills command for ipv6 flow-id filtering
|
||||
* note that the 20 bit flow number is stored in a array of u_int32_t
|
||||
* it's supported lists of flow-id, so in the o.arg1 we store how many
|
||||
* additional flow-id we want to filter, the basic is 1
|
||||
*/
|
||||
void
|
||||
fill_flow6( ipfw_insn_u32 *cmd, char *av, int cblen)
|
||||
{
|
||||
u_int32_t type; /* Current flow number */
|
||||
u_int16_t nflow = 0; /* Current flow index */
|
||||
char *s = av;
|
||||
cmd->d[0] = 0; /* Initializing the base number*/
|
||||
|
||||
while (s) {
|
||||
CHECK_LENGTH(cblen,
|
||||
(int)F_INSN_SIZE(ipfw_insn_u32) + nflow + 1);
|
||||
|
||||
av = strsep( &s, ",") ;
|
||||
type = strtoul(av, &av, 0);
|
||||
if (*av != ',' && *av != '\0')
|
||||
errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
|
||||
if (type > 0xfffff)
|
||||
errx(EX_DATAERR, "flow number out of range %s", av);
|
||||
cmd->d[nflow] |= type;
|
||||
nflow++;
|
||||
}
|
||||
if( nflow > 0 ) {
|
||||
cmd->o.opcode = O_FLOW6ID;
|
||||
cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
|
||||
cmd->o.arg1 = nflow;
|
||||
}
|
||||
else {
|
||||
errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
|
||||
}
|
||||
}
|
||||
|
||||
ipfw_insn *
|
||||
add_srcip6(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate)
|
||||
{
|
||||
|
||||
fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen, tstate);
|
||||
if (cmd->opcode == O_IP_DST_SET) /* set */
|
||||
cmd->opcode = O_IP_SRC_SET;
|
||||
else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
|
||||
cmd->opcode = O_IP_SRC_LOOKUP;
|
||||
else if (F_LEN(cmd) == 0) { /* any */
|
||||
} else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */
|
||||
cmd->opcode = O_IP6_SRC_ME;
|
||||
} else if (F_LEN(cmd) ==
|
||||
(F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
|
||||
/* single IP, no mask*/
|
||||
cmd->opcode = O_IP6_SRC;
|
||||
} else { /* addr/mask opt */
|
||||
cmd->opcode = O_IP6_SRC_MASK;
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
ipfw_insn *
|
||||
add_dstip6(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate)
|
||||
{
|
||||
|
||||
fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen, tstate);
|
||||
if (cmd->opcode == O_IP_DST_SET) /* set */
|
||||
;
|
||||
else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
|
||||
;
|
||||
else if (F_LEN(cmd) == 0) { /* any */
|
||||
} else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */
|
||||
cmd->opcode = O_IP6_DST_ME;
|
||||
} else if (F_LEN(cmd) ==
|
||||
(F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
|
||||
/* single IP, no mask*/
|
||||
cmd->opcode = O_IP6_DST;
|
||||
} else { /* addr/mask opt */
|
||||
cmd->opcode = O_IP6_DST_MASK;
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
@@ -0,0 +1,716 @@
|
||||
/*-
|
||||
* Copyright (c) 2002-2003,2010 Luigi Rizzo
|
||||
* Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
|
||||
* Copyright (c) 1994 Ugen J.S.Antsilevich
|
||||
*
|
||||
* Idea and grammar partially left from:
|
||||
* Copyright (c) 1993 Daniel Boulet
|
||||
*
|
||||
* Redistribution and use in source forms, with and without modification,
|
||||
* are permitted provided that this entire comment appears intact.
|
||||
*
|
||||
* Redistribution in binary form may occur without any restrictions.
|
||||
* Obviously, it would be nice if you gave credit where credit is due
|
||||
* but requiring it would be too onerous.
|
||||
*
|
||||
* This software is provided ``AS IS'' without any warranties of any kind.
|
||||
*
|
||||
* Command line interface for IP firewall facility
|
||||
*/
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "ipfw2.h"
|
||||
|
||||
static void
|
||||
help(void)
|
||||
{
|
||||
if (is_ipfw()) {
|
||||
fprintf(stderr,
|
||||
"IPFW compatibility binary from RELENG_15\n\n"
|
||||
"ipfw syntax summary (but please do read the ipfw(8) manpage):\n\n"
|
||||
"\tipfw [-abcdefhnNqStTv] <command>\n\n"
|
||||
"where <command> is one of the following:\n\n"
|
||||
"add [num] [set N] [prob x] RULE-BODY\n"
|
||||
"{pipe|queue} N config PIPE-BODY\n"
|
||||
"[pipe|queue] {zero|delete|show} [N{,N}]\n"
|
||||
"nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|unreg_cgn|udp_eim|\n"
|
||||
" reset|reverse|proxy_only|redirect_addr linkspec|\n"
|
||||
" redirect_port linkspec|redirect_proto linkspec|\n"
|
||||
" port_range lower-upper}\n"
|
||||
"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
|
||||
"set N {show|list|zero|resetlog|delete} [N{,N}] | flush\n"
|
||||
"table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
|
||||
"table all {flush | list}\n"
|
||||
"\n"
|
||||
"RULE-BODY: check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
|
||||
"ACTION: check-state | allow | count | deny | unreach{,6} CODE |\n"
|
||||
" skipto N | {divert|tee} PORT | forward ADDR |\n"
|
||||
" pipe N | queue N | nat N | setfib FIB | reass\n"
|
||||
"PARAMS: [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n"
|
||||
"ADDR: [ MAC dst src ether_type ] \n"
|
||||
" [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
|
||||
" [ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n"
|
||||
"IPADDR: [not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n"
|
||||
"IP6ADDR: [not] { any | me | me6 | ip6/bits | IP6LIST }\n"
|
||||
"IP6LIST: { ip6 | ip6/bits }[,IP6LIST]\n"
|
||||
"IPLIST: { ip | ip/bits | ip:mask }[,IPLIST]\n"
|
||||
"OPTION_LIST: OPTION [OPTION_LIST]\n"
|
||||
"OPTION: bridged | diverted | diverted-loopback | diverted-output |\n"
|
||||
" {dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n"
|
||||
" {dst-port|src-port} LIST |\n"
|
||||
" estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n"
|
||||
" iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n"
|
||||
" ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n"
|
||||
" icmp6types LIST | ext6hdr LIST | flow-id N[,N] | fib FIB |\n"
|
||||
" mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n"
|
||||
" setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n"
|
||||
" tcpdatalen LIST | verrevpath | versrcreach | antispoof\n"
|
||||
);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"IPFW compatibility binary from RELENG_15\n\n"
|
||||
"dnctl syntax summary (but please do read the dnctl(8) manpage):\n\n"
|
||||
"\tdnctl [-hnsv] <command>\n\n"
|
||||
"where <command> is one of the following:\n\n"
|
||||
"[pipe|queue|sched] N config PIPE-BODY\n"
|
||||
"[pipe|queue|sched] {delete|list|show} [N{,N}]\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with the arguments, including program name because getopt
|
||||
* wants it to be present.
|
||||
* Returns 0 if successful, 1 if empty command, errx() in case of errors.
|
||||
* First thing we do is process parameters creating an argv[] array
|
||||
* which includes the program name and a NULL entry at the end.
|
||||
* If we are called with a single string, we split it on whitespace.
|
||||
* Also, arguments with a trailing ',' are joined to the next one.
|
||||
* The pointers (av[]) and data are in a single chunk of memory.
|
||||
* av[0] points to the original program name, all other entries
|
||||
* point into the allocated chunk.
|
||||
*/
|
||||
static int
|
||||
ipfw_main(int oldac, char **oldav)
|
||||
{
|
||||
int ch, ac;
|
||||
const char *errstr;
|
||||
char **av, **save_av;
|
||||
int do_acct = 0; /* Show packet/byte count */
|
||||
int try_next = 0; /* set if pipe cmd not found */
|
||||
int av_size; /* compute the av size */
|
||||
char *av_p; /* used to build the av list */
|
||||
|
||||
#define WHITESP " \t\f\v\n\r"
|
||||
if (oldac < 2)
|
||||
return 1; /* need at least one argument */
|
||||
|
||||
if (oldac == 2) {
|
||||
/*
|
||||
* If we are called with one argument, try to split it into
|
||||
* words for subsequent parsing. Spaces after a ',' are
|
||||
* removed by copying the string in-place.
|
||||
*/
|
||||
char *arg = oldav[1]; /* The string is the first arg. */
|
||||
int l = strlen(arg);
|
||||
int copy = 0; /* 1 if we need to copy, 0 otherwise */
|
||||
int i, j;
|
||||
|
||||
for (i = j = 0; i < l; i++) {
|
||||
if (arg[i] == '#') /* comment marker */
|
||||
break;
|
||||
if (copy) {
|
||||
arg[j++] = arg[i];
|
||||
copy = !strchr("," WHITESP, arg[i]);
|
||||
} else {
|
||||
copy = !strchr(WHITESP, arg[i]);
|
||||
if (copy)
|
||||
arg[j++] = arg[i];
|
||||
}
|
||||
}
|
||||
if (!copy && j > 0) /* last char was a 'blank', remove it */
|
||||
j--;
|
||||
l = j; /* the new argument length */
|
||||
arg[j++] = '\0';
|
||||
if (l == 0) /* empty string! */
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* First, count number of arguments. Because of the previous
|
||||
* processing, this is just the number of blanks plus 1.
|
||||
*/
|
||||
for (i = 0, ac = 1; i < l; i++)
|
||||
if (strchr(WHITESP, arg[i]) != NULL)
|
||||
ac++;
|
||||
|
||||
/*
|
||||
* Allocate the argument list structure as a single block
|
||||
* of memory, containing pointers and the argument
|
||||
* strings. We include one entry for the program name
|
||||
* because getopt expects it, and a NULL at the end
|
||||
* to simplify further parsing.
|
||||
*/
|
||||
ac++; /* add 1 for the program name */
|
||||
av_size = (ac+1) * sizeof(char *) + l + 1;
|
||||
av = safe_calloc(av_size, 1);
|
||||
|
||||
/*
|
||||
* Init the argument pointer to the end of the array
|
||||
* and copy arguments from arg[] to av[]. For each one,
|
||||
* j is the initial character, i is the one past the end.
|
||||
*/
|
||||
av_p = (char *)&av[ac+1];
|
||||
for (ac = 1, i = j = 0; i < l; i++) {
|
||||
if (strchr(WHITESP, arg[i]) != NULL || i == l-1) {
|
||||
if (i == l-1)
|
||||
i++;
|
||||
bcopy(arg+j, av_p, i-j);
|
||||
av[ac] = av_p;
|
||||
av_p += i-j; /* the length of the string */
|
||||
*av_p++ = '\0';
|
||||
ac++;
|
||||
j = i + 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* If an argument ends with ',' join with the next one.
|
||||
*/
|
||||
int first, i, l=0;
|
||||
|
||||
/*
|
||||
* Allocate the argument list structure as a single block
|
||||
* of memory, containing both pointers and the argument
|
||||
* strings. We include some space for the program name
|
||||
* because getopt expects it.
|
||||
* We add an extra pointer to the end of the array,
|
||||
* to make simpler further parsing.
|
||||
*/
|
||||
for (i=0; i<oldac; i++)
|
||||
l += strlen(oldav[i]);
|
||||
|
||||
av_size = (oldac+1) * sizeof(char *) + l + oldac;
|
||||
av = safe_calloc(av_size, 1);
|
||||
|
||||
/*
|
||||
* Init the argument pointer to the end of the array
|
||||
* and copy arguments from arg[] to av[]
|
||||
*/
|
||||
av_p = (char *)&av[oldac+1];
|
||||
for (first = i = ac = 1, l = 0; i < oldac; i++) {
|
||||
char *arg = oldav[i];
|
||||
int k = strlen(arg);
|
||||
|
||||
l += k;
|
||||
if (arg[k-1] != ',' || i == oldac-1) {
|
||||
/* Time to copy. */
|
||||
av[ac] = av_p;
|
||||
for (l=0; first <= i; first++) {
|
||||
strcat(av_p, oldav[first]);
|
||||
av_p += strlen(oldav[first]);
|
||||
}
|
||||
*av_p++ = '\0';
|
||||
ac++;
|
||||
l = 0;
|
||||
first = i+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set the progname pointer to the original string
|
||||
* and terminate the array with null
|
||||
*/
|
||||
av[0] = oldav[0];
|
||||
av[ac] = NULL;
|
||||
|
||||
/* Set the force flag for non-interactive processes */
|
||||
if (!g_co.do_force)
|
||||
g_co.do_force = !isatty(STDIN_FILENO);
|
||||
|
||||
#ifdef EMULATE_SYSCTL /* sysctl emulation */
|
||||
if (is_ipfw() && ac >= 2 &&
|
||||
!strcmp(av[1], "sysctl")) {
|
||||
char *s;
|
||||
int i;
|
||||
|
||||
if (ac != 3) {
|
||||
printf( "sysctl emulation usage:\n"
|
||||
" ipfw sysctl name[=value]\n"
|
||||
" ipfw sysctl -a\n");
|
||||
return 0;
|
||||
}
|
||||
s = strchr(av[2], '=');
|
||||
if (s == NULL) {
|
||||
s = !strcmp(av[2], "-a") ? NULL : av[2];
|
||||
sysctlbyname(s, NULL, NULL, NULL, 0);
|
||||
} else { /* ipfw sysctl x.y.z=value */
|
||||
/* assume an INT value, will extend later */
|
||||
if (s[1] == '\0') {
|
||||
printf("ipfw sysctl: missing value\n\n");
|
||||
return 0;
|
||||
}
|
||||
*s = '\0';
|
||||
i = strtol(s+1, NULL, 0);
|
||||
sysctlbyname(av[2], NULL, NULL, &i, sizeof(int));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Save arguments for final freeing of memory. */
|
||||
save_av = av;
|
||||
|
||||
optind = optreset = 1; /* restart getopt() */
|
||||
if (is_ipfw()) {
|
||||
while ((ch = getopt(ac, av, "abcdDefhinNp:qs:STtvx")) != -1)
|
||||
switch (ch) {
|
||||
case 'a':
|
||||
do_acct = 1;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
g_co.comment_only = 1;
|
||||
g_co.do_compact = 1;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
g_co.do_compact = 1;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
g_co.do_dynamic = 1;
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
g_co.do_dynamic = 2;
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
/* nop for compatibility */
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
g_co.do_force = 1;
|
||||
break;
|
||||
|
||||
case 'h': /* help */
|
||||
free(save_av);
|
||||
help();
|
||||
break; /* NOTREACHED */
|
||||
|
||||
case 'i':
|
||||
g_co.do_value_as_ip = 1;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
g_co.test_only = 1;
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
g_co.do_resolv = 1;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
errx(EX_USAGE, "An absolute pathname must be used "
|
||||
"with -p option.");
|
||||
/* NOTREACHED */
|
||||
|
||||
case 'q':
|
||||
g_co.do_quiet = 1;
|
||||
break;
|
||||
|
||||
case 's': /* sort */
|
||||
g_co.do_sort = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
g_co.show_sets = 1;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
g_co.do_time = TIMESTAMP_STRING;
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
g_co.do_time = TIMESTAMP_NUMERIC;
|
||||
break;
|
||||
|
||||
case 'v': /* verbose */
|
||||
g_co.verbose = 1;
|
||||
break;
|
||||
|
||||
case 'x': /* debug output */
|
||||
g_co.debug_only = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
free(save_av);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
while ((ch = getopt(ac, av, "hns:v")) != -1)
|
||||
switch (ch) {
|
||||
|
||||
case 'h': /* help */
|
||||
free(save_av);
|
||||
help();
|
||||
break; /* NOTREACHED */
|
||||
|
||||
case 'n':
|
||||
g_co.test_only = 1;
|
||||
break;
|
||||
|
||||
case 's': /* sort */
|
||||
g_co.do_sort = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'v': /* verbose */
|
||||
g_co.verbose = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
free(save_av);
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ac -= optind;
|
||||
av += optind;
|
||||
NEED1("bad arguments, for usage summary ``ipfw''");
|
||||
|
||||
/*
|
||||
* An undocumented behaviour of ipfw1 was to allow rule numbers first,
|
||||
* e.g. "100 add allow ..." instead of "add 100 allow ...".
|
||||
* In case, swap first and second argument to get the normal form.
|
||||
*/
|
||||
if (ac > 1 && isdigit(*av[0])) {
|
||||
char *p = av[0];
|
||||
|
||||
av[0] = av[1];
|
||||
av[1] = p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Optional: pipe, queue or nat.
|
||||
*/
|
||||
g_co.do_nat = 0;
|
||||
g_co.do_pipe = 0;
|
||||
g_co.use_set = 0;
|
||||
if (is_ipfw() && !strncmp(*av, "nat", strlen(*av)))
|
||||
g_co.do_nat = 1;
|
||||
else if (!strncmp(*av, "pipe", strlen(*av)))
|
||||
g_co.do_pipe = 1;
|
||||
else if (_substrcmp(*av, "queue") == 0)
|
||||
g_co.do_pipe = 2;
|
||||
else if (_substrcmp(*av, "flowset") == 0)
|
||||
g_co.do_pipe = 2;
|
||||
else if (_substrcmp(*av, "sched") == 0)
|
||||
g_co.do_pipe = 3;
|
||||
else if (is_ipfw() && !strncmp(*av, "set", strlen(*av))) {
|
||||
if (ac > 1 && isdigit(av[1][0])) {
|
||||
g_co.use_set = strtonum(av[1], 0, resvd_set_number,
|
||||
&errstr);
|
||||
if (errstr)
|
||||
errx(EX_DATAERR,
|
||||
"invalid set number %s\n", av[1]);
|
||||
ac -= 2; av += 2; g_co.use_set++;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_co.do_pipe || g_co.do_nat) {
|
||||
ac--;
|
||||
av++;
|
||||
}
|
||||
NEED1("missing command");
|
||||
|
||||
/*
|
||||
* For pipes, queues and nats we normally say 'nat|pipe NN config'
|
||||
* but the code is easier to parse as 'nat|pipe config NN'
|
||||
* so we swap the two arguments.
|
||||
*/
|
||||
if ((g_co.do_pipe || g_co.do_nat) && ac > 1 && isdigit(*av[0])) {
|
||||
char *p = av[0];
|
||||
|
||||
av[0] = av[1];
|
||||
av[1] = p;
|
||||
}
|
||||
|
||||
if (! is_ipfw() && g_co.do_pipe == 0) {
|
||||
help();
|
||||
}
|
||||
|
||||
if (g_co.use_set == 0) {
|
||||
if (is_ipfw() && _substrcmp(*av, "add") == 0)
|
||||
ipfw_add(av);
|
||||
else if (g_co.do_nat && _substrcmp(*av, "show") == 0)
|
||||
ipfw_show_nat(ac, av);
|
||||
else if (g_co.do_pipe && _substrcmp(*av, "config") == 0)
|
||||
ipfw_config_pipe(ac, av);
|
||||
else if (g_co.do_nat && _substrcmp(*av, "config") == 0)
|
||||
ipfw_config_nat(ac, av);
|
||||
else if (is_ipfw() && _substrcmp(*av, "set") == 0)
|
||||
ipfw_sets_handler(av);
|
||||
else if (is_ipfw() && _substrcmp(*av, "table") == 0)
|
||||
ipfw_table_handler(ac, av);
|
||||
else if (is_ipfw() && _substrcmp(*av, "enable") == 0)
|
||||
ipfw_sysctl_handler(av, 1);
|
||||
else if (is_ipfw() && _substrcmp(*av, "disable") == 0)
|
||||
ipfw_sysctl_handler(av, 0);
|
||||
else
|
||||
try_next = 1;
|
||||
}
|
||||
|
||||
if (g_co.use_set || try_next) {
|
||||
if (_substrcmp(*av, "delete") == 0)
|
||||
ipfw_delete(av);
|
||||
else if (is_ipfw() && !strncmp(*av, "nat64clat", strlen(*av)))
|
||||
ipfw_nat64clat_handler(ac, av);
|
||||
else if (is_ipfw() && !strncmp(*av, "nat64stl", strlen(*av)))
|
||||
ipfw_nat64stl_handler(ac, av);
|
||||
else if (is_ipfw() && !strncmp(*av, "nat64lsn", strlen(*av)))
|
||||
ipfw_nat64lsn_handler(ac, av);
|
||||
else if (is_ipfw() && !strncmp(*av, "nptv6", strlen(*av)))
|
||||
ipfw_nptv6_handler(ac, av);
|
||||
else if (_substrcmp(*av, "flush") == 0)
|
||||
ipfw_flush(g_co.do_force);
|
||||
else if (is_ipfw() && _substrcmp(*av, "zero") == 0)
|
||||
ipfw_zero(ac, av, 0 /* IP_FW_ZERO */);
|
||||
else if (is_ipfw() && _substrcmp(*av, "resetlog") == 0)
|
||||
ipfw_zero(ac, av, 1 /* IP_FW_RESETLOG */);
|
||||
else if (_substrcmp(*av, "print") == 0 ||
|
||||
_substrcmp(*av, "list") == 0)
|
||||
ipfw_list(ac, av, do_acct);
|
||||
else if (_substrcmp(*av, "show") == 0)
|
||||
ipfw_list(ac, av, 1 /* show counters */);
|
||||
else if (is_ipfw() && _substrcmp(*av, "table") == 0)
|
||||
ipfw_table_handler(ac, av);
|
||||
else if (is_ipfw() && _substrcmp(*av, "internal") == 0)
|
||||
ipfw_internal_handler(ac, av);
|
||||
else
|
||||
errx(EX_USAGE, "bad command `%s'", *av);
|
||||
}
|
||||
|
||||
/* Free memory allocated in the argument parsing. */
|
||||
free(save_av);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ipfw_readfile(int ac, char *av[])
|
||||
{
|
||||
#define MAX_ARGS 32
|
||||
char buf[4096];
|
||||
char *progname = av[0]; /* original program name */
|
||||
const char *cmd = NULL; /* preprocessor name, if any */
|
||||
const char *filename = av[ac-1]; /* file to read */
|
||||
int c, lineno=0;
|
||||
FILE *f = NULL;
|
||||
pid_t preproc = 0;
|
||||
|
||||
if (is_ipfw()) {
|
||||
while ((c = getopt(ac, av, "cfNnp:qS")) != -1) {
|
||||
switch(c) {
|
||||
case 'c':
|
||||
g_co.do_compact = 1;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
g_co.do_force = 1;
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
g_co.do_resolv = 1;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
g_co.test_only = 1;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
/*
|
||||
* ipfw -p cmd [args] filename
|
||||
*
|
||||
* We are done with getopt(). All arguments
|
||||
* except the filename go to the preprocessor,
|
||||
* so we need to do the following:
|
||||
* - check that a filename is actually present;
|
||||
* - advance av by optind-1 to skip arguments
|
||||
* already processed;
|
||||
* - decrease ac by optind, to remove the args
|
||||
* already processed and the final filename;
|
||||
* - set the last entry in av[] to NULL so
|
||||
* popen() can detect the end of the array;
|
||||
* - set optind=ac to let getopt() terminate.
|
||||
*/
|
||||
if (optind == ac)
|
||||
errx(EX_USAGE, "no filename argument");
|
||||
cmd = optarg;
|
||||
av[ac-1] = NULL;
|
||||
av += optind - 1;
|
||||
ac -= optind;
|
||||
optind = ac;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
g_co.do_quiet = 1;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
g_co.show_sets = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
errx(EX_USAGE, "bad arguments, for usage"
|
||||
" summary ``ipfw''");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while ((c = getopt(ac, av, "nq")) != -1) {
|
||||
switch(c) {
|
||||
case 'n':
|
||||
g_co.test_only = 1;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
g_co.do_quiet = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
errx(EX_USAGE, "bad arguments, for usage"
|
||||
" summary ``dnctl''");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd == NULL && ac != optind + 1)
|
||||
errx(EX_USAGE, "extraneous filename arguments %s", av[ac-1]);
|
||||
|
||||
if ((f = fopen(filename, "r")) == NULL)
|
||||
err(EX_UNAVAILABLE, "fopen: %s", filename);
|
||||
|
||||
if (cmd != NULL) { /* pipe through preprocessor */
|
||||
int pipedes[2];
|
||||
|
||||
if (pipe(pipedes) == -1)
|
||||
err(EX_OSERR, "cannot create pipe");
|
||||
|
||||
preproc = fork();
|
||||
if (preproc == -1)
|
||||
err(EX_OSERR, "cannot fork");
|
||||
|
||||
if (preproc == 0) {
|
||||
/*
|
||||
* Child, will run the preprocessor with the
|
||||
* file on stdin and the pipe on stdout.
|
||||
*/
|
||||
if (dup2(fileno(f), 0) == -1
|
||||
|| dup2(pipedes[1], 1) == -1)
|
||||
err(EX_OSERR, "dup2()");
|
||||
fclose(f);
|
||||
close(pipedes[1]);
|
||||
close(pipedes[0]);
|
||||
execvp(cmd, av);
|
||||
err(EX_OSERR, "execvp(%s) failed", cmd);
|
||||
} else { /* parent, will reopen f as the pipe */
|
||||
fclose(f);
|
||||
close(pipedes[1]);
|
||||
if ((f = fdopen(pipedes[0], "r")) == NULL) {
|
||||
int savederrno = errno;
|
||||
|
||||
(void)kill(preproc, SIGTERM);
|
||||
errno = savederrno;
|
||||
err(EX_OSERR, "fdopen()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), f)) { /* read commands */
|
||||
char linename[20];
|
||||
char *args[2];
|
||||
|
||||
lineno++;
|
||||
snprintf(linename, sizeof(linename), "Line %d", lineno);
|
||||
setprogname(linename); /* XXX */
|
||||
args[0] = progname;
|
||||
args[1] = buf;
|
||||
ipfw_main(2, args);
|
||||
}
|
||||
fclose(f);
|
||||
if (cmd != NULL) {
|
||||
int status;
|
||||
|
||||
if (waitpid(preproc, &status, 0) == -1)
|
||||
errx(EX_OSERR, "waitpid()");
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
|
||||
errx(EX_UNAVAILABLE,
|
||||
"preprocessor exited with status %d",
|
||||
WEXITSTATUS(status));
|
||||
else if (WIFSIGNALED(status))
|
||||
errx(EX_UNAVAILABLE,
|
||||
"preprocessor exited with signal %d",
|
||||
WTERMSIG(status));
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int ac, char *av[])
|
||||
{
|
||||
#if defined(_WIN32) && defined(TCC)
|
||||
{
|
||||
WSADATA wsaData;
|
||||
int ret=0;
|
||||
unsigned short wVersionRequested = MAKEWORD(2, 2);
|
||||
ret = WSAStartup(wVersionRequested, &wsaData);
|
||||
if (ret != 0) {
|
||||
/* Tell the user that we could not find a usable */
|
||||
/* Winsock DLL. */
|
||||
printf("WSAStartup failed with error: %d\n", ret);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (strcmp("dnctl", basename(av[0])) == 0)
|
||||
g_co.prog = cmdline_prog_dnctl;
|
||||
else if (strcmp("dnctl15", basename(av[0])) == 0)
|
||||
g_co.prog = cmdline_prog_dnctl;
|
||||
else
|
||||
g_co.prog = cmdline_prog_ipfw;
|
||||
|
||||
/*
|
||||
* If the last argument is an absolute pathname, interpret it
|
||||
* as a file to be preprocessed.
|
||||
*/
|
||||
|
||||
if (ac > 1 && av[ac - 1][0] == '/') {
|
||||
if (access(av[ac - 1], R_OK) == 0)
|
||||
ipfw_readfile(ac, av);
|
||||
else
|
||||
err(EX_USAGE, "pathname: %s", av[ac - 1]);
|
||||
} else {
|
||||
if (ipfw_main(ac, av)) {
|
||||
errx(EX_USAGE,
|
||||
"usage: %s [options]\n"
|
||||
"do \"%s -h\" or \"man %s\" for details", av[0],
|
||||
av[0], av[0]);
|
||||
}
|
||||
}
|
||||
return EX_OK;
|
||||
}
|
||||
+1196
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,536 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2019 Yandex LLC
|
||||
* Copyright (c) 2019 Andrey V. Elsukov <ae@FreeBSD.org>
|
||||
* Copyright (c) 2019 Boris N. Lytochkin <lytboris@gmail.com>
|
||||
*
|
||||
* 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 ``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 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.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "ipfw2.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip_fw15.h>
|
||||
#include <netinet6/ip_fw_nat64.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
typedef int (nat64clat_cb_t)(ipfw_nat64clat_cfg *i, const char *name,
|
||||
uint8_t set);
|
||||
static int nat64clat_foreach(nat64clat_cb_t *f, const char *name, uint8_t set,
|
||||
int sort);
|
||||
|
||||
static void nat64clat_create(const char *name, uint8_t set, int ac, char **av);
|
||||
static void nat64clat_config(const char *name, uint8_t set, int ac, char **av);
|
||||
static void nat64clat_destroy(const char *name, uint8_t set);
|
||||
static void nat64clat_stats(const char *name, uint8_t set);
|
||||
static void nat64clat_reset_stats(const char *name, uint8_t set);
|
||||
static int nat64clat_show_cb(ipfw_nat64clat_cfg *cfg, const char *name,
|
||||
uint8_t set);
|
||||
static int nat64clat_destroy_cb(ipfw_nat64clat_cfg *cfg, const char *name,
|
||||
uint8_t set);
|
||||
|
||||
static struct _s_x nat64cmds[] = {
|
||||
{ "create", TOK_CREATE },
|
||||
{ "config", TOK_CONFIG },
|
||||
{ "destroy", TOK_DESTROY },
|
||||
{ "list", TOK_LIST },
|
||||
{ "show", TOK_LIST },
|
||||
{ "stats", TOK_STATS },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
static struct _s_x nat64statscmds[] = {
|
||||
{ "reset", TOK_RESET },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* This one handles all nat64clat-related commands
|
||||
* ipfw [set N] nat64clat NAME {create | config} ...
|
||||
* ipfw [set N] nat64clat NAME stats [reset]
|
||||
* ipfw [set N] nat64clat {NAME | all} destroy
|
||||
* ipfw [set N] nat64clat {NAME | all} {list | show}
|
||||
*/
|
||||
#define nat64clat_check_name table_check_name
|
||||
void
|
||||
ipfw_nat64clat_handler(int ac, char *av[])
|
||||
{
|
||||
const char *name;
|
||||
int tcmd;
|
||||
uint8_t set;
|
||||
|
||||
if (g_co.use_set != 0)
|
||||
set = g_co.use_set - 1;
|
||||
else
|
||||
set = 0;
|
||||
ac--; av++;
|
||||
|
||||
NEED1("nat64clat needs instance name");
|
||||
name = *av;
|
||||
if (nat64clat_check_name(name) != 0) {
|
||||
if (strcmp(name, "all") == 0)
|
||||
name = NULL;
|
||||
else
|
||||
errx(EX_USAGE, "nat64clat instance name %s is invalid",
|
||||
name);
|
||||
}
|
||||
ac--; av++;
|
||||
NEED1("nat64clat needs command");
|
||||
|
||||
tcmd = get_token(nat64cmds, *av, "nat64clat command");
|
||||
if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST)
|
||||
errx(EX_USAGE, "nat64clat instance name required");
|
||||
switch (tcmd) {
|
||||
case TOK_CREATE:
|
||||
ac--; av++;
|
||||
nat64clat_create(name, set, ac, av);
|
||||
break;
|
||||
case TOK_CONFIG:
|
||||
ac--; av++;
|
||||
nat64clat_config(name, set, ac, av);
|
||||
break;
|
||||
case TOK_LIST:
|
||||
nat64clat_foreach(nat64clat_show_cb, name, set, 1);
|
||||
break;
|
||||
case TOK_DESTROY:
|
||||
if (name == NULL)
|
||||
nat64clat_foreach(nat64clat_destroy_cb, NULL, set, 0);
|
||||
else
|
||||
nat64clat_destroy(name, set);
|
||||
break;
|
||||
case TOK_STATS:
|
||||
ac--; av++;
|
||||
if (ac == 0) {
|
||||
nat64clat_stats(name, set);
|
||||
break;
|
||||
}
|
||||
tcmd = get_token(nat64statscmds, *av, "stats command");
|
||||
if (tcmd == TOK_RESET)
|
||||
nat64clat_reset_stats(name, set);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
nat64clat_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set)
|
||||
{
|
||||
|
||||
ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */
|
||||
ntlv->head.length = sizeof(ipfw_obj_ntlv);
|
||||
ntlv->idx = 1;
|
||||
ntlv->set = set;
|
||||
strlcpy(ntlv->name, name, sizeof(ntlv->name));
|
||||
}
|
||||
|
||||
static struct _s_x nat64newcmds[] = {
|
||||
{ "plat_prefix", TOK_PLAT_PREFIX },
|
||||
{ "clat_prefix", TOK_CLAT_PREFIX },
|
||||
{ "log", TOK_LOG },
|
||||
{ "-log", TOK_LOGOFF },
|
||||
{ "allow_private", TOK_PRIVATE },
|
||||
{ "-allow_private", TOK_PRIVATEOFF },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates new nat64clat instance
|
||||
* ipfw nat64clat <NAME> create clat_prefix <prefix> plat_prefix <prefix>
|
||||
* Request: [ ipfw_obj_lheader ipfw_nat64clat_cfg ]
|
||||
*/
|
||||
#define NAT64CLAT_HAS_CLAT_PREFIX 0x01
|
||||
#define NAT64CLAT_HAS_PLAT_PREFIX 0x02
|
||||
static void
|
||||
nat64clat_create(const char *name, uint8_t set, int ac, char *av[])
|
||||
{
|
||||
char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nat64clat_cfg)];
|
||||
ipfw_nat64clat_cfg *cfg;
|
||||
ipfw_obj_lheader *olh;
|
||||
int tcmd, flags;
|
||||
char *p;
|
||||
struct in6_addr prefix;
|
||||
uint8_t plen;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
olh = (ipfw_obj_lheader *)buf;
|
||||
cfg = (ipfw_nat64clat_cfg *)(olh + 1);
|
||||
|
||||
/* Some reasonable defaults */
|
||||
inet_pton(AF_INET6, "64:ff9b::", &cfg->plat_prefix);
|
||||
cfg->plat_plen = 96;
|
||||
cfg->set = set;
|
||||
flags = NAT64CLAT_HAS_PLAT_PREFIX;
|
||||
while (ac > 0) {
|
||||
tcmd = get_token(nat64newcmds, *av, "option");
|
||||
ac--; av++;
|
||||
|
||||
switch (tcmd) {
|
||||
case TOK_PLAT_PREFIX:
|
||||
case TOK_CLAT_PREFIX:
|
||||
if (tcmd == TOK_PLAT_PREFIX) {
|
||||
NEED1("IPv6 plat_prefix required");
|
||||
} else {
|
||||
NEED1("IPv6 clat_prefix required");
|
||||
}
|
||||
|
||||
if ((p = strchr(*av, '/')) != NULL)
|
||||
*p++ = '\0';
|
||||
if (inet_pton(AF_INET6, *av, &prefix) != 1)
|
||||
errx(EX_USAGE,
|
||||
"Bad prefix: %s", *av);
|
||||
plen = strtol(p, NULL, 10);
|
||||
if (ipfw_check_nat64prefix(&prefix, plen) != 0)
|
||||
errx(EX_USAGE,
|
||||
"Bad prefix length: %s", p);
|
||||
if (tcmd == TOK_PLAT_PREFIX) {
|
||||
flags |= NAT64CLAT_HAS_PLAT_PREFIX;
|
||||
cfg->plat_prefix = prefix;
|
||||
cfg->plat_plen = plen;
|
||||
} else {
|
||||
flags |= NAT64CLAT_HAS_CLAT_PREFIX;
|
||||
cfg->clat_prefix = prefix;
|
||||
cfg->clat_plen = plen;
|
||||
}
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_LOG:
|
||||
cfg->flags |= NAT64_LOG;
|
||||
break;
|
||||
case TOK_LOGOFF:
|
||||
cfg->flags &= ~NAT64_LOG;
|
||||
break;
|
||||
case TOK_PRIVATE:
|
||||
cfg->flags |= NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
case TOK_PRIVATEOFF:
|
||||
cfg->flags &= ~NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check validness */
|
||||
if ((flags & NAT64CLAT_HAS_PLAT_PREFIX) != NAT64CLAT_HAS_PLAT_PREFIX)
|
||||
errx(EX_USAGE, "plat_prefix required");
|
||||
if ((flags & NAT64CLAT_HAS_CLAT_PREFIX) != NAT64CLAT_HAS_CLAT_PREFIX)
|
||||
errx(EX_USAGE, "clat_prefix required");
|
||||
|
||||
olh->count = 1;
|
||||
olh->objsize = sizeof(*cfg);
|
||||
olh->size = sizeof(buf);
|
||||
strlcpy(cfg->name, name, sizeof(cfg->name));
|
||||
if (do_set3(IP_FW_NAT64CLAT_CREATE, &olh->opheader, sizeof(buf)) != 0)
|
||||
err(EX_OSERR, "nat64clat instance creation failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* Configures existing nat64clat instance
|
||||
* ipfw nat64clat <NAME> config <options>
|
||||
* Request: [ ipfw_obj_header ipfw_nat64clat_cfg ]
|
||||
*/
|
||||
static void
|
||||
nat64clat_config(const char *name, uint8_t set, int ac, char **av)
|
||||
{
|
||||
char buf[sizeof(ipfw_obj_header) + sizeof(ipfw_nat64clat_cfg)];
|
||||
ipfw_nat64clat_cfg *cfg;
|
||||
ipfw_obj_header *oh;
|
||||
char *opt;
|
||||
char *p;
|
||||
size_t sz;
|
||||
int tcmd;
|
||||
struct in6_addr prefix;
|
||||
uint8_t plen;
|
||||
|
||||
if (ac == 0)
|
||||
errx(EX_USAGE, "config options required");
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
oh = (ipfw_obj_header *)buf;
|
||||
cfg = (ipfw_nat64clat_cfg *)(oh + 1);
|
||||
sz = sizeof(buf);
|
||||
|
||||
nat64clat_fill_ntlv(&oh->ntlv, name, set);
|
||||
if (do_get3(IP_FW_NAT64CLAT_CONFIG, &oh->opheader, &sz) != 0)
|
||||
err(EX_OSERR, "failed to get config for instance %s", name);
|
||||
|
||||
while (ac > 0) {
|
||||
tcmd = get_token(nat64newcmds, *av, "option");
|
||||
opt = *av;
|
||||
ac--; av++;
|
||||
|
||||
switch (tcmd) {
|
||||
case TOK_PLAT_PREFIX:
|
||||
case TOK_CLAT_PREFIX:
|
||||
if (tcmd == TOK_PLAT_PREFIX) {
|
||||
NEED1("IPv6 plat_prefix required");
|
||||
} else {
|
||||
NEED1("IPv6 clat_prefix required");
|
||||
}
|
||||
|
||||
if ((p = strchr(*av, '/')) != NULL)
|
||||
*p++ = '\0';
|
||||
else
|
||||
errx(EX_USAGE,
|
||||
"Prefix length required: %s", *av);
|
||||
if (inet_pton(AF_INET6, *av, &prefix) != 1)
|
||||
errx(EX_USAGE,
|
||||
"Bad prefix: %s", *av);
|
||||
plen = strtol(p, NULL, 10);
|
||||
if (ipfw_check_nat64prefix(&prefix, plen) != 0)
|
||||
errx(EX_USAGE,
|
||||
"Bad prefix length: %s", p);
|
||||
if (tcmd == TOK_PLAT_PREFIX) {
|
||||
cfg->plat_prefix = prefix;
|
||||
cfg->plat_plen = plen;
|
||||
} else {
|
||||
cfg->clat_prefix = prefix;
|
||||
cfg->clat_plen = plen;
|
||||
}
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_LOG:
|
||||
cfg->flags |= NAT64_LOG;
|
||||
break;
|
||||
case TOK_LOGOFF:
|
||||
cfg->flags &= ~NAT64_LOG;
|
||||
break;
|
||||
case TOK_PRIVATE:
|
||||
cfg->flags |= NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
case TOK_PRIVATEOFF:
|
||||
cfg->flags &= ~NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
default:
|
||||
errx(EX_USAGE, "Can't change %s option", opt);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_set3(IP_FW_NAT64CLAT_CONFIG, &oh->opheader, sizeof(buf)) != 0)
|
||||
err(EX_OSERR, "nat64clat instance configuration failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroys nat64clat instance.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
*/
|
||||
static void
|
||||
nat64clat_destroy(const char *name, uint8_t set)
|
||||
{
|
||||
ipfw_obj_header oh;
|
||||
|
||||
memset(&oh, 0, sizeof(oh));
|
||||
nat64clat_fill_ntlv(&oh.ntlv, name, set);
|
||||
if (do_set3(IP_FW_NAT64CLAT_DESTROY, &oh.opheader, sizeof(oh)) != 0)
|
||||
err(EX_OSERR, "failed to destroy nat instance %s", name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get nat64clat instance statistics.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
* Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ]
|
||||
*/
|
||||
static int
|
||||
nat64clat_get_stats(const char *name, uint8_t set,
|
||||
struct ipfw_nat64clat_stats *stats)
|
||||
{
|
||||
ipfw_obj_header *oh;
|
||||
ipfw_obj_ctlv *oc;
|
||||
size_t sz;
|
||||
|
||||
sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats);
|
||||
oh = calloc(1, sz);
|
||||
nat64clat_fill_ntlv(&oh->ntlv, name, set);
|
||||
if (do_get3(IP_FW_NAT64CLAT_STATS, &oh->opheader, &sz) == 0) {
|
||||
oc = (ipfw_obj_ctlv *)(oh + 1);
|
||||
memcpy(stats, oc + 1, sizeof(*stats));
|
||||
free(oh);
|
||||
return (0);
|
||||
}
|
||||
free(oh);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static void
|
||||
nat64clat_stats(const char *name, uint8_t set)
|
||||
{
|
||||
struct ipfw_nat64clat_stats stats;
|
||||
|
||||
if (nat64clat_get_stats(name, set, &stats) != 0)
|
||||
err(EX_OSERR, "Error retrieving stats");
|
||||
|
||||
if (g_co.use_set != 0 || set != 0)
|
||||
printf("set %u ", set);
|
||||
printf("nat64clat %s\n", name);
|
||||
|
||||
printf("\t%ju packets translated from IPv6 to IPv4\n",
|
||||
(uintmax_t)stats.opcnt64);
|
||||
printf("\t%ju packets translated from IPv4 to IPv6\n",
|
||||
(uintmax_t)stats.opcnt46);
|
||||
printf("\t%ju IPv6 fragments created\n",
|
||||
(uintmax_t)stats.ofrags);
|
||||
printf("\t%ju IPv4 fragments received\n",
|
||||
(uintmax_t)stats.ifrags);
|
||||
printf("\t%ju output packets dropped due to no bufs, etc.\n",
|
||||
(uintmax_t)stats.oerrors);
|
||||
printf("\t%ju output packets discarded due to no IPv4 route\n",
|
||||
(uintmax_t)stats.noroute4);
|
||||
printf("\t%ju output packets discarded due to no IPv6 route\n",
|
||||
(uintmax_t)stats.noroute6);
|
||||
printf("\t%ju packets discarded due to unsupported protocol\n",
|
||||
(uintmax_t)stats.noproto);
|
||||
printf("\t%ju packets discarded due to memory allocation problems\n",
|
||||
(uintmax_t)stats.nomem);
|
||||
printf("\t%ju packets discarded due to some errors\n",
|
||||
(uintmax_t)stats.dropped);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset nat64clat instance statistics specified by @oh->ntlv.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
*/
|
||||
static void
|
||||
nat64clat_reset_stats(const char *name, uint8_t set)
|
||||
{
|
||||
ipfw_obj_header oh;
|
||||
|
||||
memset(&oh, 0, sizeof(oh));
|
||||
nat64clat_fill_ntlv(&oh.ntlv, name, set);
|
||||
if (do_set3(IP_FW_NAT64CLAT_RESET_STATS, &oh.opheader, sizeof(oh)) != 0)
|
||||
err(EX_OSERR, "failed to reset stats for instance %s", name);
|
||||
}
|
||||
|
||||
static int
|
||||
nat64clat_show_cb(ipfw_nat64clat_cfg *cfg, const char *name, uint8_t set)
|
||||
{
|
||||
char plat_buf[INET6_ADDRSTRLEN], clat_buf[INET6_ADDRSTRLEN];
|
||||
|
||||
if (name != NULL && strcmp(cfg->name, name) != 0)
|
||||
return (ESRCH);
|
||||
|
||||
if (g_co.use_set != 0 && cfg->set != set)
|
||||
return (ESRCH);
|
||||
|
||||
if (g_co.use_set != 0 || cfg->set != 0)
|
||||
printf("set %u ", cfg->set);
|
||||
|
||||
inet_ntop(AF_INET6, &cfg->clat_prefix, clat_buf, sizeof(clat_buf));
|
||||
inet_ntop(AF_INET6, &cfg->plat_prefix, plat_buf, sizeof(plat_buf));
|
||||
printf("nat64clat %s clat_prefix %s/%u plat_prefix %s/%u",
|
||||
cfg->name, clat_buf, cfg->clat_plen, plat_buf, cfg->plat_plen);
|
||||
if (cfg->flags & NAT64_LOG)
|
||||
printf(" log");
|
||||
if (cfg->flags & NAT64_ALLOW_PRIVATE)
|
||||
printf(" allow_private");
|
||||
printf("\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nat64clat_destroy_cb(ipfw_nat64clat_cfg *cfg, const char *name __unused,
|
||||
uint8_t set)
|
||||
{
|
||||
|
||||
if (g_co.use_set != 0 && cfg->set != set)
|
||||
return (ESRCH);
|
||||
|
||||
nat64clat_destroy(cfg->name, cfg->set);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compare nat64clat instances names.
|
||||
* Honor number comparison.
|
||||
*/
|
||||
static int
|
||||
nat64name_cmp(const void *a, const void *b)
|
||||
{
|
||||
const ipfw_nat64clat_cfg *ca, *cb;
|
||||
|
||||
ca = (const ipfw_nat64clat_cfg *)a;
|
||||
cb = (const ipfw_nat64clat_cfg *)b;
|
||||
|
||||
if (ca->set > cb->set)
|
||||
return (1);
|
||||
else if (ca->set < cb->set)
|
||||
return (-1);
|
||||
return (stringnum_cmp(ca->name, cb->name));
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves nat64clat instance list from kernel,
|
||||
* optionally sorts it and calls requested function for each instance.
|
||||
*
|
||||
* Request: [ ipfw_obj_lheader ]
|
||||
* Reply: [ ipfw_obj_lheader ipfw_nat64clat_cfg x N ]
|
||||
*/
|
||||
static int
|
||||
nat64clat_foreach(nat64clat_cb_t *f, const char *name, uint8_t set, int sort)
|
||||
{
|
||||
ipfw_obj_lheader *olh;
|
||||
ipfw_nat64clat_cfg *cfg;
|
||||
size_t sz;
|
||||
uint32_t i;
|
||||
|
||||
/* Start with reasonable default */
|
||||
sz = sizeof(*olh) + 16 * sizeof(*cfg);
|
||||
for (;;) {
|
||||
if ((olh = calloc(1, sz)) == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
olh->size = sz;
|
||||
if (do_get3(IP_FW_NAT64CLAT_LIST, &olh->opheader, &sz) != 0) {
|
||||
sz = olh->size;
|
||||
free(olh);
|
||||
if (errno != ENOMEM)
|
||||
return (errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sort != 0)
|
||||
qsort(olh + 1, olh->count, olh->objsize,
|
||||
nat64name_cmp);
|
||||
|
||||
cfg = (ipfw_nat64clat_cfg *)(olh + 1);
|
||||
for (i = 0; i < olh->count; i++) {
|
||||
(void)f(cfg, name, set); /* Ignore errors for now */
|
||||
cfg = (ipfw_nat64clat_cfg *)((caddr_t)cfg +
|
||||
olh->objsize);
|
||||
}
|
||||
free(olh);
|
||||
break;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,901 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2015-2019 Yandex LLC
|
||||
* Copyright (c) 2015-2016 Alexander V. Chernikov <melifaro@FreeBSD.org>
|
||||
* Copyright (c) 2015-2019 Andrey V. Elsukov <ae@FreeBSD.org>
|
||||
*
|
||||
* 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 ``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 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.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "ipfw2.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip_fw15.h>
|
||||
#include <netinet6/ip_fw_nat64_15.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
static void nat64lsn_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name,
|
||||
uint8_t set);
|
||||
typedef int (nat64lsn_cb_t)(ipfw_nat64lsn_cfg *cfg, const char *name,
|
||||
uint8_t set);
|
||||
static int nat64lsn_foreach(nat64lsn_cb_t *f, const char *name, uint8_t set,
|
||||
int sort);
|
||||
|
||||
static void nat64lsn_create(const char *name, uint8_t set, int ac, char **av);
|
||||
static void nat64lsn_config(const char *name, uint8_t set, int ac, char **av);
|
||||
static void nat64lsn_destroy(const char *name, uint8_t set);
|
||||
static void nat64lsn_stats(const char *name, uint8_t set);
|
||||
static void nat64lsn_reset_stats(const char *name, uint8_t set);
|
||||
static int nat64lsn_show_cb(ipfw_nat64lsn_cfg *cfg, const char *name,
|
||||
uint8_t set);
|
||||
static int nat64lsn_destroy_cb(ipfw_nat64lsn_cfg *cfg, const char *name,
|
||||
uint8_t set);
|
||||
static int nat64lsn_states_cb(ipfw_nat64lsn_cfg *cfg, const char *name,
|
||||
uint8_t set);
|
||||
|
||||
static struct _s_x nat64cmds[] = {
|
||||
{ "create", TOK_CREATE },
|
||||
{ "config", TOK_CONFIG },
|
||||
{ "destroy", TOK_DESTROY },
|
||||
{ "list", TOK_LIST },
|
||||
{ "show", TOK_LIST },
|
||||
{ "stats", TOK_STATS },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
static uint64_t
|
||||
nat64lsn_print_states(void *buf)
|
||||
{
|
||||
char s[INET6_ADDRSTRLEN], a[INET_ADDRSTRLEN], f[INET_ADDRSTRLEN];
|
||||
const char *proto;
|
||||
char sflags[4], *sf;
|
||||
ipfw_obj_header *oh;
|
||||
ipfw_obj_data *od;
|
||||
ipfw_nat64lsn_stg_v1 *stg;
|
||||
ipfw_nat64lsn_state_v1 *ste;
|
||||
uint64_t next_idx;
|
||||
uint32_t i;
|
||||
int sz;
|
||||
|
||||
oh = (ipfw_obj_header *)buf;
|
||||
od = (ipfw_obj_data *)(oh + 1);
|
||||
stg = (ipfw_nat64lsn_stg_v1 *)(od + 1);
|
||||
sz = od->head.length - sizeof(*od);
|
||||
next_idx = 0;
|
||||
proto = NULL;
|
||||
while (sz > 0 && next_idx != 0xFF) {
|
||||
next_idx = stg->next.index;
|
||||
sz -= sizeof(*stg);
|
||||
if (stg->count == 0) {
|
||||
stg++;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* NOTE: addresses are in network byte order,
|
||||
* ports are in host byte order.
|
||||
*/
|
||||
inet_ntop(AF_INET, &stg->alias4, a, sizeof(a));
|
||||
ste = (ipfw_nat64lsn_state_v1 *)(stg + 1);
|
||||
for (i = 0; i < stg->count && sz > 0; i++) {
|
||||
sf = sflags;
|
||||
inet_ntop(AF_INET6, &ste->host6, s, sizeof(s));
|
||||
inet_ntop(AF_INET, &ste->daddr, f, sizeof(f));
|
||||
switch (ste->proto) {
|
||||
case IPPROTO_TCP:
|
||||
proto = "TCP";
|
||||
if (ste->flags & 0x02)
|
||||
*sf++ = 'S';
|
||||
if (ste->flags & 0x04)
|
||||
*sf++ = 'E';
|
||||
if (ste->flags & 0x01)
|
||||
*sf++ = 'F';
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
proto = "UDP";
|
||||
break;
|
||||
case IPPROTO_ICMP:
|
||||
proto = "ICMPv6";
|
||||
break;
|
||||
}
|
||||
*sf = '\0';
|
||||
switch (ste->proto) {
|
||||
case IPPROTO_TCP:
|
||||
case IPPROTO_UDP:
|
||||
printf("%s:%d\t%s:%d\t%s\t%s\t%d\t%s:%d\n",
|
||||
s, ste->sport, a, ste->aport, proto,
|
||||
sflags, ste->idle, f, ste->dport);
|
||||
break;
|
||||
case IPPROTO_ICMP:
|
||||
printf("%s\t%s\t%s\t\t%d\t%s\n",
|
||||
s, a, proto, ste->idle, f);
|
||||
break;
|
||||
default:
|
||||
printf("%s\t%s\t%d\t\t%d\t%s\n",
|
||||
s, a, ste->proto, ste->idle, f);
|
||||
}
|
||||
ste++;
|
||||
sz -= sizeof(*ste);
|
||||
}
|
||||
stg = (ipfw_nat64lsn_stg_v1 *)ste;
|
||||
}
|
||||
return (next_idx);
|
||||
}
|
||||
|
||||
static int
|
||||
nat64lsn_states_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set)
|
||||
{
|
||||
ipfw_obj_header *oh;
|
||||
ipfw_obj_data *od;
|
||||
void *buf;
|
||||
uint64_t next_idx;
|
||||
size_t sz;
|
||||
|
||||
if (name != NULL && strcmp(cfg->name, name) != 0)
|
||||
return (ESRCH);
|
||||
|
||||
if (set != 0 && cfg->set != set)
|
||||
return (ESRCH);
|
||||
|
||||
next_idx = 0;
|
||||
sz = 4096;
|
||||
if ((buf = calloc(1, sz)) == NULL)
|
||||
err(EX_OSERR, NULL);
|
||||
do {
|
||||
oh = (ipfw_obj_header *)buf;
|
||||
oh->opheader.version = 1; /* Force using ov new API */
|
||||
od = (ipfw_obj_data *)(oh + 1);
|
||||
nat64lsn_fill_ntlv(&oh->ntlv, cfg->name, set);
|
||||
od->head.type = IPFW_TLV_OBJDATA;
|
||||
od->head.length = sizeof(*od) + sizeof(next_idx);
|
||||
*((uint64_t *)(od + 1)) = next_idx;
|
||||
if (do_get3(IP_FW_NAT64LSN_LIST_STATES, &oh->opheader, &sz))
|
||||
err(EX_OSERR, "Error reading nat64lsn states");
|
||||
next_idx = nat64lsn_print_states(buf);
|
||||
sz = 4096;
|
||||
memset(buf, 0, sz);
|
||||
} while (next_idx != 0xFF);
|
||||
|
||||
free(buf);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static struct _s_x nat64statscmds[] = {
|
||||
{ "reset", TOK_RESET },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
static void
|
||||
ipfw_nat64lsn_stats_handler(const char *name, uint8_t set, int ac, char *av[])
|
||||
{
|
||||
int tcmd;
|
||||
|
||||
if (ac == 0) {
|
||||
nat64lsn_stats(name, set);
|
||||
return;
|
||||
}
|
||||
NEED1("nat64lsn stats needs command");
|
||||
tcmd = get_token(nat64statscmds, *av, "nat64lsn stats command");
|
||||
switch (tcmd) {
|
||||
case TOK_RESET:
|
||||
nat64lsn_reset_stats(name, set);
|
||||
}
|
||||
}
|
||||
|
||||
static struct _s_x nat64listcmds[] = {
|
||||
{ "states", TOK_STATES },
|
||||
{ "config", TOK_CONFIG },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
static void
|
||||
ipfw_nat64lsn_list_handler(const char *name, uint8_t set, int ac, char *av[])
|
||||
{
|
||||
int tcmd;
|
||||
|
||||
if (ac == 0) {
|
||||
nat64lsn_foreach(nat64lsn_show_cb, name, set, 1);
|
||||
return;
|
||||
}
|
||||
NEED1("nat64lsn list needs command");
|
||||
tcmd = get_token(nat64listcmds, *av, "nat64lsn list command");
|
||||
switch (tcmd) {
|
||||
case TOK_STATES:
|
||||
nat64lsn_foreach(nat64lsn_states_cb, name, set, 1);
|
||||
break;
|
||||
case TOK_CONFIG:
|
||||
nat64lsn_foreach(nat64lsn_show_cb, name, set, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This one handles all nat64lsn-related commands
|
||||
* ipfw [set N] nat64lsn NAME {create | config} ...
|
||||
* ipfw [set N] nat64lsn NAME stats
|
||||
* ipfw [set N] nat64lsn {NAME | all} destroy
|
||||
* ipfw [set N] nat64lsn {NAME | all} {list | show} [config | states]
|
||||
*/
|
||||
#define nat64lsn_check_name table_check_name
|
||||
void
|
||||
ipfw_nat64lsn_handler(int ac, char *av[])
|
||||
{
|
||||
const char *name;
|
||||
int tcmd;
|
||||
uint8_t set;
|
||||
|
||||
if (g_co.use_set != 0)
|
||||
set = g_co.use_set - 1;
|
||||
else
|
||||
set = 0;
|
||||
ac--; av++;
|
||||
|
||||
NEED1("nat64lsn needs instance name");
|
||||
name = *av;
|
||||
if (nat64lsn_check_name(name) != 0) {
|
||||
if (strcmp(name, "all") == 0)
|
||||
name = NULL;
|
||||
else
|
||||
errx(EX_USAGE, "nat64lsn instance name %s is invalid",
|
||||
name);
|
||||
}
|
||||
ac--; av++;
|
||||
NEED1("nat64lsn needs command");
|
||||
|
||||
tcmd = get_token(nat64cmds, *av, "nat64lsn command");
|
||||
if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST)
|
||||
errx(EX_USAGE, "nat64lsn instance name required");
|
||||
switch (tcmd) {
|
||||
case TOK_CREATE:
|
||||
ac--; av++;
|
||||
nat64lsn_create(name, set, ac, av);
|
||||
break;
|
||||
case TOK_CONFIG:
|
||||
ac--; av++;
|
||||
nat64lsn_config(name, set, ac, av);
|
||||
break;
|
||||
case TOK_LIST:
|
||||
ac--; av++;
|
||||
ipfw_nat64lsn_list_handler(name, set, ac, av);
|
||||
break;
|
||||
case TOK_DESTROY:
|
||||
if (name == NULL)
|
||||
nat64lsn_foreach(nat64lsn_destroy_cb, NULL, set, 0);
|
||||
else
|
||||
nat64lsn_destroy(name, set);
|
||||
break;
|
||||
case TOK_STATS:
|
||||
ac--; av++;
|
||||
ipfw_nat64lsn_stats_handler(name, set, ac, av);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nat64lsn_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set)
|
||||
{
|
||||
|
||||
ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */
|
||||
ntlv->head.length = sizeof(ipfw_obj_ntlv);
|
||||
ntlv->idx = 1;
|
||||
ntlv->set = set;
|
||||
strlcpy(ntlv->name, name, sizeof(ntlv->name));
|
||||
}
|
||||
|
||||
static void
|
||||
nat64lsn_apply_mask(int af, void *prefix, uint16_t plen)
|
||||
{
|
||||
struct in6_addr mask6, *p6;
|
||||
struct in_addr mask4, *p4;
|
||||
|
||||
if (af == AF_INET) {
|
||||
p4 = (struct in_addr *)prefix;
|
||||
mask4.s_addr = htonl(~((1 << (32 - plen)) - 1));
|
||||
p4->s_addr &= mask4.s_addr;
|
||||
} else if (af == AF_INET6) {
|
||||
p6 = (struct in6_addr *)prefix;
|
||||
n2mask(&mask6, plen);
|
||||
APPLY_MASK(p6, &mask6);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nat64lsn_parse_prefix(const char *arg, int af, void *prefix, uint16_t *plen)
|
||||
{
|
||||
char *p, *l;
|
||||
|
||||
p = strdup(arg);
|
||||
if (p == NULL)
|
||||
err(EX_OSERR, NULL);
|
||||
if ((l = strchr(p, '/')) != NULL)
|
||||
*l++ = '\0';
|
||||
if (l == NULL)
|
||||
errx(EX_USAGE, "Prefix length required");
|
||||
if (inet_pton(af, p, prefix) != 1)
|
||||
errx(EX_USAGE, "Bad prefix: %s", p);
|
||||
*plen = (uint16_t)strtol(l, &l, 10);
|
||||
if (*l != '\0' || *plen == 0 || (af == AF_INET && *plen > 32) ||
|
||||
(af == AF_INET6 && *plen > 96))
|
||||
errx(EX_USAGE, "Bad prefix length: %s", arg);
|
||||
nat64lsn_apply_mask(af, prefix, *plen);
|
||||
free(p);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
nat64lsn_parse_int(const char *arg, const char *desc)
|
||||
{
|
||||
char *p;
|
||||
uint32_t val;
|
||||
|
||||
val = (uint32_t)strtol(arg, &p, 10);
|
||||
if (*p != '\0')
|
||||
errx(EX_USAGE, "Invalid %s value: %s\n", desc, arg);
|
||||
return (val);
|
||||
}
|
||||
|
||||
static struct _s_x nat64newcmds[] = {
|
||||
{ "prefix6", TOK_PREFIX6 },
|
||||
{ "jmaxlen", TOK_JMAXLEN },
|
||||
{ "prefix4", TOK_PREFIX4 },
|
||||
{ "host_del_age", TOK_HOST_DEL_AGE },
|
||||
{ "pg_del_age", TOK_PG_DEL_AGE },
|
||||
{ "tcp_syn_age", TOK_TCP_SYN_AGE },
|
||||
{ "tcp_close_age",TOK_TCP_CLOSE_AGE },
|
||||
{ "tcp_est_age", TOK_TCP_EST_AGE },
|
||||
{ "udp_age", TOK_UDP_AGE },
|
||||
{ "icmp_age", TOK_ICMP_AGE },
|
||||
{ "states_chunks",TOK_STATES_CHUNKS },
|
||||
{ "log", TOK_LOG },
|
||||
{ "-log", TOK_LOGOFF },
|
||||
{ "allow_private", TOK_PRIVATE },
|
||||
{ "-allow_private", TOK_PRIVATEOFF },
|
||||
{ "swap_conf", TOK_SWAPCONF },
|
||||
{ "-swap_conf", TOK_SWAPCONFOFF },
|
||||
/* for compatibility with old configurations */
|
||||
{ "max_ports", TOK_MAX_PORTS }, /* unused */
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates new nat64lsn instance
|
||||
* ipfw nat64lsn <NAME> create
|
||||
* [ max_ports <N> ]
|
||||
* Request: [ ipfw_obj_lheader ipfw_nat64lsn_cfg ]
|
||||
*/
|
||||
#define NAT64LSN_HAS_PREFIX4 0x01
|
||||
#define NAT64LSN_HAS_PREFIX6 0x02
|
||||
static void
|
||||
nat64lsn_create(const char *name, uint8_t set, int ac, char **av)
|
||||
{
|
||||
char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nat64lsn_cfg)];
|
||||
ipfw_nat64lsn_cfg *cfg;
|
||||
ipfw_obj_lheader *olh;
|
||||
int tcmd, flags;
|
||||
char *opt;
|
||||
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
olh = (ipfw_obj_lheader *)buf;
|
||||
cfg = (ipfw_nat64lsn_cfg *)(olh + 1);
|
||||
|
||||
/* Some reasonable defaults */
|
||||
inet_pton(AF_INET6, "64:ff9b::", &cfg->prefix6);
|
||||
cfg->plen6 = 96;
|
||||
cfg->set = set;
|
||||
cfg->max_ports = NAT64LSN_MAX_PORTS;
|
||||
cfg->jmaxlen = NAT64LSN_JMAXLEN;
|
||||
cfg->nh_delete_delay = NAT64LSN_HOST_AGE;
|
||||
cfg->pg_delete_delay = NAT64LSN_PG_AGE;
|
||||
cfg->st_syn_ttl = NAT64LSN_TCP_SYN_AGE;
|
||||
cfg->st_estab_ttl = NAT64LSN_TCP_EST_AGE;
|
||||
cfg->st_close_ttl = NAT64LSN_TCP_FIN_AGE;
|
||||
cfg->st_udp_ttl = NAT64LSN_UDP_AGE;
|
||||
cfg->st_icmp_ttl = NAT64LSN_ICMP_AGE;
|
||||
flags = NAT64LSN_HAS_PREFIX6;
|
||||
while (ac > 0) {
|
||||
tcmd = get_token(nat64newcmds, *av, "option");
|
||||
opt = *av;
|
||||
ac--; av++;
|
||||
|
||||
switch (tcmd) {
|
||||
case TOK_PREFIX4:
|
||||
NEED1("IPv4 prefix required");
|
||||
nat64lsn_parse_prefix(*av, AF_INET, &cfg->prefix4,
|
||||
&cfg->plen4);
|
||||
flags |= NAT64LSN_HAS_PREFIX4;
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_PREFIX6:
|
||||
NEED1("IPv6 prefix required");
|
||||
nat64lsn_parse_prefix(*av, AF_INET6, &cfg->prefix6,
|
||||
&cfg->plen6);
|
||||
if (ipfw_check_nat64prefix(&cfg->prefix6,
|
||||
cfg->plen6) != 0 &&
|
||||
!IN6_IS_ADDR_UNSPECIFIED(&cfg->prefix6))
|
||||
errx(EX_USAGE, "Bad prefix6 %s", *av);
|
||||
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_JMAXLEN:
|
||||
NEED1("job queue length required");
|
||||
cfg->jmaxlen = nat64lsn_parse_int(*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_MAX_PORTS:
|
||||
NEED1("Max per-user ports required");
|
||||
cfg->max_ports = nat64lsn_parse_int(*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_HOST_DEL_AGE:
|
||||
NEED1("host delete delay required");
|
||||
cfg->nh_delete_delay = (uint16_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_PG_DEL_AGE:
|
||||
NEED1("portgroup delete delay required");
|
||||
cfg->pg_delete_delay = (uint16_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_TCP_SYN_AGE:
|
||||
NEED1("tcp syn age required");
|
||||
cfg->st_syn_ttl = (uint16_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_TCP_CLOSE_AGE:
|
||||
NEED1("tcp close age required");
|
||||
cfg->st_close_ttl = (uint16_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_TCP_EST_AGE:
|
||||
NEED1("tcp est age required");
|
||||
cfg->st_estab_ttl = (uint16_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_UDP_AGE:
|
||||
NEED1("udp age required");
|
||||
cfg->st_udp_ttl = (uint16_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_ICMP_AGE:
|
||||
NEED1("icmp age required");
|
||||
cfg->st_icmp_ttl = (uint16_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_STATES_CHUNKS:
|
||||
NEED1("number of chunks required");
|
||||
cfg->states_chunks = (uint8_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_LOG:
|
||||
cfg->flags |= NAT64_LOG;
|
||||
break;
|
||||
case TOK_LOGOFF:
|
||||
cfg->flags &= ~NAT64_LOG;
|
||||
break;
|
||||
case TOK_PRIVATE:
|
||||
cfg->flags |= NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
case TOK_PRIVATEOFF:
|
||||
cfg->flags &= ~NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
case TOK_SWAPCONF:
|
||||
cfg->flags |= NAT64LSN_ALLOW_SWAPCONF;
|
||||
break;
|
||||
case TOK_SWAPCONFOFF:
|
||||
cfg->flags &= ~NAT64LSN_ALLOW_SWAPCONF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check validness */
|
||||
if ((flags & NAT64LSN_HAS_PREFIX4) != NAT64LSN_HAS_PREFIX4)
|
||||
errx(EX_USAGE, "prefix4 required");
|
||||
|
||||
olh->count = 1;
|
||||
olh->objsize = sizeof(*cfg);
|
||||
olh->size = sizeof(buf);
|
||||
strlcpy(cfg->name, name, sizeof(cfg->name));
|
||||
if (do_set3(IP_FW_NAT64LSN_CREATE, &olh->opheader, sizeof(buf)) != 0)
|
||||
err(EX_OSERR, "nat64lsn instance creation failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* Configures existing nat64lsn instance
|
||||
* ipfw nat64lsn <NAME> config <options>
|
||||
* Request: [ ipfw_obj_header ipfw_nat64lsn_cfg ]
|
||||
*/
|
||||
static void
|
||||
nat64lsn_config(const char *name, uint8_t set, int ac, char **av)
|
||||
{
|
||||
char buf[sizeof(ipfw_obj_header) + sizeof(ipfw_nat64lsn_cfg)];
|
||||
ipfw_nat64lsn_cfg *cfg;
|
||||
ipfw_obj_header *oh;
|
||||
size_t sz;
|
||||
char *opt;
|
||||
int tcmd;
|
||||
|
||||
if (ac == 0)
|
||||
errx(EX_USAGE, "config options required");
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
oh = (ipfw_obj_header *)buf;
|
||||
cfg = (ipfw_nat64lsn_cfg *)(oh + 1);
|
||||
sz = sizeof(buf);
|
||||
|
||||
nat64lsn_fill_ntlv(&oh->ntlv, name, set);
|
||||
if (do_get3(IP_FW_NAT64LSN_CONFIG, &oh->opheader, &sz) != 0)
|
||||
err(EX_OSERR, "failed to get config for instance %s", name);
|
||||
|
||||
while (ac > 0) {
|
||||
tcmd = get_token(nat64newcmds, *av, "option");
|
||||
opt = *av;
|
||||
ac--; av++;
|
||||
|
||||
switch (tcmd) {
|
||||
case TOK_MAX_PORTS:
|
||||
NEED1("Max per-user ports required");
|
||||
cfg->max_ports = nat64lsn_parse_int(*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_JMAXLEN:
|
||||
NEED1("job queue length required");
|
||||
cfg->jmaxlen = nat64lsn_parse_int(*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_HOST_DEL_AGE:
|
||||
NEED1("host delete delay required");
|
||||
cfg->nh_delete_delay = (uint16_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_PG_DEL_AGE:
|
||||
NEED1("portgroup delete delay required");
|
||||
cfg->pg_delete_delay = (uint16_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_TCP_SYN_AGE:
|
||||
NEED1("tcp syn age required");
|
||||
cfg->st_syn_ttl = (uint16_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_TCP_CLOSE_AGE:
|
||||
NEED1("tcp close age required");
|
||||
cfg->st_close_ttl = (uint16_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_TCP_EST_AGE:
|
||||
NEED1("tcp est age required");
|
||||
cfg->st_estab_ttl = (uint16_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_UDP_AGE:
|
||||
NEED1("udp age required");
|
||||
cfg->st_udp_ttl = (uint16_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_ICMP_AGE:
|
||||
NEED1("icmp age required");
|
||||
cfg->st_icmp_ttl = (uint16_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_STATES_CHUNKS:
|
||||
NEED1("number of chunks required");
|
||||
cfg->states_chunks = (uint8_t)nat64lsn_parse_int(
|
||||
*av, opt);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_LOG:
|
||||
cfg->flags |= NAT64_LOG;
|
||||
break;
|
||||
case TOK_LOGOFF:
|
||||
cfg->flags &= ~NAT64_LOG;
|
||||
break;
|
||||
case TOK_PRIVATE:
|
||||
cfg->flags |= NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
case TOK_PRIVATEOFF:
|
||||
cfg->flags &= ~NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
case TOK_SWAPCONF:
|
||||
cfg->flags |= NAT64LSN_ALLOW_SWAPCONF;
|
||||
break;
|
||||
case TOK_SWAPCONFOFF:
|
||||
cfg->flags &= ~NAT64LSN_ALLOW_SWAPCONF;
|
||||
break;
|
||||
default:
|
||||
errx(EX_USAGE, "Can't change %s option", opt);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_set3(IP_FW_NAT64LSN_CONFIG, &oh->opheader, sizeof(buf)) != 0)
|
||||
err(EX_OSERR, "nat64lsn instance configuration failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset nat64lsn instance statistics specified by @oh->ntlv.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
*/
|
||||
static void
|
||||
nat64lsn_reset_stats(const char *name, uint8_t set)
|
||||
{
|
||||
ipfw_obj_header oh;
|
||||
|
||||
memset(&oh, 0, sizeof(oh));
|
||||
nat64lsn_fill_ntlv(&oh.ntlv, name, set);
|
||||
if (do_set3(IP_FW_NAT64LSN_RESET_STATS, &oh.opheader, sizeof(oh)) != 0)
|
||||
err(EX_OSERR, "failed to reset stats for instance %s", name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroys nat64lsn instance specified by @oh->ntlv.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
*/
|
||||
static void
|
||||
nat64lsn_destroy(const char *name, uint8_t set)
|
||||
{
|
||||
ipfw_obj_header oh;
|
||||
|
||||
memset(&oh, 0, sizeof(oh));
|
||||
nat64lsn_fill_ntlv(&oh.ntlv, name, set);
|
||||
if (do_set3(IP_FW_NAT64LSN_DESTROY, &oh.opheader, sizeof(oh)) != 0)
|
||||
err(EX_OSERR, "failed to destroy nat instance %s", name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get nat64lsn instance statistics.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
* Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ]
|
||||
*/
|
||||
static int
|
||||
nat64lsn_get_stats(const char *name, uint8_t set,
|
||||
struct ipfw_nat64lsn_stats *stats)
|
||||
{
|
||||
ipfw_obj_header *oh;
|
||||
ipfw_obj_ctlv *oc;
|
||||
size_t sz;
|
||||
|
||||
sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats);
|
||||
oh = calloc(1, sz);
|
||||
nat64lsn_fill_ntlv(&oh->ntlv, name, set);
|
||||
if (do_get3(IP_FW_NAT64LSN_STATS, &oh->opheader, &sz) == 0) {
|
||||
oc = (ipfw_obj_ctlv *)(oh + 1);
|
||||
memcpy(stats, oc + 1, sizeof(*stats));
|
||||
free(oh);
|
||||
return (0);
|
||||
}
|
||||
free(oh);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static void
|
||||
nat64lsn_stats(const char *name, uint8_t set)
|
||||
{
|
||||
struct ipfw_nat64lsn_stats stats;
|
||||
|
||||
if (nat64lsn_get_stats(name, set, &stats) != 0)
|
||||
err(EX_OSERR, "Error retrieving stats");
|
||||
|
||||
if (g_co.use_set != 0 || set != 0)
|
||||
printf("set %u ", set);
|
||||
printf("nat64lsn %s\n", name);
|
||||
printf("\t%ju packets translated from IPv6 to IPv4\n",
|
||||
(uintmax_t)stats.opcnt64);
|
||||
printf("\t%ju packets translated from IPv4 to IPv6\n",
|
||||
(uintmax_t)stats.opcnt46);
|
||||
printf("\t%ju IPv6 fragments created\n",
|
||||
(uintmax_t)stats.ofrags);
|
||||
printf("\t%ju IPv4 fragments received\n",
|
||||
(uintmax_t)stats.ifrags);
|
||||
printf("\t%ju output packets dropped due to no bufs, etc.\n",
|
||||
(uintmax_t)stats.oerrors);
|
||||
printf("\t%ju output packets discarded due to no IPv4 route\n",
|
||||
(uintmax_t)stats.noroute4);
|
||||
printf("\t%ju output packets discarded due to no IPv6 route\n",
|
||||
(uintmax_t)stats.noroute6);
|
||||
printf("\t%ju packets discarded due to unsupported protocol\n",
|
||||
(uintmax_t)stats.noproto);
|
||||
printf("\t%ju packets discarded due to memory allocation problems\n",
|
||||
(uintmax_t)stats.nomem);
|
||||
printf("\t%ju packets discarded due to some errors\n",
|
||||
(uintmax_t)stats.dropped);
|
||||
printf("\t%ju packets not matched with IPv4 prefix\n",
|
||||
(uintmax_t)stats.nomatch4);
|
||||
|
||||
printf("\t%ju mbufs queued for post processing\n",
|
||||
(uintmax_t)stats.jreinjected);
|
||||
printf("\t%ju times the job queue was processed\n",
|
||||
(uintmax_t)stats.jcalls);
|
||||
printf("\t%ju job requests queued\n",
|
||||
(uintmax_t)stats.jrequests);
|
||||
printf("\t%ju job requests queue limit reached\n",
|
||||
(uintmax_t)stats.jmaxlen);
|
||||
printf("\t%ju job requests failed due to memory allocation problems\n",
|
||||
(uintmax_t)stats.jnomem);
|
||||
|
||||
printf("\t%ju hosts allocated\n", (uintmax_t)stats.hostcount);
|
||||
printf("\t%ju hosts requested\n", (uintmax_t)stats.jhostsreq);
|
||||
printf("\t%ju host requests failed\n", (uintmax_t)stats.jhostfails);
|
||||
|
||||
printf("\t%ju portgroups requested\n", (uintmax_t)stats.jportreq);
|
||||
printf("\t%ju portgroups allocated\n", (uintmax_t)stats.spgcreated);
|
||||
printf("\t%ju portgroups deleted\n", (uintmax_t)stats.spgdeleted);
|
||||
printf("\t%ju portgroup requests failed\n",
|
||||
(uintmax_t)stats.jportfails);
|
||||
printf("\t%ju portgroups allocated for TCP\n",
|
||||
(uintmax_t)stats.tcpchunks);
|
||||
printf("\t%ju portgroups allocated for UDP\n",
|
||||
(uintmax_t)stats.udpchunks);
|
||||
printf("\t%ju portgroups allocated for ICMP\n",
|
||||
(uintmax_t)stats.icmpchunks);
|
||||
|
||||
printf("\t%ju states created\n", (uintmax_t)stats.screated);
|
||||
printf("\t%ju states deleted\n", (uintmax_t)stats.sdeleted);
|
||||
}
|
||||
|
||||
static int
|
||||
nat64lsn_show_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set)
|
||||
{
|
||||
char abuf[INET6_ADDRSTRLEN];
|
||||
|
||||
if (name != NULL && strcmp(cfg->name, name) != 0)
|
||||
return (ESRCH);
|
||||
|
||||
if (g_co.use_set != 0 && cfg->set != set)
|
||||
return (ESRCH);
|
||||
|
||||
if (g_co.use_set != 0 || cfg->set != 0)
|
||||
printf("set %u ", cfg->set);
|
||||
inet_ntop(AF_INET, &cfg->prefix4, abuf, sizeof(abuf));
|
||||
printf("nat64lsn %s prefix4 %s/%u", cfg->name, abuf, cfg->plen4);
|
||||
inet_ntop(AF_INET6, &cfg->prefix6, abuf, sizeof(abuf));
|
||||
printf(" prefix6 %s/%u", abuf, cfg->plen6);
|
||||
if (g_co.verbose || cfg->states_chunks > 1)
|
||||
printf(" states_chunks %u", cfg->states_chunks);
|
||||
if (g_co.verbose || cfg->nh_delete_delay != NAT64LSN_HOST_AGE)
|
||||
printf(" host_del_age %u", cfg->nh_delete_delay);
|
||||
if (g_co.verbose || cfg->pg_delete_delay != NAT64LSN_PG_AGE)
|
||||
printf(" pg_del_age %u", cfg->pg_delete_delay);
|
||||
if (g_co.verbose || cfg->st_syn_ttl != NAT64LSN_TCP_SYN_AGE)
|
||||
printf(" tcp_syn_age %u", cfg->st_syn_ttl);
|
||||
if (g_co.verbose || cfg->st_close_ttl != NAT64LSN_TCP_FIN_AGE)
|
||||
printf(" tcp_close_age %u", cfg->st_close_ttl);
|
||||
if (g_co.verbose || cfg->st_estab_ttl != NAT64LSN_TCP_EST_AGE)
|
||||
printf(" tcp_est_age %u", cfg->st_estab_ttl);
|
||||
if (g_co.verbose || cfg->st_udp_ttl != NAT64LSN_UDP_AGE)
|
||||
printf(" udp_age %u", cfg->st_udp_ttl);
|
||||
if (g_co.verbose || cfg->st_icmp_ttl != NAT64LSN_ICMP_AGE)
|
||||
printf(" icmp_age %u", cfg->st_icmp_ttl);
|
||||
if (g_co.verbose || cfg->jmaxlen != NAT64LSN_JMAXLEN)
|
||||
printf(" jmaxlen %u", cfg->jmaxlen);
|
||||
if (cfg->flags & NAT64LSN_ALLOW_SWAPCONF)
|
||||
printf(" swap_conf");
|
||||
if (cfg->flags & NAT64_LOG)
|
||||
printf(" log");
|
||||
if (cfg->flags & NAT64_ALLOW_PRIVATE)
|
||||
printf(" allow_private");
|
||||
printf("\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nat64lsn_destroy_cb(ipfw_nat64lsn_cfg *cfg, const char *name __unused,
|
||||
uint8_t set)
|
||||
{
|
||||
|
||||
if (g_co.use_set != 0 && cfg->set != set)
|
||||
return (ESRCH);
|
||||
|
||||
nat64lsn_destroy(cfg->name, cfg->set);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compare nat64lsn instances names.
|
||||
* Honor number comparison.
|
||||
*/
|
||||
static int
|
||||
nat64name_cmp(const void *a, const void *b)
|
||||
{
|
||||
const ipfw_nat64lsn_cfg *ca, *cb;
|
||||
|
||||
ca = (const ipfw_nat64lsn_cfg *)a;
|
||||
cb = (const ipfw_nat64lsn_cfg *)b;
|
||||
|
||||
if (ca->set > cb->set)
|
||||
return (1);
|
||||
else if (ca->set < cb->set)
|
||||
return (-1);
|
||||
return (stringnum_cmp(ca->name, cb->name));
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves nat64lsn instance list from kernel,
|
||||
* optionally sorts it and calls requested function for each instance.
|
||||
*
|
||||
* Request: [ ipfw_obj_lheader ]
|
||||
* Reply: [ ipfw_obj_lheader ipfw_nat64lsn_cfg x N ]
|
||||
*/
|
||||
static int
|
||||
nat64lsn_foreach(nat64lsn_cb_t *f, const char *name, uint8_t set, int sort)
|
||||
{
|
||||
ipfw_obj_lheader *olh;
|
||||
ipfw_nat64lsn_cfg *cfg;
|
||||
size_t sz;
|
||||
uint32_t i;
|
||||
|
||||
/* Start with reasonable default */
|
||||
sz = sizeof(*olh) + 16 * sizeof(ipfw_nat64lsn_cfg);
|
||||
|
||||
for (;;) {
|
||||
if ((olh = calloc(1, sz)) == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
olh->size = sz;
|
||||
if (do_get3(IP_FW_NAT64LSN_LIST, &olh->opheader, &sz) != 0) {
|
||||
sz = olh->size;
|
||||
free(olh);
|
||||
if (errno != ENOMEM)
|
||||
return (errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sort != 0)
|
||||
qsort(olh + 1, olh->count, olh->objsize,
|
||||
nat64name_cmp);
|
||||
|
||||
cfg = (ipfw_nat64lsn_cfg *)(olh + 1);
|
||||
for (i = 0; i < olh->count; i++) {
|
||||
(void)f(cfg, name, set); /* Ignore errors for now */
|
||||
cfg = (ipfw_nat64lsn_cfg *)((caddr_t)cfg +
|
||||
olh->objsize);
|
||||
}
|
||||
free(olh);
|
||||
break;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,552 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2015-2019 Yandex LLC
|
||||
* Copyright (c) 2015-2019 Andrey V. Elsukov <ae@FreeBSD.org>
|
||||
*
|
||||
* 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 ``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 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.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "ipfw2.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip_fw15.h>
|
||||
#include <netinet6/ip_fw_nat64_15.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
typedef int (nat64stl_cb_t)(ipfw_nat64stl_cfg *i, const char *name,
|
||||
uint8_t set);
|
||||
static int nat64stl_foreach(nat64stl_cb_t *f, const char *name, uint8_t set,
|
||||
int sort);
|
||||
|
||||
static void nat64stl_create(const char *name, uint8_t set, int ac, char **av);
|
||||
static void nat64stl_config(const char *name, uint8_t set, int ac, char **av);
|
||||
static void nat64stl_destroy(const char *name, uint8_t set);
|
||||
static void nat64stl_stats(const char *name, uint8_t set);
|
||||
static void nat64stl_reset_stats(const char *name, uint8_t set);
|
||||
static int nat64stl_show_cb(ipfw_nat64stl_cfg *cfg, const char *name,
|
||||
uint8_t set);
|
||||
static int nat64stl_destroy_cb(ipfw_nat64stl_cfg *cfg, const char *name,
|
||||
uint8_t set);
|
||||
|
||||
static struct _s_x nat64cmds[] = {
|
||||
{ "create", TOK_CREATE },
|
||||
{ "config", TOK_CONFIG },
|
||||
{ "destroy", TOK_DESTROY },
|
||||
{ "list", TOK_LIST },
|
||||
{ "show", TOK_LIST },
|
||||
{ "stats", TOK_STATS },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
#define IPV6_ADDR_INT32_WKPFX htonl(0x64ff9b)
|
||||
#define IN6_IS_ADDR_WKPFX(a) \
|
||||
((a)->__u6_addr.__u6_addr32[0] == IPV6_ADDR_INT32_WKPFX && \
|
||||
(a)->__u6_addr.__u6_addr32[1] == 0 && \
|
||||
(a)->__u6_addr.__u6_addr32[2] == 0)
|
||||
int
|
||||
ipfw_check_nat64prefix(const struct in6_addr *prefix, int length)
|
||||
{
|
||||
|
||||
switch (length) {
|
||||
case 32:
|
||||
case 40:
|
||||
case 48:
|
||||
case 56:
|
||||
case 64:
|
||||
/* Well-known prefix has 96 prefix length */
|
||||
if (IN6_IS_ADDR_WKPFX(prefix))
|
||||
return (EINVAL);
|
||||
/* FALLTHROUGH */
|
||||
case 96:
|
||||
/* Bits 64 to 71 must be set to zero */
|
||||
if (prefix->__u6_addr.__u6_addr8[8] != 0)
|
||||
return (EINVAL);
|
||||
/* XXX: looks incorrect */
|
||||
if (IN6_IS_ADDR_MULTICAST(prefix) ||
|
||||
IN6_IS_ADDR_UNSPECIFIED(prefix) ||
|
||||
IN6_IS_ADDR_LOOPBACK(prefix))
|
||||
return (EINVAL);
|
||||
return (0);
|
||||
}
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
static struct _s_x nat64statscmds[] = {
|
||||
{ "reset", TOK_RESET },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* This one handles all nat64stl-related commands
|
||||
* ipfw [set N] nat64stl NAME {create | config} ...
|
||||
* ipfw [set N] nat64stl NAME stats [reset]
|
||||
* ipfw [set N] nat64stl {NAME | all} destroy
|
||||
* ipfw [set N] nat64stl {NAME | all} {list | show}
|
||||
*/
|
||||
#define nat64stl_check_name table_check_name
|
||||
void
|
||||
ipfw_nat64stl_handler(int ac, char *av[])
|
||||
{
|
||||
const char *name;
|
||||
int tcmd;
|
||||
uint8_t set;
|
||||
|
||||
if (g_co.use_set != 0)
|
||||
set = g_co.use_set - 1;
|
||||
else
|
||||
set = 0;
|
||||
ac--; av++;
|
||||
|
||||
NEED1("nat64stl needs instance name");
|
||||
name = *av;
|
||||
if (nat64stl_check_name(name) != 0) {
|
||||
if (strcmp(name, "all") == 0)
|
||||
name = NULL;
|
||||
else
|
||||
errx(EX_USAGE, "nat64stl instance name %s is invalid",
|
||||
name);
|
||||
}
|
||||
ac--; av++;
|
||||
NEED1("nat64stl needs command");
|
||||
|
||||
tcmd = get_token(nat64cmds, *av, "nat64stl command");
|
||||
if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST)
|
||||
errx(EX_USAGE, "nat64stl instance name required");
|
||||
switch (tcmd) {
|
||||
case TOK_CREATE:
|
||||
ac--; av++;
|
||||
nat64stl_create(name, set, ac, av);
|
||||
break;
|
||||
case TOK_CONFIG:
|
||||
ac--; av++;
|
||||
nat64stl_config(name, set, ac, av);
|
||||
break;
|
||||
case TOK_LIST:
|
||||
nat64stl_foreach(nat64stl_show_cb, name, set, 1);
|
||||
break;
|
||||
case TOK_DESTROY:
|
||||
if (name == NULL)
|
||||
nat64stl_foreach(nat64stl_destroy_cb, NULL, set, 0);
|
||||
else
|
||||
nat64stl_destroy(name, set);
|
||||
break;
|
||||
case TOK_STATS:
|
||||
ac--; av++;
|
||||
if (ac == 0) {
|
||||
nat64stl_stats(name, set);
|
||||
break;
|
||||
}
|
||||
tcmd = get_token(nat64statscmds, *av, "stats command");
|
||||
if (tcmd == TOK_RESET)
|
||||
nat64stl_reset_stats(name, set);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
nat64stl_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set)
|
||||
{
|
||||
|
||||
ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */
|
||||
ntlv->head.length = sizeof(ipfw_obj_ntlv);
|
||||
ntlv->idx = 1;
|
||||
ntlv->set = set;
|
||||
strlcpy(ntlv->name, name, sizeof(ntlv->name));
|
||||
}
|
||||
|
||||
static struct _s_x nat64newcmds[] = {
|
||||
{ "table4", TOK_TABLE4 },
|
||||
{ "table6", TOK_TABLE6 },
|
||||
{ "prefix6", TOK_PREFIX6 },
|
||||
{ "log", TOK_LOG },
|
||||
{ "-log", TOK_LOGOFF },
|
||||
{ "allow_private", TOK_PRIVATE },
|
||||
{ "-allow_private", TOK_PRIVATEOFF },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates new nat64stl instance
|
||||
* ipfw nat64stl <NAME> create table4 <name> table6 <name> [ prefix6 <prefix>]
|
||||
* Request: [ ipfw_obj_lheader ipfw_nat64stl_cfg ]
|
||||
*/
|
||||
#define NAT64STL_HAS_TABLE4 0x01
|
||||
#define NAT64STL_HAS_TABLE6 0x02
|
||||
#define NAT64STL_HAS_PREFIX6 0x04
|
||||
static void
|
||||
nat64stl_create(const char *name, uint8_t set, int ac, char *av[])
|
||||
{
|
||||
char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nat64stl_cfg)];
|
||||
ipfw_nat64stl_cfg *cfg;
|
||||
ipfw_obj_lheader *olh;
|
||||
int tcmd, flags;
|
||||
char *p;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
olh = (ipfw_obj_lheader *)buf;
|
||||
cfg = (ipfw_nat64stl_cfg *)(olh + 1);
|
||||
|
||||
/* Some reasonable defaults */
|
||||
inet_pton(AF_INET6, "64:ff9b::", &cfg->prefix6);
|
||||
cfg->plen6 = 96;
|
||||
cfg->set = set;
|
||||
flags = NAT64STL_HAS_PREFIX6;
|
||||
while (ac > 0) {
|
||||
tcmd = get_token(nat64newcmds, *av, "option");
|
||||
ac--; av++;
|
||||
|
||||
switch (tcmd) {
|
||||
case TOK_TABLE4:
|
||||
NEED1("table name required");
|
||||
table_fill_ntlv(&cfg->ntlv4, *av, set, 4);
|
||||
flags |= NAT64STL_HAS_TABLE4;
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_TABLE6:
|
||||
NEED1("table name required");
|
||||
table_fill_ntlv(&cfg->ntlv6, *av, set, 6);
|
||||
flags |= NAT64STL_HAS_TABLE6;
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_PREFIX6:
|
||||
NEED1("IPv6 prefix6 required");
|
||||
if ((p = strchr(*av, '/')) != NULL)
|
||||
*p++ = '\0';
|
||||
else
|
||||
errx(EX_USAGE,
|
||||
"Prefix length required: %s", *av);
|
||||
if (inet_pton(AF_INET6, *av, &cfg->prefix6) != 1)
|
||||
errx(EX_USAGE,
|
||||
"Bad prefix: %s", *av);
|
||||
cfg->plen6 = strtol(p, NULL, 10);
|
||||
if (ipfw_check_nat64prefix(&cfg->prefix6,
|
||||
cfg->plen6) != 0)
|
||||
errx(EX_USAGE,
|
||||
"Bad prefix length: %s", p);
|
||||
flags |= NAT64STL_HAS_PREFIX6;
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_LOG:
|
||||
cfg->flags |= NAT64_LOG;
|
||||
break;
|
||||
case TOK_LOGOFF:
|
||||
cfg->flags &= ~NAT64_LOG;
|
||||
break;
|
||||
case TOK_PRIVATE:
|
||||
cfg->flags |= NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
case TOK_PRIVATEOFF:
|
||||
cfg->flags &= ~NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check validness */
|
||||
if ((flags & NAT64STL_HAS_TABLE4) != NAT64STL_HAS_TABLE4)
|
||||
errx(EX_USAGE, "table4 required");
|
||||
if ((flags & NAT64STL_HAS_TABLE6) != NAT64STL_HAS_TABLE6)
|
||||
errx(EX_USAGE, "table6 required");
|
||||
if ((flags & NAT64STL_HAS_PREFIX6) != NAT64STL_HAS_PREFIX6)
|
||||
errx(EX_USAGE, "prefix6 required");
|
||||
|
||||
olh->count = 1;
|
||||
olh->objsize = sizeof(*cfg);
|
||||
olh->size = sizeof(buf);
|
||||
strlcpy(cfg->name, name, sizeof(cfg->name));
|
||||
if (do_set3(IP_FW_NAT64STL_CREATE, &olh->opheader, sizeof(buf)) != 0)
|
||||
err(EX_OSERR, "nat64stl instance creation failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* Configures existing nat64stl instance
|
||||
* ipfw nat64stl <NAME> config <options>
|
||||
* Request: [ ipfw_obj_header ipfw_nat64stl_cfg ]
|
||||
*/
|
||||
static void
|
||||
nat64stl_config(const char *name, uint8_t set, int ac, char **av)
|
||||
{
|
||||
char buf[sizeof(ipfw_obj_header) + sizeof(ipfw_nat64stl_cfg)];
|
||||
ipfw_nat64stl_cfg *cfg;
|
||||
ipfw_obj_header *oh;
|
||||
char *opt;
|
||||
size_t sz;
|
||||
int tcmd;
|
||||
|
||||
if (ac == 0)
|
||||
errx(EX_USAGE, "config options required");
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
oh = (ipfw_obj_header *)buf;
|
||||
cfg = (ipfw_nat64stl_cfg *)(oh + 1);
|
||||
sz = sizeof(buf);
|
||||
|
||||
nat64stl_fill_ntlv(&oh->ntlv, name, set);
|
||||
if (do_get3(IP_FW_NAT64STL_CONFIG, &oh->opheader, &sz) != 0)
|
||||
err(EX_OSERR, "failed to get config for instance %s", name);
|
||||
|
||||
while (ac > 0) {
|
||||
tcmd = get_token(nat64newcmds, *av, "option");
|
||||
opt = *av;
|
||||
ac--; av++;
|
||||
|
||||
switch (tcmd) {
|
||||
#if 0
|
||||
case TOK_TABLE4:
|
||||
NEED1("table name required");
|
||||
table_fill_ntlv(&cfg->ntlv4, *av, set, 4);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_TABLE6:
|
||||
NEED1("table name required");
|
||||
table_fill_ntlv(&cfg->ntlv6, *av, set, 6);
|
||||
ac--; av++;
|
||||
break;
|
||||
#endif
|
||||
case TOK_LOG:
|
||||
cfg->flags |= NAT64_LOG;
|
||||
break;
|
||||
case TOK_LOGOFF:
|
||||
cfg->flags &= ~NAT64_LOG;
|
||||
break;
|
||||
case TOK_PRIVATE:
|
||||
cfg->flags |= NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
case TOK_PRIVATEOFF:
|
||||
cfg->flags &= ~NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
default:
|
||||
errx(EX_USAGE, "Can't change %s option", opt);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_set3(IP_FW_NAT64STL_CONFIG, &oh->opheader, sizeof(buf)) != 0)
|
||||
err(EX_OSERR, "nat64stl instance configuration failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroys nat64stl instance.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
*/
|
||||
static void
|
||||
nat64stl_destroy(const char *name, uint8_t set)
|
||||
{
|
||||
ipfw_obj_header oh;
|
||||
|
||||
memset(&oh, 0, sizeof(oh));
|
||||
nat64stl_fill_ntlv(&oh.ntlv, name, set);
|
||||
if (do_set3(IP_FW_NAT64STL_DESTROY, &oh.opheader, sizeof(oh)) != 0)
|
||||
err(EX_OSERR, "failed to destroy nat instance %s", name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get nat64stl instance statistics.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
* Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ]
|
||||
*/
|
||||
static int
|
||||
nat64stl_get_stats(const char *name, uint8_t set,
|
||||
struct ipfw_nat64stl_stats *stats)
|
||||
{
|
||||
ipfw_obj_header *oh;
|
||||
ipfw_obj_ctlv *oc;
|
||||
size_t sz;
|
||||
|
||||
sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats);
|
||||
oh = calloc(1, sz);
|
||||
nat64stl_fill_ntlv(&oh->ntlv, name, set);
|
||||
if (do_get3(IP_FW_NAT64STL_STATS, &oh->opheader, &sz) == 0) {
|
||||
oc = (ipfw_obj_ctlv *)(oh + 1);
|
||||
memcpy(stats, oc + 1, sizeof(*stats));
|
||||
free(oh);
|
||||
return (0);
|
||||
}
|
||||
free(oh);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static void
|
||||
nat64stl_stats(const char *name, uint8_t set)
|
||||
{
|
||||
struct ipfw_nat64stl_stats stats;
|
||||
|
||||
if (nat64stl_get_stats(name, set, &stats) != 0)
|
||||
err(EX_OSERR, "Error retrieving stats");
|
||||
|
||||
if (g_co.use_set != 0 || set != 0)
|
||||
printf("set %u ", set);
|
||||
printf("nat64stl %s\n", name);
|
||||
|
||||
printf("\t%ju packets translated from IPv6 to IPv4\n",
|
||||
(uintmax_t)stats.opcnt64);
|
||||
printf("\t%ju packets translated from IPv4 to IPv6\n",
|
||||
(uintmax_t)stats.opcnt46);
|
||||
printf("\t%ju IPv6 fragments created\n",
|
||||
(uintmax_t)stats.ofrags);
|
||||
printf("\t%ju IPv4 fragments received\n",
|
||||
(uintmax_t)stats.ifrags);
|
||||
printf("\t%ju output packets dropped due to no bufs, etc.\n",
|
||||
(uintmax_t)stats.oerrors);
|
||||
printf("\t%ju output packets discarded due to no IPv4 route\n",
|
||||
(uintmax_t)stats.noroute4);
|
||||
printf("\t%ju output packets discarded due to no IPv6 route\n",
|
||||
(uintmax_t)stats.noroute6);
|
||||
printf("\t%ju packets discarded due to unsupported protocol\n",
|
||||
(uintmax_t)stats.noproto);
|
||||
printf("\t%ju packets discarded due to memory allocation problems\n",
|
||||
(uintmax_t)stats.nomem);
|
||||
printf("\t%ju packets discarded due to some errors\n",
|
||||
(uintmax_t)stats.dropped);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset nat64stl instance statistics specified by @oh->ntlv.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
*/
|
||||
static void
|
||||
nat64stl_reset_stats(const char *name, uint8_t set)
|
||||
{
|
||||
ipfw_obj_header oh;
|
||||
|
||||
memset(&oh, 0, sizeof(oh));
|
||||
nat64stl_fill_ntlv(&oh.ntlv, name, set);
|
||||
if (do_set3(IP_FW_NAT64STL_RESET_STATS, &oh.opheader, sizeof(oh)) != 0)
|
||||
err(EX_OSERR, "failed to reset stats for instance %s", name);
|
||||
}
|
||||
|
||||
static int
|
||||
nat64stl_show_cb(ipfw_nat64stl_cfg *cfg, const char *name, uint8_t set)
|
||||
{
|
||||
char abuf[INET6_ADDRSTRLEN];
|
||||
|
||||
if (name != NULL && strcmp(cfg->name, name) != 0)
|
||||
return (ESRCH);
|
||||
|
||||
if (g_co.use_set != 0 && cfg->set != set)
|
||||
return (ESRCH);
|
||||
|
||||
if (g_co.use_set != 0 || cfg->set != 0)
|
||||
printf("set %u ", cfg->set);
|
||||
|
||||
printf("nat64stl %s table4 %s table6 %s",
|
||||
cfg->name, cfg->ntlv4.name, cfg->ntlv6.name);
|
||||
inet_ntop(AF_INET6, &cfg->prefix6, abuf, sizeof(abuf));
|
||||
printf(" prefix6 %s/%u", abuf, cfg->plen6);
|
||||
if (cfg->flags & NAT64_LOG)
|
||||
printf(" log");
|
||||
if (cfg->flags & NAT64_ALLOW_PRIVATE)
|
||||
printf(" allow_private");
|
||||
printf("\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nat64stl_destroy_cb(ipfw_nat64stl_cfg *cfg, const char *name __unused,
|
||||
uint8_t set)
|
||||
{
|
||||
|
||||
if (g_co.use_set != 0 && cfg->set != set)
|
||||
return (ESRCH);
|
||||
|
||||
nat64stl_destroy(cfg->name, cfg->set);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compare nat64stl instances names.
|
||||
* Honor number comparison.
|
||||
*/
|
||||
static int
|
||||
nat64name_cmp(const void *a, const void *b)
|
||||
{
|
||||
const ipfw_nat64stl_cfg *ca, *cb;
|
||||
|
||||
ca = (const ipfw_nat64stl_cfg *)a;
|
||||
cb = (const ipfw_nat64stl_cfg *)b;
|
||||
|
||||
if (ca->set > cb->set)
|
||||
return (1);
|
||||
else if (ca->set < cb->set)
|
||||
return (-1);
|
||||
return (stringnum_cmp(ca->name, cb->name));
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves nat64stl instance list from kernel,
|
||||
* optionally sorts it and calls requested function for each instance.
|
||||
*
|
||||
* Request: [ ipfw_obj_lheader ]
|
||||
* Reply: [ ipfw_obj_lheader ipfw_nat64stl_cfg x N ]
|
||||
*/
|
||||
static int
|
||||
nat64stl_foreach(nat64stl_cb_t *f, const char *name, uint8_t set, int sort)
|
||||
{
|
||||
ipfw_obj_lheader *olh;
|
||||
ipfw_nat64stl_cfg *cfg;
|
||||
size_t sz;
|
||||
uint32_t i;
|
||||
|
||||
/* Start with reasonable default */
|
||||
sz = sizeof(*olh) + 16 * sizeof(*cfg);
|
||||
for (;;) {
|
||||
if ((olh = calloc(1, sz)) == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
olh->size = sz;
|
||||
if (do_get3(IP_FW_NAT64STL_LIST, &olh->opheader, &sz) != 0) {
|
||||
sz = olh->size;
|
||||
free(olh);
|
||||
if (errno != ENOMEM)
|
||||
return (errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sort != 0)
|
||||
qsort(olh + 1, olh->count, olh->objsize,
|
||||
nat64name_cmp);
|
||||
|
||||
cfg = (ipfw_nat64stl_cfg *)(olh + 1);
|
||||
for (i = 0; i < olh->count; i++) {
|
||||
(void)f(cfg, name, set); /* Ignore errors for now */
|
||||
cfg = (ipfw_nat64stl_cfg *)((caddr_t)cfg +
|
||||
olh->objsize);
|
||||
}
|
||||
free(olh);
|
||||
break;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,452 @@
|
||||
/*-
|
||||
* Copyright (c) 2016 Yandex LLC
|
||||
* Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.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 ``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 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.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "ipfw2.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip_fw15.h>
|
||||
#include <netinet6/ip_fw_nptv6.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
|
||||
typedef int (nptv6_cb_t)(ipfw_nptv6_cfg *i, const char *name, uint8_t set);
|
||||
static int nptv6_foreach(nptv6_cb_t *f, const char *name, uint8_t set,
|
||||
int sort);
|
||||
|
||||
static void nptv6_create(const char *name, uint8_t set, int ac, char **av);
|
||||
static void nptv6_destroy(const char *name, uint8_t set);
|
||||
static void nptv6_stats(const char *name, uint8_t set);
|
||||
static void nptv6_reset_stats(const char *name, uint8_t set);
|
||||
static int nptv6_show_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set);
|
||||
static int nptv6_destroy_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set);
|
||||
|
||||
static struct _s_x nptv6cmds[] = {
|
||||
{ "create", TOK_CREATE },
|
||||
{ "destroy", TOK_DESTROY },
|
||||
{ "list", TOK_LIST },
|
||||
{ "show", TOK_LIST },
|
||||
{ "stats", TOK_STATS },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
static struct _s_x nptv6statscmds[] = {
|
||||
{ "reset", TOK_RESET },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* This one handles all NPTv6-related commands
|
||||
* ipfw [set N] nptv6 NAME {create | config} ...
|
||||
* ipfw [set N] nptv6 NAME stats [reset]
|
||||
* ipfw [set N] nptv6 {NAME | all} destroy
|
||||
* ipfw [set N] nptv6 {NAME | all} {list | show}
|
||||
*/
|
||||
#define nptv6_check_name table_check_name
|
||||
void
|
||||
ipfw_nptv6_handler(int ac, char *av[])
|
||||
{
|
||||
const char *name;
|
||||
int tcmd;
|
||||
uint8_t set;
|
||||
|
||||
if (g_co.use_set != 0)
|
||||
set = g_co.use_set - 1;
|
||||
else
|
||||
set = 0;
|
||||
ac--; av++;
|
||||
|
||||
NEED1("nptv6 needs instance name");
|
||||
name = *av;
|
||||
if (nptv6_check_name(name) != 0) {
|
||||
if (strcmp(name, "all") == 0) {
|
||||
name = NULL;
|
||||
} else
|
||||
errx(EX_USAGE, "nptv6 instance name %s is invalid",
|
||||
name);
|
||||
}
|
||||
ac--; av++;
|
||||
NEED1("nptv6 needs command");
|
||||
|
||||
tcmd = get_token(nptv6cmds, *av, "nptv6 command");
|
||||
if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST)
|
||||
errx(EX_USAGE, "nptv6 instance name required");
|
||||
switch (tcmd) {
|
||||
case TOK_CREATE:
|
||||
ac--; av++;
|
||||
nptv6_create(name, set, ac, av);
|
||||
break;
|
||||
case TOK_LIST:
|
||||
nptv6_foreach(nptv6_show_cb, name, set, 1);
|
||||
break;
|
||||
case TOK_DESTROY:
|
||||
if (name == NULL)
|
||||
nptv6_foreach(nptv6_destroy_cb, NULL, set, 0);
|
||||
else
|
||||
nptv6_destroy(name, set);
|
||||
break;
|
||||
case TOK_STATS:
|
||||
ac--; av++;
|
||||
if (ac == 0) {
|
||||
nptv6_stats(name, set);
|
||||
break;
|
||||
}
|
||||
tcmd = get_token(nptv6statscmds, *av, "stats command");
|
||||
if (tcmd == TOK_RESET)
|
||||
nptv6_reset_stats(name, set);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
nptv6_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set)
|
||||
{
|
||||
|
||||
ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */
|
||||
ntlv->head.length = sizeof(ipfw_obj_ntlv);
|
||||
ntlv->idx = 1;
|
||||
ntlv->set = set;
|
||||
strlcpy(ntlv->name, name, sizeof(ntlv->name));
|
||||
}
|
||||
|
||||
static struct _s_x nptv6newcmds[] = {
|
||||
{ "int_prefix", TOK_INTPREFIX },
|
||||
{ "ext_prefix", TOK_EXTPREFIX },
|
||||
{ "prefixlen", TOK_PREFIXLEN },
|
||||
{ "ext_if", TOK_EXTIF },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
static void
|
||||
nptv6_parse_prefix(const char *arg, struct in6_addr *prefix, int *len)
|
||||
{
|
||||
long plen;
|
||||
char *p, *l;
|
||||
|
||||
p = strdup(arg);
|
||||
if (p == NULL)
|
||||
err(EX_OSERR, NULL);
|
||||
if ((l = strchr(p, '/')) != NULL)
|
||||
*l++ = '\0';
|
||||
if (inet_pton(AF_INET6, p, prefix) != 1)
|
||||
errx(EX_USAGE, "Bad prefix: %s", p);
|
||||
if (l != NULL) {
|
||||
plen = strtol(l, &l, 10);
|
||||
if (*l != '\0' || plen < 8 || plen > 64)
|
||||
errx(EX_USAGE, "Bad prefix length: %s", arg);
|
||||
*len = plen;
|
||||
} else
|
||||
*len = 0;
|
||||
free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates new nptv6 instance
|
||||
* ipfw nptv6 <NAME> create int_prefix <prefix> ext_prefix <prefix>
|
||||
* Request: [ ipfw_obj_lheader ipfw_nptv6_cfg ]
|
||||
*/
|
||||
#define NPTV6_HAS_INTPREFIX 0x01
|
||||
#define NPTV6_HAS_EXTPREFIX 0x02
|
||||
#define NPTV6_HAS_PREFIXLEN 0x04
|
||||
static void
|
||||
nptv6_create(const char *name, uint8_t set, int ac, char *av[])
|
||||
{
|
||||
char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nptv6_cfg)];
|
||||
struct in6_addr mask;
|
||||
ipfw_nptv6_cfg *cfg;
|
||||
ipfw_obj_lheader *olh;
|
||||
int tcmd, flags, iplen, eplen, pplen;
|
||||
char *p;
|
||||
|
||||
iplen = eplen = pplen = 0;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
olh = (ipfw_obj_lheader *)buf;
|
||||
cfg = (ipfw_nptv6_cfg *)(olh + 1);
|
||||
cfg->set = set;
|
||||
flags = 0;
|
||||
while (ac > 0) {
|
||||
tcmd = get_token(nptv6newcmds, *av, "option");
|
||||
ac--; av++;
|
||||
|
||||
switch (tcmd) {
|
||||
case TOK_INTPREFIX:
|
||||
NEED1("IPv6 prefix required");
|
||||
nptv6_parse_prefix(*av, &cfg->internal, &iplen);
|
||||
flags |= NPTV6_HAS_INTPREFIX;
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_EXTPREFIX:
|
||||
if (flags & NPTV6_HAS_EXTPREFIX)
|
||||
errx(EX_USAGE,
|
||||
"Only one ext_prefix or ext_if allowed");
|
||||
NEED1("IPv6 prefix required");
|
||||
nptv6_parse_prefix(*av, &cfg->external, &eplen);
|
||||
flags |= NPTV6_HAS_EXTPREFIX;
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_EXTIF:
|
||||
if (flags & NPTV6_HAS_EXTPREFIX)
|
||||
errx(EX_USAGE,
|
||||
"Only one ext_prefix or ext_if allowed");
|
||||
NEED1("Interface name required");
|
||||
if (strlen(*av) >= sizeof(cfg->if_name))
|
||||
errx(EX_USAGE, "Invalid interface name");
|
||||
flags |= NPTV6_HAS_EXTPREFIX;
|
||||
cfg->flags |= NPTV6_DYNAMIC_PREFIX;
|
||||
strncpy(cfg->if_name, *av, sizeof(cfg->if_name));
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_PREFIXLEN:
|
||||
NEED1("IPv6 prefix length required");
|
||||
pplen = strtol(*av, &p, 10);
|
||||
if (*p != '\0' || pplen < 8 || pplen > 64)
|
||||
errx(EX_USAGE, "wrong prefix length: %s", *av);
|
||||
ac--; av++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* RFC 6296 Sec. 3.1 */
|
||||
if (pplen != 0) {
|
||||
if ((eplen != 0 && eplen != pplen) ||
|
||||
(iplen != 0 && iplen != pplen))
|
||||
errx(EX_USAGE, "prefix length mismatch");
|
||||
cfg->plen = pplen;
|
||||
flags |= NPTV6_HAS_PREFIXLEN;
|
||||
} else if (eplen != 0 || iplen != 0) {
|
||||
if (eplen != 0 && iplen != 0 && eplen != iplen)
|
||||
errx(EX_USAGE, "prefix length mismatch");
|
||||
warnx("use prefixlen instead");
|
||||
cfg->plen = eplen ? eplen : iplen;
|
||||
flags |= NPTV6_HAS_PREFIXLEN;
|
||||
}
|
||||
|
||||
/* Check validness */
|
||||
if ((flags & NPTV6_HAS_INTPREFIX) != NPTV6_HAS_INTPREFIX)
|
||||
errx(EX_USAGE, "int_prefix required");
|
||||
if ((flags & NPTV6_HAS_EXTPREFIX) != NPTV6_HAS_EXTPREFIX)
|
||||
errx(EX_USAGE, "ext_prefix or ext_if required");
|
||||
if ((flags & NPTV6_HAS_PREFIXLEN) != NPTV6_HAS_PREFIXLEN)
|
||||
errx(EX_USAGE, "prefixlen required");
|
||||
|
||||
n2mask(&mask, cfg->plen);
|
||||
APPLY_MASK(&cfg->internal, &mask);
|
||||
if ((cfg->flags & NPTV6_DYNAMIC_PREFIX) == 0)
|
||||
APPLY_MASK(&cfg->external, &mask);
|
||||
|
||||
olh->count = 1;
|
||||
olh->objsize = sizeof(*cfg);
|
||||
olh->size = sizeof(buf);
|
||||
strlcpy(cfg->name, name, sizeof(cfg->name));
|
||||
if (do_set3(IP_FW_NPTV6_CREATE, &olh->opheader, sizeof(buf)) != 0)
|
||||
err(EX_OSERR, "nptv6 instance creation failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroys NPTv6 instance.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
*/
|
||||
static void
|
||||
nptv6_destroy(const char *name, uint8_t set)
|
||||
{
|
||||
ipfw_obj_header oh;
|
||||
|
||||
memset(&oh, 0, sizeof(oh));
|
||||
nptv6_fill_ntlv(&oh.ntlv, name, set);
|
||||
if (do_set3(IP_FW_NPTV6_DESTROY, &oh.opheader, sizeof(oh)) != 0)
|
||||
err(EX_OSERR, "failed to destroy nat instance %s", name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get NPTv6 instance statistics.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
* Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ]
|
||||
*/
|
||||
static int
|
||||
nptv6_get_stats(const char *name, uint8_t set, struct ipfw_nptv6_stats *stats)
|
||||
{
|
||||
ipfw_obj_header *oh;
|
||||
ipfw_obj_ctlv *oc;
|
||||
size_t sz;
|
||||
|
||||
sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats);
|
||||
oh = calloc(1, sz);
|
||||
nptv6_fill_ntlv(&oh->ntlv, name, set);
|
||||
if (do_get3(IP_FW_NPTV6_STATS, &oh->opheader, &sz) == 0) {
|
||||
oc = (ipfw_obj_ctlv *)(oh + 1);
|
||||
memcpy(stats, oc + 1, sizeof(*stats));
|
||||
free(oh);
|
||||
return (0);
|
||||
}
|
||||
free(oh);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static void
|
||||
nptv6_stats(const char *name, uint8_t set)
|
||||
{
|
||||
struct ipfw_nptv6_stats stats;
|
||||
|
||||
if (nptv6_get_stats(name, set, &stats) != 0)
|
||||
err(EX_OSERR, "Error retrieving stats");
|
||||
|
||||
if (g_co.use_set != 0 || set != 0)
|
||||
printf("set %u ", set);
|
||||
printf("nptv6 %s\n", name);
|
||||
printf("\t%ju packets translated (internal to external)\n",
|
||||
(uintmax_t)stats.in2ex);
|
||||
printf("\t%ju packets translated (external to internal)\n",
|
||||
(uintmax_t)stats.ex2in);
|
||||
printf("\t%ju packets dropped due to some error\n",
|
||||
(uintmax_t)stats.dropped);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset NPTv6 instance statistics specified by @oh->ntlv.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
*/
|
||||
static void
|
||||
nptv6_reset_stats(const char *name, uint8_t set)
|
||||
{
|
||||
ipfw_obj_header oh;
|
||||
|
||||
memset(&oh, 0, sizeof(oh));
|
||||
nptv6_fill_ntlv(&oh.ntlv, name, set);
|
||||
if (do_set3(IP_FW_NPTV6_RESET_STATS, &oh.opheader, sizeof(oh)) != 0)
|
||||
err(EX_OSERR, "failed to reset stats for instance %s", name);
|
||||
}
|
||||
|
||||
static int
|
||||
nptv6_show_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set)
|
||||
{
|
||||
char abuf[INET6_ADDRSTRLEN];
|
||||
|
||||
if (name != NULL && strcmp(cfg->name, name) != 0)
|
||||
return (ESRCH);
|
||||
|
||||
if (g_co.use_set != 0 && cfg->set != set)
|
||||
return (ESRCH);
|
||||
|
||||
if (g_co.use_set != 0 || cfg->set != 0)
|
||||
printf("set %u ", cfg->set);
|
||||
inet_ntop(AF_INET6, &cfg->internal, abuf, sizeof(abuf));
|
||||
printf("nptv6 %s int_prefix %s ", cfg->name, abuf);
|
||||
if (cfg->flags & NPTV6_DYNAMIC_PREFIX)
|
||||
printf("ext_if %s ", cfg->if_name);
|
||||
else {
|
||||
inet_ntop(AF_INET6, &cfg->external, abuf, sizeof(abuf));
|
||||
printf("ext_prefix %s ", abuf);
|
||||
}
|
||||
printf("prefixlen %u\n", cfg->plen);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nptv6_destroy_cb(ipfw_nptv6_cfg *cfg, const char *name __unused, uint8_t set)
|
||||
{
|
||||
|
||||
if (g_co.use_set != 0 && cfg->set != set)
|
||||
return (ESRCH);
|
||||
|
||||
nptv6_destroy(cfg->name, cfg->set);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compare NPTv6 instances names.
|
||||
* Honor number comparison.
|
||||
*/
|
||||
static int
|
||||
nptv6name_cmp(const void *a, const void *b)
|
||||
{
|
||||
const ipfw_nptv6_cfg *ca, *cb;
|
||||
|
||||
ca = (const ipfw_nptv6_cfg *)a;
|
||||
cb = (const ipfw_nptv6_cfg *)b;
|
||||
|
||||
if (ca->set > cb->set)
|
||||
return (1);
|
||||
else if (ca->set < cb->set)
|
||||
return (-1);
|
||||
return (stringnum_cmp(ca->name, cb->name));
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves NPTv6 instance list from kernel,
|
||||
* Request: [ ipfw_obj_lheader ]
|
||||
* Reply: [ ipfw_obj_lheader ipfw_nptv6_cfg x N ]
|
||||
*/
|
||||
static int
|
||||
nptv6_foreach(nptv6_cb_t *f, const char *name, uint8_t set, int sort)
|
||||
{
|
||||
ipfw_obj_lheader *olh;
|
||||
ipfw_nptv6_cfg *cfg;
|
||||
size_t sz;
|
||||
uint32_t i;
|
||||
|
||||
/* Start with reasonable default */
|
||||
sz = sizeof(*olh) + 16 * sizeof(*cfg);
|
||||
for (;;) {
|
||||
if ((olh = calloc(1, sz)) == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
olh->size = sz;
|
||||
if (do_get3(IP_FW_NPTV6_LIST, &olh->opheader, &sz) != 0) {
|
||||
sz = olh->size;
|
||||
free(olh);
|
||||
if (errno != ENOMEM)
|
||||
return (errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sort != 0)
|
||||
qsort(olh + 1, olh->count, olh->objsize, nptv6name_cmp);
|
||||
|
||||
cfg = (ipfw_nptv6_cfg *)(olh + 1);
|
||||
for (i = 0; i < olh->count; i++) {
|
||||
(void)f(cfg, name, set);
|
||||
cfg = (ipfw_nptv6_cfg *)((caddr_t)cfg + olh->objsize);
|
||||
}
|
||||
free(olh);
|
||||
break;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user