/*
* Author: Paul.Russell@rustcorp.com.au
*
* Based on the ipfwadm code by Jos Vos <jos@xos.nl> (see README).
*
* ipchains -- IP firewall administration for kernels with
* CONFIG_IP_FIREWALL_CHAINS.
*
* See the accompanying manual page ipchains(8) for information
* about proper usage of this program.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History:
* 1.0: First release
* 1.0.1: Generic protocols allowed (matching ip_fwtrees.h changes).
* Tighter TOS checking (replaced silent kernel TOS mangling).
* RETURN target allowed (matching ip_fwtrees.c changes).
* Bug fix for port handling (port ranges broken?).
* Bug fix for possible bad return values from delete_entry and
* append_entry.
* 1.0.2: User & marking support. Removed -a option (interface address).
* 1.1: Inverse rule support.
* Removal of -k option (ACK).
* Moved -b option to userspace (BIDIR).
* Removed multiple port support.
* Fixed bug in handling of > 8 rules.
* 1.1.1: Added ICMP code support.
* Extra check for multiple DNS resolutions and ! or -b.
* Fixed -b code (now swaps address masks as well).
* Handle EINVAL from IP_FW_DELETE command.
* 1.2: Added wildcard interface support.
* 1.2.1: Fixed parsing of inverse interfaces (broken in 1.2).
* 1.2.2: Allow /proc/net/ip_fwnames to contain policy names (so same
* across 2.0/2.1 kernels).
* 1.3: Added `-o' option for netlink support.
* Enumerated exit types.
* Added output for delete in verbose mode.
* Added -X with no chain name to delete all chains.
* Added support for policy counters (kernel change).
* Removed old-style (numeric) policy handling.
* Fixed masquerade typo so masq works again.
* Handle ICMP masq.
* 1.3.1: Move headers out to kernel_headers for glibc compatibility.
* Policies now handed to kernel in string form (kernel change).
* 1.3.2: Handle generic masq. protocols.
* Fixed -S option.
* 1.3.3: Marks now printed in hex.
* kernel_headers.h now works with libc5 again.
* Set IP_FW_F_WILDIF for zero-length interface names.
* 1.3.4: Fixed handling of REDIRECT without a port number.
* Handles "" wildcard interfaces from kernel correctly.
* 1.3.5: Fixed masq time calcs for HZ != 100.
* Improved invalid flags error messages.
* 1.3.6: Split into separate kernel-interface library libipfwc.
* Don't give MAILME error for invalid rule numbers.
* 1.3.7: Andi Kleen additions: long options.
* & read /etc/protocols for protocols.
* Fixed spelling of `CMD_MASQERADE' and `CMD_SET_MASQERADE'.
* Cleaned up -h output.
* Fixed -Z option (zero, not flush!).
* Fixed wrong list_masq return value.
* Fixed libipfwc bug with ipfwc_check_packet().
* 1.3.8: -L now works for listing chains other than input.
* [ Thanks to Bernhard Weisshuhn ].
*/
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <getopt.h>
#include <netdb.h>
#include <sys/param.h>
#include <pwd.h>
#include <sys/types.h>
#include "include/libipfwc.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef IP_FW_F_REDIR
#define IPFWADM_NO_REDIR
#define IP_FW_F_REDIR 0
#endif /* ! IP_FW_F_REDIR */
#ifndef IP_FW_MASQ_TIMEOUTS
#define IPFWADM_NO_TIMEOUT
#define IP_FW_MASQ_TIMEOUTS 0
#endif /* ! IP_FW_MASQ_TIMEOUTS */
#define CHN_NONE -1
#define CHN_FWD 0
#define CHN_IN 1
#define CHN_OUT 2
#define CHN_MASQ 3 /* only used for listing masquerading */
#define CMD_NONE 0x0000U
#define CMD_INSERT 0x0001U
#define CMD_DELETE 0x0002U
#define CMD_DELETE_NUM 0x0004U
#define CMD_REPLACE 0x0008U
#define CMD_APPEND 0x0010U
#define CMD_LIST 0x0020U
#define CMD_FLUSH 0x0040U
#define CMD_ZERO 0x0080U
#define CMD_NEW_CHAIN 0x0100u
#define CMD_DELETE_CHAIN 0x0200U
#define CMD_SET_POLICY 0x0400U
#define CMD_MASQUERADE 0x0800U
#define CMD_SET_MASQUERADE 0x1000U
#define CMD_CHECK 0x2000U
#define NUMBER_OF_CMD 14
static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
'N', 'X', 'P', 'M', 'S', 'C' };
#define OPT_NONE 0x00000U
#define OPT_NUMERIC 0x00001U
#define OPT_SOURCE 0x00002U
#define OPT_DESTINATION 0x00004U
#define OPT_PROTOCOL 0x00008U
#define OPT_JUMP 0x00010U
#define OPT_TCPSYN 0x00020U
#define OPT_BIDIR 0x00040U
#define OPT_VERBOSE 0x00080U
#define OPT_PRINTK 0x00100U
#define OPT_EXPANDED 0x00200U
#define OPT_TOS 0x00400U
#define OPT_VIANAME 0x00800U
#define OPT_FRAGMENT 0x01000U
#define OPT_MARK 0x02000U
#define OPT_SRCPT 0x04000U
#define OPT_DSTPT 0x08000U
#define OPT_NETLINK 0x10000U
#define NUMBER_OF_OPT 17
static const char optflags[] = { 'n', 's', 'd', 'p', 'j', 'y', 'b', 'v',
'l', 'x', 't', 'i', 'f', 'm', 's', 'd', 'o'};
#define FMT_NUMERIC 0x0001
#define FMT_NOCOUNTS 0x0002
#define FMT_KILOMEGAGIGA 0x0004
#define FMT_OPTIONS 0x0008
#define FMT_NOTABLE 0x0010
#define FMT_HEADER 0x0020
#define FMT_NOTARGET 0x0040
#define FMT_VIA 0x0080
#define FMT_NONEWLINE 0x0100
#define FMT_DELTAS 0x0200
#define FMT_TOS 0x0400
#define FMT_MARK 0x0800
#define FMT_NETLINK 0x1000
#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_TOS | FMT_VIA \
| FMT_NUMERIC | FMT_NOTABLE | FMT_MARK | FMT_NETLINK)
struct option opts[] = {
{ "append", 1, 0, 'A' },
{ "delete", 1, 0, 'D' },
{ "insert", 1, 0, 'I' },
{ "replace", 1, 0, 'R' },
{ "list", 2, 0, 'L' },
{ "flush", 2, 0, 'F' },
{ "zero", 2, 0, 'Z' },
{ "check", 1, 0, 'C' },
{ "new-chain", 1, 0, 'N' },
{ "delete-chain", 1, 0, 'X' },
{ "policy", 1, 0, 'P' },
{ "masquerading", 0, 0, 'M' },
{ "set", 1, 0, 'S' },
{ "bidirectional", 0, 0, 'b' },
{ "source", 1, 0, 's' },
{ "destination", 1, 0, 'd' },
{ "src", 1, 0, 's' }, /* synonym */
{ "dst", 1, 0, 'd' }, /* synonym */
{ "source-port", 1, 0, '1' },
{ "destination-port", 1, 0, '2' },
{ "icmp-type", 1, 0, '1' },
{ "sport", 1, 0, '1' }, /* synonym */
{ "dport", 1, 0, '2' }, /* synonym */
{ "proto", 1, 0, 'p' },
{ "interface", 1, 0, 'i' },
{ "jump", 1, 0, 'j' },
{ "TOS", 1, 0, 't' },
{ "mark", 1, 0, 'm' },
{ "numeric", 0, 0, 'n' },
{ "log", 0, 0, 'l' },
{ "output", 2, 0, 'o' },
{ "verbose", 0, 0, 'v' },
{ "exact", 0, 0, 'x' },
{ "fragments", 0, 0, 'f' },
{ "syn", 0, 0, 'y' },
{ "version", 0, 0, 'V' },
{ "help", 0, 0, 'h'},
{0}
};
/* TOS names and values. */
struct TOS_value
{
unsigned char TOS;
const char *name;
} TOS_values[] = {
#ifdef NOT_A_GODDAMN_YANK
{ 0x10, "Minimise Delay" },
{ 0x08, "Minimise Throughput" },
{ 0x04, "Maximise Reliability" },
{ 0x01, "Minimise Cost" },
#else
{ 0x10, "Minimize Delay" },
{ 0x08, "Minimize Throughput" },
{ 0x04, "Maximize Reliability" },
{ 0x01, "Minimize Cost" },
#endif
{ 0x00, "Normal Service" },
};
/* Table of legal combinations of commands and options. If any of the
* given commands make an option legal, that option is legal (applies to
* CMD_LIST and CMD_ZERO only).
* Key:
* + compulsory
* x illegal
* optional
*/
static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/* Well, it's better than "Re: Linux vs FreeBSD" */
{
/* -n -s -d -p -j -y -b -v -l -x -t -i -f -m -s -d -o */
/*INSERT*/ {'x',' ',' ',' ',' ',' ',' ',' ',' ','x',' ',' ',' ',' ',' ',' ',' '},
/*DELETE*/ {'x',' ',' ',' ',' ',' ',' ',' ',' ','x',' ',' ',' ',' ',' ',' ',' '},
/*DELETE_NUM*/{'x','x','x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x'},
/*REPLACE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' ',' ',' ',' ',' ',' '},
/*APPEND*/ {'x',' ',' ',' ',' ',' ',' ',' ',' ','x',' ',' ',' ',' ',' ',' ',' '},
/*LIST*/ {' ','x','x','x','x','x','x',' ','x',' ','x','x','x','x','x','x','x'},
/*FLUSH*/ {'x','x','x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x'},
/*ZERO*/ {'x','x','x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x'},
/*NEW_CHAIN*/ {'x','x','x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x'},
/*DEL_CHAIN*/ {'x','x','x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x'},
/*SET_POLICY*/{'x','x','x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x'},
/*MASQUERADE*/{'x','x','x','x','x','x','x','x','x','x','x','x','x','x','x','x','x'},
/*SET_MASQ*/ {'x','x','x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x'},
/*CHECK*/ {'x','+','+','+','x',' ',' ',' ','x','x','x','+',' ','x',' ',' ','x'}
};
static int inverse_for_options[NUMBER_OF_OPT] =
{
/* -n */ 0,
/* -s */ IP_FW_INV_SRCIP,
/* -d */ IP_FW_INV_DSTIP,
/* -p */ IP_FW_INV_PROTO,
/* -j */ 0,
/* -y */ IP_FW_INV_SYN,
/* -b */ 0,
/* -v */ 0,
/* -l */ 0,
/* -x */ 0,
/* -t */ 0,
/* -i */ IP_FW_INV_VIA,
/* -f */ IP_FW_INV_FRAG,
/* -m */ 0,
/* -s */ IP_FW_INV_SRCPT,
/* -d */ IP_FW_INV_DSTPT,
/* -o */ 0
};
struct masq {
unsigned long expires; /* Expiration timer */
char proto[20]; /* Which protocol are we talking? */
struct in_addr src, dst; /* Source and destination IP addresses */
unsigned short sport, dport; /* Source and destination ports */
unsigned short mport; /* Masqueraded port */
__u32 initseq; /* Add delta from this seq. on */
short delta; /* Delta in sequence numbers */
short pdelta; /* Delta in sequence numbers before last */
};
struct masq_timeout {
int tcp_timeout;
int tcp_fin_timeout;
int udp_timeout;
} timeouts;
static char package_version[] = "ipchains 1.3.8, 27-Oct-1998";
static const char *program;
/* Do we invert the next option? */
static int invert = FALSE;
static int check_inverse(const char option[], int *invert);
/* Parsing functions for command line. */
static struct in_addr *parse_hostnetwork(const char *, unsigned int *);
static void parse_hostnetworkmask(const char *, struct in_addr **,
struct in_addr *, unsigned int *);
static struct in_addr *parse_mask(char *);
static unsigned short int parse_protocol(const char *);
static const char * parse_policy(const char *chain, const char *policy);
static void parse_icmp(const char *srcportstring, const char *dstportstring,
__u16 *type, __u16* code, int *options, __u16 invflags,
int checking);
static void parse_ports(const char *portstring, __u16 *ports, __u16 proto);
static __u16 parse_port(const char *port, unsigned short proto);
static unsigned short parse_mark(char *markstring, __u32 *mark);
static unsigned short parse_outputsize(char *outputstring, __u16 *outputsize);
static unsigned short parse_interface(const char *ifstring, char *vianame);
static void parse_hexbyte(char *, unsigned char *);
static int parse_timeout(char *);
static const char *parse_target(const char *targetname);
static int parse_rulenumber(const char *rule);
/* Various parsing helper functions */
static struct in_addr *host_to_addr(const char *, unsigned int *);
static char *addr_to_host(struct in_addr *);
static struct in_addr *network_to_addr(const char *);
static char *addr_to_network(struct in_addr *);
static char *addr_to_anyname(struct in_addr *);
static struct in_addr *dotted_to_addr(const char *);
static char *addr_to_dotted(struct in_addr *);
static char *mask_to_dotted(struct in_addr *);
static int service_to_port(const char *, unsigned short);
static char *port_to_service(int, unsigned short);
static int string_to_number(const char *, int, int);
/* Check the options based on the tables above */
static void generic_opt_check(int command, int options);
static char opt2char(int option);
static char cmd2char(int option);
static void add_command(int *cmd, const int newcmd, const int othercmds);
/* Check for illegal TOS manipulation */
static void check_tos(unsigned char tosand, unsigned char tosxor);
static int list_masq(unsigned int options);
static void print_firewall(FILE *, struct ip_fwuser *, __u64, __u64, int);
static void print_masq(FILE *, struct masq *, int);
static int read_masqinfo(FILE *, struct masq *, int);
static void set_option(unsigned int *options,
unsigned int option,
__u16 *invflg);
static void inaddrcpy(struct in_addr *, struct in_addr *);
static void *fw_malloc(size_t);
static void *fw_calloc(size_t, size_t);
static void *fw_realloc(void *, size_t);
enum exittype {
OTHER_PROBLEM = 1,
PARAMETER_PROBLEM,
VERSION_PROBLEM
};
static void exit_error(enum exittype, char *, ...)
__attribute__((noreturn, format(printf,2,3)));
static void exit_tryhelp(int) __attribute__((noreturn));
static void exit_printicmphelp() __attribute__((noreturn));
static void exit_printhelp() __attribute__((noreturn));
struct icmp_names {
const char *name;
__u16 type;
__u16 code_min, code_max;
int use_code_min_for_testing;
};
const struct icmp_names icmp_codes[] = {
{ "echo-reply", 0, 0, 0xFFFF, TRUE },
/* Alias */ { "pong", 0, 0, 0xFFFF, TRUE },
{ "destination-unreachable", 3, 0, 0xFFFF, FALSE },
{ "network-unreachable", 3, 0, 0, TRUE },
{ "host-unreachable", 3, 1, 1, TRUE },
{ "protocol-unreachable", 3, 2, 2, TRUE },
{ "port-unreachable", 3, 3, 3, TRUE },
{ "fragmentation-needed", 3, 4, 4, TRUE },
{ "source-route-failed", 3, 5, 5, TRUE },
{ "network-unknown", 3, 6, 6, TRUE },
{ "host-unknown", 3, 7, 7, TRUE },
{ "network-prohibited", 3, 9, 9, TRUE },
{ "host-prohibited", 3, 10, 10, TRUE },
{ "TOS-network-unreachable", 3, 11, 11, TRUE },
{ "TOS-host-unreachable", 3, 12, 12, TRUE },
{ "communication-prohibited", 3, 13, 13, TRUE },
{ "host-precedence-violation", 3, 14, 14, TRUE },
{ "precedence-cutoff", 3, 15, 15, TRUE },
{ "source-quench", 4, 0, 0xFFFF, TRUE },
{ "redirect", 5, 0, 0xFFFF, FALSE },
{ "network-redirect", 5, 0, 0, TRUE },
{ "host-redirect", 5, 1, 1, TRUE },
{ "TOS-network-redirect", 5, 2, 2, TRUE },
{ "TOS-host-redirect", 5, 3, 3, TRUE },
{ "echo-request", 8, 0, 0xFFFF, TRUE },
/* Alias */ { "ping", 8, 0, 0xFFFF, TRUE },
{ "router-advertisement", 9, 0, 0xFFFF, TRUE },
{ "router-solicitation", 10, 0, 0xFFFF, TRUE },
{ "time-exceeded", 11, 0, 0xFFFF, FALSE },
/* Alias */ { "ttl-exceeded", 11, 0, 0xFFFF, FALSE },
{ "ttl-zero-during-transit", 11, 0, 0, TRUE },
{ "ttl-zero-during-reassembly", 11, 1, 1, TRUE },
{ "parameter-problem", 12, 0, 0xFFFF, FALSE },
{ "ip-header-bad", 12, 0, 0, TRUE },
{ "required-option-missing", 12, 1, 1, TRUE },
{ "timestamp-request", 13, 0, 0xFFFF, TRUE },
{ "timestamp-reply", 14, 0, 0xFFFF, TRUE },
{ "address-mask-request", 17, 0, 0xFFFF, TRUE },
{ "address-mask-reply", 18, 0, 0xFFFF, TRUE }
};
static void
swap_info(struct ip_fw *fw, int isicmp)
{
__u16 tmp;
__u32 tmpMask;
/* Swap source and dest masks. */
tmpMask = fw->fw_smsk.s_addr;
fw->fw_smsk.s_addr = fw->fw_dmsk.s_addr;
fw->fw_dmsk.s_addr = tmpMask;
/* Swap source an dest inverse flags (clear and reset)*/
tmp = fw->fw_invflg;
fw->fw_invflg &= ~(IP_FW_INV_SRCIP | IP_FW_INV_DSTIP);
fw->fw_invflg |= ((tmp & IP_FW_INV_SRCIP ? IP_FW_INV_DSTIP : 0)
| (tmp & IP_FW_INV_DSTIP ? IP_FW_INV_SRCIP : 0));
if (!isicmp) {
/* Swap source & dest ports */
tmp = fw->fw_spts[0];
fw->fw_spts[0] = fw->fw_dpts[0];
fw->fw_dpts[0] = tmp;
tmp = fw->fw_spts[1];
fw->fw_spts[1] = fw->fw_dpts[1];
fw->fw_dpts[1] = tmp;
/* Swap src & dst port inverse flags.*/
tmp = fw->fw_invflg;
fw->fw_invflg &= ~(IP_FW_INV_SRCPT | IP_FW_INV_DSTPT);
fw->fw_invflg |= ((tmp & IP_FW_INV_SRCPT ? IP_FW_INV_DSTPT : 0)
| (tmp & IP_FW_INV_DSTPT ? IP_FW_INV_SRCPT : 0));
}
}
static int
append_entry(const ip_chainlabel chain,
const struct ip_fwuser *fw,
unsigned int nsaddrs,
const struct in_addr saddrs[],
unsigned int ndaddrs,
const struct in_addr daddrs[],
int verbose,
int bidir)
{
unsigned int i, j;
struct ip_fwuser ipfw = *fw;
int ret = 1;
for (i = 0; i < nsaddrs; i++) {
ipfw.ipfw.fw_src.s_addr = saddrs[i].s_addr;
for (j = 0; j < ndaddrs; j++) {
ipfw.ipfw.fw_dst.s_addr = daddrs[j].s_addr;
if (verbose)
print_firewall(stdout, &ipfw,
0, 0, FMT_PRINT_RULE);
ret &= ipfwc_append_entry(chain, &ipfw);
}
}
if (bidir) {
swap_info(&ipfw.ipfw, ipfw.ipfw.fw_proto == IPPROTO_ICMP);
ret &= append_entry(chain, &ipfw, ndaddrs, daddrs,
nsaddrs, saddrs, verbose, 0);
}
return ret;
}
static int
replace_entry(const ip_chainlabel chain,
const struct ip_fwuser *fw,
unsigned int rulenum,
const struct in_addr *saddr,
const struct in_addr *daddr,
int verbose)
{
struct ip_fwuser ipfw = *fw;
ipfw.ipfw.fw_src.s_addr = saddr->s_addr;
ipfw.ipfw.fw_dst.s_addr = daddr->s_addr;
if (verbose)
print_firewall(stdout, &ipfw, 0, 0, FMT_PRINT_RULE);
return ipfwc_replace_entry(chain, &ipfw, rulenum);
}
static int
insert_entry(const ip_chainlabel chain,
const struct ip_fwuser *fw,
unsigned int rulenum,
unsigned int nsaddrs,
const struct in_addr saddrs[],
unsigned int ndaddrs,
const struct in_addr daddrs[],
int verbose, int bidir)
{
unsigned int i, j;
int ret = 1;
struct ip_fwuser ipfw = *fw;
for (i = 0; i < nsaddrs; i++) {
ipfw.ipfw.fw_src.s_addr = saddrs[i].s_addr;
for (j = 0; j < ndaddrs; j++) {
ipfw.ipfw.fw_dst.s_addr = daddrs[j].s_addr;
if (verbose)
print_firewall(stdout, &ipfw,
0, 0, FMT_PRINT_RULE);
ret &= ipfwc_insert_entry(chain, &ipfw, rulenum);
}
}
if (bidir) {
swap_info(&ipfw.ipfw, ipfw.ipfw.fw_proto == IPPROTO_ICMP);
ret &= insert_entry(chain, &ipfw, rulenum, ndaddrs, daddrs,
nsaddrs, saddrs, verbose, 0);
}
return ret;
}
static int
delete_entry(const ip_chainlabel chain,
const struct ip_fwuser *fw,
unsigned int nsaddrs,
const struct in_addr saddrs[],
unsigned int ndaddrs,
const struct in_addr daddrs[],
int verbose, int bidir)
{
unsigned int i, j;
struct ip_fwuser ipfw = *fw;
int ret = 1;
for (i = 0; i < nsaddrs; i++) {
ipfw.ipfw.fw_src.s_addr = saddrs[i].s_addr;
for (j = 0; j < ndaddrs; j++) {
ipfw.ipfw.fw_dst.s_addr = daddrs[j].s_addr;
if (verbose)
print_firewall(stdout, &ipfw,
0, 0, FMT_PRINT_RULE);
ret &= ipfwc_delete_entry(chain, &ipfw);
}
}
if (bidir) {
swap_info(&ipfw.ipfw, ipfw.ipfw.fw_proto == IPPROTO_ICMP);
ret &= delete_entry(chain, &ipfw, ndaddrs, daddrs,
nsaddrs, saddrs, verbose, 0);
}
return ret;
}
static int
check_packet(const ip_chainlabel chain,
const struct ip_fwuser *fw,
unsigned int nsaddrs,
const struct in_addr saddrs[],
unsigned int ndaddrs,
const struct in_addr daddrs[],
int verbose,
int bidir)
{
int ret = 1;
unsigned int i, j;
struct ip_fwuser ipfw = *fw;
const char *msg;
for (i = 0; i < nsaddrs; i++) {
ipfw.ipfw.fw_src.s_addr = saddrs[i].s_addr;
for (j = 0; j < ndaddrs; j++) {
ipfw.ipfw.fw_dst.s_addr = daddrs[j].s_addr;
if (verbose)
print_firewall(stdout, &ipfw, 0, 0,
FMT_PRINT_RULE);
msg = ipfwc_check_packet(chain, &ipfw.ipfw);
if (!msg) ret = 0;
else printf("%s\n", msg);
}
}
if (bidir) {
swap_info(&ipfw.ipfw, ipfw.ipfw.fw_proto == IPPROTO_ICMP);
ret &= check_packet(chain, &ipfw, ndaddrs, daddrs,
nsaddrs, saddrs, verbose, 0);
}
return ret;
}
static int
for_each_chain(int (*fn)(const ip_chainlabel, int verbose),
int verbose, int userchains_only)
{
unsigned int num_chains;
int ret = 1;
struct ipfwc_fwchain *chains = ipfwc_get_chainnames(&num_chains);
if (chains) {
unsigned int i = 0;
for (i = 0; i < num_chains; i++) {
if (!userchains_only ||
(strcmp(chains[i].label, IP_FW_LABEL_FORWARD) != 0
&& strcmp(chains[i].label, IP_FW_LABEL_INPUT) != 0
&& strcmp(chains[i].label, IP_FW_LABEL_OUTPUT) != 0))
ret &= fn(chains[i].label, verbose);
}
}
else
ret = 0;
return ret;
}
static int
flush_entries(const ip_chainlabel chain, int verbose)
{
if (!chain) return for_each_chain(flush_entries, verbose, 0);
else {
if (verbose)
fprintf(stdout, "Flushing chain `%s'\n", chain);
return ipfwc_flush_entries(chain);
}
}
static int
zero_entries(const ip_chainlabel chain, int verbose)
{
if (!chain) return for_each_chain(zero_entries, verbose, 0);
else {
if (verbose)
fprintf(stdout, "Zeroing chain `%s'\n", chain);
return ipfwc_zero_entries(chain);
}
}
static int
delete_chain(const ip_chainlabel chain, int verbose)
{
if (!chain) return for_each_chain(delete_chain, verbose, 1);
else {
if (verbose)
fprintf(stdout, "Deleting chain `%s'\n", chain);
return ipfwc_delete_chain(chain);
}
}
static int
list_entries(const ip_chainlabel chain, int zero, int verbose, int numeric,
int expanded)
{
int format, found = 0;
unsigned int num_rules, num_chains, i;
struct ipfwc_fwchain *chains = ipfwc_get_chainnames(&num_chains);
struct ipfwc_fwrule *rules;
if (!chains)
return 0;
/* Impatience, hubris and laziness all in one line! */
chains = memcpy(malloc(num_chains * sizeof(struct ipfwc_fwchain)),
chains, num_chains * sizeof(struct ipfwc_fwchain));
rules = ipfwc_get_rules(&num_rules, zero);
if (!rules)
return 0;
format = FMT_OPTIONS | FMT_HEADER;
if (!verbose)
format |= FMT_NOCOUNTS;
else format |= FMT_TOS | FMT_VIA | FMT_MARK | FMT_NETLINK;
if (numeric)
format |= FMT_NUMERIC;
if (!expanded)
format |= FMT_KILOMEGAGIGA;
/* This is unreliable if a new chain is created and a rule
added between ipfwc_get_chainnames and ipfwc_get_rules.
*/
for (i = 0; i < num_chains; i++) {
if (!chain || strcmp(chain, chains[i].label) == 0) {
int rulenum;
found = 1;
printf("Chain %s ", chains[i].label);
if (strcmp(chains[i].policy, "-") == 0)
printf("(%i references):",
chains[i].refcnt);
else {
printf("(policy %s",
chains[i].policy);
if (!(format & FMT_NOCOUNTS))
printf(": %llu packets, %llu bytes",
chains[i].packets,
chains[i].bytes);
printf("):");
}
printf("\n");
/* First one gets header. */
format |= FMT_HEADER;
for (rulenum = 0; rulenum < num_rules; rulenum++) {
if (strcmp(rules[rulenum].chain->label,
chains[i].label) == 0) {
print_firewall(stdout,
&rules[rulenum].ipfw,
rules[rulenum].bytes,
rules[rulenum].packets,
format);
format &= ~FMT_HEADER;
}
}
}
}
errno = ENOENT;
return found;
}
int
ipfw_init(int args, char **arglist, unsigned int command, const char *chain, unsigned int rulenum)
{
unsigned int nsaddrs = 0, ndaddrs = 0;
struct in_addr *saddrs = NULL, *daddrs = NULL;
int c;
/* By declaring it static, it gets initialised to all 0. */
static struct ip_fwuser fw;
const char *jumpto = NULL;
const char *srcpts = NULL, *dstpts = NULL;
const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
const char *rport = "0";
const char *policy = NULL;
unsigned int options = 0;
int ret = 1;
/* program = arglist[0]; */
program = "IPChains";
/* static works great for single-programs, but in a module
the struct is not reinitialized */
memset(&fw, 0, sizeof(fw));
while ((c = getopt_long(args, arglist, "-o::p:s:d:j:i:fbvm:nlt:xy", opts, NULL)) != -1) {
switch (c) {
/*
* Option selection
*/
case 'p':
if (check_inverse(optarg, &invert))
optind++;
set_option(&options, OPT_PROTOCOL, &fw.ipfw.fw_invflg);
fw.ipfw.fw_proto = parse_protocol(arglist[optind-1]);
if (fw.ipfw.fw_proto == 0
&& (fw.ipfw.fw_invflg & IP_FW_INV_PROTO))
exit_error(PARAMETER_PROBLEM,
"rule would never match protocol");
break;
case 's':
if (check_inverse(optarg, &invert))
optind++;
set_option(&options, OPT_SOURCE, &fw.ipfw.fw_invflg);
shostnetworkmask = arglist[optind-1];
if (check_inverse(arglist[optind], &invert))
optind++;
if (optind < args && arglist[optind][0] != '-'
&& arglist[optind][0] != '!')
{
srcpts = arglist[optind++];
set_option(&options, OPT_SRCPT,
&fw.ipfw.fw_invflg);
}
break;
case 'd':
if (check_inverse(optarg, &invert))
optind++;
set_option(&options, OPT_DESTINATION,
&fw.ipfw.fw_invflg);
dhostnetworkmask = arglist[optind-1];
if (check_inverse(arglist[optind], &invert))
optind++;
if (optind < args && arglist[optind][0] != '-'
&& arglist[optind][0] != '!')
{
dstpts = arglist[optind++];
set_option(&options, OPT_DSTPT,
&fw.ipfw.fw_invflg);
}
break;
case '1':
if (check_inverse(optarg, &invert))
optind++;
set_option(&options, OPT_SRCPT, &fw.ipfw.fw_invflg);
srcpts = optarg;
break;
case '2':
if (check_inverse(optarg, &invert))
optind++;
set_option(&options, OPT_DSTPT, &fw.ipfw.fw_invflg);
dstpts = optarg;
break;
case 'j':
if (invert)
exit_error(PARAMETER_PROBLEM,
"unexpected ! flag");
set_option(&options, OPT_JUMP, &fw.ipfw.fw_invflg);
jumpto = parse_target(optarg);
if (strcmp(jumpto, IP_FW_LABEL_REDIRECT) == 0
&& optind < args && arglist[optind][0] != '-'
&& arglist[optind][0] != '!')
rport = arglist[optind++];
break;
case 'i':
if (check_inverse(optarg, &invert))
optind++;
set_option(&options, OPT_VIANAME, &fw.ipfw.fw_invflg);
fw.ipfw.fw_flg |= parse_interface(arglist[optind-1],
fw.ipfw.fw_vianame);
break;
case 'f':
set_option(&options, OPT_FRAGMENT, &fw.ipfw.fw_invflg);
invert = FALSE;
fw.ipfw.fw_flg |= IP_FW_F_FRAG;
break;
case 'b':
if (invert)
exit_error(PARAMETER_PROBLEM,
"unexpected ! flag");
set_option(&options, OPT_BIDIR, &fw.ipfw.fw_invflg);
break;
case 'v':
if (invert)
exit_error(PARAMETER_PROBLEM,
"unexpected ! flag");
set_option(&options, OPT_VERBOSE, &fw.ipfw.fw_invflg);
break;
case 'm':
if (invert)
exit_error(PARAMETER_PROBLEM,
"unexpected ! flag");
set_option(&options, OPT_MARK, &fw.ipfw.fw_invflg);
fw.ipfw.fw_flg |= parse_mark(optarg, &fw.ipfw.fw_mark);
break;
case 'n':
if (invert)
exit_error(PARAMETER_PROBLEM,
"unexpected ! flag");
set_option(&options, OPT_NUMERIC, &fw.ipfw.fw_invflg);
break;
case 'l':
if (invert)
exit_error(PARAMETER_PROBLEM,
"unexpected ! flag");
set_option(&options, OPT_PRINTK, &fw.ipfw.fw_invflg);
fw.ipfw.fw_flg |= IP_FW_F_PRN;
break;
case 'o':
if (invert)
exit_error(PARAMETER_PROBLEM,
"unexpected ! flag");
set_option(&options, OPT_NETLINK, &fw.ipfw.fw_invflg);
if (!optarg && optind < args && arglist[optind][0] != '-'
&& arglist[optind][0] != '!')
optarg = arglist[optind++];
fw.ipfw.fw_flg
|= parse_outputsize(optarg,
&fw.ipfw.fw_outputsize);
break;
case 't':
if (invert)
exit_error(PARAMETER_PROBLEM,
"unexpected ! flag");
set_option(&options, OPT_TOS, &fw.ipfw.fw_invflg);
if (optind < args && arglist[optind][0] != '-'
&& arglist[optind][0] != '!') {
parse_hexbyte(optarg, &fw.ipfw.fw_tosand);
parse_hexbyte(arglist[optind++],
&fw.ipfw.fw_tosxor);
check_tos(fw.ipfw.fw_tosand,
fw.ipfw.fw_tosxor);
} else
exit_error(PARAMETER_PROBLEM,
"-%c requires 2 hexbyte arguments",
opt2char(OPT_TOS));
break;
case 'x':
if (invert)
exit_error(PARAMETER_PROBLEM,
"unexpected ! flag");
set_option(&options, OPT_EXPANDED, &fw.ipfw.fw_invflg);
break;
case 'y':
set_option(&options, OPT_TCPSYN, &fw.ipfw.fw_invflg);
fw.ipfw.fw_flg |= IP_FW_F_TCPSYN;
invert = FALSE;
break;
case 1: /* non option */
if (optarg[0] == '!' && optarg[1] == '\0') {
if (invert)
exit_error(PARAMETER_PROBLEM,
"multiple consecutive ! not allowed\n");
invert = TRUE;
optarg[0] = '\0';
continue;
}
/* FALL THOUGH */
}
invert = FALSE;
}
if (optind < args)
exit_error(PARAMETER_PROBLEM,
"unknown arguments found on commandline");
else if (!command)
exit_error(PARAMETER_PROBLEM, "no command specified");
else if (invert)
exit_error(PARAMETER_PROBLEM,
"nothing appropriate following !");
if ((command & CMD_REPLACE)
|| (command & CMD_INSERT)
|| (command & CMD_DELETE)
|| (command & CMD_APPEND)) {
if (!(options & OPT_DESTINATION))
dhostnetworkmask = "0.0.0.0/0";
if (!(options & OPT_SOURCE))
shostnetworkmask = "0.0.0.0/0";
}
if (shostnetworkmask) {
parse_hostnetworkmask(shostnetworkmask, &saddrs,
&(fw.ipfw.fw_smsk), &nsaddrs);
if (fw.ipfw.fw_proto != IPPROTO_ICMP)
parse_ports(srcpts, fw.ipfw.fw_spts, fw.ipfw.fw_proto);
}
if (dhostnetworkmask) {
parse_hostnetworkmask(dhostnetworkmask, &daddrs,
&(fw.ipfw.fw_dmsk), &ndaddrs);
if (fw.ipfw.fw_proto != IPPROTO_ICMP)
parse_ports(dstpts, fw.ipfw.fw_dpts, fw.ipfw.fw_proto);
}
if (fw.ipfw.fw_proto == IPPROTO_ICMP) {
parse_icmp(srcpts, dstpts, fw.ipfw.fw_spts, fw.ipfw.fw_dpts,
&options, fw.ipfw.fw_invflg, command == CMD_CHECK);
}
if ((nsaddrs > 1 || ndaddrs > 1) &&
(fw.ipfw.fw_invflg & (IP_FW_INV_SRCIP | IP_FW_INV_DSTIP)))
exit_error(PARAMETER_PROBLEM, "! not allowed with multiple source or destination IP addresses");
if ((nsaddrs > 1 || ndaddrs > 1) && (options & OPT_BIDIR))
exit_error(PARAMETER_PROBLEM, "-b not allowed with multiple source or destination IP addresses");
if ((fw.ipfw.fw_proto == 0 || (fw.ipfw.fw_invflg & IP_FW_INV_PROTO))
&& ((options & OPT_SRCPT) || (options & OPT_DSTPT)))
exit_error(PARAMETER_PROBLEM, "no ports allowed without specific protocol");
if ((fw.ipfw.fw_proto != IPPROTO_TCP
|| (fw.ipfw.fw_invflg & IP_FW_INV_PROTO))
&& (options & OPT_TCPSYN))
exit_error(PARAMETER_PROBLEM, "-%c only allowed with TCP protocol",
opt2char(OPT_TCPSYN));
if ((options & OPT_FRAGMENT) && !(fw.ipfw.fw_invflg & IP_FW_INV_FRAG)
&& ((options & OPT_SRCPT) || (options & OPT_DSTPT)))
exit_error(PARAMETER_PROBLEM, "no ports allowed with -%c",
opt2char(OPT_FRAGMENT));
if ((options & OPT_FRAGMENT) && !(fw.ipfw.fw_invflg & IP_FW_INV_FRAG)
&& (options & OPT_TCPSYN))
exit_error(PARAMETER_PROBLEM, "-%c not allowed with -%c",
opt2char(OPT_TCPSYN), opt2char(OPT_FRAGMENT));
if (command == CMD_CHECK
&& (fw.ipfw.fw_proto == IPPROTO_TCP
|| fw.ipfw.fw_proto == IPPROTO_UDP
|| fw.ipfw.fw_proto == IPPROTO_ICMP)
&& !(options & OPT_FRAGMENT)
&& (!(options & OPT_SRCPT)
|| fw.ipfw.fw_spts[0] != fw.ipfw.fw_spts[1]
|| !(options & OPT_DSTPT)
|| fw.ipfw.fw_dpts[0] != fw.ipfw.fw_dpts[1]))
exit_error(PARAMETER_PROBLEM, "one port required with source/destination "
"address for -%c", cmd2char(CMD_CHECK));
if (command == CMD_CHECK && fw.ipfw.fw_invflg != 0)
exit_error(PARAMETER_PROBLEM, "! not allowed with -%c", cmd2char(CMD_CHECK));
if (((fw.ipfw.fw_proto != IPPROTO_TCP && fw.ipfw.fw_proto != IPPROTO_UDP)
|| (fw.ipfw.fw_invflg & IP_FW_INV_PROTO))
&& jumpto && strcmp(jumpto, IP_FW_LABEL_REDIRECT) == 0)
exit_error(PARAMETER_PROBLEM, "redirecting only allowed with TCP or UDP");
if (jumpto && strcmp(jumpto, IP_FW_LABEL_MASQUERADE) == 0
&& chain && (strcmp(chain, IP_FW_LABEL_INPUT) == 0
|| strcmp(chain, IP_FW_LABEL_OUTPUT) == 0))
exit_error(PARAMETER_PROBLEM,
"masquerading not allowed for input and output chains");
if (jumpto && strcmp(jumpto, IP_FW_LABEL_REDIRECT) == 0
&& chain && (strcmp(chain, IP_FW_LABEL_OUTPUT) == 0
|| strcmp(chain, IP_FW_LABEL_FORWARD) == 0))
exit_error(PARAMETER_PROBLEM,
"redirect not allowed for output and forward chains");
if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
"specify a unique address");
/* Allow lazy -S without -M */
if (command == CMD_SET_MASQUERADE)
command |= CMD_MASQUERADE;
generic_opt_check(command, options);
if (chain && strlen(chain) > IP_FW_MAX_LABEL_LENGTH)
exit_error(PARAMETER_PROBLEM,
"chain name `%s' too long (must be under %i chars)",
chain, IP_FW_MAX_LABEL_LENGTH);
if (jumpto && strcmp(jumpto, IP_FW_LABEL_REDIRECT) == 0) {
fw.ipfw.fw_redirpt = parse_port(rport, fw.ipfw.fw_proto);
}
if (!(options & OPT_TOS)) {
fw.ipfw.fw_tosand = 0xFF;
fw.ipfw.fw_tosxor = 0x00;
}
if (options & OPT_JUMP)
strcpy(fw.label, jumpto);
if (command == CMD_MASQUERADE)
exit_error(PARAMETER_PROBLEM,
"-M/--masquerading requires -L/--list or -S/--set");
switch (command) {
case CMD_APPEND:
ret = append_entry(chain, &fw,
nsaddrs, saddrs, ndaddrs, daddrs,
options&OPT_VERBOSE,
options&OPT_BIDIR);
break;
case CMD_CHECK:
ret = check_packet(chain, &fw,
nsaddrs, saddrs, ndaddrs, daddrs,
options&OPT_VERBOSE,
options&OPT_BIDIR);
break;
case CMD_DELETE:
ret = delete_entry(chain, &fw,
nsaddrs, saddrs, ndaddrs, daddrs,
options&OPT_VERBOSE,
options&OPT_BIDIR);
break;
case CMD_DELETE_NUM:
ret = ipfwc_delete_num_entry(chain, rulenum);
break;
case CMD_REPLACE:
ret = replace_entry(chain, &fw, rulenum,
saddrs, daddrs, options&OPT_VERBOSE);
break;
case CMD_INSERT:
ret = insert_entry(chain, &fw, rulenum,
nsaddrs, saddrs, ndaddrs, daddrs,
options&OPT_VERBOSE,
options&OPT_BIDIR);
break;
case CMD_LIST|CMD_MASQUERADE:
ret = list_masq(options);
break;
case CMD_LIST:
case CMD_LIST|CMD_ZERO:
ret = list_entries(chain, command&CMD_ZERO,
options&OPT_VERBOSE,
options&OPT_NUMERIC,
options&OPT_EXPANDED);
break;
case CMD_FLUSH:
ret = flush_entries(chain, options&OPT_VERBOSE);
break;
case CMD_ZERO:
ret = zero_entries(chain, options&OPT_VERBOSE);
break;
case CMD_NEW_CHAIN:
ret = ipfwc_create_chain(chain);
break;
case CMD_DELETE_CHAIN:
ret = delete_chain(chain, options&OPT_VERBOSE);
break;
case CMD_MASQUERADE|CMD_SET_MASQUERADE: {
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) {
perror("ipchains: socket creation failed");
exit(1);
}
if (setsockopt(sockfd, IPPROTO_IP, IP_FW_MASQ_TIMEOUTS,
(char *) &timeouts, sizeof(timeouts)) != 0) {
perror("ipchains: setting MASQ timeouts failed");
exit(1);
}
close(sockfd);
ret = 1;
break;
}
case CMD_SET_POLICY:
policy = parse_policy(chain, arglist[1]);
ret = ipfwc_set_policy(chain, policy);
break;
default:
/* We should never reach this... */
exit_tryhelp(2);
}
if (!ret)
fprintf(stderr, "ipchains: %s\n", ipfwc_strerror(errno));
return(!ret);
}
/* adapted interface code begin */
int
ipfw_append(int args, char **arglist, const char *chain)
{
unsigned int command = 0;
add_command(&command, CMD_APPEND, CMD_NONE);
ipfw_init(args, arglist, command, chain, 0);
optind = 0;
return(0);
}
int
ipfw_delete(int args, char **arglist, const char *chain, unsigned int rulenum)
{
unsigned int command = 0;
add_command(&command, CMD_DELETE, CMD_NONE);
command = CMD_DELETE_NUM;
ipfw_init(args, arglist, command, chain, rulenum);
optind = 0;
return(0);
}
int
ipfw_check(int args, char **arglist, const char *chain)
{
unsigned int command = 0;
add_command(&command, CMD_CHECK, CMD_NONE);
ipfw_init(args, arglist, command, chain, 0);
optind = 0;
return(0);
}
int
ipfw_replace(int args, char **arglist, const char *chain, unsigned int rulenum)
{
unsigned int command = 0;
add_command(&command, CMD_REPLACE, CMD_NONE);
ipfw_init(args, arglist, command, chain, rulenum);
optind = 0;
return(0);
}
int
ipfw_insert(int args, char **arglist, const char *chain, unsigned int rulenum)
{
unsigned int command = 0;
add_command(&command, CMD_INSERT, CMD_NONE);
if(!rulenum)
rulenum = 1;
ipfw_init(args, arglist, command, chain, rulenum);
optind = 0;
return(0);
}
int
ipfw_list(int args, char **arglist, const char *chain)
{
unsigned int command = 0;
add_command(&command, CMD_LIST, CMD_ZERO|CMD_MASQUERADE);
ipfw_init(args, arglist, command, chain, 0);
optind = 0;
return(0);
}
int
ipfw_flush(int args, char **arglist, const char *chain)
{
unsigned int command = 0;
add_command(&command, CMD_FLUSH, CMD_NONE);
ipfw_init(args, arglist, command, chain, 0);
optind = 0;
return(0);
}
int
ipfw_zero(int args, char **arglist)
{
unsigned int command = 0;
add_command(&command, CMD_ZERO, CMD_LIST);
ipfw_init(args, arglist, command, NULL, 0);
optind = 0;
return(0);
}
int
ipfw_new_chain(int args, char **arglist, const char *chain)
{
unsigned int command = 0;
add_command(&command, CMD_NEW_CHAIN, CMD_NONE);
ipfw_init(args, arglist, command, chain, 0);
optind = 0;
return(0);
}
int
ipfw_del_chain(int args, char **arglist, const char *chain)
{
unsigned int command = 0;
add_command(&command, CMD_DELETE_CHAIN, CMD_NONE);
ipfw_init(args, arglist, command, chain, 0);
optind = 0;
return(0);
}
int
ipfw_set_policy(int args, char **arglist, const char *chain)
{
unsigned int command = 0;
add_command(&command, CMD_SET_POLICY, CMD_NONE);
ipfw_init(args, arglist, command, chain, 0);
optind = 0;
return(0);
}
int
ipfw_masq(int args, char **arglist)
{
unsigned int command = 0;
add_command(&command, CMD_MASQUERADE, CMD_LIST|CMD_SET_MASQUERADE);
add_command(&command, CMD_LIST, CMD_ZERO|CMD_MASQUERADE);
ipfw_init(args, arglist, command, NULL, 0);
optind = 0;
return(0);
}
/* adapted interface code end */
static
int check_inverse(const char option[], int *invert)
{
if (option && strcmp(option, "!") == 0) {
if (*invert)
exit_error(PARAMETER_PROBLEM, "Multiple `!' flags not allowed");
*invert = TRUE;
return TRUE;
}
return FALSE;
}
/*
* All functions starting with "parse" should succeed, otherwise
* the program fails.
* Most routines return pointers to static data that may change
* between calls to the same or other routines with a few exceptions:
* "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
* return global static data.
*/
static struct in_addr *
parse_hostnetwork(const char *name, unsigned int *naddrs)
{
struct in_addr *addrp, *addrptmp;
if ((addrptmp = dotted_to_addr(name)) != NULL) {
addrp = fw_malloc(sizeof(struct in_addr));
inaddrcpy(addrp, addrptmp);
*naddrs = 1;
return addrp;
} else if ((addrptmp = network_to_addr(name)) != NULL) {
addrp = fw_malloc(sizeof(struct in_addr));
inaddrcpy(addrp, addrptmp);
*naddrs = 1;
return addrp;
} else if ((addrp = host_to_addr(name, naddrs)) != NULL) {
return addrp;
} else
exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
}
static void
parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
struct in_addr *maskp, unsigned int *naddrs)
{
struct in_addr *addrp;
char buf[256];
char *p;
int i, j, k, n;
strncpy(buf, name, sizeof(buf) - 1);
if ((p = strrchr(buf, '/')) != NULL) {
*p = '\0';
addrp = parse_mask(p + 1);
} else
addrp = parse_mask(NULL);
inaddrcpy(maskp, addrp);
/* if a null mask is given, the name is ignored, like in "any/0" */
if (maskp->s_addr == 0L)
strcpy(buf, "0.0.0.0");
addrp = *addrpp = parse_hostnetwork(buf, naddrs);
n = *naddrs;
for (i = 0, j = 0; i < n; i++) {
addrp[j++].s_addr &= maskp->s_addr;
for (k = 0; k < j - 1; k++) {
if (addrp[k].s_addr == addrp[j - 1].s_addr) {
(*naddrs)--;
j--;
break;
}
}
}
}
static struct in_addr *
parse_mask(char *mask)
{
static struct in_addr maskaddr;
struct in_addr *addrp;
int bits;
if (mask == NULL) {
/* no mask at all defaults to 32 bits */
maskaddr.s_addr = 0xFFFFFFFF;
return &maskaddr;
} else if ((addrp = dotted_to_addr(mask)) != NULL) {
/* dotted_to_addr already returns a network byte order addr */
return addrp;
} else if ((bits = string_to_number(mask, 0, 32)) == -1) {
exit_error(PARAMETER_PROBLEM, "invalid mask `%s' specified", mask);
} else if (bits != 0) {
maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
return &maskaddr;
} else {
maskaddr.s_addr = 0L;
return &maskaddr;
}
}
static const char *
parse_policy(const char *chain, const char *policy)
{
if (strcmp(policy, IP_FW_LABEL_MASQUERADE) == 0) {
if (strcmp(chain, IP_FW_LABEL_FORWARD) != 0)
exit_error(PARAMETER_PROBLEM,
"`-%c %s' only valid for `%s' chain.",
cmd2char(CMD_SET_POLICY),
IP_FW_LABEL_MASQUERADE,
IP_FW_LABEL_FORWARD);
else return policy;
}
if (strcmp(chain, IP_FW_LABEL_INPUT) != 0
&& strcmp(chain, IP_FW_LABEL_OUTPUT) != 0
&& strcmp(chain, IP_FW_LABEL_FORWARD) != 0)
exit_error(PARAMETER_PROBLEM,
"Can only set policy in built-in chains.");
if (strcmp(policy, IP_FW_LABEL_ACCEPT) != 0
&& strcmp(policy, IP_FW_LABEL_BLOCK) != 0
&& strcmp(policy, IP_FW_LABEL_REJECT) != 0)
exit_error(PARAMETER_PROBLEM, "Invalid policy `%s' for -%c", policy,
cmd2char(CMD_SET_POLICY));
return policy;
}
/* A few hardcoded protocols for 'all' and in case the user has no
/etc/protocols */
struct pprot {
char *name;
unsigned short num;
};
static const struct pprot chain_protos[] = {
{ "all", 0 },
{ "tcp", IPPROTO_TCP },
{ "udp", IPPROTO_UDP },
{ "icmp", IPPROTO_ICMP },
{0}
};
static unsigned short int
parse_protocol(const char *s)
{
struct protoent *pent;
const struct pprot *pp;
int proto = string_to_number(s, 0, 65535);
if (proto != -1) return (unsigned short)proto;
pent = getprotobyname(s);
if (pent != NULL)
return pent->p_proto;
for (pp = &chain_protos[0]; pp->name; pp++)
if (!strcasecmp(s, pp->name))
return pp->num;
exit_error(PARAMETER_PROBLEM, "unknown protocol `%s' specified", s);
}
static char *
proto_to_name(unsigned short proto)
{
const struct pprot *pp;
if (proto) {
struct protoent *pent = getprotobynumber(proto);
if (pent)
return pent->p_name;
}
for (pp = &chain_protos[0]; pp->name; pp++)
if (pp->num == proto)
return pp->name;
return NULL;
}
static void
parse_icmp(const char *srcportstring, const char *dstportstring,
__u16 *type, __u16* code, int *options, __u16 invflags,
int checking)
{
if (srcportstring) {
const unsigned int limit
= sizeof(icmp_codes)/sizeof(struct icmp_names);
unsigned int match = limit;
unsigned int i;
for (i = 0; i < limit; i++) {
if (strncasecmp(icmp_codes[i].name, srcportstring,
strlen(srcportstring)) == 0) {
if (match != limit)
exit_error(PARAMETER_PROBLEM, "Ambiguous ICMP `%s' : "
"`%s' or `%s'?",
srcportstring,
icmp_codes[match].name,
icmp_codes[i].name);
match = i;
}
}
if (match != limit) {
if (dstportstring)
exit_error(PARAMETER_PROBLEM, "Can't use ICMP name and destination port together");
*options |= OPT_DSTPT;
type[0] = type[1] = icmp_codes[match].type;
code[0] = icmp_codes[match].code_min;
code[1] = icmp_codes[match].code_max;
if (checking) {
if (icmp_codes[match].use_code_min_for_testing)
code[1] = code[0];
else if (code[0] != code[1])
exit_error(PARAMETER_PROBLEM, "Must specify exact ICMP type for testing");
}
/* Inverting won't do what they think it will do. */
if (invflags & (IP_FW_INV_SRCPT | IP_FW_INV_DSTPT))
exit_error(PARAMETER_PROBLEM, "Can't invert named ICMP types");
return;
}
}
parse_ports(srcportstring, type, IPPROTO_ICMP);
parse_ports(dstportstring, code, IPPROTO_ICMP);
}
static void
parse_ports(const char *portstring, __u16 *ports, __u16 proto)
{
char *buffer;
char *cp;
if (portstring == NULL) {
ports[0] = 0;
ports[1] = 0xFFFF;
return;
}
buffer = strdup(portstring);
if ((cp = strchr(buffer, ':')) == NULL) {
ports[0] = ports[1] = parse_port(buffer, proto);
}
else {
*cp = '\0';
cp++;
ports[0] = buffer[0] ? parse_port(buffer, proto) : 0;
ports[1] = cp[0] ? parse_port(cp, proto) : 0xFFFF;
}
free(buffer);
}
static unsigned short
parse_mark(char *markstring, __u32 *mark)
{
char *end, *ptr;
unsigned long l;
if (*markstring == '-' || *markstring == '+')
ptr = markstring+1;
else ptr = markstring;
l = strtoul(ptr, &end, 0);
if (end[0] != '\0' || end == ptr || l > UINT_MAX)
exit_error(PARAMETER_PROBLEM, "Bad value `%s' for -%c.\n",
markstring, opt2char(OPT_MARK));
/* This gives the effect of subtraction in the kernel */
if (*markstring == '-')
*mark = (__u32)(-(__s32)l);
else *mark = (__u32)l;
if (*markstring == '-' || *markstring == '+')
return 0;
else return IP_FW_F_MARKABS;
}
static unsigned short
parse_outputsize(char *outputstring, __u16 *outputsize)
{
if (!outputstring) {
*outputsize = 65535;
}
else {
int i = string_to_number(outputstring, 0, 65535);
if (i == -1)
exit_error(PARAMETER_PROBLEM, "-o argument must be 0-65535, not `%s'",
outputstring);
*outputsize = (__u16)i;
}
return IP_FW_F_NETLINK;
}
static unsigned short
parse_interface(const char *ifstring, char *vianame)
{
if (strlen(ifstring) > IFNAMSIZ)
exit_error(PARAMETER_PROBLEM, "interface name `%s' must be shorter than IFNAMSIZ (%i)",
ifstring, IFNAMSIZ);
strncpy(vianame, ifstring, IFNAMSIZ);
if (vianame[0] == '\0')
return IP_FW_F_WILDIF;
else if(vianame[strlen(ifstring)-1] == '+') {
vianame[strlen(ifstring)-1] = '\0';
return IP_FW_F_WILDIF;
}
else return 0;
}
static __u16
parse_port(const char *port, unsigned short proto)
{
int portnum;
if (proto != IPPROTO_ICMP
&& proto != IPPROTO_TCP
&& proto != IPPROTO_UDP)
exit_error(PARAMETER_PROBLEM, "can only specify ports for icmp, tcp or udp");
else if ((portnum = string_to_number(port, 0, 65535)) != -1)
return (unsigned short) portnum;
else if (proto == IPPROTO_ICMP) {
/* ICMP types (given as port numbers) must be numeric! */
exit_error(PARAMETER_PROBLEM, "invalid ICMP type `%s' specified", port);
} else if ((portnum = service_to_port(port, proto)) != -1)
return (unsigned short) portnum;
else {
exit_error(PARAMETER_PROBLEM, "invalid port/service `%s' specified", port);
}
}
/* Can't be zero. */
static int
parse_rulenumber(const char *rule)
{
int rulenum = string_to_number(rule, 1, INT_MAX);
if (rulenum == -1) exit_error(PARAMETER_PROBLEM, "Invalid rule number `%s'", rule);
return rulenum;
}
static void
parse_hexbyte(char *s, unsigned char *b)
{
long number;
char *end;
number = strtol(s, &end, 16);
if (*end == '\0' && end != s) {
/* we parsed a number, let's see if we want this */
if (0 <= number && number <= 255)
*b = (unsigned char) number;
else {
exit_error(PARAMETER_PROBLEM, "invalid hexbyte `%s' specified", s);
}
} else {
exit_error(PARAMETER_PROBLEM, "invalid hexbyte `%s' specified", s);
}
}
static int
parse_timeout(char *s)
{
int timeout;
if ((timeout = string_to_number(s, 0, INT_MAX)) != -1)
return timeout;
else
exit_error(PARAMETER_PROBLEM, "invalid timeout value `%s' specified", s);
}
static const char *
parse_target(const char *targetname)
{
const char *ptr;
if (targetname == NULL || strlen(targetname) < 1)
exit_error(PARAMETER_PROBLEM, "Invalid target name (too short)");
if (strlen(targetname)+1 > sizeof(ip_chainlabel))
exit_error(PARAMETER_PROBLEM, "Invalid target name `%s' (%i chars max)",
targetname, sizeof(ip_chainlabel)-1);
for (ptr = targetname; *ptr; ptr++)
if (isspace(*ptr)) exit_error(PARAMETER_PROBLEM, "Invalid target name `%s'",
targetname);
return targetname;
}
static struct in_addr *
host_to_addr(const char *name, unsigned int *naddr)
{
struct hostent *host;
struct in_addr *addr;
unsigned int i;
*naddr = 0;
if ((host = gethostbyname(name)) != NULL) {
if (host->h_addrtype != AF_INET ||
host->h_length != sizeof(struct in_addr))
return (struct in_addr *) NULL;
while (host->h_addr_list[*naddr] != (char *) NULL)
(*naddr)++;
addr = fw_calloc(*naddr, sizeof(struct in_addr));
for (i = 0; i < *naddr; i++)
inaddrcpy(&(addr[i]), (struct in_addr *) host->h_addr_list[i]);
return addr;
} else
return (struct in_addr *) NULL;
}
static char *
addr_to_host(struct in_addr *addr)
{
struct hostent *host;
if ((host = gethostbyaddr((char *) addr,
sizeof(struct in_addr), AF_INET)) != NULL)
return (char *) host->h_name;
else
return (char *) NULL;
}
static struct in_addr *
network_to_addr(const char *name)
{
struct netent *net;
static struct in_addr addr;
if ((net = getnetbyname(name)) != NULL) {
if (net->n_addrtype != AF_INET)
return (struct in_addr *) NULL;
addr.s_addr = htonl((unsigned long) net->n_net);
return &addr;
} else
return (struct in_addr *) NULL;
}
static char *
addr_to_network(struct in_addr *addr)
{
struct netent *net;
if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
return (char *) net->n_name;
else
return (char *) NULL;
}
static char *
addr_to_anyname(struct in_addr *addr)
{
char *name;
if ((name = addr_to_host(addr)) != NULL)
return name;
else if ((name = addr_to_network(addr)) != NULL)
return name;
else
return addr_to_dotted(addr);
}
static struct in_addr *
dotted_to_addr(const char *dotted)
{
static struct in_addr addr;
unsigned char *addrp;
char *p, *q;
int onebyte, i;
char buf[20];
/* copy dotted string, because we need to modify it */
strncpy(buf, dotted, sizeof(buf) - 1);
addrp = (unsigned char *) &(addr.s_addr);
p = buf;
for (i = 0; i < 3; i++) {
if ((q = strchr(p, '.')) == NULL)
return (struct in_addr *) NULL;
else {
*q = '\0';
if ((onebyte = string_to_number(p, 0, 255)) == -1)
return (struct in_addr *) NULL;
else
addrp[i] = (unsigned char) onebyte;
}
p = q + 1;
}
/* we've checked 3 bytes, now we check the last one */
if ((onebyte = string_to_number(p, 0, 255)) == -1)
return (struct in_addr *) NULL;
else
addrp[3] = (unsigned char) onebyte;
return &addr;
}
static char *
addr_to_dotted(struct in_addr *addrp)
{
static char buf[20];
unsigned char *bytep;
bytep = (unsigned char *) &(addrp->s_addr);
sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
return buf;
}
static char *
mask_to_dotted(struct in_addr *mask)
{
int i;
static char buf[20];
__u32 maskaddr, bits;
maskaddr = ntohl(mask->s_addr);
if (maskaddr == 0xFFFFFFFFL)
/* we don't want to see "/32" */
return "";
else {
i = 32;
bits = 0xFFFFFFFEL;
while (--i >= 0 && maskaddr != bits)
bits <<= 1;
if (i >= 0)
sprintf(buf, "/%d", i);
else
/* mask was not a decent combination of 1's and 0's */
sprintf(buf, "/%s", addr_to_dotted(mask));
return buf;
}
}
static int
service_to_port(const char *name, unsigned short proto)
{
struct servent *service;
if (proto == IPPROTO_TCP
&& (service = getservbyname(name, "tcp")) != NULL)
return ntohs((unsigned short) service->s_port);
else if (proto == IPPROTO_UDP
&& (service = getservbyname(name, "udp")) != NULL)
return ntohs((unsigned short) service->s_port);
else
return -1;
}
static char *
port_to_service(int port, unsigned short proto)
{
struct servent *service;
if (proto == IPPROTO_TCP &&
(service = getservbyport(htons(port), "tcp")) != NULL)
return service->s_name;
else if (proto == IPPROTO_UDP &&
(service = getservbyport(htons(port), "udp")) != NULL)
return service->s_name;
else
return (char *) NULL;
}
static int
string_to_number(const char *s, int min, int max)
{
int number;
char *end;
number = (int)strtol(s, &end, 10);
if (*end == '\0' && end != s) {
/* we parsed a number, let's see if we want this */
if (min <= number && number <= max)
return number;
else
return -1;
} else
return -1;
}
static int
list_masq(unsigned int options)
{
FILE *fp;
int i;
char buf[256];
struct masq *mslist;
int ntotal = 0, nread, format;
if ((fp = fopen("/proc/net/ip_masquerade", "r")) == NULL)
exit_error(OTHER_PROBLEM, "cannot open file `/proc/net/ip_masquerade'");
if (fgets(buf, sizeof(buf), fp) == NULL)
exit_error(VERSION_PROBLEM,
"unexpected input from `/proc/net/ip_masquerade'");
fputs("IP masquerading entries\n", stdout);
mslist = (struct masq *) fw_malloc(16 * sizeof(struct masq));
while ((nread = read_masqinfo(fp, &(mslist[ntotal]), 16)) == 16) {
ntotal += nread;
mslist = (struct masq *) fw_realloc(mslist,
(ntotal + 16) * sizeof(struct masq));
}
ntotal += nread;
fclose(fp);
format = 0;
if (options & OPT_NUMERIC)
format |= FMT_NUMERIC;
if (options & OPT_VERBOSE)
format |= FMT_DELTAS;
if (ntotal > 0)
for (i = 0; i < ntotal; i++)
print_masq(stdout, &(mslist[i]),
(i) ? format : (format | FMT_HEADER));
free(mslist);
return 1;
}
static void
print_port(FILE *fp, int numeric, __u16 port, __u16 proto)
{
char *service;
if (numeric || (service = port_to_service(port, proto)) == NULL)
fprintf(fp, "%u", port);
else
fputs(service, fp);
}
static void
print_firewall(FILE *fp,
struct ip_fwuser *fw,
__u64 bytes, __u64 packets,
int format)
{
unsigned short flags;
__u64 cnt, cntkb, cntmb, cntgb;
char buf[BUFSIZ];
flags = fw->ipfw.fw_flg;
#define FMT(tab,notab) ((format & FMT_NOTABLE) ? notab : tab)
if (format & FMT_HEADER) {
if (!(format & FMT_NOCOUNTS)) {
if (format & FMT_KILOMEGAGIGA) {
fprintf(fp, FMT("%5s ","%s "), "pkts");
fprintf(fp, FMT("%5s ","%s "), "bytes");
} else {
fprintf(fp, FMT("%8s ","%s "), "pkts");
fprintf(fp, FMT("%10s ","%s "), "bytes");
}
}
if (!(format & FMT_NOTARGET)) {
fprintf(fp, FMT("%-9s ","%s "), "target");
}
fputs(" prot ", fp);
if (format & FMT_OPTIONS)
fputs("opt ", fp);
if (format & FMT_TOS)
fputs("tosa tosx ", fp);
if (format & FMT_VIA)
fprintf(fp, FMT(" %-10s ","%s "), "ifname");
if (format & FMT_MARK)
fprintf(fp, FMT("%-10s ","%s "), "mark");
if (format & FMT_NETLINK)
fprintf(fp, FMT("%-7s ","%s "), "outsize");
fprintf(fp, FMT(" %-20s ","%s "), "source");
fprintf(fp, FMT(" %-20s ","%s "), "destination");
fputs(" ports\n", fp);
}
if (!(format & FMT_NOCOUNTS)) {
cnt = packets;
if (format & FMT_KILOMEGAGIGA) {
if (cnt > 99999) {
cntkb = (cnt + 500) / 1000;
if (cntkb > 9999) {
cntmb = (cnt + 500000) / 1000000;
if (cntmb > 9999) {
cntgb = (cntmb + 500) / 1000;
fprintf(fp,
FMT("%4lluG ","%lluG "),
cntgb);
}
else
fprintf(fp, FMT("%4lluM ","%lluM "), cntmb);
} else
fprintf(fp, FMT("%4lluK ","%lluK "), cntkb);
} else
fprintf(fp, FMT("%5llu ","%llu "), cnt);
} else
fprintf(fp, FMT("%8llu ","%llu "), cnt);
cnt = bytes;
if (format & FMT_KILOMEGAGIGA) {
if (cnt > 99999) {
cntkb = (cnt + 500) / 1000;
if (cntkb > 9999) {
cntmb = (cnt + 500000) / 1000000;
if (cntmb > 9999) {
cntgb = (cntmb + 500) / 1000;
fprintf(fp,
FMT("%4lluG ","%lluG "),
cntgb);
}
else
fprintf(fp, FMT("%4lluM ","%lluM "), cntmb);
} else
fprintf(fp, FMT("%4lluK ","%lluK "), cntkb);
} else
fprintf(fp, FMT("%5llu ","%llu "), cnt);
} else
fprintf(fp, FMT("%10llu ","%llu "), cnt);
}
if (!(format & FMT_NOTARGET))
fprintf(fp, FMT("%-9s ", "%s "), fw->label);
fputc(fw->ipfw.fw_invflg & IP_FW_INV_PROTO ? '!' : ' ', fp);
{
char *pname = proto_to_name(fw->ipfw.fw_proto);
if (pname)
fprintf(fp, FMT("%-5s", "%s "), pname);
else
fprintf(fp, FMT("%-5hu", "%hu "), fw->ipfw.fw_proto);
}
if (format & FMT_OPTIONS) {
if (format & FMT_NOTABLE)
fputs("opt ", fp);
fputc((fw->ipfw.fw_invflg & IP_FW_INV_SYN) ? '!' : '-', fp);
fputc((flags & IP_FW_F_TCPSYN) ? 'y' : '-', fp);
fputc((fw->ipfw.fw_invflg & IP_FW_INV_FRAG) ? '!' : '-', fp);
fputc((flags & IP_FW_F_FRAG) ? 'f' : '-', fp);
fputc((flags & IP_FW_F_PRN) ? 'l' : '-', fp);
fputc((flags & IP_FW_F_NETLINK) ? 'o' : '-', fp);
fputc(' ', fp);
}
if (format & FMT_TOS) {
if (format & FMT_NOTABLE)
fputs("tos ", fp);
fprintf(fp, "0x%02hX 0x%02hX ",
(unsigned short) fw->ipfw.fw_tosand,
(unsigned short) fw->ipfw.fw_tosxor);
}
if (format & FMT_VIA) {
fputc(fw->ipfw.fw_invflg & IP_FW_INV_VIA ? '!' : ' ', fp);
if (fw->ipfw.fw_flg & IP_FW_F_WILDIF
&& (fw->ipfw.fw_vianame)[0]) {
fw->ipfw.fw_vianame[strlen(fw->ipfw.fw_vianame)+1]='\0';
fw->ipfw.fw_vianame[strlen(fw->ipfw.fw_vianame)]='+';
}
fprintf(fp, FMT("%-10.16s ","via %.16s "),
(fw->ipfw.fw_vianame)[0] ? fw->ipfw.fw_vianame :
((format & FMT_NUMERIC) ? "*" : "any"));
}
if (format & FMT_MARK) {
if (fw->ipfw.fw_flg & IP_FW_F_MARKABS)
fprintf(fp, FMT("0x%-10x ","mark 0x%x "),fw->ipfw.fw_mark);
else if (fw->ipfw.fw_mark == 0)
fputs(FMT(" ", ""), fp);
else
fprintf(fp, FMT("0x%+-10x ","mark 0x%+x "),
(int)fw->ipfw.fw_mark);
}
if (format & FMT_NETLINK) {
if ((fw->ipfw.fw_flg & IP_FW_F_NETLINK)
&& (fw->ipfw.fw_outputsize != 0xFFFF))
fprintf(fp, FMT("%-7hu ","outsize %hu "),
fw->ipfw.fw_outputsize);
else
fputs(FMT(" ", ""), fp);
}
if (format & FMT_NOTABLE)
fputs(" ", fp);
fputc(fw->ipfw.fw_invflg & IP_FW_INV_SRCIP ? '!' : ' ', fp);
if (fw->ipfw.fw_smsk.s_addr == 0L && !(format & FMT_NUMERIC))
fprintf(fp, FMT("%-20s ","%s "), "anywhere");
else {
if (format & FMT_NUMERIC)
sprintf(buf, "%s", addr_to_dotted(&(fw->ipfw.fw_src)));
else
sprintf(buf, "%s", addr_to_anyname(&(fw->ipfw.fw_src)));
strcat(buf, mask_to_dotted(&(fw->ipfw.fw_smsk)));
fprintf(fp, FMT("%-20s ","%s "), buf);
}
fputc(fw->ipfw.fw_invflg & IP_FW_INV_DSTIP ? '!' : ' ', fp);
if (fw->ipfw.fw_dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
fprintf(fp, FMT("%-20s","-> %s"), "anywhere");
else {
if (format & FMT_NUMERIC)
sprintf(buf, "%s", addr_to_dotted(&(fw->ipfw.fw_dst)));
else
sprintf(buf, "%s", addr_to_anyname(&(fw->ipfw.fw_dst)));
strcat(buf, mask_to_dotted(&(fw->ipfw.fw_dmsk)));
fprintf(fp, FMT("%-20s","-> %s"), buf);
}
if (format & FMT_NOTABLE)
fputs(" ", fp);
if (fw->ipfw.fw_proto != IPPROTO_TCP
&& fw->ipfw.fw_proto != IPPROTO_UDP
&& fw->ipfw.fw_proto != IPPROTO_ICMP) {
fputs(" n/a", fp);
if (!(format & FMT_NONEWLINE))
putc('\n', fp);
return;
}
/* ICMP handled specially. */
if (fw->ipfw.fw_proto == IPPROTO_ICMP
&& !(fw->ipfw.fw_invflg & IP_FW_INV_SRCPT)
&& !(fw->ipfw.fw_invflg & IP_FW_INV_DSTPT)
&& !(format & FMT_NUMERIC)) {
unsigned int i;
for (i = 0;
i < sizeof(icmp_codes)/sizeof(struct icmp_names);
i++) {
if (icmp_codes[i].type == fw->ipfw.fw_spts[0]
&& icmp_codes[i].type == fw->ipfw.fw_spts[1]
&& icmp_codes[i].code_min == fw->ipfw.fw_dpts[0]
&& icmp_codes[i].code_max == fw->ipfw.fw_dpts[1]) {
fprintf(fp, " %s", icmp_codes[i].name);
if (!(format & FMT_NONEWLINE))
putc('\n', fp);
return;
}
}
}
fputs(fw->ipfw.fw_invflg & IP_FW_INV_SRCPT ? " !" : " ", fp);
if (fw->ipfw.fw_spts[0] == 0 && fw->ipfw.fw_spts[1] == 0xFFFF)
fputs((format & FMT_NUMERIC) ? "*" : "any", fp);
else if (fw->ipfw.fw_spts[0] == fw->ipfw.fw_spts[1]) {
print_port(fp, format & FMT_NUMERIC,
fw->ipfw.fw_spts[0], fw->ipfw.fw_proto);
}
else {
print_port(fp, format & FMT_NUMERIC,
fw->ipfw.fw_spts[0], fw->ipfw.fw_proto);
fputc(':', fp);
print_port(fp, format & FMT_NUMERIC,
fw->ipfw.fw_spts[1], fw->ipfw.fw_proto);
}
fputs(" -> ", fp);
fputs(fw->ipfw.fw_invflg & IP_FW_INV_DSTPT ? " !" : " ", fp);
if (fw->ipfw.fw_dpts[0] == 0 && fw->ipfw.fw_dpts[1] == 0xFFFF)
fputs((format & FMT_NUMERIC) ? "*" : "any", fp);
else if (fw->ipfw.fw_dpts[0] == fw->ipfw.fw_dpts[1]) {
print_port(fp, format & FMT_NUMERIC,
fw->ipfw.fw_dpts[0], fw->ipfw.fw_proto);
}
else {
print_port(fp, format & FMT_NUMERIC,
fw->ipfw.fw_dpts[0], fw->ipfw.fw_proto);
fputc(':', fp);
print_port(fp, format & FMT_NUMERIC,
fw->ipfw.fw_dpts[1], fw->ipfw.fw_proto);
}
if (strcmp(fw->label, IP_FW_LABEL_REDIRECT) == 0) {
fputs(FMT(" => ", ""), fp);
if (fw->ipfw.fw_redirpt == 0)
fputs((format & FMT_NUMERIC) ? " *" : " any", fp);
else
print_port(fp, format & FMT_NUMERIC,
fw->ipfw.fw_redirpt, fw->ipfw.fw_proto);
}
if (!(format & FMT_NONEWLINE))
putc('\n', fp);
}
static void
print_masq(FILE *fp, struct masq *ms, int format)
{
unsigned long minutes, seconds, sec100s;
unsigned short proto;
char *service;
if (format & FMT_HEADER) {
fputs("prot ", fp);
fprintf(fp, "%-8s ", "expire");
if (format & FMT_DELTAS) {
fprintf(fp, "%10s delta prevd ", "initseq");
}
fprintf(fp, "%-20s ", "source");
fprintf(fp, "%-20s ", "destination");
fputs("ports\n", fp);
}
fprintf(fp, "%-5s", ms->proto);
sec100s = ((ms->expires % HZ) * 100) / HZ;
seconds = (ms->expires / HZ) % 60;
minutes = ms->expires / (60 * HZ);
fprintf(fp, "%02ld:%02ld.%02ld ", minutes, seconds, sec100s);
if (format & FMT_DELTAS) {
fprintf(fp, "%10lu %5hd %5hd ", (unsigned long) ms->initseq,
ms->delta, ms->pdelta);
}
if (format & FMT_NUMERIC) {
fprintf(fp, "%-20s ", addr_to_dotted(&(ms->src)));
fprintf(fp, "%-20s ", addr_to_dotted(&(ms->dst)));
} else {
fprintf(fp, "%-20s ", addr_to_anyname(&(ms->src)));
fprintf(fp, "%-20s ", addr_to_anyname(&(ms->dst)));
}
if (strcmp(ms->proto, "TCP") == 0) proto = IPPROTO_TCP;
else if (strcmp(ms->proto, "UDP") == 0) proto = IPPROTO_UDP;
else proto = 0;
if ((format & FMT_NUMERIC) || !proto)
fprintf(fp, "%u (%u) -> %u\n", ms->sport, ms->mport, ms->dport);
else {
if ((service = port_to_service(ms->sport, proto)) != NULL)
fprintf(fp, "%s (%u) -> ", service, ms->mport);
else
fprintf(fp, "%u (%u) -> ", ms->sport, ms->mport);
if ((service = port_to_service(ms->dport, proto)) != NULL)
fprintf(fp, "%s\n", service);
else
fprintf(fp, "%u\n", ms->dport);
}
}
static int
read_masqinfo(FILE *fp, struct masq *mslist, int nmslist)
{
int n, nread = 0;
struct masq *ms;
char buf[256];
unsigned long temp[3];
for (nread = 0; nread < nmslist; nread++) {
ms = &mslist[nread];
if ((n = fscanf(fp, " %s %lX:%hX %lX:%hX %hX %lX %hd %hd %lu",
buf, &temp[0], &ms->sport, &temp[1], &ms->dport,
&ms->mport, &temp[2], &ms->delta,
&ms->pdelta, &ms->expires)) == -1)
return nread;
else if (n != 10)
exit_error(VERSION_PROBLEM, "unexpected input data");
strncpy(ms->proto, buf, sizeof(ms->proto));
ms->proto[sizeof(ms->proto)-1] = '\0';
/* we always keep these addresses in network byte order */
ms->src.s_addr = (__u32) htonl(temp[0]);
ms->dst.s_addr = (__u32) htonl(temp[1]);
ms->initseq = (__u32) temp[2];
}
return nread;
}
static void
set_option(unsigned int *options, unsigned int option, __u16 *invflg)
{
if (*options & option)
exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed\n",
opt2char(option));
else *options |= option;
if (invert) {
unsigned int i;
for (i = 0; 1 << i != option; i++);
if (!inverse_for_options[i])
exit_error(PARAMETER_PROBLEM, "cannot have ! before -%c\n",
opt2char(option));
*invflg |= inverse_for_options[i];
invert = FALSE;
}
}
static void
inaddrcpy(struct in_addr *dst, struct in_addr *src)
{
/* memcpy(dst, src, sizeof(struct in_addr)); */
dst->s_addr = src->s_addr;
}
static void *
fw_malloc(size_t size)
{
void *p;
if ((p = malloc(size)) == NULL) {
perror("ipchains: malloc failed");
exit(1);
}
return p;
}
static void *
fw_calloc(size_t count, size_t size)
{
void *p;
if ((p = calloc(count, size)) == NULL) {
perror("ipchains: calloc failed");
exit(1);
}
return p;
}
static void *
fw_realloc(void *ptr, size_t size)
{
void *p;
if ((p = realloc(ptr, size)) == NULL) {
perror("ipchains: realloc failed");
exit(1);
}
return p;
}
static void
exit_error(enum exittype status, char *msg, ...)
{
va_list args;
va_start(args, msg);
fprintf(stderr, "%s: ", program);
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr, "\n");
if (status == PARAMETER_PROBLEM) exit_tryhelp(status);
else if (status == VERSION_PROBLEM)
fprintf(stderr, "Perhaps ipchains or your kernel need to be upgraded.\n");
exit(status);
}
static void
exit_tryhelp(int status)
{
fprintf(stderr, "See IPChains.pm documentation for more information.\n");
exit(status);
}
static void
exit_printicmphelp()
{
unsigned int i;
printf("%s\n\n"
"Valid ICMP Types:",
package_version);
for (i = 0; i < sizeof(icmp_codes)/sizeof(struct icmp_names); i++) {
if (i && icmp_codes[i].type == icmp_codes[i-1].type) {
if (icmp_codes[i].code_min == icmp_codes[i-1].code_min
&& (icmp_codes[i].code_max
== icmp_codes[i-1].code_max))
printf(" (%s)", icmp_codes[i].name);
else
printf("\n %s", icmp_codes[i].name);
}
else
printf("\n%s", icmp_codes[i].name);
}
printf("\n");
exit(0);
}
static void
exit_printhelp()
{
printf("%s\n\n"
"Usage: %s -[ADC] chain rule-specification [options]\n"
" %s -[RI] chain rulenum rule-specification [options]\n"
" %s -D chain rulenum [options]\n"
" %s -[LFZNX] [chain] [options]\n"
" %s -P chain target [options]\n"
" %s -M [ -L | -S ] [options]\n"
" %s -h [icmp] (print this help information, or ICMP list)\n\n",
package_version, program, program, program, program, program,
program, program);
printf(
"Commands:\n"
"Either long or short options are allowed.\n"
" --add -A chain Append to chain\n"
" --delete -D chain Delete matching rule from chain\n"
" --delete -D chain rulenum\n"
" Delete rule rulenum (1 = first) from chain\n"
" --insert -I chain [rulenum]\n"
" Insert in chain as rulenum (default 1=first)\n"
" --replace -R chain rulenum\n"
" Replace rule rulenum (1 = first) in chain\n"
" --list -L [chain] List the rules in a chain or all chains\n"
" --flush -F [chain] Delete all rules in chain or all chains\n"
" --zero -Z [chain] Zero counters in chain or all chains\n"
" --check -C chain Test this packet on chain\n"
" --new -N chain Create a new user-defined chain\n"
" --delete -X chain Delete a user-defined chain\n"
" --policy -P chain target\n"
" Change policy on chain to target\n"
" --masquerade -M -L List current masqerading connections\n"
" --masquerade -M -S tcp tcpfin udp\n"
" Set masquerading timeout values\n\n"
"Options:\n"
" --bidirectional -b insert two rules: one with -s & -d reversed\n"
" --proto -p [!] proto protocol: by number or name, eg. `tcp'\n"
" --source -s [!] address[/mask] [!] [port[:port]]\n"
" source specification\n"
" --source-port [!] [port[:port]]\n"
" source port specification\n"
" --destination -d [!] address[/mask] [!] [port[:port]]\n"
" destination specification\n"
" --destination-port [!] [port[:port]]\n"
" destination port specification\n"
" --icmp-type [!] typename specify ICMP type\n"
" --interface -i [!] name[+]\n"
" network interface name ([+] for wildcard)\n"
" --jump -j target [port]\n"
" target for rule ([port] for REDIRECT)\n"
" --mark -m [+-]mark number to mark on matching packet\n"
" --numeric -n numeric output of addresses and ports\n"
" --log -l turn on kernel logging for matching packets\n"
" --output -o [maxsize] output matching packet to netlink device\n"
" --TOS -t and xor and/xor masks for TOS field\n"
" --verbose -v verbose mode\n"
" --exact -x expand numbers (display exact values)\n"
"[!] --fragment -f match second or further fragments only\n"
"[!] --syn -y match TCP packets only when SYN set\n"
"[!] --version -V print package version.\n");
exit(0);
}
static void
generic_opt_check(int command, int options)
{
int i,j,legal=0;
/* Check that commands are valid with options. Complicated by the
* fact that if an option is legal with *any* command given, it is
* legal overall (ie. -z and -l).
*/
for (i = 0; i < NUMBER_OF_OPT; i++) {
legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
for (j = 0; j < NUMBER_OF_CMD; j++) {
if (!(command & (1<<j)))
continue;
if (!(options & (1<<i))) {
if (commands_v_options[j][i] == '+')
exit_error(PARAMETER_PROBLEM, "You need to supply the `-%c' option for this command\n", optflags[i]);
}
else {
if (commands_v_options[j][i] != 'x')
legal = 1;
else if (legal == 0)
legal = -1;
}
}
if (legal == -1)
exit_error(PARAMETER_PROBLEM,
"Illegal option `-%c' with this command\n",
optflags[i]);
}
}
static char
opt2char(int option)
{
const char *ptr;
for (ptr = optflags; option > 1; option >>=1, ptr++);
return *ptr;
}
static char
cmd2char(int option)
{
const char *ptr;
for (ptr = cmdflags; option > 1; option >>=1, ptr++);
return *ptr;
}
static int
count_tos_bits(unsigned char bitpattern)
{
int bits;
for (bits = 0, bitpattern &= 0x0E; bitpattern > 0x00; bitpattern >>= 1)
bits += bitpattern & 0x01;
return bits;
}
static void
check_tos(unsigned char tosand, unsigned char tosxor)
{
unsigned int i;
/* The LSB Must Be Untouched (RFC 1349). */
if (!(tosand & 1) || (tosxor & 1))
exit_error(PARAMETER_PROBLEM, "TOS manipulation cannot alter bit 0 (RFC1349)\n");
/* Don't create packets with more than 1 TOS bit set, (RFC 1349). */
/* If it will always create a packet with > 1 TOS bit, it's an
* error. If it MIGHT create a packet with > 1 TOS bit set,
* issue a warning to stdout (so the user can filter this out
* if they really want this.)
*/
if (count_tos_bits((~tosand) & tosxor) > 1)
exit_error(PARAMETER_PROBLEM, "TOS manipulation cannot set multiple TOS bits "
"(RFC1349)\n");
for (i = 0; i < sizeof(TOS_values)/sizeof(struct TOS_value); i++)
if (count_tos_bits((TOS_values[i].TOS & tosand) ^ tosxor) > 1)
printf("Warning: TOS manipulation may set multiple "
"TOS bits for %s TOS (0x%02x)\n",
TOS_values[i].name, TOS_values[i].TOS);
}
static void
add_command(int *cmd, const int newcmd, const int othercmds)
{
if (invert)
exit_error(PARAMETER_PROBLEM, "unexpected ! flag");
if (*cmd & (~othercmds))
exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
else *cmd |= newcmd;
}