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:
Boris Lytochkin authored and Andrey V. Elsukov committed 2026-02-10 14:50:20 +03:00
1 parent 30c04399f2
commit 704ec5e68c
23 files changed
+24049

No files matched your search

+1
View File
@@ -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
+26
View File
@@ -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.
+23
View File
@@ -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
+19
View File
@@ -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
+152
View File
@@ -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
+259
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+6129
View File
File diff suppressed because it is too large. Load diff
+470
View File
@@ -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);
+519
View File
@@ -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;
}
+716
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+536
View File
@@ -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);
}
+901
View File
@@ -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);
}
+552
View File
@@ -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);
}
+452
View File
@@ -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);
}
+2096
View File
File diff suppressed because it is too large. Load diff