Commit 9ad165c5 authored by Arturo Borrero Gonzalez's avatar Arturo Borrero Gonzalez
Browse files

Merge tag 'upstream/1.6.0'

Upstream version 1.6.0
parents 7f04bf74 615f9fca
/*
* (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
* (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
*
* 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 code has been sponsored by Sophos Astaro <http://www.sophos.com>
*/
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/ip6.h>
#include <xtables.h>
#include <linux/netfilter/nf_tables.h>
#include "nft.h"
#include "nft-shared.h"
static int nft_ipv6_add(struct nftnl_rule *r, void *data)
{
struct iptables_command_state *cs = data;
struct xtables_rule_match *matchp;
uint32_t op;
if (cs->fw6.ipv6.iniface[0] != '\0') {
op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_VIA_IN);
add_iniface(r, cs->fw6.ipv6.iniface, op);
}
if (cs->fw6.ipv6.outiface[0] != '\0') {
op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_VIA_OUT);
add_outiface(r, cs->fw6.ipv6.outiface, op);
}
if (cs->fw6.ipv6.proto != 0) {
op = nft_invflags2cmp(cs->fw6.ipv6.invflags, XT_INV_PROTO);
add_proto(r, offsetof(struct ip6_hdr, ip6_nxt), 1,
cs->fw6.ipv6.proto, op);
}
if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src)) {
op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_SRCIP);
add_addr(r, offsetof(struct ip6_hdr, ip6_src),
&cs->fw6.ipv6.src, &cs->fw6.ipv6.smsk,
sizeof(struct in6_addr), op);
}
if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst)) {
op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_DSTIP);
add_addr(r, offsetof(struct ip6_hdr, ip6_dst),
&cs->fw6.ipv6.dst, &cs->fw6.ipv6.dmsk,
sizeof(struct in6_addr), op);
}
add_compat(r, cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags);
for (matchp = cs->matches; matchp; matchp = matchp->next) {
if (add_match(r, matchp->match->m) < 0)
break;
}
/* Counters need to me added before the target, otherwise they are
* increased for each rule because of the way nf_tables works.
*/
if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0)
return -1;
return add_action(r, cs, !!(cs->fw6.ipv6.flags & IP6T_F_GOTO));
}
static bool nft_ipv6_is_same(const void *data_a,
const void *data_b)
{
const struct iptables_command_state *a = data_a;
const struct iptables_command_state *b = data_b;
if (memcmp(a->fw6.ipv6.src.s6_addr, b->fw6.ipv6.src.s6_addr,
sizeof(struct in6_addr)) != 0
|| memcmp(a->fw6.ipv6.dst.s6_addr, b->fw6.ipv6.dst.s6_addr,
sizeof(struct in6_addr)) != 0
|| a->fw6.ipv6.proto != b->fw6.ipv6.proto
|| a->fw6.ipv6.flags != b->fw6.ipv6.flags
|| a->fw6.ipv6.invflags != b->fw6.ipv6.invflags) {
DEBUGP("different src/dst/proto/flags/invflags\n");
return false;
}
return is_same_interfaces(a->fw6.ipv6.iniface, a->fw6.ipv6.outiface,
a->fw6.ipv6.iniface_mask,
a->fw6.ipv6.outiface_mask,
b->fw6.ipv6.iniface, b->fw6.ipv6.outiface,
b->fw6.ipv6.iniface_mask,
b->fw6.ipv6.outiface_mask);
}
static void nft_ipv6_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
void *data)
{
struct iptables_command_state *cs = data;
parse_meta(e, ctx->meta.key, cs->fw6.ipv6.iniface,
cs->fw6.ipv6.iniface_mask, cs->fw6.ipv6.outiface,
cs->fw6.ipv6.outiface_mask, &cs->fw6.ipv6.invflags);
}
static void parse_mask_ipv6(struct nft_xt_ctx *ctx, struct in6_addr *mask)
{
memcpy(mask, ctx->bitwise.mask, sizeof(struct in6_addr));
}
static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx,
struct nftnl_expr *e, void *data)
{
struct iptables_command_state *cs = data;
struct in6_addr addr;
uint8_t proto;
bool inv;
switch (ctx->payload.offset) {
case offsetof(struct ip6_hdr, ip6_src):
get_cmp_data(e, &addr, sizeof(addr), &inv);
memcpy(cs->fw6.ipv6.src.s6_addr, &addr, sizeof(addr));
if (ctx->flags & NFT_XT_CTX_BITWISE) {
parse_mask_ipv6(ctx, &cs->fw6.ipv6.smsk);
ctx->flags &= ~NFT_XT_CTX_BITWISE;
} else {
memset(&cs->fw.ip.smsk, 0xff, sizeof(struct in6_addr));
}
if (inv)
cs->fw6.ipv6.invflags |= IPT_INV_SRCIP;
break;
case offsetof(struct ip6_hdr, ip6_dst):
get_cmp_data(e, &addr, sizeof(addr), &inv);
memcpy(cs->fw6.ipv6.dst.s6_addr, &addr, sizeof(addr));
if (ctx->flags & NFT_XT_CTX_BITWISE) {
parse_mask_ipv6(ctx, &cs->fw6.ipv6.dmsk);
ctx->flags &= ~NFT_XT_CTX_BITWISE;
} else {
memset(&cs->fw.ip.dmsk, 0xff, sizeof(struct in6_addr));
}
if (inv)
cs->fw6.ipv6.invflags |= IPT_INV_DSTIP;
break;
case offsetof(struct ip6_hdr, ip6_nxt):
get_cmp_data(e, &proto, sizeof(proto), &inv);
cs->fw6.ipv6.flags |= IP6T_F_PROTO;
cs->fw6.ipv6.proto = proto;
if (inv)
cs->fw6.ipv6.invflags |= IPT_INV_PROTO;
default:
DEBUGP("unknown payload offset %d\n", ctx->payload.offset);
break;
}
}
static void nft_ipv6_parse_immediate(const char *jumpto, bool nft_goto,
void *data)
{
struct iptables_command_state *cs = data;
cs->jumpto = jumpto;
if (nft_goto)
cs->fw6.ipv6.flags |= IP6T_F_GOTO;
}
static void nft_ipv6_print_header(unsigned int format, const char *chain,
const char *pol,
const struct xt_counters *counters,
bool basechain, uint32_t refs)
{
print_header(format, chain, pol, counters, basechain, refs);
}
static void print_ipv6_addr(const struct iptables_command_state *cs,
unsigned int format)
{
char buf[BUFSIZ];
fputc(cs->fw6.ipv6.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
if (IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src)
&& !(format & FMT_NUMERIC))
printf(FMT("%-19s ","%s "), "anywhere");
else {
if (format & FMT_NUMERIC)
strcpy(buf,
xtables_ip6addr_to_numeric(&cs->fw6.ipv6.src));
else
strcpy(buf,
xtables_ip6addr_to_anyname(&cs->fw6.ipv6.src));
strcat(buf, xtables_ip6mask_to_numeric(&cs->fw6.ipv6.smsk));
printf(FMT("%-19s ","%s "), buf);
}
fputc(cs->fw6.ipv6.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
if (IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst)
&& !(format & FMT_NUMERIC))
printf(FMT("%-19s ","-> %s"), "anywhere");
else {
if (format & FMT_NUMERIC)
strcpy(buf,
xtables_ip6addr_to_numeric(&cs->fw6.ipv6.dst));
else
strcpy(buf,
xtables_ip6addr_to_anyname(&cs->fw6.ipv6.dst));
strcat(buf, xtables_ip6mask_to_numeric(&cs->fw6.ipv6.dmsk));
printf(FMT("%-19s ","-> %s"), buf);
}
}
static void nft_ipv6_print_firewall(struct nftnl_rule *r, unsigned int num,
unsigned int format)
{
struct iptables_command_state cs = {};
nft_rule_to_iptables_command_state(r, &cs);
print_firewall_details(&cs, cs.jumpto, cs.fw6.ipv6.flags,
cs.fw6.ipv6.invflags, cs.fw6.ipv6.proto,
num, format);
print_ifaces(cs.fw6.ipv6.iniface, cs.fw6.ipv6.outiface,
cs.fw6.ipv6.invflags, format);
print_ipv6_addr(&cs, format);
if (format & FMT_NOTABLE)
fputs(" ", stdout);
if (cs.fw6.ipv6.flags & IP6T_F_GOTO)
printf("[goto] ");
print_matches_and_target(&cs, format);
if (!(format & FMT_NONEWLINE))
fputc('\n', stdout);
}
static void save_ipv6_addr(char letter, const struct in6_addr *addr,
int invert)
{
char addr_str[INET6_ADDRSTRLEN];
if (!invert && IN6_IS_ADDR_UNSPECIFIED(addr))
return;
inet_ntop(AF_INET6, addr, addr_str, INET6_ADDRSTRLEN);
printf("%s-%c %s ", invert ? "! " : "", letter, addr_str);
}
static void nft_ipv6_save_firewall(const void *data, unsigned int format)
{
const struct iptables_command_state *cs = data;
save_firewall_details(cs, cs->fw6.ipv6.invflags, cs->fw6.ipv6.proto,
cs->fw6.ipv6.iniface, cs->fw6.ipv6.iniface_mask,
cs->fw6.ipv6.outiface,
cs->fw6.ipv6.outiface_mask);
save_ipv6_addr('s', &cs->fw6.ipv6.src,
cs->fw6.ipv6.invflags & IPT_INV_SRCIP);
save_ipv6_addr('d', &cs->fw6.ipv6.dst,
cs->fw6.ipv6.invflags & IPT_INV_DSTIP);
save_matches_and_target(cs->matches, cs->target,
cs->jumpto, cs->fw6.ipv6.flags, &cs->fw6);
if (cs->target == NULL && strlen(cs->jumpto) > 0) {
printf("-%c %s", cs->fw6.ipv6.flags & IP6T_F_GOTO ? 'g' : 'j',
cs->jumpto);
}
printf("\n");
}
/* These are invalid numbers as upper layer protocol */
static int is_exthdr(uint16_t proto)
{
return (proto == IPPROTO_ROUTING ||
proto == IPPROTO_FRAGMENT ||
proto == IPPROTO_AH ||
proto == IPPROTO_DSTOPTS);
}
static void nft_ipv6_proto_parse(struct iptables_command_state *cs,
struct xtables_args *args)
{
cs->fw6.ipv6.proto = args->proto;
cs->fw6.ipv6.invflags = args->invflags;
if (is_exthdr(cs->fw6.ipv6.proto)
&& (cs->fw6.ipv6.invflags & XT_INV_PROTO) == 0)
fprintf(stderr,
"Warning: never matched protocol: %s. "
"use extension match instead.\n",
cs->protocol);
}
static void nft_ipv6_post_parse(int command, struct iptables_command_state *cs,
struct xtables_args *args)
{
if (args->proto != 0)
args->flags |= IP6T_F_PROTO;
cs->fw6.ipv6.flags = args->flags;
/* We already set invflags in proto_parse, but we need to refresh it
* to include new parsed options.
*/
cs->fw6.ipv6.invflags = args->invflags;
strncpy(cs->fw6.ipv6.iniface, args->iniface, IFNAMSIZ);
memcpy(cs->fw6.ipv6.iniface_mask,
args->iniface_mask, IFNAMSIZ*sizeof(unsigned char));
strncpy(cs->fw6.ipv6.outiface, args->outiface, IFNAMSIZ);
memcpy(cs->fw6.ipv6.outiface_mask,
args->outiface_mask, IFNAMSIZ*sizeof(unsigned char));
if (args->goto_set)
cs->fw6.ipv6.flags |= IP6T_F_GOTO;
cs->fw6.counters.pcnt = args->pcnt_cnt;
cs->fw6.counters.bcnt = args->bcnt_cnt;
if (command & (CMD_REPLACE | CMD_INSERT |
CMD_DELETE | CMD_APPEND | CMD_CHECK)) {
if (!(cs->options & OPT_DESTINATION))
args->dhostnetworkmask = "::0/0";
if (!(cs->options & OPT_SOURCE))
args->shostnetworkmask = "::0/0";
}
if (args->shostnetworkmask)
xtables_ip6parse_multiple(args->shostnetworkmask,
&args->s.addr.v6,
&args->s.mask.v6,
&args->s.naddrs);
if (args->dhostnetworkmask)
xtables_ip6parse_multiple(args->dhostnetworkmask,
&args->d.addr.v6,
&args->d.mask.v6,
&args->d.naddrs);
if ((args->s.naddrs > 1 || args->d.naddrs > 1) &&
(cs->fw6.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP)))
xtables_error(PARAMETER_PROBLEM,
"! not allowed with multiple"
" source or destination IP addresses");
}
static void nft_ipv6_parse_target(struct xtables_target *t, void *data)
{
struct iptables_command_state *cs = data;
cs->target = t;
}
static bool nft_ipv6_rule_find(struct nft_family_ops *ops,
struct nftnl_rule *r, void *data)
{
struct iptables_command_state *cs = data;
return nft_ipv46_rule_find(ops, r, cs);
}
static void nft_ipv6_save_counters(const void *data)
{
const struct iptables_command_state *cs = data;
save_counters(cs->counters.pcnt, cs->counters.bcnt);
}
struct nft_family_ops nft_family_ops_ipv6 = {
.add = nft_ipv6_add,
.is_same = nft_ipv6_is_same,
.parse_meta = nft_ipv6_parse_meta,
.parse_payload = nft_ipv6_parse_payload,
.parse_immediate = nft_ipv6_parse_immediate,
.print_header = nft_ipv6_print_header,
.print_firewall = nft_ipv6_print_firewall,
.save_firewall = nft_ipv6_save_firewall,
.save_counters = nft_ipv6_save_counters,
.proto_parse = nft_ipv6_proto_parse,
.post_parse = nft_ipv6_post_parse,
.parse_target = nft_ipv6_parse_target,
.rule_find = nft_ipv6_rule_find,
};
/*
* (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
* (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
*
* 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 code has been sponsored by Sophos Astaro <http://www.sophos.com>
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <netdb.h>
#include <errno.h>
#include <xtables.h>
#include <linux/netfilter/nf_tables.h>
#include <libmnl/libmnl.h>
#include <libnftnl/rule.h>
#include <libnftnl/expr.h>
#include "nft-shared.h"
#include "nft-bridge.h"
#include "xshared.h"
#include "nft.h"
extern struct nft_family_ops nft_family_ops_ipv4;
extern struct nft_family_ops nft_family_ops_ipv6;
extern struct nft_family_ops nft_family_ops_arp;
extern struct nft_family_ops nft_family_ops_bridge;
void add_meta(struct nftnl_rule *r, uint32_t key)
{
struct nftnl_expr *expr;
expr = nftnl_expr_alloc("meta");
if (expr == NULL)
return;
nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, key);
nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, NFT_REG_1);
nftnl_rule_add_expr(r, expr);
}
void add_payload(struct nftnl_rule *r, int offset, int len, uint32_t base)
{
struct nftnl_expr *expr;
expr = nftnl_expr_alloc("payload");
if (expr == NULL)
return;
nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_BASE, base);
nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_DREG, NFT_REG_1);
nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_LEN, len);
nftnl_rule_add_expr(r, expr);
}
/* bitwise operation is = sreg & mask ^ xor */
void add_bitwise_u16(struct nftnl_rule *r, int mask, int xor)
{
struct nftnl_expr *expr;
expr = nftnl_expr_alloc("bitwise");
if (expr == NULL)
return;
nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, NFT_REG_1);
nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, NFT_REG_1);
nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, sizeof(uint16_t));
nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_MASK, &mask, sizeof(uint16_t));
nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_XOR, &xor, sizeof(uint16_t));
nftnl_rule_add_expr(r, expr);
}
static void add_bitwise(struct nftnl_rule *r, uint8_t *mask, size_t len)
{
struct nftnl_expr *expr;
uint32_t xor[4] = { 0 };
expr = nftnl_expr_alloc("bitwise");
if (expr == NULL)
return;
nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, NFT_REG_1);
nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, NFT_REG_1);
nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, len);
nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_MASK, mask, len);
nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_XOR, &xor, len);
nftnl_rule_add_expr(r, expr);
}
void add_cmp_ptr(struct nftnl_rule *r, uint32_t op, void *data, size_t len)
{
struct nftnl_expr *expr;
expr = nftnl_expr_alloc("cmp");
if (expr == NULL)
return;
nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_SREG, NFT_REG_1);
nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_OP, op);
nftnl_expr_set(expr, NFTNL_EXPR_CMP_DATA, data, len);
nftnl_rule_add_expr(r, expr);
}
void add_cmp_u8(struct nftnl_rule *r, uint8_t val, uint32_t op)
{
add_cmp_ptr(r, op, &val, sizeof(val));
}
void add_cmp_u16(struct nftnl_rule *r, uint16_t val, uint32_t op)
{
add_cmp_ptr(r, op, &val, sizeof(val));
}
void add_cmp_u32(struct nftnl_rule *r, uint32_t val, uint32_t op)
{
add_cmp_ptr(r, op, &val, sizeof(val));
}
void add_iniface(struct nftnl_rule *r, char *iface, uint32_t op)
{
int iface_len;
iface_len = strlen(iface);
add_meta(r, NFT_META_IIFNAME);
if (iface[iface_len - 1] == '+')
add_cmp_ptr(r, op, iface, iface_len - 1);
else
add_cmp_ptr(r, op, iface, iface_len + 1);
}
void add_outiface(struct nftnl_rule *r, char *iface, uint32_t op)
{
int iface_len;
iface_len = strlen(iface);
add_meta(r, NFT_META_OIFNAME);
if (iface[iface_len - 1] == '+')
add_cmp_ptr(r, op, iface, iface_len - 1);
else
add_cmp_ptr(r, op, iface, iface_len + 1);
}
void add_addr(struct nftnl_rule *r, int offset,
void *data, void *mask, size_t len, uint32_t op)
{
add_payload(r, offset, len, NFT_PAYLOAD_NETWORK_HEADER);
add_bitwise(r, mask, len);
add_cmp_ptr(r, op, data, len);
}
void add_proto(struct nftnl_rule *r, int offset, size_t len,
uint8_t proto, uint32_t op)
{
add_payload(r, offset, len, NFT_PAYLOAD_NETWORK_HEADER);
add_cmp_u8(r, proto, op);
}
bool is_same_interfaces(const char *a_iniface, const char *a_outiface,
unsigned const char *a_iniface_mask,
unsigned const char *a_outiface_mask,
const char *b_iniface, const char *b_outiface,
unsigned const char *b_iniface_mask,
unsigned const char *b_outiface_mask)
{
int i;
for (i = 0; i < IFNAMSIZ; i++) {
if (a_iniface_mask[i] != b_iniface_mask[i]) {
DEBUGP("different iniface mask %x, %x (%d)\n",
a_iniface_mask[i] & 0xff, b_iniface_mask[i] & 0xff, i);
return false;
}
if ((a_iniface[i] & a_iniface_mask[i])
!= (b_iniface[i] & b_iniface_mask[i])) {
DEBUGP("different iniface\n");
return false;
}
if (a_outiface_mask[i] != b_outiface_mask[i]) {
DEBUGP("different outiface mask\n");
return false;
}
if ((a_outiface[i] & a_outiface_mask[i])
!= (b_outiface[i] & b_outiface_mask[i])) {
DEBUGP("different outiface\n");
return false;
}
}
return true;
}
int parse_meta(struct nftnl_expr *e, uint8_t key, char *iniface,
unsigned char *iniface_mask, char *outiface,
unsigned char *outiface_mask, uint8_t *invflags)
{
uint32_t value;
const void *ifname;
uint32_t len;
switch(key) {
case NFT_META_IIF:
value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
*invflags |= IPT_INV_VIA_IN;
if_indextoname(value, iniface);
memset(iniface_mask, 0xff, strlen(iniface)+1);
break;
case NFT_META_OIF:
value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
*invflags |= IPT_INV_VIA_OUT;
if_indextoname(value, outiface);
memset(outiface_mask, 0xff, strlen(outiface)+1);
break;
case NFT_META_IIFNAME:
ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
*invflags |= IPT_INV_VIA_IN;
memcpy(iniface, ifname, len);
if (iniface[len] == '\0')
memset(iniface_mask, 0xff, len);
else {
iniface[len] = '+';
iniface[len+1] = '\0';
memset(iniface_mask, 0xff, len + 1);
}
break;
case NFT_META_OIFNAME:
ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
*invflags |= IPT_INV_VIA_OUT;
memcpy(outiface, ifname, len);
if (outiface[len] == '\0')
memset(outiface_mask, 0xff, len);
else {
outiface[len] = '+';
outiface[len+1] = '\0';
memset(outiface_mask, 0xff, len + 1);
}
break;
default:
return -1;
}
return 0;
}
static void *nft_get_data(struct nft_xt_ctx *ctx)
{
switch(ctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
return ctx->state.cs;
case NFPROTO_ARP:
return ctx->state.cs_arp;
case NFPROTO_BRIDGE:
return ctx->state.cs_eb;
default:
/* Should not happen */
return NULL;
}
}
void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
{
uint32_t tg_len;
const char *targname = nftnl_expr_get_str(e, NFTNL_EXPR_TG_NAME);
const void *targinfo = nftnl_expr_get(e, NFTNL_EXPR_TG_INFO, &tg_len);
struct xtables_target *target;
struct xt_entry_target *t;
size_t size;
struct nft_family_ops *ops;
void *data = nft_get_data(ctx);
target = xtables_find_target(targname, XTF_TRY_LOAD);
if (target == NULL)
return;
size = XT_ALIGN(sizeof(struct xt_entry_target)) + tg_len;
t = calloc(1, size);
if (t == NULL) {
fprintf(stderr, "OOM");
exit(EXIT_FAILURE);
}
memcpy(&t->data, targinfo, tg_len);
t->u.target_size = size;
t->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
strcpy(t->u.user.name, target->name);
target->t = t;
ops = nft_family_ops_lookup(ctx->family);
ops->parse_target(target, data);
}
void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
{
uint32_t mt_len;
const char *mt_name = nftnl_expr_get_str(e, NFTNL_EXPR_MT_NAME);
const void *mt_info = nftnl_expr_get(e, NFTNL_EXPR_MT_INFO, &mt_len);
struct xtables_match *match;
struct xtables_rule_match **matches;
struct xt_entry_match *m;
struct nft_family_ops *ops;
switch (ctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
matches = &ctx->state.cs->matches;
break;
case NFPROTO_BRIDGE:
matches = &ctx->state.cs_eb->matches;
break;
default:
fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
ctx->family);
exit(EXIT_FAILURE);
}
match = xtables_find_match(mt_name, XTF_TRY_LOAD, matches);
if (match == NULL)
return;
m = calloc(1, sizeof(struct xt_entry_match) + mt_len);
if (m == NULL) {
fprintf(stderr, "OOM");
exit(EXIT_FAILURE);
}
memcpy(&m->data, mt_info, mt_len);
m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
m->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
strcpy(m->u.user.name, match->name);
match->m = m;
ops = nft_family_ops_lookup(ctx->family);
if (ops->parse_match != NULL)
ops->parse_match(match, nft_get_data(ctx));
}
void print_proto(uint16_t proto, int invert)
{
const struct protoent *pent = getprotobynumber(proto);
if (invert)
printf("! ");
if (pent) {
printf("-p %s ", pent->p_name);
return;
}
printf("-p %u ", proto);
}
void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv)
{
uint32_t len;
uint8_t op;
memcpy(data, nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len), dlen);
op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
if (op == NFT_CMP_NEQ)
*inv = true;
else
*inv = false;
}
void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
{
ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
ctx->meta.key = nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY);
ctx->flags |= NFT_XT_CTX_META;
}
void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
{
ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
ctx->payload.offset = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET);
ctx->flags |= NFT_XT_CTX_PAYLOAD;
}
void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
{
uint32_t reg, len;
const void *data;
reg = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_SREG);
if (ctx->reg && reg != ctx->reg)
return;
data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_XOR, &len);
memcpy(ctx->bitwise.xor, data, len);
data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_MASK, &len);
memcpy(ctx->bitwise.mask, data, len);
ctx->flags |= NFT_XT_CTX_BITWISE;
}
void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
{
struct nft_family_ops *ops = nft_family_ops_lookup(ctx->family);
void *data = nft_get_data(ctx);
uint32_t reg;
reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
if (ctx->reg && reg != ctx->reg)
return;
if (ctx->flags & NFT_XT_CTX_META) {
ops->parse_meta(ctx, e, data);
ctx->flags &= ~NFT_XT_CTX_META;
}
/* bitwise context is interpreted from payload */
if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
ops->parse_payload(ctx, e, data);
ctx->flags &= ~NFT_XT_CTX_PAYLOAD;
}
}
void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters)
{
counters->pcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_PACKETS);
counters->bcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_BYTES);
}
void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
{
int verdict = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_VERDICT);
const char *chain = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN);
struct nft_family_ops *ops;
const char *jumpto = NULL;
bool nft_goto = false;
void *data = nft_get_data(ctx);
/* Standard target? */
switch(verdict) {
case NF_ACCEPT:
jumpto = "ACCEPT";
break;
case NF_DROP:
jumpto = "DROP";
break;
case NFT_RETURN:
jumpto = "RETURN";
break;;
case NFT_GOTO:
nft_goto = true;
case NFT_JUMP:
jumpto = chain;
break;
}
ops = nft_family_ops_lookup(ctx->family);
ops->parse_immediate(jumpto, nft_goto, data);
}
void nft_rule_to_iptables_command_state(struct nftnl_rule *r,
struct iptables_command_state *cs)
{
struct nftnl_expr_iter *iter;
struct nftnl_expr *expr;
int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
struct nft_xt_ctx ctx = {
.state.cs = cs,
.family = family,
};
iter = nftnl_expr_iter_create(r);
if (iter == NULL)
return;
ctx.iter = iter;
expr = nftnl_expr_iter_next(iter);
while (expr != NULL) {
const char *name =
nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
if (strcmp(name, "counter") == 0)
nft_parse_counter(expr, &ctx.state.cs->counters);
else if (strcmp(name, "payload") == 0)
nft_parse_payload(&ctx, expr);
else if (strcmp(name, "meta") == 0)
nft_parse_meta(&ctx, expr);
else if (strcmp(name, "bitwise") == 0)
nft_parse_bitwise(&ctx, expr);
else if (strcmp(name, "cmp") == 0)
nft_parse_cmp(&ctx, expr);
else if (strcmp(name, "immediate") == 0)
nft_parse_immediate(&ctx, expr);
else if (strcmp(name, "match") == 0)
nft_parse_match(&ctx, expr);
else if (strcmp(name, "target") == 0)
nft_parse_target(&ctx, expr);
expr = nftnl_expr_iter_next(iter);
}
nftnl_expr_iter_destroy(iter);
if (cs->target != NULL)
cs->jumpto = cs->target->name;
else if (cs->jumpto != NULL)
cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
else
cs->jumpto = "";
}
void print_header(unsigned int format, const char *chain, const char *pol,
const struct xt_counters *counters, bool basechain,
uint32_t refs)
{
printf("Chain %s", chain);
if (basechain) {
printf(" (policy %s", pol);
if (!(format & FMT_NOCOUNTS)) {
fputc(' ', stdout);
xtables_print_num(counters->pcnt, (format|FMT_NOTABLE));
fputs("packets, ", stdout);
xtables_print_num(counters->bcnt, (format|FMT_NOTABLE));
fputs("bytes", stdout);
}
printf(")\n");
} else {
printf(" (%u references)\n", refs);
}
if (format & FMT_LINENUMBERS)
printf(FMT("%-4s ", "%s "), "num");
if (!(format & FMT_NOCOUNTS)) {
if (format & FMT_KILOMEGAGIGA) {
printf(FMT("%5s ","%s "), "pkts");
printf(FMT("%5s ","%s "), "bytes");
} else {
printf(FMT("%8s ","%s "), "pkts");
printf(FMT("%10s ","%s "), "bytes");
}
}
if (!(format & FMT_NOTARGET))
printf(FMT("%-9s ","%s "), "target");
fputs(" prot ", stdout);
if (format & FMT_OPTIONS)
fputs("opt", stdout);
if (format & FMT_VIA) {
printf(FMT(" %-6s ","%s "), "in");
printf(FMT("%-6s ","%s "), "out");
}
printf(FMT(" %-19s ","%s "), "source");
printf(FMT(" %-19s "," %s "), "destination");
printf("\n");
}
void print_firewall_details(const struct iptables_command_state *cs,
const char *targname, uint8_t flags,
uint8_t invflags, uint8_t proto,
unsigned int num, unsigned int format)
{
if (format & FMT_LINENUMBERS)
printf(FMT("%-4u ", "%u "), num);
if (!(format & FMT_NOCOUNTS)) {
xtables_print_num(cs->counters.pcnt, format);
xtables_print_num(cs->counters.bcnt, format);
}
if (!(format & FMT_NOTARGET))
printf(FMT("%-9s ", "%s "), targname ? targname : "");
fputc(invflags & XT_INV_PROTO ? '!' : ' ', stdout);
{
const char *pname =
proto_to_name(proto, format&FMT_NUMERIC);
if (pname)
printf(FMT("%-5s", "%s "), pname);
else
printf(FMT("%-5hu", "%hu "), proto);
}
}
void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags,
unsigned int format)
{
char iface[IFNAMSIZ+2];
if (!(format & FMT_VIA))
return;
if (invflags & IPT_INV_VIA_IN) {
iface[0] = '!';
iface[1] = '\0';
} else
iface[0] = '\0';
if (iniface[0] != '\0')
strcat(iface, iniface);
else if (format & FMT_NUMERIC)
strcat(iface, "*");
else
strcat(iface, "any");
printf(FMT(" %-6s ","in %s "), iface);
if (invflags & IPT_INV_VIA_OUT) {
iface[0] = '!';
iface[1] = '\0';
} else
iface[0] = '\0';
if (outiface[0] != '\0')
strcat(iface, outiface);
else if (format & FMT_NUMERIC)
strcat(iface, "*");
else
strcat(iface, "any");
printf(FMT("%-6s ","out %s "), iface);
}
static void
print_iface(char letter, const char *iface, const unsigned char *mask, int inv)
{
unsigned int i;
if (mask[0] == 0)
return;
printf("%s-%c ", inv ? "! " : "", letter);
for (i = 0; i < IFNAMSIZ; i++) {
if (mask[i] != 0) {
if (iface[i] != '\0')
printf("%c", iface[i]);
} else {
if (iface[i-1] != '\0')
printf("+");
break;
}
}
printf(" ");
}
void save_firewall_details(const struct iptables_command_state *cs,
uint8_t invflags, uint16_t proto,
const char *iniface,
unsigned const char *iniface_mask,
const char *outiface,
unsigned const char *outiface_mask)
{
if (iniface != NULL) {
print_iface('i', iniface, iniface_mask,
invflags & IPT_INV_VIA_IN);
}
if (outiface != NULL) {
print_iface('o', outiface, outiface_mask,
invflags & IPT_INV_VIA_OUT);
}
if (proto > 0) {
const struct protoent *pent = getprotobynumber(proto);
if (invflags & XT_INV_PROTO)
printf("! ");
if (pent)
printf("-p %s ", pent->p_name);
else
printf("-p %u ", proto);
}
}
void save_counters(uint64_t pcnt, uint64_t bcnt)
{
printf("[%llu:%llu] ", (unsigned long long)pcnt,
(unsigned long long)bcnt);
}
void save_matches_and_target(struct xtables_rule_match *m,
struct xtables_target *target,
const char *jumpto, uint8_t flags, const void *fw)
{
struct xtables_rule_match *matchp;
for (matchp = m; matchp; matchp = matchp->next) {
if (matchp->match->alias) {
printf("-m %s",
matchp->match->alias(matchp->match->m));
} else
printf("-m %s", matchp->match->name);
if (matchp->match->save != NULL) {
/* cs->fw union makes the trick */
matchp->match->save(fw, matchp->match->m);
}
printf(" ");
}
if (target != NULL) {
if (target->alias) {
printf("-j %s", target->alias(target->t));
} else
printf("-j %s", jumpto);
if (target->save != NULL)
target->save(fw, target->t);
}
}
void print_matches_and_target(struct iptables_command_state *cs,
unsigned int format)
{
struct xtables_rule_match *matchp;
for (matchp = cs->matches; matchp; matchp = matchp->next) {
if (matchp->match->print != NULL) {
matchp->match->print(&cs->fw, matchp->match->m,
format & FMT_NUMERIC);
}
}
if (cs->target != NULL) {
if (cs->target->print != NULL) {
cs->target->print(&cs->fw, cs->target->t,
format & FMT_NUMERIC);
}
}
}
struct nft_family_ops *nft_family_ops_lookup(int family)
{
switch (family) {
case AF_INET:
return &nft_family_ops_ipv4;
case AF_INET6:
return &nft_family_ops_ipv6;
case NFPROTO_ARP:
return &nft_family_ops_arp;
case NFPROTO_BRIDGE:
return &nft_family_ops_bridge;
default:
break;
}
return NULL;
}
bool compare_matches(struct xtables_rule_match *mt1,
struct xtables_rule_match *mt2)
{
struct xtables_rule_match *mp1;
struct xtables_rule_match *mp2;
for (mp1 = mt1, mp2 = mt2; mp1 && mp2; mp1 = mp1->next, mp2 = mp2->next) {
struct xt_entry_match *m1 = mp1->match->m;
struct xt_entry_match *m2 = mp2->match->m;
if (strcmp(m1->u.user.name, m2->u.user.name) != 0) {
DEBUGP("mismatching match name\n");
return false;
}
if (m1->u.user.match_size != m2->u.user.match_size) {
DEBUGP("mismatching match size\n");
return false;
}
if (memcmp(m1->data, m2->data,
mp1->match->userspacesize) != 0) {
DEBUGP("mismatch match data\n");
return false;
}
}
/* Both cursors should be NULL */
if (mp1 != mp2) {
DEBUGP("mismatch matches amount\n");
return false;
}
return true;
}
bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2)
{
if (tg1 == NULL && tg2 == NULL)
return true;
if ((tg1 == NULL && tg2 != NULL) || (tg1 != NULL && tg2 == NULL))
return false;
if (strcmp(tg1->t->u.user.name, tg2->t->u.user.name) != 0)
return false;
if (memcmp(tg1->t->data, tg2->t->data, tg1->userspacesize) != 0)
return false;
return true;
}
bool nft_ipv46_rule_find(struct nft_family_ops *ops,
struct nftnl_rule *r, struct iptables_command_state *cs)
{
struct iptables_command_state this = {};
nft_rule_to_iptables_command_state(r, &this);
DEBUGP("comparing with... ");
#ifdef DEBUG_DEL
nft_rule_print_save(&this, r, NFT_RULE_APPEND, 0);
#endif
if (!ops->is_same(cs, &this))
return false;
if (!compare_matches(cs->matches, this.matches)) {
DEBUGP("Different matches\n");
return false;
}
if (!compare_targets(cs->target, this.target)) {
DEBUGP("Different target\n");
return false;
}
if (strcmp(cs->jumpto, this.jumpto) != 0) {
DEBUGP("Different verdict\n");
return false;
}
return true;
}
#ifndef _NFT_SHARED_H_
#define _NFT_SHARED_H_
#include <stdbool.h>
#include <libnftnl/rule.h>
#include <libnftnl/expr.h>
#include <linux/netfilter_arp/arp_tables.h>
#include "xshared.h"
#if 0
#define DEBUGP(x, args...) fprintf(stdout, x, ## args)
#define NLDEBUG
#define DEBUG_DEL
#else
#define DEBUGP(x, args...)
#endif
/*
* iptables print output emulation
*/
#define FMT_NUMERIC 0x0001
#define FMT_NOCOUNTS 0x0002
#define FMT_KILOMEGAGIGA 0x0004
#define FMT_OPTIONS 0x0008
#define FMT_NOTABLE 0x0010
#define FMT_NOTARGET 0x0020
#define FMT_VIA 0x0040
#define FMT_NONEWLINE 0x0080
#define FMT_LINENUMBERS 0x0100
#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
| FMT_NUMERIC | FMT_NOTABLE)
#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
struct xtables_args;
enum {
NFT_XT_CTX_PAYLOAD = (1 << 0),
NFT_XT_CTX_META = (1 << 1),
NFT_XT_CTX_BITWISE = (1 << 2),
};
struct nft_xt_ctx {
union {
struct iptables_command_state *cs;
struct arptables_command_state *cs_arp;
struct ebtables_command_state *cs_eb;
} state;
struct nftnl_expr_iter *iter;
int family;
uint32_t flags;
uint32_t reg;
struct {
uint32_t offset;
uint32_t len;
} payload;
struct {
uint32_t key;
} meta;
struct {
uint32_t mask[4];
uint32_t xor[4];
} bitwise;
};
struct nft_family_ops {
int (*add)(struct nftnl_rule *r, void *data);
bool (*is_same)(const void *data_a,
const void *data_b);
void (*print_payload)(struct nftnl_expr *e,
struct nftnl_expr_iter *iter);
void (*parse_meta)(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
void *data);
void (*parse_payload)(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
void *data);
void (*parse_bitwise)(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
void *data);
void (*parse_cmp)(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
void *data);
void (*parse_immediate)(const char *jumpto, bool nft_goto, void *data);
void (*print_table_header)(const char *tablename);
void (*print_header)(unsigned int format, const char *chain,
const char *pol,
const struct xt_counters *counters, bool basechain,
uint32_t refs);
void (*print_firewall)(struct nftnl_rule *r, unsigned int num,
unsigned int format);
void (*save_firewall)(const void *data, unsigned int format);
void (*save_counters)(const void *data);
void (*proto_parse)(struct iptables_command_state *cs,
struct xtables_args *args);
void (*post_parse)(int command, struct iptables_command_state *cs,
struct xtables_args *args);
void (*parse_match)(struct xtables_match *m, void *data);
void (*parse_target)(struct xtables_target *t, void *data);
bool (*rule_find)(struct nft_family_ops *ops, struct nftnl_rule *r,
void *data);
};
void add_meta(struct nftnl_rule *r, uint32_t key);
void add_payload(struct nftnl_rule *r, int offset, int len, uint32_t base);
void add_bitwise_u16(struct nftnl_rule *r, int mask, int xor);
void add_cmp_ptr(struct nftnl_rule *r, uint32_t op, void *data, size_t len);
void add_cmp_u8(struct nftnl_rule *r, uint8_t val, uint32_t op);
void add_cmp_u16(struct nftnl_rule *r, uint16_t val, uint32_t op);
void add_cmp_u32(struct nftnl_rule *r, uint32_t val, uint32_t op);
void add_iniface(struct nftnl_rule *r, char *iface, uint32_t op);
void add_outiface(struct nftnl_rule *r, char *iface, uint32_t op);
void add_addr(struct nftnl_rule *r, int offset,
void *data, void *mask, size_t len, uint32_t op);
void add_proto(struct nftnl_rule *r, int offset, size_t len,
uint8_t proto, uint32_t op);
void add_compat(struct nftnl_rule *r, uint32_t proto, bool inv);
bool is_same_interfaces(const char *a_iniface, const char *a_outiface,
unsigned const char *a_iniface_mask,
unsigned const char *a_outiface_mask,
const char *b_iniface, const char *b_outiface,
unsigned const char *b_iniface_mask,
unsigned const char *b_outiface_mask);
int parse_meta(struct nftnl_expr *e, uint8_t key, char *iniface,
unsigned char *iniface_mask, char *outiface,
unsigned char *outiface_mask, uint8_t *invflags);
void print_proto(uint16_t proto, int invert);
void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv);
void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters);
void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
void nft_rule_to_iptables_command_state(struct nftnl_rule *r,
struct iptables_command_state *cs);
void print_header(unsigned int format, const char *chain, const char *pol,
const struct xt_counters *counters, bool basechain,
uint32_t refs);
void print_firewall_details(const struct iptables_command_state *cs,
const char *targname, uint8_t flags,
uint8_t invflags, uint8_t proto,
unsigned int num, unsigned int format);
void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags,
unsigned int format);
void print_matches_and_target(struct iptables_command_state *cs,
unsigned int format);
void save_firewall_details(const struct iptables_command_state *cs,
uint8_t invflags, uint16_t proto,
const char *iniface,
unsigned const char *iniface_mask,
const char *outiface,
unsigned const char *outiface_mask);
void save_counters(uint64_t pcnt, uint64_t bcnt);
void save_matches_and_target(struct xtables_rule_match *m,
struct xtables_target *target,
const char *jumpto,
uint8_t flags, const void *fw);
struct nft_family_ops *nft_family_ops_lookup(int family);
struct nft_handle;
bool nft_ipv46_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
struct iptables_command_state *cs);
bool compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2);
bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2);
struct addr_mask {
union {
struct in_addr *v4;
struct in6_addr *v6;
} addr;
unsigned int naddrs;
union {
struct in_addr *v4;
struct in6_addr *v6;
} mask;
};
struct xtables_args {
int family;
uint16_t proto;
uint8_t flags;
uint8_t invflags;
char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
bool goto_set;
const char *shostnetworkmask, *dhostnetworkmask;
const char *pcnt, *bcnt;
struct addr_mask s, d;
unsigned long long pcnt_cnt, bcnt_cnt;
};
#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_RENAME_CHAIN 0x0800U
#define CMD_LIST_RULES 0x1000U
#define CMD_ZERO_NUM 0x2000U
#define CMD_CHECK 0x4000U
#endif
/*
* (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* 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 code has been sponsored by Sophos Astaro <http://www.sophos.com>
*/
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdbool.h>
#include <errno.h>
#include <netdb.h> /* getprotobynumber */
#include <time.h>
#include <stdarg.h>
#include <inttypes.h>
#include <xtables.h>
#include <libiptc/libxtc.h>
#include <libiptc/xtcshared.h>
#include <stdlib.h>
#include <string.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <netinet/ip6.h>
#include <linux/netlink.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_tables_compat.h>
#include <libmnl/libmnl.h>
#include <libnftnl/table.h>
#include <libnftnl/chain.h>
#include <libnftnl/rule.h>
#include <libnftnl/expr.h>
#include <libnftnl/set.h>
#include <netinet/in.h> /* inet_ntoa */
#include <arpa/inet.h>
#include "nft.h"
#include "xshared.h" /* proto_to_name */
#include "nft-shared.h"
#include "xtables-config-parser.h"
static void *nft_fn;
int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
int (*cb)(const struct nlmsghdr *nlh, void *data),
void *data)
{
int ret;
char buf[MNL_SOCKET_BUFFER_SIZE];
if (mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len) < 0)
return -1;
ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, h->seq, h->portid, cb, data);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
}
if (ret == -1) {
return -1;
}
return 0;
}
static LIST_HEAD(batch_page_list);
static int batch_num_pages;
struct batch_page {
struct list_head head;
struct mnl_nlmsg_batch *batch;
};
/* selected batch page is 256 Kbytes long to load ruleset of
* half a million rules without hitting -EMSGSIZE due to large
* iovec.
*/
#define BATCH_PAGE_SIZE getpagesize() * 32
static struct mnl_nlmsg_batch *mnl_nftnl_batch_alloc(void)
{
static char *buf;
/* libmnl needs higher buffer to handle batch overflows */
buf = malloc(BATCH_PAGE_SIZE + getpagesize());
if (buf == NULL)
return NULL;
return mnl_nlmsg_batch_start(buf, BATCH_PAGE_SIZE);
}
static struct mnl_nlmsg_batch *
mnl_nftnl_batch_page_add(struct mnl_nlmsg_batch *batch)
{
struct batch_page *batch_page;
batch_page = malloc(sizeof(struct batch_page));
if (batch_page == NULL)
return NULL;
batch_page->batch = batch;
list_add_tail(&batch_page->head, &batch_page_list);
batch_num_pages++;
return mnl_nftnl_batch_alloc();
}
static int nlbuffsiz;
static void mnl_nft_set_sndbuffer(const struct mnl_socket *nl)
{
int newbuffsiz;
if (batch_num_pages * BATCH_PAGE_SIZE <= nlbuffsiz)
return;
newbuffsiz = batch_num_pages * BATCH_PAGE_SIZE;
/* Rise sender buffer length to avoid hitting -EMSGSIZE */
if (setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUFFORCE,
&newbuffsiz, sizeof(socklen_t)) < 0)
return;
nlbuffsiz = newbuffsiz;
}
static void mnl_nftnl_batch_reset(void)
{
struct batch_page *batch_page, *next;
list_for_each_entry_safe(batch_page, next, &batch_page_list, head) {
list_del(&batch_page->head);
free(batch_page->batch);
free(batch_page);
batch_num_pages--;
}
}
static ssize_t mnl_nft_socket_sendmsg(const struct mnl_socket *nl)
{
static const struct sockaddr_nl snl = {
.nl_family = AF_NETLINK
};
struct iovec iov[batch_num_pages];
struct msghdr msg = {
.msg_name = (struct sockaddr *) &snl,
.msg_namelen = sizeof(snl),
.msg_iov = iov,
.msg_iovlen = batch_num_pages,
};
struct batch_page *batch_page;
int i = 0, ret;
mnl_nft_set_sndbuffer(nl);
list_for_each_entry(batch_page, &batch_page_list, head) {
iov[i].iov_base = mnl_nlmsg_batch_head(batch_page->batch);
iov[i].iov_len = mnl_nlmsg_batch_size(batch_page->batch);
i++;
#ifdef NL_DEBUG
mnl_nlmsg_fprintf(stdout,
mnl_nlmsg_batch_head(batch_page->batch),
mnl_nlmsg_batch_size(batch_page->batch),
sizeof(struct nfgenmsg));
#endif
}
ret = sendmsg(mnl_socket_get_fd(nl), &msg, 0);
mnl_nftnl_batch_reset();
return ret;
}
static int mnl_nftnl_batch_talk(struct nft_handle *h)
{
int ret, fd = mnl_socket_get_fd(h->nl);
char rcv_buf[MNL_SOCKET_BUFFER_SIZE];
fd_set readfds;
struct timeval tv = {
.tv_sec = 0,
.tv_usec = 0
};
int err = 0;
ret = mnl_nft_socket_sendmsg(h->nl);
if (ret == -1)
return -1;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
/* receive and digest all the acknowledgments from the kernel. */
ret = select(fd+1, &readfds, NULL, NULL, &tv);
if (ret == -1)
return -1;
while (ret > 0 && FD_ISSET(fd, &readfds)) {
ret = mnl_socket_recvfrom(h->nl, rcv_buf, sizeof(rcv_buf));
if (ret == -1)
return -1;
ret = mnl_cb_run(rcv_buf, ret, 0, h->portid, NULL, NULL);
/* Annotate first error and continue, make sure we get all
* acknoledgments.
*/
if (!err && ret == -1)
err = errno;
ret = select(fd+1, &readfds, NULL, NULL, &tv);
if (ret == -1)
return -1;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
}
errno = err;
return err ? -1 : 0;
}
static void mnl_nftnl_batch_begin(struct mnl_nlmsg_batch *batch, uint32_t seq)
{
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq);
if (!mnl_nlmsg_batch_next(batch))
mnl_nftnl_batch_page_add(batch);
}
static void mnl_nftnl_batch_end(struct mnl_nlmsg_batch *batch, uint32_t seq)
{
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq);
if (!mnl_nlmsg_batch_next(batch))
mnl_nftnl_batch_page_add(batch);
}
enum obj_update_type {
NFT_COMPAT_TABLE_ADD,
NFT_COMPAT_CHAIN_ADD,
NFT_COMPAT_CHAIN_USER_ADD,
NFT_COMPAT_CHAIN_USER_DEL,
NFT_COMPAT_CHAIN_UPDATE,
NFT_COMPAT_CHAIN_RENAME,
NFT_COMPAT_RULE_APPEND,
NFT_COMPAT_RULE_INSERT,
NFT_COMPAT_RULE_REPLACE,
NFT_COMPAT_RULE_DELETE,
NFT_COMPAT_RULE_FLUSH,
};
enum obj_action {
NFT_COMPAT_COMMIT,
NFT_COMPAT_ABORT,
};
struct obj_update {
struct list_head head;
enum obj_update_type type;
union {
struct nftnl_table *table;
struct nftnl_chain *chain;
struct nftnl_rule *rule;
void *ptr;
};
};
static int batch_add(struct nft_handle *h, enum obj_update_type type, void *ptr)
{
struct obj_update *obj;
obj = calloc(1, sizeof(struct obj_update));
if (obj == NULL)
return -1;
obj->ptr = ptr;
obj->type = type;
list_add_tail(&obj->head, &h->obj_list);
h->obj_list_num++;
return 0;
}
static int batch_table_add(struct nft_handle *h, enum obj_update_type type,
struct nftnl_table *t)
{
return batch_add(h, type, t);
}
static int batch_chain_add(struct nft_handle *h, enum obj_update_type type,
struct nftnl_chain *c)
{
return batch_add(h, type, c);
}
static int batch_rule_add(struct nft_handle *h, enum obj_update_type type,
struct nftnl_rule *r)
{
return batch_add(h, type, r);
}
struct builtin_table xtables_ipv4[TABLES_MAX] = {
[RAW] = {
.name = "raw",
.chains = {
{
.name = "PREROUTING",
.type = "filter",
.prio = -300, /* NF_IP_PRI_RAW */
.hook = NF_INET_PRE_ROUTING,
},
{
.name = "OUTPUT",
.type = "filter",
.prio = -300, /* NF_IP_PRI_RAW */
.hook = NF_INET_LOCAL_OUT,
},
},
},
[MANGLE] = {
.name = "mangle",
.chains = {
{
.name = "PREROUTING",
.type = "filter",
.prio = -150, /* NF_IP_PRI_MANGLE */
.hook = NF_INET_PRE_ROUTING,
},
{
.name = "INPUT",
.type = "filter",
.prio = -150, /* NF_IP_PRI_MANGLE */
.hook = NF_INET_LOCAL_IN,
},
{
.name = "FORWARD",
.type = "filter",
.prio = -150, /* NF_IP_PRI_MANGLE */
.hook = NF_INET_FORWARD,
},
{
.name = "OUTPUT",
.type = "route",
.prio = -150, /* NF_IP_PRI_MANGLE */
.hook = NF_INET_LOCAL_OUT,
},
{
.name = "POSTROUTING",
.type = "filter",
.prio = -150, /* NF_IP_PRI_MANGLE */
.hook = NF_INET_POST_ROUTING,
},
},
},
[FILTER] = {
.name = "filter",
.chains = {
{
.name = "INPUT",
.type = "filter",
.prio = 0, /* NF_IP_PRI_FILTER */
.hook = NF_INET_LOCAL_IN,
},
{
.name = "FORWARD",
.type = "filter",
.prio = 0, /* NF_IP_PRI_FILTER */
.hook = NF_INET_FORWARD,
},
{
.name = "OUTPUT",
.type = "filter",
.prio = 0, /* NF_IP_PRI_FILTER */
.hook = NF_INET_LOCAL_OUT,
},
},
},
[SECURITY] = {
.name = "security",
.chains = {
{
.name = "INPUT",
.type = "filter",
.prio = 150, /* NF_IP_PRI_SECURITY */
.hook = NF_INET_LOCAL_IN,
},
{
.name = "FORWARD",
.type = "filter",
.prio = 150, /* NF_IP_PRI_SECURITY */
.hook = NF_INET_FORWARD,
},
{
.name = "OUTPUT",
.type = "filter",
.prio = 150, /* NF_IP_PRI_SECURITY */
.hook = NF_INET_LOCAL_OUT,
},
},
},
[NAT] = {
.name = "nat",
.chains = {
{
.name = "PREROUTING",
.type = "nat",
.prio = -100, /* NF_IP_PRI_NAT_DST */
.hook = NF_INET_PRE_ROUTING,
},
{
.name = "INPUT",
.type = "nat",
.prio = 100, /* NF_IP_PRI_NAT_SRC */
.hook = NF_INET_LOCAL_IN,
},
{
.name = "POSTROUTING",
.type = "nat",
.prio = 100, /* NF_IP_PRI_NAT_SRC */
.hook = NF_INET_POST_ROUTING,
},
{
.name = "OUTPUT",
.type = "nat",
.prio = -100, /* NF_IP_PRI_NAT_DST */
.hook = NF_INET_LOCAL_OUT,
},
},
},
};
#include <linux/netfilter_arp.h>
struct builtin_table xtables_arp[TABLES_MAX] = {
[FILTER] = {
.name = "filter",
.chains = {
{
.name = "INPUT",
.type = "filter",
.prio = NF_IP_PRI_FILTER,
.hook = NF_ARP_IN,
},
{
.name = "FORWARD",
.type = "filter",
.prio = NF_IP_PRI_FILTER,
.hook = NF_ARP_FORWARD,
},
{
.name = "OUTPUT",
.type = "filter",
.prio = NF_IP_PRI_FILTER,
.hook = NF_ARP_OUT,
},
},
},
};
#include <linux/netfilter_bridge.h>
struct builtin_table xtables_bridge[TABLES_MAX] = {
[FILTER] = {
.name = "filter",
.chains = {
{
.name = "INPUT",
.type = "filter",
.prio = NF_BR_PRI_FILTER_BRIDGED,
.hook = NF_BR_LOCAL_IN,
},
{
.name = "FORWARD",
.type = "filter",
.prio = NF_BR_PRI_FILTER_BRIDGED,
.hook = NF_BR_FORWARD,
},
{
.name = "OUTPUT",
.type = "filter",
.prio = NF_BR_PRI_FILTER_BRIDGED,
.hook = NF_BR_LOCAL_OUT,
},
},
},
[NAT] = {
.name = "nat",
.chains = {
{
.name = "PREROUTING",
.type = "filter",
.prio = NF_BR_PRI_NAT_DST_BRIDGED,
.hook = NF_BR_PRE_ROUTING,
},
{
.name = "OUTPUT",
.type = "filter",
.prio = NF_BR_PRI_NAT_DST_OTHER,
.hook = NF_BR_LOCAL_OUT,
},
{
.name = "POSTROUTING",
.type = "filter",
.prio = NF_BR_PRI_NAT_SRC,
.hook = NF_BR_POST_ROUTING,
},
},
},
};
int nft_table_add(struct nft_handle *h, struct nftnl_table *t, uint16_t flags)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
int ret;
nlh = nftnl_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family,
NLM_F_ACK|flags, h->seq);
nftnl_table_nlmsg_build_payload(nlh, t);
nftnl_table_free(t);
#ifdef NLDEBUG
char tmp[1024];
nft_table_snprintf(tmp, sizeof(tmp), t, 0, 0);
printf("DEBUG: table: %s\n", tmp);
mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
#endif
ret = mnl_talk(h, nlh, NULL, NULL);
return (ret == 0 || (ret == -1 && errno == EEXIST)) ? 0 : -1;
}
static int nft_table_builtin_add(struct nft_handle *h,
struct builtin_table *_t)
{
struct nftnl_table *t;
int ret;
if (_t->initialized)
return 0;
t = nftnl_table_alloc();
if (t == NULL)
return -1;
nftnl_table_set(t, NFTNL_TABLE_NAME, (char *)_t->name);
if (h->batch_support)
ret = batch_table_add(h, NFT_COMPAT_TABLE_ADD, t);
else
ret = nft_table_add(h, t, NLM_F_EXCL);
if (ret == 0)
_t->initialized = true;
return ret;
}
static struct nftnl_chain *
nft_chain_builtin_alloc(struct builtin_table *table,
struct builtin_chain *chain, int policy)
{
struct nftnl_chain *c;
c = nftnl_chain_alloc();
if (c == NULL)
return NULL;
nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table->name);
nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)chain->name);
nftnl_chain_set_u32(c, NFTNL_CHAIN_HOOKNUM, chain->hook);
nftnl_chain_set_u32(c, NFTNL_CHAIN_PRIO, chain->prio);
nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, policy);
nftnl_chain_set(c, NFTNL_CHAIN_TYPE, (char *)chain->type);
return c;
}
int nft_chain_add(struct nft_handle *h, struct nftnl_chain *c, uint16_t flags)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
/* NLM_F_CREATE requests module autoloading */
nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family,
NLM_F_ACK|flags|NLM_F_CREATE,
h->seq);
nftnl_chain_nlmsg_build_payload(nlh, c);
nftnl_chain_free(c);
#ifdef NLDEBUG
char tmp[1024];
nft_chain_snprintf(tmp, sizeof(tmp), c, 0, 0);
printf("DEBUG: chain: %s\n", tmp);
mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
#endif
return mnl_talk(h, nlh, NULL, NULL);
}
static void nft_chain_builtin_add(struct nft_handle *h,
struct builtin_table *table,
struct builtin_chain *chain)
{
struct nftnl_chain *c;
c = nft_chain_builtin_alloc(table, chain, NF_ACCEPT);
if (c == NULL)
return;
if (h->batch_support)
batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, c);
else
nft_chain_add(h, c, NLM_F_EXCL);
}
/* find if built-in table already exists */
static struct builtin_table *
nft_table_builtin_find(struct nft_handle *h, const char *table)
{
int i;
bool found = false;
for (i=0; i<TABLES_MAX; i++) {
if (h->tables[i].name == NULL)
continue;
if (strcmp(h->tables[i].name, table) != 0)
continue;
found = true;
break;
}
return found ? &h->tables[i] : NULL;
}
/* find if built-in chain already exists */
static struct builtin_chain *
nft_chain_builtin_find(struct builtin_table *t, const char *chain)
{
int i;
bool found = false;
for (i=0; i<NF_IP_NUMHOOKS && t->chains[i].name != NULL; i++) {
if (strcmp(t->chains[i].name, chain) != 0)
continue;
found = true;
break;
}
return found ? &t->chains[i] : NULL;
}
static void nft_chain_builtin_init(struct nft_handle *h,
struct builtin_table *table)
{
int i;
struct nftnl_chain_list *list = nft_chain_dump(h);
struct nftnl_chain *c;
/* Initialize built-in chains if they don't exist yet */
for (i=0; i<NF_IP_NUMHOOKS && table->chains[i].name != NULL; i++) {
c = nft_chain_list_find(list, table->name,
table->chains[i].name);
if (c != NULL)
continue;
nft_chain_builtin_add(h, table, &table->chains[i]);
}
nftnl_chain_list_free(list);
}
static int nft_xt_builtin_init(struct nft_handle *h, const char *table)
{
int ret = 0;
struct builtin_table *t;
t = nft_table_builtin_find(h, table);
if (t == NULL) {
ret = -1;
goto out;
}
if (nft_table_builtin_add(h, t) < 0) {
/* Built-in table already initialized, skip. */
if (errno == EEXIST)
goto out;
}
nft_chain_builtin_init(h, t);
out:
return ret;
}
static bool nft_chain_builtin(struct nftnl_chain *c)
{
/* Check if this chain has hook number, in that case is built-in.
* Should we better export the flags to user-space via nf_tables?
*/
return nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM) != NULL;
}
static bool mnl_batch_supported(struct nft_handle *h)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
uint32_t seq = 1;
int ret;
mnl_nftnl_batch_begin(h->batch, seq++);
nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
NFT_MSG_NEWSET, AF_INET,
NLM_F_ACK, seq++);
mnl_nlmsg_batch_next(h->batch);
mnl_nftnl_batch_end(h->batch, seq++);
ret = mnl_socket_sendto(h->nl, mnl_nlmsg_batch_head(h->batch),
mnl_nlmsg_batch_size(h->batch));
if (ret < 0)
goto err;
mnl_nlmsg_batch_reset(h->batch);
ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(h->nl),
NULL, NULL);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
}
/* We're sending an incomplete message to see if the kernel supports
* set messages in batches. EINVAL means that we sent an incomplete
* message with missing attributes. The kernel just ignores messages
* that we cannot include in the batch.
*/
return (ret == -1 && errno == EINVAL) ? true : false;
err:
mnl_nlmsg_batch_reset(h->batch);
return ret;
}
int nft_init(struct nft_handle *h, struct builtin_table *t)
{
h->nl = mnl_socket_open(NETLINK_NETFILTER);
if (h->nl == NULL)
return -1;
if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0)
return -1;
h->portid = mnl_socket_get_portid(h->nl);
h->tables = t;
INIT_LIST_HEAD(&h->obj_list);
h->batch = mnl_nftnl_batch_alloc();
h->batch_support = mnl_batch_supported(h);
return 0;
}
void nft_fini(struct nft_handle *h)
{
mnl_socket_close(h->nl);
free(mnl_nlmsg_batch_head(h->batch));
mnl_nlmsg_batch_stop(h->batch);
}
static void nft_chain_print_debug(struct nftnl_chain *c, struct nlmsghdr *nlh)
{
#ifdef NLDEBUG
char tmp[1024];
nft_chain_snprintf(tmp, sizeof(tmp), c, 0, 0);
printf("DEBUG: chain: %s\n", tmp);
mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
#endif
}
static struct nftnl_chain *nft_chain_new(struct nft_handle *h,
const char *table, const char *chain,
int policy,
const struct xt_counters *counters)
{
struct nftnl_chain *c;
struct builtin_table *_t;
struct builtin_chain *_c;
_t = nft_table_builtin_find(h, table);
/* if this built-in table does not exists, create it */
if (_t != NULL)
nft_table_builtin_add(h, _t);
_c = nft_chain_builtin_find(_t, chain);
if (_c != NULL) {
/* This is a built-in chain */
c = nft_chain_builtin_alloc(_t, _c, policy);
if (c == NULL)
return NULL;
} else {
errno = ENOENT;
return NULL;
}
if (counters) {
nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES,
counters->bcnt);
nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS,
counters->pcnt);
}
return c;
}
int nft_chain_set(struct nft_handle *h, const char *table,
const char *chain, const char *policy,
const struct xt_counters *counters)
{
struct nftnl_chain *c = NULL;
int ret;
nft_fn = nft_chain_set;
if (strcmp(policy, "DROP") == 0)
c = nft_chain_new(h, table, chain, NF_DROP, counters);
else if (strcmp(policy, "ACCEPT") == 0)
c = nft_chain_new(h, table, chain, NF_ACCEPT, counters);
if (c == NULL)
return 0;
if (h->batch_support)
ret = batch_chain_add(h, NFT_COMPAT_CHAIN_UPDATE, c);
else
ret = nft_chain_add(h, c, 0);
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
static int __add_match(struct nftnl_expr *e, struct xt_entry_match *m)
{
void *info;
nftnl_expr_set(e, NFTNL_EXPR_MT_NAME, m->u.user.name, strlen(m->u.user.name));
nftnl_expr_set_u32(e, NFTNL_EXPR_MT_REV, m->u.user.revision);
info = calloc(1, m->u.match_size);
if (info == NULL)
return -ENOMEM;
memcpy(info, m->data, m->u.match_size - sizeof(*m));
nftnl_expr_set(e, NFTNL_EXPR_MT_INFO, info, m->u.match_size - sizeof(*m));
return 0;
}
int add_match(struct nftnl_rule *r, struct xt_entry_match *m)
{
struct nftnl_expr *expr;
int ret;
expr = nftnl_expr_alloc("match");
if (expr == NULL)
return -ENOMEM;
ret = __add_match(expr, m);
nftnl_rule_add_expr(r, expr);
return ret;
}
static int __add_target(struct nftnl_expr *e, struct xt_entry_target *t)
{
void *info;
nftnl_expr_set(e, NFTNL_EXPR_TG_NAME, t->u.user.name,
strlen(t->u.user.name));
nftnl_expr_set_u32(e, NFTNL_EXPR_TG_REV, t->u.user.revision);
info = calloc(1, t->u.target_size);
if (info == NULL)
return -ENOMEM;
memcpy(info, t->data, t->u.target_size - sizeof(*t));
nftnl_expr_set(e, NFTNL_EXPR_TG_INFO, info, t->u.target_size - sizeof(*t));
return 0;
}
int add_target(struct nftnl_rule *r, struct xt_entry_target *t)
{
struct nftnl_expr *expr;
int ret;
expr = nftnl_expr_alloc("target");
if (expr == NULL)
return -ENOMEM;
ret = __add_target(expr, t);
nftnl_rule_add_expr(r, expr);
return ret;
}
int add_jumpto(struct nftnl_rule *r, const char *name, int verdict)
{
struct nftnl_expr *expr;
expr = nftnl_expr_alloc("immediate");
if (expr == NULL)
return -ENOMEM;
nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT);
nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_VERDICT, verdict);
nftnl_expr_set_str(expr, NFTNL_EXPR_IMM_CHAIN, (char *)name);
nftnl_rule_add_expr(r, expr);
return 0;
}
int add_verdict(struct nftnl_rule *r, int verdict)
{
struct nftnl_expr *expr;
expr = nftnl_expr_alloc("immediate");
if (expr == NULL)
return -ENOMEM;
nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT);
nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_VERDICT, verdict);
nftnl_rule_add_expr(r, expr);
return 0;
}
int add_action(struct nftnl_rule *r, struct iptables_command_state *cs,
bool goto_set)
{
int ret = 0;
/* If no target at all, add nothing (default to continue) */
if (cs->target != NULL) {
/* Standard target? */
if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
ret = add_verdict(r, NF_ACCEPT);
else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
ret = add_verdict(r, NF_DROP);
else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
ret = add_verdict(r, NFT_RETURN);
else
ret = add_target(r, cs->target->t);
} else if (strlen(cs->jumpto) > 0) {
/* Not standard, then it's a go / jump to chain */
if (goto_set)
ret = add_jumpto(r, cs->jumpto, NFT_GOTO);
else
ret = add_jumpto(r, cs->jumpto, NFT_JUMP);
}
return ret;
}
static void nft_rule_print_debug(struct nftnl_rule *r, struct nlmsghdr *nlh)
{
#ifdef NLDEBUG
char tmp[1024];
nft_rule_snprintf(tmp, sizeof(tmp), r, 0, 0);
printf("DEBUG: rule: %s\n", tmp);
mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
#endif
}
int add_counters(struct nftnl_rule *r, uint64_t packets, uint64_t bytes)
{
struct nftnl_expr *expr;
expr = nftnl_expr_alloc("counter");
if (expr == NULL)
return -ENOMEM;
nftnl_expr_set_u64(expr, NFTNL_EXPR_CTR_PACKETS, packets);
nftnl_expr_set_u64(expr, NFTNL_EXPR_CTR_BYTES, bytes);
nftnl_rule_add_expr(r, expr);
return 0;
}
void add_compat(struct nftnl_rule *r, uint32_t proto, bool inv)
{
nftnl_rule_set_u32(r, NFTNL_RULE_COMPAT_PROTO, proto);
nftnl_rule_set_u32(r, NFTNL_RULE_COMPAT_FLAGS,
inv ? NFT_RULE_COMPAT_F_INV : 0);
}
static struct nftnl_rule *
nft_rule_new(struct nft_handle *h, const char *chain, const char *table,
void *data)
{
struct nftnl_rule *r;
r = nftnl_rule_alloc();
if (r == NULL)
return NULL;
nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, h->family);
nftnl_rule_set(r, NFTNL_RULE_TABLE, (char *)table);
nftnl_rule_set(r, NFTNL_RULE_CHAIN, (char *)chain);
if (h->ops->add(r, data) < 0)
goto err;
return r;
err:
nftnl_rule_free(r);
return NULL;
}
int
nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
void *data, uint64_t handle, bool verbose)
{
struct nftnl_rule *r;
int type;
/* If built-in chains don't exist for this table, create them */
if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
nft_xt_builtin_init(h, table);
nft_fn = nft_rule_append;
r = nft_rule_new(h, chain, table, data);
if (r == NULL)
return 0;
if (handle > 0) {
nftnl_rule_set(r, NFTNL_RULE_HANDLE, &handle);
type = NFT_COMPAT_RULE_REPLACE;
} else
type = NFT_COMPAT_RULE_APPEND;
if (batch_rule_add(h, type, r) < 0)
nftnl_rule_free(r);
return 1;
}
void
nft_rule_print_save(const void *data,
struct nftnl_rule *r, enum nft_rule_print type,
unsigned int format)
{
const char *chain = nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
struct nft_family_ops *ops;
ops = nft_family_ops_lookup(family);
if (!(format & FMT_NOCOUNTS) && ops->save_counters)
ops->save_counters(data);
/* print chain name */
switch(type) {
case NFT_RULE_APPEND:
printf("-A %s ", chain);
break;
case NFT_RULE_DEL:
printf("-D %s ", chain);
break;
}
if (ops->save_firewall)
ops->save_firewall(data, format);
}
static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
{
struct nftnl_chain *c;
struct nftnl_chain_list *list = data;
c = nftnl_chain_alloc();
if (c == NULL)
goto err;
if (nftnl_chain_nlmsg_parse(nlh, c) < 0)
goto out;
nftnl_chain_list_add_tail(c, list);
return MNL_CB_OK;
out:
nftnl_chain_free(c);
err:
return MNL_CB_OK;
}
static struct nftnl_chain_list *nftnl_chain_list_get(struct nft_handle *h)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct nftnl_chain_list *list;
list = nftnl_chain_list_alloc();
if (list == NULL) {
errno = ENOMEM;
return NULL;
}
nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family,
NLM_F_DUMP, h->seq);
mnl_talk(h, nlh, nftnl_chain_list_cb, list);
return list;
}
struct nftnl_chain_list *nft_chain_dump(struct nft_handle *h)
{
return nftnl_chain_list_get(h);
}
static const char *policy_name[NF_ACCEPT+1] = {
[NF_DROP] = "DROP",
[NF_ACCEPT] = "ACCEPT",
};
static void nft_chain_print_save(struct nftnl_chain *c, bool basechain)
{
const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
uint64_t pkts = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS);
uint64_t bytes = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES);
/* print chain name */
if (basechain) {
uint32_t pol = NF_ACCEPT;
/* no default chain policy? don't crash, display accept */
if (nftnl_chain_get(c, NFTNL_CHAIN_POLICY))
pol = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
printf(":%s %s [%"PRIu64":%"PRIu64"]\n", chain, policy_name[pol],
pkts, bytes);
} else
printf(":%s - [%"PRIu64":%"PRIu64"]\n", chain, pkts, bytes);
}
int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list,
const char *table)
{
struct nftnl_chain_list_iter *iter;
struct nftnl_chain *c;
iter = nftnl_chain_list_iter_create(list);
if (iter == NULL)
return 0;
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
const char *chain_table =
nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
bool basechain = false;
if (strcmp(table, chain_table) != 0)
goto next;
basechain = nft_chain_builtin(c);
nft_chain_print_save(c, basechain);
next:
c = nftnl_chain_list_iter_next(iter);
}
nftnl_chain_list_iter_destroy(iter);
nftnl_chain_list_free(list);
return 1;
}
static int nftnl_rule_list_cb(const struct nlmsghdr *nlh, void *data)
{
struct nftnl_rule *r;
struct nftnl_rule_list *list = data;
r = nftnl_rule_alloc();
if (r == NULL)
goto err;
if (nftnl_rule_nlmsg_parse(nlh, r) < 0)
goto out;
nftnl_rule_list_add_tail(r, list);
return MNL_CB_OK;
out:
nftnl_rule_free(r);
nftnl_rule_list_free(list);
err:
return MNL_CB_OK;
}
static struct nftnl_rule_list *nft_rule_list_get(struct nft_handle *h)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct nftnl_rule_list *list;
int ret;
list = nftnl_rule_list_alloc();
if (list == NULL)
return 0;
nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, h->family,
NLM_F_DUMP, h->seq);
ret = mnl_talk(h, nlh, nftnl_rule_list_cb, list);
if (ret < 0) {
nftnl_rule_list_free(list);
return NULL;
}
return list;
}
int nft_rule_save(struct nft_handle *h, const char *table, bool counters)
{
struct nftnl_rule_list *list;
struct nftnl_rule_list_iter *iter;
struct nftnl_rule *r;
list = nft_rule_list_get(h);
if (list == NULL)
return 0;
iter = nftnl_rule_list_iter_create(list);
if (iter == NULL)
return 0;
r = nftnl_rule_list_iter_next(iter);
while (r != NULL) {
const char *rule_table =
nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
struct iptables_command_state cs = {};
if (strcmp(table, rule_table) != 0)
goto next;
nft_rule_to_iptables_command_state(r, &cs);
nft_rule_print_save(&cs, r, NFT_RULE_APPEND,
counters ? 0 : FMT_NOCOUNTS);
next:
r = nftnl_rule_list_iter_next(iter);
}
nftnl_rule_list_iter_destroy(iter);
nftnl_rule_list_free(list);
/* the core expects 1 for success and 0 for error */
return 1;
}
static void
__nft_rule_flush(struct nft_handle *h, const char *table, const char *chain)
{
struct nftnl_rule *r;
r = nftnl_rule_alloc();
if (r == NULL)
return;
nftnl_rule_set(r, NFTNL_RULE_TABLE, (char *)table);
nftnl_rule_set(r, NFTNL_RULE_CHAIN, (char *)chain);
if (batch_rule_add(h, NFT_COMPAT_RULE_FLUSH, r) < 0)
nftnl_rule_free(r);
}
int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table)
{
int ret;
struct nftnl_chain_list *list;
struct nftnl_chain_list_iter *iter;
struct nftnl_chain *c;
nft_fn = nft_rule_flush;
list = nftnl_chain_list_get(h);
if (list == NULL) {
ret = 0;
goto err;
}
iter = nftnl_chain_list_iter_create(list);
if (iter == NULL)
goto err;
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
const char *table_name =
nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
const char *chain_name =
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
if (strcmp(table, table_name) != 0)
goto next;
if (chain != NULL && strcmp(chain, chain_name) != 0)
goto next;
__nft_rule_flush(h, table_name, chain_name);
if (chain != NULL)
break;
next:
c = nftnl_chain_list_iter_next(iter);
}
nftnl_chain_list_iter_destroy(iter);
err:
nftnl_chain_list_free(list);
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table)
{
struct nftnl_chain *c;
int ret;
nft_fn = nft_chain_user_add;
/* If built-in chains don't exist for this table, create them */
if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
nft_xt_builtin_init(h, table);
c = nftnl_chain_alloc();
if (c == NULL)
return 0;
nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table);
nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)chain);
if (h->batch_support) {
ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
} else {
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN,
h->family,
NLM_F_ACK|NLM_F_EXCL, h->seq);
nftnl_chain_nlmsg_build_payload(nlh, c);
nftnl_chain_free(c);
ret = mnl_talk(h, nlh, NULL, NULL);
}
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
static int __nft_chain_del(struct nft_handle *h, struct nftnl_chain *c)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_DELCHAIN, h->family,
NLM_F_ACK, h->seq);
nftnl_chain_nlmsg_build_payload(nlh, c);
return mnl_talk(h, nlh, NULL, NULL);
}
int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table)
{
struct nftnl_chain_list *list;
struct nftnl_chain_list_iter *iter;
struct nftnl_chain *c;
int ret = 0;
int deleted_ctr = 0;
list = nftnl_chain_list_get(h);
if (list == NULL)
goto err;
iter = nftnl_chain_list_iter_create(list);
if (iter == NULL)
goto err;
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
const char *table_name =
nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
const char *chain_name =
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
/* don't delete built-in chain */
if (nft_chain_builtin(c))
goto next;
if (strcmp(table, table_name) != 0)
goto next;
if (chain != NULL && strcmp(chain, chain_name) != 0)
goto next;
if (h->batch_support)
ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_DEL, c);
else
ret = __nft_chain_del(h, c);
if (ret < 0)
break;
deleted_ctr++;
if (chain != NULL)
break;
next:
c = nftnl_chain_list_iter_next(iter);
}
nftnl_chain_list_iter_destroy(iter);
err:
if (!h->batch_support)
nftnl_chain_list_free(list);
/* chain not found */
if (deleted_ctr == 0) {
ret = -1;
errno = ENOENT;
}
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
struct nftnl_chain *
nft_chain_list_find(struct nftnl_chain_list *list,
const char *table, const char *chain)
{
struct nftnl_chain_list_iter *iter;
struct nftnl_chain *c;
iter = nftnl_chain_list_iter_create(list);
if (iter == NULL)
return NULL;
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
const char *table_name =
nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
const char *chain_name =
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
if (strcmp(table, table_name) != 0)
goto next;
if (strcmp(chain, chain_name) != 0)
goto next;
nftnl_chain_list_iter_destroy(iter);
return c;
next:
c = nftnl_chain_list_iter_next(iter);
}
nftnl_chain_list_iter_destroy(iter);
return NULL;
}
static struct nftnl_chain *
nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
{
struct nftnl_chain_list *list;
list = nftnl_chain_list_get(h);
if (list == NULL)
return NULL;
return nft_chain_list_find(list, table, chain);
}
int nft_chain_user_rename(struct nft_handle *h,const char *chain,
const char *table, const char *newname)
{
struct nftnl_chain *c;
uint64_t handle;
int ret;
nft_fn = nft_chain_user_add;
/* If built-in chains don't exist for this table, create them */
if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
nft_xt_builtin_init(h, table);
/* Config load changed errno. Ensure genuine info for our callers. */
errno = 0;
/* Find the old chain to be renamed */
c = nft_chain_find(h, table, chain);
if (c == NULL) {
errno = ENOENT;
return -1;
}
handle = nftnl_chain_get_u64(c, NFTNL_CHAIN_HANDLE);
/* Now prepare the new name for the chain */
c = nftnl_chain_alloc();
if (c == NULL)
return -1;
nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table);
nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)newname);
nftnl_chain_set_u64(c, NFTNL_CHAIN_HANDLE, handle);
if (h->batch_support) {
ret = batch_chain_add(h, NFT_COMPAT_CHAIN_RENAME, c);
} else {
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN,
h->family, NLM_F_ACK, h->seq);
nftnl_chain_nlmsg_build_payload(nlh, c);
nftnl_chain_free(c);
ret = mnl_talk(h, nlh, NULL, NULL);
}
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
static int nftnl_table_list_cb(const struct nlmsghdr *nlh, void *data)
{
struct nftnl_table *t;
struct nftnl_table_list *list = data;
t = nftnl_table_alloc();
if (t == NULL)
goto err;
if (nftnl_table_nlmsg_parse(nlh, t) < 0)
goto out;
nftnl_table_list_add_tail(t, list);
return MNL_CB_OK;
out:
nftnl_table_free(t);
err:
return MNL_CB_OK;
}
static struct nftnl_table_list *nftnl_table_list_get(struct nft_handle *h)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct nftnl_table_list *list;
list = nftnl_table_list_alloc();
if (list == NULL)
return 0;
nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, h->family,
NLM_F_DUMP, h->seq);
mnl_talk(h, nlh, nftnl_table_list_cb, list);
return list;
}
bool nft_table_find(struct nft_handle *h, const char *tablename)
{
struct nftnl_table_list *list;
struct nftnl_table_list_iter *iter;
struct nftnl_table *t;
bool ret = false;
list = nftnl_table_list_get(h);
if (list == NULL)
goto err;
iter = nftnl_table_list_iter_create(list);
if (iter == NULL)
goto err;
t = nftnl_table_list_iter_next(iter);
while (t != NULL) {
const char *this_tablename =
nftnl_table_get(t, NFTNL_TABLE_NAME);
if (strcmp(tablename, this_tablename) == 0)
return true;
t = nftnl_table_list_iter_next(iter);
}
nftnl_table_list_free(list);
err:
return ret;
}
int nft_for_each_table(struct nft_handle *h,
int (*func)(struct nft_handle *h, const char *tablename, bool counters),
bool counters)
{
int ret = 1;
struct nftnl_table_list *list;
struct nftnl_table_list_iter *iter;
struct nftnl_table *t;
list = nftnl_table_list_get(h);
if (list == NULL) {
ret = 0;
goto err;
}
iter = nftnl_table_list_iter_create(list);
if (iter == NULL)
return 0;
t = nftnl_table_list_iter_next(iter);
while (t != NULL) {
const char *tablename =
nftnl_table_get(t, NFTNL_TABLE_NAME);
func(h, tablename, counters);
t = nftnl_table_list_iter_next(iter);
}
nftnl_table_list_free(list);
err:
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
int nft_table_purge_chains(struct nft_handle *h, const char *this_table,
struct nftnl_chain_list *chain_list)
{
struct nftnl_chain_list_iter *iter;
struct nftnl_chain *chain_obj;
iter = nftnl_chain_list_iter_create(chain_list);
if (iter == NULL)
return 0;
chain_obj = nftnl_chain_list_iter_next(iter);
while (chain_obj != NULL) {
const char *table =
nftnl_chain_get_str(chain_obj, NFTNL_CHAIN_TABLE);
if (strcmp(this_table, table) != 0)
goto next;
if (nft_chain_builtin(chain_obj))
goto next;
if ( __nft_chain_del(h, chain_obj) < 0) {
if (errno != EBUSY)
return -1;
}
next:
chain_obj = nftnl_chain_list_iter_next(iter);
}
nftnl_chain_list_iter_destroy(iter);
return 0;
}
static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule_list *list,
struct nftnl_rule *r)
{
int ret;
nftnl_rule_list_del(r);
ret = batch_rule_add(h, NFT_COMPAT_RULE_DELETE, r);
if (ret < 0) {
nftnl_rule_free(r);
return -1;
}
return 1;
}
struct nftnl_rule_list *nft_rule_list_create(struct nft_handle *h)
{
return nft_rule_list_get(h);
}
void nft_rule_list_destroy(struct nftnl_rule_list *list)
{
nftnl_rule_list_free(list);
}
static struct nftnl_rule *
nft_rule_find(struct nft_handle *h, struct nftnl_rule_list *list,
const char *chain, const char *table, void *data, int rulenum)
{
struct nftnl_rule *r;
struct nftnl_rule_list_iter *iter;
int rule_ctr = 0;
bool found = false;
iter = nftnl_rule_list_iter_create(list);
if (iter == NULL)
return 0;
r = nftnl_rule_list_iter_next(iter);
while (r != NULL) {
const char *rule_table =
nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
const char *rule_chain =
nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
if (strcmp(table, rule_table) != 0 ||
strcmp(chain, rule_chain) != 0) {
DEBUGP("different chain / table\n");
goto next;
}
if (rulenum >= 0) {
/* Delete by rule number case */
if (rule_ctr == rulenum) {
found = true;
break;
}
} else {
found = h->ops->rule_find(h->ops, r, data);
if (found)
break;
}
rule_ctr++;
next:
r = nftnl_rule_list_iter_next(iter);
}
nftnl_rule_list_iter_destroy(iter);
return found ? r : NULL;
}
int nft_rule_check(struct nft_handle *h, const char *chain,
const char *table, void *data, bool verbose)
{
struct nftnl_rule_list *list;
int ret;
nft_fn = nft_rule_check;
list = nft_rule_list_create(h);
if (list == NULL)
return 0;
ret = nft_rule_find(h, list, chain, table, data, -1) ? 1 : 0;
if (ret == 0)
errno = ENOENT;
nft_rule_list_destroy(list);
return ret;
}
int nft_rule_delete(struct nft_handle *h, const char *chain,
const char *table, void *data, bool verbose)
{
int ret = 0;
struct nftnl_rule *r;
struct nftnl_rule_list *list;
nft_fn = nft_rule_delete;
list = nft_rule_list_create(h);
if (list == NULL)
return 0;
r = nft_rule_find(h, list, chain, table, data, -1);
if (r != NULL) {
ret =__nft_rule_del(h, list, r);
if (ret < 0)
errno = ENOMEM;
} else
errno = ENOENT;
nft_rule_list_destroy(list);
return ret;
}
static int
nft_rule_add(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *cs,
uint64_t handle, bool verbose)
{
struct nftnl_rule *r;
r = nft_rule_new(h, chain, table, cs);
if (r == NULL)
return 0;
if (handle > 0)
nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, handle);
if (batch_rule_add(h, NFT_COMPAT_RULE_INSERT, r) < 0) {
nftnl_rule_free(r);
return 0;
}
return 1;
}
int nft_rule_insert(struct nft_handle *h, const char *chain,
const char *table, void *data, int rulenum, bool verbose)
{
struct nftnl_rule_list *list;
struct nftnl_rule *r;
uint64_t handle = 0;
/* If built-in chains don't exist for this table, create them */
if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
nft_xt_builtin_init(h, table);
nft_fn = nft_rule_insert;
if (rulenum > 0) {
list = nft_rule_list_create(h);
if (list == NULL)
goto err;
r = nft_rule_find(h, list, chain, table, data, rulenum);
if (r == NULL) {
/* special case: iptables allows to insert into
* rule_count + 1 position.
*/
r = nft_rule_find(h, list, chain, table, data,
rulenum - 1);
if (r != NULL) {
nft_rule_list_destroy(list);
return nft_rule_append(h, chain, table, data,
0, verbose);
}
errno = ENOENT;
goto err;
}
handle = nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE);
DEBUGP("adding after rule handle %"PRIu64"\n", handle);
nft_rule_list_destroy(list);
}
return nft_rule_add(h, chain, table, data, handle, verbose);
err:
nft_rule_list_destroy(list);
return 0;
}
int nft_rule_delete_num(struct nft_handle *h, const char *chain,
const char *table, int rulenum, bool verbose)
{
int ret = 0;
struct nftnl_rule *r;
struct nftnl_rule_list *list;
nft_fn = nft_rule_delete_num;
list = nft_rule_list_create(h);
if (list == NULL)
return 0;
r = nft_rule_find(h, list, chain, table, NULL, rulenum);
if (r != NULL) {
ret = 1;
DEBUGP("deleting rule by number %d\n", rulenum);
ret = __nft_rule_del(h, list, r);
if (ret < 0)
errno = ENOMEM;
} else
errno = ENOENT;
nft_rule_list_destroy(list);
return ret;
}
int nft_rule_replace(struct nft_handle *h, const char *chain,
const char *table, void *data, int rulenum, bool verbose)
{
int ret = 0;
struct nftnl_rule *r;
struct nftnl_rule_list *list;
nft_fn = nft_rule_replace;
list = nft_rule_list_create(h);
if (list == NULL)
return 0;
r = nft_rule_find(h, list, chain, table, data, rulenum);
if (r != NULL) {
DEBUGP("replacing rule with handle=%llu\n",
(unsigned long long)
nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE));
ret = nft_rule_append(h, chain, table, data,
nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE),
verbose);
} else
errno = ENOENT;
nft_rule_list_destroy(list);
return ret;
}
static int
__nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
int rulenum, unsigned int format,
void (*cb)(struct nftnl_rule *r, unsigned int num,
unsigned int format))
{
struct nftnl_rule_list *list;
struct nftnl_rule_list_iter *iter;
struct nftnl_rule *r;
int rule_ctr = 0, ret = 0;
list = nft_rule_list_get(h);
if (list == NULL)
return 0;
iter = nftnl_rule_list_iter_create(list);
if (iter == NULL)
goto err;
r = nftnl_rule_list_iter_next(iter);
while (r != NULL) {
const char *rule_table =
nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
const char *rule_chain =
nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
if (strcmp(table, rule_table) != 0 ||
strcmp(chain, rule_chain) != 0)
goto next;
rule_ctr++;
if (rulenum > 0 && rule_ctr != rulenum) {
/* List by rule number case */
goto next;
}
cb(r, rule_ctr, format);
if (rulenum > 0 && rule_ctr == rulenum) {
ret = 1;
break;
}
next:
r = nftnl_rule_list_iter_next(iter);
}
nftnl_rule_list_iter_destroy(iter);
err:
nftnl_rule_list_free(list);
if (ret == 0)
errno = ENOENT;
return ret;
}
int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
int rulenum, unsigned int format)
{
const struct nft_family_ops *ops;
struct nftnl_chain_list *list;
struct nftnl_chain_list_iter *iter;
struct nftnl_chain *c;
bool found = false;
/* If built-in chains don't exist for this table, create them */
if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) {
nft_xt_builtin_init(h, table);
/* Force table and chain creation, otherwise first iptables -L
* lists no table/chains.
*/
if (!list_empty(&h->obj_list))
nft_commit(h);
}
ops = nft_family_ops_lookup(h->family);
if (chain && rulenum) {
__nft_rule_list(h, chain, table,
rulenum, format, ops->print_firewall);
return 1;
}
list = nft_chain_dump(h);
iter = nftnl_chain_list_iter_create(list);
if (iter == NULL)
goto err;
if (ops->print_table_header)
ops->print_table_header(table);
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
const char *chain_table =
nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
const char *chain_name =
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
uint32_t policy =
nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
uint32_t refs =
nftnl_chain_get_u32(c, NFTNL_CHAIN_USE);
struct xt_counters ctrs = {
.pcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS),
.bcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES),
};
bool basechain = false;
if (nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM))
basechain = true;
if (strcmp(table, chain_table) != 0)
goto next;
if (chain && strcmp(chain, chain_name) != 0)
goto next;
if (found)
printf("\n");
ops->print_header(format, chain_name, policy_name[policy],
&ctrs, basechain, refs);
__nft_rule_list(h, chain_name, table,
rulenum, format, ops->print_firewall);
/* we printed the chain we wanted, stop processing. */
if (chain)
break;
found = true;
next:
c = nftnl_chain_list_iter_next(iter);
}
nftnl_chain_list_iter_destroy(iter);
err:
nftnl_chain_list_free(list);
return 1;
}
static void
list_save(struct nftnl_rule *r, unsigned int num, unsigned int format)
{
struct iptables_command_state cs = {};
nft_rule_to_iptables_command_state(r, &cs);
nft_rule_print_save(&cs, r, NFT_RULE_APPEND, !(format & FMT_NOCOUNTS));
}
static int
nftnl_rule_list_chain_save(struct nft_handle *h, const char *chain,
const char *table, struct nftnl_chain_list *list,
int counters)
{
struct nftnl_chain_list_iter *iter;
struct nftnl_chain *c;
iter = nftnl_chain_list_iter_create(list);
if (iter == NULL)
return 0;
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
const char *chain_table =
nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
const char *chain_name =
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
uint32_t policy =
nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
if (strcmp(table, chain_table) != 0 ||
(chain && strcmp(chain, chain_name) != 0))
goto next;
/* this is a base chain */
if (nft_chain_builtin(c)) {
printf("-P %s %s", chain_name, policy_name[policy]);
if (counters) {
printf(" -c %"PRIu64" %"PRIu64"\n",
nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS),
nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES));
} else
printf("\n");
} else {
printf("-N %s\n", chain_name);
}
next:
c = nftnl_chain_list_iter_next(iter);
}
nftnl_chain_list_iter_destroy(iter);
return 1;
}
int nft_rule_list_save(struct nft_handle *h, const char *chain,
const char *table, int rulenum, int counters)
{
struct nftnl_chain_list *list;
struct nftnl_chain_list_iter *iter;
struct nftnl_chain *c;
int ret = 1;
list = nft_chain_dump(h);
/* Dump policies and custom chains first */
if (!rulenum)
nftnl_rule_list_chain_save(h, chain, table, list, counters);
/* Now dump out rules in this table */
iter = nftnl_chain_list_iter_create(list);
if (iter == NULL)
goto err;
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
const char *chain_table =
nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
const char *chain_name =
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
if (strcmp(table, chain_table) != 0)
goto next;
if (chain && strcmp(chain, chain_name) != 0)
goto next;
ret = __nft_rule_list(h, chain_name, table, rulenum,
counters ? 0 : FMT_NOCOUNTS, list_save);
/* we printed the chain we wanted, stop processing. */
if (chain)
break;
next:
c = nftnl_chain_list_iter_next(iter);
}
nftnl_chain_list_iter_destroy(iter);
err:
nftnl_chain_list_free(list);
return ret;
}
int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
const char *table, int rulenum)
{
struct iptables_command_state cs = {};
struct nftnl_rule_list *list;
struct nftnl_rule *r;
int ret = 0;
nft_fn = nft_rule_delete;
list = nft_rule_list_create(h);
if (list == NULL)
return 0;
r = nft_rule_find(h, list, chain, table, NULL, rulenum);
if (r == NULL) {
errno = ENOENT;
ret = 1;
goto error;
}
nft_rule_to_iptables_command_state(r, &cs);
cs.counters.pcnt = cs.counters.bcnt = 0;
ret = nft_rule_append(h, chain, table, &cs,
nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE),
false);
error:
nft_rule_list_destroy(list);
return ret;
}
static void nft_compat_table_batch_add(struct nft_handle *h, uint16_t type,
uint16_t flags, uint32_t seq,
struct nftnl_table *table)
{
struct nlmsghdr *nlh;
nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
type, h->family, flags, seq);
nftnl_table_nlmsg_build_payload(nlh, table);
nftnl_table_free(table);
}
static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type,
uint16_t flags, uint32_t seq,
struct nftnl_chain *chain)
{
struct nlmsghdr *nlh;
nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
type, h->family, flags, seq);
nftnl_chain_nlmsg_build_payload(nlh, chain);
nft_chain_print_debug(chain, nlh);
nftnl_chain_free(chain);
}
static void nft_compat_rule_batch_add(struct nft_handle *h, uint16_t type,
uint16_t flags, uint32_t seq,
struct nftnl_rule *rule)
{
struct nlmsghdr *nlh;
nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
type, h->family, flags, seq);
nftnl_rule_nlmsg_build_payload(nlh, rule);
nft_rule_print_debug(rule, nlh);
nftnl_rule_free(rule);
}
static int nft_action(struct nft_handle *h, int action)
{
struct obj_update *n, *tmp;
uint32_t seq = 1;
int ret = 0;
mnl_nftnl_batch_begin(h->batch, seq++);
list_for_each_entry_safe(n, tmp, &h->obj_list, head) {
switch (n->type) {
case NFT_COMPAT_TABLE_ADD:
nft_compat_table_batch_add(h, NFT_MSG_NEWTABLE,
NLM_F_CREATE, seq++,
n->table);
break;
case NFT_COMPAT_CHAIN_ADD:
nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
NLM_F_CREATE, seq++,
n->chain);
break;
case NFT_COMPAT_CHAIN_USER_ADD:
nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
NLM_F_EXCL, seq++,
n->chain);
break;
case NFT_COMPAT_CHAIN_USER_DEL:
nft_compat_chain_batch_add(h, NFT_MSG_DELCHAIN,
0, seq++, n->chain);
break;
case NFT_COMPAT_CHAIN_UPDATE:
nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
h->restore ?
NLM_F_CREATE : 0,
seq++, n->chain);
break;
case NFT_COMPAT_CHAIN_RENAME:
nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN, 0,
seq++, n->chain);
break;
case NFT_COMPAT_RULE_APPEND:
nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
NLM_F_CREATE | NLM_F_APPEND,
seq++, n->rule);
break;
case NFT_COMPAT_RULE_INSERT:
nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
NLM_F_CREATE, seq++,
n->rule);
break;
case NFT_COMPAT_RULE_REPLACE:
nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
NLM_F_CREATE | NLM_F_REPLACE,
seq++, n->rule);
break;
case NFT_COMPAT_RULE_DELETE:
case NFT_COMPAT_RULE_FLUSH:
nft_compat_rule_batch_add(h, NFT_MSG_DELRULE, 0,
seq++, n->rule);
break;
}
h->obj_list_num--;
list_del(&n->head);
free(n);
if (!mnl_nlmsg_batch_next(h->batch))
h->batch = mnl_nftnl_batch_page_add(h->batch);
}
switch (action) {
case NFT_COMPAT_COMMIT:
mnl_nftnl_batch_end(h->batch, seq++);
break;
case NFT_COMPAT_ABORT:
break;
}
if (!mnl_nlmsg_batch_is_empty(h->batch))
h->batch = mnl_nftnl_batch_page_add(h->batch);
ret = mnl_nftnl_batch_talk(h);
mnl_nlmsg_batch_reset(h->batch);
return ret == 0 ? 1 : 0;
}
int nft_commit(struct nft_handle *h)
{
return nft_action(h, NFT_COMPAT_COMMIT);
}
int nft_abort(struct nft_handle *h)
{
return nft_action(h, NFT_COMPAT_ABORT);
}
int nft_compatible_revision(const char *name, uint8_t rev, int opt)
{
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t portid, seq, type;
int ret = 0;
if (opt == IPT_SO_GET_REVISION_MATCH ||
opt == IP6T_SO_GET_REVISION_MATCH)
type = 0;
else
type = 1;
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = (NFNL_SUBSYS_NFT_COMPAT << 8) | NFNL_MSG_COMPAT_GET;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_seq = seq = time(NULL);
struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
nfg->nfgen_family = AF_INET;
nfg->version = NFNETLINK_V0;
nfg->res_id = 0;
mnl_attr_put_strz(nlh, NFTA_COMPAT_NAME, name);
mnl_attr_put_u32(nlh, NFTA_COMPAT_REV, htonl(rev));
mnl_attr_put_u32(nlh, NFTA_COMPAT_TYPE, htonl(type));
DEBUGP("requesting `%s' rev=%d type=%d via nft_compat\n",
name, rev, type);
nl = mnl_socket_open(NETLINK_NETFILTER);
if (nl == NULL)
return 0;
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
goto err;
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
goto err;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1)
goto err;
ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
if (ret == -1)
goto err;
err:
mnl_socket_close(nl);
return ret < 0 ? 0 : 1;
}
/* Translates errno numbers into more human-readable form than strerror. */
const char *nft_strerror(int err)
{
unsigned int i;
static struct table_struct {
void *fn;
int err;
const char *message;
} table[] =
{
{ nft_chain_user_del, ENOTEMPTY, "Chain is not empty" },
{ nft_chain_user_del, EINVAL, "Can't delete built-in chain" },
{ nft_chain_user_del, EBUSY, "Directory not empty" },
{ nft_chain_user_del, EMLINK,
"Can't delete chain with references left" },
{ nft_chain_user_add, EEXIST, "Chain already exists" },
{ nft_rule_add, E2BIG, "Index of insertion too big" },
{ nft_rule_check, ENOENT, "Bad rule (does a matching rule exist in that chain?)" },
{ nft_rule_replace, ENOENT, "Index of replacement too big" },
{ nft_rule_delete_num, E2BIG, "Index of deletion too big" },
/* { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
{ TC_ZERO_COUNTER, E2BIG, "Index of counter too big" }, */
{ nft_rule_add, ELOOP, "Loop found in table" },
{ nft_rule_add, EINVAL, "Target problem" },
/* ENOENT for DELETE probably means no matching rule */
{ nft_rule_delete, ENOENT,
"Bad rule (does a matching rule exist in that chain?)" },
{ nft_chain_set, ENOENT, "Bad built-in chain name" },
{ nft_chain_set, EINVAL, "Bad policy name" },
{ NULL, EPERM, "Permission denied (you must be root)" },
{ NULL, 0, "Incompatible with this kernel" },
{ NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
{ NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
{ NULL, ENOMEM, "Memory allocation problem" },
{ NULL, ENOENT, "No chain/target/match by that name" },
};
for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
if ((!table[i].fn || table[i].fn == nft_fn)
&& table[i].err == err)
return table[i].message;
}
return strerror(err);
}
static void xtables_config_perror(uint32_t flags, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (flags & NFT_LOAD_VERBOSE)
vfprintf(stderr, fmt, args);
va_end(args);
}
int nft_xtables_config_load(struct nft_handle *h, const char *filename,
uint32_t flags)
{
struct nftnl_table_list *table_list = nftnl_table_list_alloc();
struct nftnl_chain_list *chain_list = nftnl_chain_list_alloc();
struct nftnl_table_list_iter *titer = NULL;
struct nftnl_chain_list_iter *citer = NULL;
struct nftnl_table *table;
struct nftnl_chain *chain;
uint32_t table_family, chain_family;
bool found = false;
if (h->restore)
return 0;
if (xtables_config_parse(filename, table_list, chain_list) < 0) {
if (errno == ENOENT) {
xtables_config_perror(flags,
"configuration file `%s' does not exists\n",
filename);
} else {
xtables_config_perror(flags,
"Fatal error parsing config file: %s\n",
strerror(errno));
}
goto err;
}
/* Stage 1) create tables */
titer = nftnl_table_list_iter_create(table_list);
while ((table = nftnl_table_list_iter_next(titer)) != NULL) {
table_family = nftnl_table_get_u32(table,
NFTNL_TABLE_FAMILY);
if (h->family != table_family)
continue;
found = true;
if (batch_table_add(h, NFT_COMPAT_TABLE_ADD, table) < 0) {
if (errno == EEXIST) {
xtables_config_perror(flags,
"table `%s' already exists, skipping\n",
(char *)nftnl_table_get(table, NFTNL_TABLE_NAME));
} else {
xtables_config_perror(flags,
"table `%s' cannot be create, reason `%s'. Exitting\n",
(char *)nftnl_table_get(table, NFTNL_TABLE_NAME),
strerror(errno));
goto err;
}
continue;
}
xtables_config_perror(flags, "table `%s' has been created\n",
(char *)nftnl_table_get(table, NFTNL_TABLE_NAME));
}
nftnl_table_list_iter_destroy(titer);
nftnl_table_list_free(table_list);
if (!found)
goto err;
/* Stage 2) create chains */
citer = nftnl_chain_list_iter_create(chain_list);
while ((chain = nftnl_chain_list_iter_next(citer)) != NULL) {
chain_family = nftnl_chain_get_u32(chain,
NFTNL_CHAIN_TABLE);
if (h->family != chain_family)
continue;
if (batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, chain) < 0) {
if (errno == EEXIST) {
xtables_config_perror(flags,
"chain `%s' already exists in table `%s', skipping\n",
(char *)nftnl_chain_get(chain, NFTNL_CHAIN_NAME),
(char *)nftnl_chain_get(chain, NFTNL_CHAIN_TABLE));
} else {
xtables_config_perror(flags,
"chain `%s' cannot be create, reason `%s'. Exitting\n",
(char *)nftnl_chain_get(chain, NFTNL_CHAIN_NAME),
strerror(errno));
goto err;
}
continue;
}
xtables_config_perror(flags,
"chain `%s' in table `%s' has been created\n",
(char *)nftnl_chain_get(chain, NFTNL_CHAIN_NAME),
(char *)nftnl_chain_get(chain, NFTNL_CHAIN_TABLE));
}
nftnl_chain_list_iter_destroy(citer);
nftnl_chain_list_free(chain_list);
return 0;
err:
nftnl_table_list_free(table_list);
nftnl_chain_list_free(chain_list);
if (titer != NULL)
nftnl_table_list_iter_destroy(titer);
if (citer != NULL)
nftnl_chain_list_iter_destroy(citer);
return -1;
}
int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
const char *table)
{
struct nftnl_chain_list *list;
struct nftnl_chain_list_iter *iter;
struct nftnl_chain *c;
int ret = 0;
list = nftnl_chain_list_get(h);
if (list == NULL)
goto err;
iter = nftnl_chain_list_iter_create(list);
if (iter == NULL)
goto err;
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
const char *chain_name =
nftnl_chain_get(c, NFTNL_CHAIN_NAME);
const char *chain_table =
nftnl_chain_get(c, NFTNL_CHAIN_TABLE);
if (strcmp(table, chain_table) != 0)
goto next;
if (chain != NULL && strcmp(chain, chain_name) != 0)
goto next;
nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, 0);
nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, 0);
nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE);
if (h->batch_support) {
ret = batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, c);
} else {
struct nlmsghdr *nlh;
char buf[MNL_SOCKET_BUFFER_SIZE];
nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN,
h->family, NLM_F_ACK,
h->seq);
nftnl_chain_nlmsg_build_payload(nlh, c);
ret = mnl_talk(h, nlh, NULL, NULL);
}
if (chain != NULL)
break;
next:
c = nftnl_chain_list_iter_next(iter);
}
if (!h->batch_support)
nftnl_chain_list_free(list);
nftnl_chain_list_iter_destroy(iter);
err:
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag)
{
if (invflags & flag)
return NFT_CMP_NEQ;
return NFT_CMP_EQ;
}
#ifndef _NFT_H_
#define _NFT_H_
#include "xshared.h"
#include "nft-shared.h"
#include <libiptc/linux_list.h>
#define FILTER 0
#define MANGLE 1
#define RAW 2
#define SECURITY 3
#define NAT 4
#define TABLES_MAX 5
struct builtin_chain {
const char *name;
const char *type;
uint32_t prio;
uint32_t hook;
};
struct builtin_table {
const char *name;
struct builtin_chain chains[NF_INET_NUMHOOKS];
bool initialized;
};
struct nft_handle {
int family;
struct mnl_socket *nl;
uint32_t portid;
uint32_t seq;
struct list_head obj_list;
int obj_list_num;
struct mnl_nlmsg_batch *batch;
struct nft_family_ops *ops;
struct builtin_table *tables;
bool restore;
bool batch_support;
};
extern struct builtin_table xtables_ipv4[TABLES_MAX];
extern struct builtin_table xtables_arp[TABLES_MAX];
extern struct builtin_table xtables_bridge[TABLES_MAX];
int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
int (*cb)(const struct nlmsghdr *nlh, void *data),
void *data);
int nft_init(struct nft_handle *h, struct builtin_table *t);
void nft_fini(struct nft_handle *h);
/*
* Operations with tables.
*/
struct nftnl_table;
struct nftnl_chain_list;
int nft_table_add(struct nft_handle *h, struct nftnl_table *t, uint16_t flags);
int nft_for_each_table(struct nft_handle *h, int (*func)(struct nft_handle *h, const char *tablename, bool counters), bool counters);
bool nft_table_find(struct nft_handle *h, const char *tablename);
int nft_table_purge_chains(struct nft_handle *h, const char *table, struct nftnl_chain_list *list);
/*
* Operations with chains.
*/
struct nftnl_chain;
int nft_chain_add(struct nft_handle *h, struct nftnl_chain *c, uint16_t flags);
int nft_chain_set(struct nft_handle *h, const char *table, const char *chain, const char *policy, const struct xt_counters *counters);
struct nftnl_chain_list *nft_chain_dump(struct nft_handle *h);
struct nftnl_chain *nft_chain_list_find(struct nftnl_chain_list *list, const char *table, const char *chain);
int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list, const char *table);
int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table);
int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table);
int nft_chain_user_rename(struct nft_handle *h, const char *chain, const char *table, const char *newname);
int nft_chain_zero_counters(struct nft_handle *h, const char *chain, const char *table);
/*
* Operations with rule-set.
*/
struct nftnl_rule;
int nft_rule_append(struct nft_handle *h, const char *chain, const char *table, void *data, uint64_t handle, bool verbose);
int nft_rule_insert(struct nft_handle *h, const char *chain, const char *table, void *data, int rulenum, bool verbose);
int nft_rule_check(struct nft_handle *h, const char *chain, const char *table, void *data, bool verbose);
int nft_rule_delete(struct nft_handle *h, const char *chain, const char *table, void *data, bool verbose);
int nft_rule_delete_num(struct nft_handle *h, const char *chain, const char *table, int rulenum, bool verbose);
int nft_rule_replace(struct nft_handle *h, const char *chain, const char *table, void *data, int rulenum, bool verbose);
int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, int rulenum, unsigned int format);
int nft_rule_list_save(struct nft_handle *h, const char *chain, const char *table, int rulenum, int counters);
int nft_rule_save(struct nft_handle *h, const char *table, bool counters);
int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table);
int nft_rule_zero_counters(struct nft_handle *h, const char *chain, const char *table, int rulenum);
struct nftnl_rule_list *nft_rule_list_create(struct nft_handle *h);
void nft_rule_list_destroy(struct nftnl_rule_list *list);
/*
* Operations used in userspace tools
*/
int add_counters(struct nftnl_rule *r, uint64_t packets, uint64_t bytes);
int add_verdict(struct nftnl_rule *r, int verdict);
int add_match(struct nftnl_rule *r, struct xt_entry_match *m);
int add_target(struct nftnl_rule *r, struct xt_entry_target *t);
int add_jumpto(struct nftnl_rule *r, const char *name, int verdict);
int add_action(struct nftnl_rule *r, struct iptables_command_state *cs, bool goto_set);
enum nft_rule_print {
NFT_RULE_APPEND,
NFT_RULE_DEL,
};
void nft_rule_print_save(const void *data,
struct nftnl_rule *r, enum nft_rule_print type,
unsigned int format);
uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag);
/*
* global commit and abort
*/
int nft_commit(struct nft_handle *h);
int nft_abort(struct nft_handle *h);
/*
* revision compatibility.
*/
int nft_compatible_revision(const char *name, uint8_t rev, int opt);
/*
* Error reporting.
*/
const char *nft_strerror(int err);
/* For xtables.c */
int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table, bool restore);
/* For xtables-arptables.c */
int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table);
/* For xtables-eb.c */
int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table);
/*
* Parse config for tables and chain helper functions
*/
#define XTABLES_CONFIG_DEFAULT "/etc/xtables.conf"
struct nftnl_table_list;
struct nftnl_chain_list;
extern int xtables_config_parse(const char *filename, struct nftnl_table_list *table_list, struct nftnl_chain_list *chain_list);
enum {
NFT_LOAD_VERBOSE = (1 << 0),
};
int nft_xtables_config_load(struct nft_handle *h, const char *filename, uint32_t flags);
/*
* ARP
*/
struct arpt_entry;
int nft_arp_rule_append(struct nft_handle *h, const char *chain,
const char *table, struct arpt_entry *fw,
bool verbose);
int nft_arp_rule_insert(struct nft_handle *h, const char *chain,
const char *table, struct arpt_entry *fw,
int rulenum, bool verbose);
void nft_rule_to_arpt_entry(struct nftnl_rule *r, struct arpt_entry *fw);
#endif
......@@ -6,14 +6,15 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <fcntl.h>
#include <xtables.h>
#include "xshared.h"
#define XT_SOCKET_NAME "xtables"
#define XT_SOCKET_LEN 8
#define XT_LOCK_NAME "/run/xtables.lock"
/*
* Print out any special helps. A user might like to be able to add a --help
......@@ -243,29 +244,23 @@ void xs_init_match(struct xtables_match *match)
match->init(match->m);
}
bool xtables_lock(bool wait)
bool xtables_lock(int wait)
{
int i = 0, ret, xt_socket;
struct sockaddr_un xt_addr;
memset(&xt_addr, 0, sizeof(xt_addr));
xt_addr.sun_family = AF_UNIX;
strcpy(xt_addr.sun_path+1, XT_SOCKET_NAME);
xt_socket = socket(AF_UNIX, SOCK_STREAM, 0);
/* If we can't even create a socket, fall back to prior (lockless) behavior */
if (xt_socket < 0)
int fd, waited = 0, i = 0;
fd = open(XT_LOCK_NAME, O_CREAT, 0600);
if (fd < 0)
return true;
while (1) {
ret = bind(xt_socket, (struct sockaddr*)&xt_addr,
offsetof(struct sockaddr_un, sun_path)+XT_SOCKET_LEN);
if (ret == 0)
if (flock(fd, LOCK_EX | LOCK_NB) == 0)
return true;
else if (wait == false)
else if (wait >= 0 && waited >= wait)
return false;
if (++i % 2 == 0)
fprintf(stderr, "Another app is currently holding the xtables lock; "
"waiting for it to exit...\n");
"waiting (%ds) for it to exit...\n", waited);
waited++;
sleep(1);
}
}
......@@ -58,6 +58,7 @@ struct iptables_command_state {
unsigned int options;
struct xtables_rule_match *matches;
struct xtables_target *target;
struct xt_counters counters;
char *protocol;
int proto_used;
const char *jumpto;
......@@ -84,7 +85,7 @@ extern struct xtables_match *load_proto(struct iptables_command_state *);
extern int subcmd_main(int, char **, const struct subcommand *);
extern void xs_init_target(struct xtables_target *);
extern void xs_init_match(struct xtables_match *);
extern bool xtables_lock(bool wait);
extern bool xtables_lock(int wait);
extern const struct xtables_afinfo *afinfo;
......
/*
* Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
*
* Based on the ipchains code by Paul Russell and Michael Neuling
*
* (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
* Paul 'Rusty' Russell <rusty@rustcorp.com.au>
* Marc Boucher <marc+nf@mbsi.ca>
* James Morris <jmorris@intercode.com.au>
* Harald Welte <laforge@gnumonks.org>
* Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* arptables -- IP firewall administration for kernels with
* firewall table (aimed for the 2.3 kernels)
*
* See the accompanying manual page arptables(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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <xtables.h>
#include "nft.h"
#include <linux/netfilter_arp/arp_tables.h>
#include "xtables-multi.h"
extern struct xtables_globals arptables_globals;
int xtables_arp_main(int argc, char *argv[])
{
int ret;
char *table = "filter";
struct nft_handle h = {
.family = NFPROTO_ARP,
};
arptables_globals.program_name = "arptables";
ret = xtables_init_all(&arptables_globals, NFPROTO_ARP);
if (ret < 0) {
fprintf(stderr, "%s/%s Failed to initialize arptables-compat\n",
arptables_globals.program_name,
arptables_globals.program_version);
exit(1);
}
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensionsa();
#endif
ret = do_commandarp(&h, argc, argv, &table);
if (ret)
ret = nft_commit(&h);
nft_fini(&h);
if (!ret)
fprintf(stderr, "arptables: %s\n", nft_strerror(errno));
exit(!ret);
}
/* Code to take an arptables-style command line and do it. */
/*
* arptables:
* Author: Bart De Schuymer <bdschuym@pandora.be>, but
* almost all code is from the iptables userspace program, which has main
* authors: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
*
* 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.
*/
/*
Currently, only support for specifying hardware addresses for Ethernet
is available.
This tool is not luser-proof: you can specify an Ethernet source address
and set hardware length to something different than 6, f.e.
*/
#include <getopt.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <dlfcn.h>
#include <ctype.h>
#include <stdarg.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <iptables.h>
#include <xtables.h>
#include "xshared.h"
#include "nft.h"
#include "nft-arp.h"
#include <linux/netfilter_arp/arp_tables.h>
typedef char arpt_chainlabel[32];
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/* XXX: command defined by nft-shared.h do not overlap with these two */
#undef CMD_CHECK
#undef CMD_RENAME_CHAIN
#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_CHECK 0x0800U
#define CMD_RENAME_CHAIN 0x1000U
#define NUMBER_OF_CMD 13
static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
'N', 'X', 'P', 'E' };
#define OPTION_OFFSET 256
#define OPT_NONE 0x00000U
#define OPT_NUMERIC 0x00001U
#define OPT_S_IP 0x00002U
#define OPT_D_IP 0x00004U
#define OPT_S_MAC 0x00008U
#define OPT_D_MAC 0x00010U
#define OPT_H_LENGTH 0x00020U
#define OPT_P_LENGTH 0x00040U
#define OPT_OPCODE 0x00080U
#define OPT_H_TYPE 0x00100U
#define OPT_P_TYPE 0x00200U
#define OPT_JUMP 0x00400U
#define OPT_VERBOSE 0x00800U
#define OPT_VIANAMEIN 0x01000U
#define OPT_VIANAMEOUT 0x02000U
#define OPT_LINENUMBERS 0x04000U
#define OPT_COUNTERS 0x08000U
#define NUMBER_OF_OPT 16
static const char optflags[NUMBER_OF_OPT]
= { 'n', 's', 'd', 2, 3, 7, 8, 4, 5, 6, 'j', 'v', 'i', 'o', '0', 'c'};
static struct option original_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' },
{ "new-chain", 1, 0, 'N' },
{ "delete-chain", 2, 0, 'X' },
{ "rename-chain", 1, 0, 'E' },
{ "policy", 1, 0, 'P' },
{ "source-ip", 1, 0, 's' },
{ "destination-ip", 1, 0, 'd' },
{ "src-ip", 1, 0, 's' },
{ "dst-ip", 1, 0, 'd' },
{ "source-mac", 1, 0, 2},
{ "destination-mac", 1, 0, 3},
{ "src-mac", 1, 0, 2},
{ "dst-mac", 1, 0, 3},
{ "h-length", 1, 0, 'l' },
{ "p-length", 1, 0, 8 },
{ "opcode", 1, 0, 4 },
{ "h-type", 1, 0, 5 },
{ "proto-type", 1, 0, 6 },
{ "in-interface", 1, 0, 'i' },
{ "jump", 1, 0, 'j' },
{ "table", 1, 0, 't' },
{ "match", 1, 0, 'm' },
{ "numeric", 0, 0, 'n' },
{ "out-interface", 1, 0, 'o' },
{ "verbose", 0, 0, 'v' },
{ "exact", 0, 0, 'x' },
{ "version", 0, 0, 'V' },
{ "help", 2, 0, 'h' },
{ "line-numbers", 0, 0, '0' },
{ "modprobe", 1, 0, 'M' },
{ 0 }
};
int RUNTIME_NF_ARP_NUMHOOKS = 3;
static struct option *opts = original_opts;
static unsigned int global_option_offset = 0;
extern void xtables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
struct xtables_globals arptables_globals = {
.option_offset = 0,
.program_version = IPTABLES_VERSION,
.orig_opts = original_opts,
.exit_err = xtables_exit_error,
.compat_rev = nft_compatible_revision,
};
/* 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 -v -x -i -o -f --line */
/*INSERT*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
/*DELETE*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
/*DELETE_NUM*/{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
/*REPLACE*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
/*APPEND*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
/*LIST*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
/*FLUSH*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
/*ZERO*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
/*NEW_CHAIN*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
/*DEL_CHAIN*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
/*SET_POLICY*/{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
/*CHECK*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
/*RENAME*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}
};
static int inverse_for_options[NUMBER_OF_OPT] =
{
/* -n */ 0,
/* -s */ ARPT_INV_SRCIP,
/* -d */ ARPT_INV_TGTIP,
/* 2 */ ARPT_INV_SRCDEVADDR,
/* 3 */ ARPT_INV_TGTDEVADDR,
/* -l */ ARPT_INV_ARPHLN,
/* 8 */ 0,
/* 4 */ ARPT_INV_ARPOP,
/* 5 */ ARPT_INV_ARPHRD,
/* 6 */ ARPT_INV_ARPPRO,
/* -j */ 0,
/* -v */ 0,
/* -i */ ARPT_INV_VIA_IN,
/* -o */ ARPT_INV_VIA_OUT,
/*--line*/ 0,
/* -c */ 0,
};
const char *program_version = XTABLES_VERSION;
const char *program_name = "arptables";
/* A few hardcoded protocols for 'all' and in case the user has no
/etc/protocols */
struct pprot {
char *name;
u_int8_t num;
};
/* Primitive headers... */
/* defined in netinet/in.h */
#if 0
#ifndef IPPROTO_ESP
#define IPPROTO_ESP 50
#endif
#ifndef IPPROTO_AH
#define IPPROTO_AH 51
#endif
#endif
/***********************************************/
/* ARPTABLES SPECIFIC NEW FUNCTIONS ADDED HERE */
/***********************************************/
unsigned char mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0};
unsigned char msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0};
unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
/*
* put the mac address into 6 (ETH_ALEN) bytes
*/
static int getmac_and_mask(char *from, char *to, char *mask)
{
char *p;
int i;
struct ether_addr *addr;
if (strcasecmp(from, "Unicast") == 0) {
memcpy(to, mac_type_unicast, ETH_ALEN);
memcpy(mask, msk_type_unicast, ETH_ALEN);
return 0;
}
if (strcasecmp(from, "Multicast") == 0) {
memcpy(to, mac_type_multicast, ETH_ALEN);
memcpy(mask, msk_type_multicast, ETH_ALEN);
return 0;
}
if (strcasecmp(from, "Broadcast") == 0) {
memcpy(to, mac_type_broadcast, ETH_ALEN);
memcpy(mask, msk_type_broadcast, ETH_ALEN);
return 0;
}
if ( (p = strrchr(from, '/')) != NULL) {
*p = '\0';
if (!(addr = ether_aton(p + 1)))
return -1;
memcpy(mask, addr, ETH_ALEN);
} else
memset(mask, 0xff, ETH_ALEN);
if (!(addr = ether_aton(from)))
return -1;
memcpy(to, addr, ETH_ALEN);
for (i = 0; i < ETH_ALEN; i++)
to[i] &= mask[i];
return 0;
}
static int getlength_and_mask(char *from, uint8_t *to, uint8_t *mask)
{
char *p, *buffer;
int i;
if ( (p = strrchr(from, '/')) != NULL) {
*p = '\0';
i = strtol(p+1, &buffer, 10);
if (*buffer != '\0' || i < 0 || i > 255)
return -1;
*mask = (uint8_t)i;
} else
*mask = 255;
i = strtol(from, &buffer, 10);
if (*buffer != '\0' || i < 0 || i > 255)
return -1;
*to = (uint8_t)i;
return 0;
}
static int get16_and_mask(char *from, uint16_t *to, uint16_t *mask, int base)
{
char *p, *buffer;
int i;
if ( (p = strrchr(from, '/')) != NULL) {
*p = '\0';
i = strtol(p+1, &buffer, base);
if (*buffer != '\0' || i < 0 || i > 65535)
return -1;
*mask = htons((uint16_t)i);
} else
*mask = 65535;
i = strtol(from, &buffer, base);
if (*buffer != '\0' || i < 0 || i > 65535)
return -1;
*to = htons((uint16_t)i);
return 0;
}
static int
string_to_number(const char *s, unsigned int min, unsigned int max,
unsigned int *ret)
{
long number;
char *end;
/* Handle hex, octal, etc. */
errno = 0;
number = strtol(s, &end, 0);
if (*end == '\0' && end != s) {
/* we parsed a number, let's see if we want this */
if (errno != ERANGE && min <= number && number <= max) {
*ret = number;
return 0;
}
}
return -1;
}
/*********************************************/
/* ARPTABLES SPECIFIC NEW FUNCTIONS END HERE */
/*********************************************/
static struct in_addr *
dotted_to_addr(const char *dotted)
{
static struct in_addr addr;
unsigned char *addrp;
char *p, *q;
unsigned int onebyte;
int 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;
*q = '\0';
if (string_to_number(p, 0, 255, &onebyte) == -1)
return (struct in_addr *) NULL;
addrp[i] = (unsigned char) onebyte;
p = q + 1;
}
/* we've checked 3 bytes, now we check the last one */
if (string_to_number(p, 0, 255, &onebyte) == -1)
return (struct in_addr *) NULL;
addrp[3] = (unsigned char) onebyte;
return &addr;
}
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;
}
return (struct in_addr *) NULL;
}
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
exit_tryhelp(int status)
{
fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
program_name, program_name );
exit(status);
}
static void
exit_printhelp(void)
{
struct xtables_target *t = NULL;
int i;
printf("%s v%s\n\n"
"Usage: %s -[AD] chain rule-specification [options]\n"
" %s -[RI] chain rulenum rule-specification [options]\n"
" %s -D chain rulenum [options]\n"
" %s -[LFZ] [chain] [options]\n"
" %s -[NX] chain\n"
" %s -E old-chain-name new-chain-name\n"
" %s -P chain target [options]\n"
" %s -h (print this help information)\n\n",
program_name, program_version, program_name, program_name,
program_name, program_name, program_name, program_name,
program_name, program_name);
printf(
"Commands:\n"
"Either long or short options are allowed.\n"
" --append -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"
" --new -N chain Create a new user-defined chain\n"
" --delete-chain\n"
" -X [chain] Delete a user-defined chain\n"
" --policy -P chain target\n"
" Change policy on chain to target\n"
" --rename-chain\n"
" -E old-chain new-chain\n"
" Change chain name, (moving any references)\n"
"Options:\n"
" --source-ip -s [!] address[/mask]\n"
" source specification\n"
" --destination-ip -d [!] address[/mask]\n"
" destination specification\n"
" --source-mac [!] address[/mask]\n"
" --destination-mac [!] address[/mask]\n"
" --h-length -l length[/mask] hardware length (nr of bytes)\n"
" --opcode code[/mask] operation code (2 bytes)\n"
" --h-type type[/mask] hardware type (2 bytes, hexadecimal)\n"
" --proto-type type[/mask] protocol type (2 bytes)\n"
" --in-interface -i [!] input name[+]\n"
" network interface name ([+] for wildcard)\n"
" --out-interface -o [!] output name[+]\n"
" network interface name ([+] for wildcard)\n"
" --jump -j target\n"
" target for rule (may load target extension)\n"
" --match -m match\n"
" extended match (may load extension)\n"
" --numeric -n numeric output of addresses and ports\n"
" --table -t table table to manipulate (default: `filter')\n"
" --verbose -v verbose mode\n"
" --line-numbers print line numbers when listing\n"
" --exact -x expand numbers (display exact values)\n"
" --modprobe=<command> try to insert modules using this command\n"
" --set-counters PKTS BYTES set the counter during insert/append\n"
"[!] --version -V print package version.\n");
printf(" opcode strings: \n");
for (i = 0; i < NUMOPCODES; i++)
printf(" %d = %s\n", i + 1, opcodes[i]);
printf(
" hardware type string: 1 = Ethernet\n"
" protocol type string: 0x800 = IPv4\n");
/* Print out any special helps. A user might like to be able
to add a --help to the commandline, and see expected
results. So we call help for all matches & targets */
for (t = xtables_targets; t; t = t->next) {
if (strcmp(t->name, "CLASSIFY") && strcmp(t->name, "mangle"))
continue;
printf("\n");
t->help();
}
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] == '+')
xtables_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)
xtables_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 void
add_command(unsigned int *cmd, const int newcmd, const unsigned int othercmds, int invert)
{
if (invert)
xtables_error(PARAMETER_PROBLEM, "unexpected ! flag");
if (*cmd & (~othercmds))
xtables_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
*cmd |= newcmd;
}
static int
check_inverse(const char option[], int *invert, int *optidx, int argc)
{
if (option && strcmp(option, "!") == 0) {
if (*invert)
xtables_error(PARAMETER_PROBLEM,
"Multiple `!' flags not allowed");
*invert = TRUE;
if (optidx) {
*optidx = *optidx+1;
if (argc && *optidx > argc)
xtables_error(PARAMETER_PROBLEM,
"no argument following `!'");
}
return TRUE;
}
return FALSE;
}
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 = xtables_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;
}
return (struct in_addr *) NULL;
}
/*
* 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 ||
(addrptmp = network_to_addr(name)) != NULL) {
addrp = xtables_malloc(sizeof(struct in_addr));
inaddrcpy(addrp, addrptmp);
*naddrs = 1;
return addrp;
}
if ((addrp = host_to_addr(name, naddrs)) != NULL)
return addrp;
xtables_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
}
static struct in_addr *
parse_mask(char *mask)
{
static struct in_addr maskaddr;
struct in_addr *addrp;
unsigned int bits;
if (mask == NULL) {
/* no mask at all defaults to 32 bits */
maskaddr.s_addr = 0xFFFFFFFF;
return &maskaddr;
}
if ((addrp = dotted_to_addr(mask)) != NULL)
/* dotted_to_addr already returns a network byte order addr */
return addrp;
if (string_to_number(mask, 0, 32, &bits) == -1)
xtables_error(PARAMETER_PROBLEM,
"invalid mask `%s' specified", mask);
if (bits != 0) {
maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
return &maskaddr;
}
maskaddr.s_addr = 0L;
return &maskaddr;
}
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 void
parse_interface(const char *arg, char *vianame, unsigned char *mask)
{
int vialen = strlen(arg);
unsigned int i;
memset(mask, 0, IFNAMSIZ);
memset(vianame, 0, IFNAMSIZ);
if (vialen + 1 > IFNAMSIZ)
xtables_error(PARAMETER_PROBLEM,
"interface name `%s' must be shorter than IFNAMSIZ"
" (%i)", arg, IFNAMSIZ-1);
strcpy(vianame, arg);
if (vialen == 0)
memset(mask, 0, IFNAMSIZ);
else if (vianame[vialen - 1] == '+') {
memset(mask, 0xFF, vialen - 1);
memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
/* Don't remove `+' here! -HW */
} else {
/* Include nul-terminator in match */
memset(mask, 0xFF, vialen + 1);
memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
for (i = 0; vianame[i]; i++) {
if (!isalnum(vianame[i])
&& vianame[i] != '_'
&& vianame[i] != '.') {
printf("Warning: wierd character in interface"
" `%s' (No aliases, :, ! or *).\n",
vianame);
break;
}
}
}
}
/* Can't be zero. */
static int
parse_rulenumber(const char *rule)
{
unsigned int rulenum;
if (!xtables_strtoui(rule, NULL, &rulenum, 1, INT_MAX))
xtables_error(PARAMETER_PROBLEM,
"Invalid rule number `%s'", rule);
return rulenum;
}
static const char *
parse_target(const char *targetname)
{
const char *ptr;
if (strlen(targetname) < 1)
xtables_error(PARAMETER_PROBLEM,
"Invalid target name (too short)");
if (strlen(targetname)+1 > sizeof(arpt_chainlabel))
xtables_error(PARAMETER_PROBLEM,
"Invalid target name `%s' (%zu chars max)",
targetname, sizeof(arpt_chainlabel)-1);
for (ptr = targetname; *ptr; ptr++)
if (isspace(*ptr))
xtables_error(PARAMETER_PROBLEM,
"Invalid target name `%s'", targetname);
return targetname;
}
static void
set_option(unsigned int *options, unsigned int option, u_int16_t *invflg,
int invert)
{
if (*options & option)
xtables_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
opt2char(option));
*options |= option;
if (invert) {
unsigned int i;
for (i = 0; 1 << i != option; i++);
if (!inverse_for_options[i])
xtables_error(PARAMETER_PROBLEM,
"cannot have ! before -%c",
opt2char(option));
*invflg |= inverse_for_options[i];
}
}
static int
list_entries(struct nft_handle *h, const char *chain, const char *table,
int rulenum, int verbose, int numeric, int expanded,
int linenumbers)
{
unsigned int format;
format = FMT_OPTIONS;
if (!verbose)
format |= FMT_NOCOUNTS;
else
format |= FMT_VIA;
if (numeric)
format |= FMT_NUMERIC;
if (!expanded)
format |= FMT_KILOMEGAGIGA;
if (linenumbers)
format |= FMT_LINENUMBERS;
return nft_rule_list(h, chain, table, rulenum, format);
}
static struct xtables_target *command_jump(struct arpt_entry *fw,
const char *jumpto)
{
struct xtables_target *target;
size_t size;
/* XTF_TRY_LOAD (may be chain name) */
target = xtables_find_target(jumpto, XTF_TRY_LOAD);
if (!target)
return NULL;
size = XT_ALIGN(sizeof(struct xt_entry_target))
+ target->size;
target->t = xtables_calloc(1, size);
target->t->u.target_size = size;
strncpy(target->t->u.user.name, jumpto, sizeof(target->t->u.user.name));
target->t->u.user.name[sizeof(target->t->u.user.name)-1] = '\0';
target->t->u.user.revision = target->revision;
xs_init_target(target);
if (target->x6_options != NULL)
opts = xtables_options_xfrm(arptables_globals.orig_opts,
opts, target->x6_options,
&target->option_offset);
else
opts = xtables_merge_options(arptables_globals.orig_opts,
opts, target->extra_opts,
&target->option_offset);
return target;
}
static int
append_entry(struct nft_handle *h,
const char *chain,
const char *table,
struct arptables_command_state *cs,
int rulenum,
unsigned int nsaddrs,
const struct in_addr saddrs[],
unsigned int ndaddrs,
const struct in_addr daddrs[],
bool verbose, bool append)
{
unsigned int i, j;
int ret = 1;
for (i = 0; i < nsaddrs; i++) {
cs->fw.arp.src.s_addr = saddrs[i].s_addr;
for (j = 0; j < ndaddrs; j++) {
cs->fw.arp.tgt.s_addr = daddrs[j].s_addr;
if (append) {
ret = nft_rule_append(h, chain, table, cs, 0,
verbose);
} else {
ret = nft_rule_insert(h, chain, table, cs,
rulenum, verbose);
}
}
}
return ret;
}
static int
replace_entry(const char *chain,
const char *table,
struct arptables_command_state *cs,
unsigned int rulenum,
const struct in_addr *saddr,
const struct in_addr *daddr,
bool verbose, struct nft_handle *h)
{
cs->fw.arp.src.s_addr = saddr->s_addr;
cs->fw.arp.tgt.s_addr = daddr->s_addr;
return nft_rule_replace(h, chain, table, cs, rulenum, verbose);
}
static int
delete_entry(const char *chain,
const char *table,
struct arptables_command_state *cs,
unsigned int nsaddrs,
const struct in_addr saddrs[],
unsigned int ndaddrs,
const struct in_addr daddrs[],
bool verbose, struct nft_handle *h)
{
unsigned int i, j;
int ret = 1;
for (i = 0; i < nsaddrs; i++) {
cs->fw.arp.src.s_addr = saddrs[i].s_addr;
for (j = 0; j < ndaddrs; j++) {
cs->fw.arp.tgt.s_addr = daddrs[j].s_addr;
ret = nft_rule_delete(h, chain, table, cs, verbose);
}
}
return ret;
}
int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
{
struct arptables_command_state cs;
int invert = 0;
unsigned int nsaddrs = 0, ndaddrs = 0;
struct in_addr *saddrs = NULL, *daddrs = NULL;
int c, verbose = 0;
const char *chain = NULL;
const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
const char *policy = NULL, *newname = NULL;
unsigned int rulenum = 0, options = 0, command = 0;
const char *pcnt = NULL, *bcnt = NULL;
int ret = 1;
struct xtables_target *t;
memset(&cs, 0, sizeof(cs));
cs.jumpto = "";
opts = original_opts;
global_option_offset = 0;
xtables_globals.orig_opts = original_opts;
/* re-set optind to 0 in case do_command gets called
* a second time */
optind = 0;
for (t = xtables_targets; t; t = t->next) {
t->tflags = 0;
t->used = 0;
}
/* Suppress error messages: we may add new options if we
demand-load a protocol. */
opterr = 0;
while ((c = getopt_long(argc, argv,
"-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:l:i:vnt:m:c:",
opts, NULL)) != -1) {
switch (c) {
/*
* Command selection
*/
case 'A':
add_command(&command, CMD_APPEND, CMD_NONE,
invert);
chain = optarg;
break;
case 'D':
add_command(&command, CMD_DELETE, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!') {
rulenum = parse_rulenumber(argv[optind++]);
command = CMD_DELETE_NUM;
}
break;
case 'R':
add_command(&command, CMD_REPLACE, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
rulenum = parse_rulenumber(argv[optind++]);
else
xtables_error(PARAMETER_PROBLEM,
"-%c requires a rule number",
cmd2char(CMD_REPLACE));
break;
case 'I':
add_command(&command, CMD_INSERT, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
rulenum = parse_rulenumber(argv[optind++]);
else rulenum = 1;
break;
case 'L':
add_command(&command, CMD_LIST, CMD_ZERO,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
chain = argv[optind++];
break;
case 'F':
add_command(&command, CMD_FLUSH, CMD_NONE,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
chain = argv[optind++];
break;
case 'Z':
add_command(&command, CMD_ZERO, CMD_LIST,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
chain = argv[optind++];
break;
case 'N':
if (optarg && *optarg == '-')
xtables_error(PARAMETER_PROBLEM,
"chain name not allowed to start "
"with `-'\n");
if (xtables_find_target(optarg, XTF_TRY_LOAD))
xtables_error(PARAMETER_PROBLEM,
"chain name may not clash "
"with target name\n");
add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
invert);
chain = optarg;
break;
case 'X':
add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
chain = argv[optind++];
break;
case 'E':
add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
newname = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM,
"-%c requires old-chain-name and "
"new-chain-name",
cmd2char(CMD_RENAME_CHAIN));
break;
case 'P':
add_command(&command, CMD_SET_POLICY, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
policy = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM,
"-%c requires a chain and a policy",
cmd2char(CMD_SET_POLICY));
break;
case 'h':
if (!optarg)
optarg = argv[optind];
exit_printhelp();
break;
case 's':
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_S_IP, &cs.fw.arp.invflags,
invert);
shostnetworkmask = argv[optind-1];
break;
case 'd':
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_D_IP, &cs.fw.arp.invflags,
invert);
dhostnetworkmask = argv[optind-1];
break;
case 2:/* src-mac */
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_S_MAC, &cs.fw.arp.invflags,
invert);
if (getmac_and_mask(argv[optind - 1],
cs.fw.arp.src_devaddr.addr, cs.fw.arp.src_devaddr.mask))
xtables_error(PARAMETER_PROBLEM, "Problem with specified "
"source mac");
break;
case 3:/* dst-mac */
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_D_MAC, &cs.fw.arp.invflags,
invert);
if (getmac_and_mask(argv[optind - 1],
cs.fw.arp.tgt_devaddr.addr, cs.fw.arp.tgt_devaddr.mask))
xtables_error(PARAMETER_PROBLEM, "Problem with specified "
"destination mac");
break;
case 'l':/* hardware length */
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_H_LENGTH, &cs.fw.arp.invflags,
invert);
getlength_and_mask(argv[optind - 1], &cs.fw.arp.arhln,
&cs.fw.arp.arhln_mask);
if (cs.fw.arp.arhln != 6) {
xtables_error(PARAMETER_PROBLEM,
"Only harware address length of"
" 6 is supported currently.");
}
break;
case 8:/* protocol length */
xtables_error(PARAMETER_PROBLEM, "not supported");
/*
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_P_LENGTH, &cs.fw.arp.invflags,
invert);
getlength_and_mask(argv[optind - 1], &cs.fw.arp.arpln,
&cs.fw.arp.arpln_mask);
break;
*/
case 4:/* opcode */
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_OPCODE, &cs.fw.arp.invflags,
invert);
if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arpop,
&cs.fw.arp.arpop_mask, 10)) {
int i;
for (i = 0; i < NUMOPCODES; i++)
if (!strcasecmp(opcodes[i], optarg))
break;
if (i == NUMOPCODES)
xtables_error(PARAMETER_PROBLEM, "Problem with specified opcode");
cs.fw.arp.arpop = htons(i+1);
}
break;
case 5:/* h-type */
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_H_TYPE, &cs.fw.arp.invflags,
invert);
if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arhrd,
&cs.fw.arp.arhrd_mask, 16)) {
if (strcasecmp(argv[optind-1], "Ethernet"))
xtables_error(PARAMETER_PROBLEM, "Problem with specified hardware type");
cs.fw.arp.arhrd = htons(1);
}
break;
case 6:/* proto-type */
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_P_TYPE, &cs.fw.arp.invflags,
invert);
if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arpro,
&cs.fw.arp.arpro_mask, 0)) {
if (strcasecmp(argv[optind-1], "ipv4"))
xtables_error(PARAMETER_PROBLEM, "Problem with specified protocol type");
cs.fw.arp.arpro = htons(0x800);
}
break;
case 'j':
set_option(&options, OPT_JUMP, &cs.fw.arp.invflags,
invert);
cs.jumpto = parse_target(optarg);
cs.target = command_jump(&cs.fw, cs.jumpto);
break;
case 'i':
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_VIANAMEIN, &cs.fw.arp.invflags,
invert);
parse_interface(argv[optind-1],
cs.fw.arp.iniface,
cs.fw.arp.iniface_mask);
/* cs.fw.nfcache |= NFC_IP_IF_IN; */
break;
case 'o':
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_VIANAMEOUT, &cs.fw.arp.invflags,
invert);
parse_interface(argv[optind-1],
cs.fw.arp.outiface,
cs.fw.arp.outiface_mask);
/* cs.fw.nfcache |= NFC_IP_IF_OUT; */
break;
case 'v':
if (!verbose)
set_option(&options, OPT_VERBOSE,
&cs.fw.arp.invflags, invert);
verbose++;
break;
case 'm': /*{
size_t size;
if (invert)
exit_error(PARAMETER_PROBLEM,
"unexpected ! flag before --match");
m = find_match(optarg, LOAD_MUST_SUCCEED);
size = ARPT_ALIGN(sizeof(struct arpt_entry_match))
+ m->size;
m->m = fw_calloc(1, size);
m->m->u.match_size = size;
strcpy(m->m->u.user.name, m->name);
m->init(m->m, &fw.nfcache);
opts = merge_options(opts, m->extra_opts, &m->option_offset);
}*/
break;
case 'n':
set_option(&options, OPT_NUMERIC, &cs.fw.arp.invflags,
invert);
break;
case 't':
if (invert)
xtables_error(PARAMETER_PROBLEM,
"unexpected ! flag before --table");
*table = argv[optind-1];
break;
case 'V':
if (invert)
printf("Not %s ;-)\n", program_version);
else
printf("%s v%s\n",
program_name, program_version);
exit(0);
case '0':
set_option(&options, OPT_LINENUMBERS, &cs.fw.arp.invflags,
invert);
break;
case 'M':
//modprobe = optarg;
break;
case 'c':
set_option(&options, OPT_COUNTERS, &cs.fw.arp.invflags,
invert);
pcnt = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
bcnt = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM,
"-%c requires packet and byte counter",
opt2char(OPT_COUNTERS));
if (sscanf(pcnt, "%llu", &cs.fw.counters.pcnt) != 1)
xtables_error(PARAMETER_PROBLEM,
"-%c packet counter not numeric",
opt2char(OPT_COUNTERS));
if (sscanf(bcnt, "%llu", &cs.fw.counters.bcnt) != 1)
xtables_error(PARAMETER_PROBLEM,
"-%c byte counter not numeric",
opt2char(OPT_COUNTERS));
break;
case 1: /* non option */
if (optarg[0] == '!' && optarg[1] == '\0') {
if (invert)
xtables_error(PARAMETER_PROBLEM,
"multiple consecutive ! not"
" allowed");
invert = TRUE;
optarg[0] = '\0';
continue;
}
printf("Bad argument `%s'\n", optarg);
exit_tryhelp(2);
default:
if (cs.target) {
xtables_option_tpcall(c, argv,
invert, cs.target, &cs.fw);
}
break;
}
invert = FALSE;
}
if (cs.target)
xtables_option_tfcall(cs.target);
if (optind < argc)
xtables_error(PARAMETER_PROBLEM,
"unknown arguments found on commandline");
if (!command)
xtables_error(PARAMETER_PROBLEM, "no command specified");
if (invert)
xtables_error(PARAMETER_PROBLEM,
"nothing appropriate following !");
if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
if (!(options & OPT_D_IP))
dhostnetworkmask = "0.0.0.0/0";
if (!(options & OPT_S_IP))
shostnetworkmask = "0.0.0.0/0";
}
if (shostnetworkmask)
parse_hostnetworkmask(shostnetworkmask, &saddrs,
&(cs.fw.arp.smsk), &nsaddrs);
if (dhostnetworkmask)
parse_hostnetworkmask(dhostnetworkmask, &daddrs,
&(cs.fw.arp.tmsk), &ndaddrs);
if ((nsaddrs > 1 || ndaddrs > 1) &&
(cs.fw.arp.invflags & (ARPT_INV_SRCIP | ARPT_INV_TGTIP)))
xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple"
" source or destination IP addresses");
if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
"specify a unique address");
generic_opt_check(command, options);
if (chain && strlen(chain) > ARPT_FUNCTION_MAXNAMELEN)
xtables_error(PARAMETER_PROBLEM,
"chain name `%s' too long (must be under %i chars)",
chain, ARPT_FUNCTION_MAXNAMELEN);
if (nft_init(h, xtables_arp) < 0)
xtables_error(OTHER_PROBLEM,
"Could not initialize nftables layer.");
h->ops = nft_family_ops_lookup(h->family);
if (h->ops == NULL)
xtables_error(PARAMETER_PROBLEM, "Unknown family");
if (command == CMD_APPEND
|| command == CMD_DELETE
|| command == CMD_INSERT
|| command == CMD_REPLACE) {
if (strcmp(chain, "PREROUTING") == 0
|| strcmp(chain, "INPUT") == 0) {
/* -o not valid with incoming packets. */
if (options & OPT_VIANAMEOUT)
xtables_error(PARAMETER_PROBLEM,
"Can't use -%c with %s\n",
opt2char(OPT_VIANAMEOUT),
chain);
}
if (strcmp(chain, "POSTROUTING") == 0
|| strcmp(chain, "OUTPUT") == 0) {
/* -i not valid with outgoing packets */
if (options & OPT_VIANAMEIN)
xtables_error(PARAMETER_PROBLEM,
"Can't use -%c with %s\n",
opt2char(OPT_VIANAMEIN),
chain);
}
if (!cs.target && strlen(cs.jumpto) != 0) {
size_t size;
cs.target = xtables_find_target(XT_STANDARD_TARGET,
XTF_LOAD_MUST_SUCCEED);
size = sizeof(struct arpt_entry_target) + cs.target->size;
cs.target->t = xtables_calloc(1, size);
cs.target->t->u.target_size = size;
strcpy(cs.target->t->u.user.name, cs.jumpto);
}
}
switch (command) {
case CMD_APPEND:
ret = append_entry(h, chain, *table, &cs, 0,
nsaddrs, saddrs, ndaddrs, daddrs,
options&OPT_VERBOSE, true);
break;
case CMD_DELETE:
ret = delete_entry(chain, *table, &cs,
nsaddrs, saddrs, ndaddrs, daddrs,
options&OPT_VERBOSE, h);
break;
case CMD_DELETE_NUM:
ret = nft_rule_delete_num(h, chain, *table, rulenum - 1, verbose);
break;
case CMD_REPLACE:
ret = replace_entry(chain, *table, &cs, rulenum - 1,
saddrs, daddrs, options&OPT_VERBOSE, h);
break;
case CMD_INSERT:
ret = append_entry(h, chain, *table, &cs, rulenum - 1,
nsaddrs, saddrs, ndaddrs, daddrs,
options&OPT_VERBOSE, false);
break;
case CMD_LIST:
ret = list_entries(h, chain, *table,
rulenum,
options&OPT_VERBOSE,
options&OPT_NUMERIC,
/*options&OPT_EXPANDED*/0,
options&OPT_LINENUMBERS);
break;
case CMD_FLUSH:
ret = nft_rule_flush(h, chain, *table);
break;
case CMD_ZERO:
ret = nft_chain_zero_counters(h, chain, *table);
break;
case CMD_LIST|CMD_ZERO:
ret = list_entries(h, chain, *table, rulenum,
options&OPT_VERBOSE,
options&OPT_NUMERIC,
/*options&OPT_EXPANDED*/0,
options&OPT_LINENUMBERS);
if (ret)
ret = nft_chain_zero_counters(h, chain, *table);
break;
case CMD_NEW_CHAIN:
ret = nft_chain_user_add(h, chain, *table);
break;
case CMD_DELETE_CHAIN:
ret = nft_chain_user_del(h, chain, *table);
break;
case CMD_RENAME_CHAIN:
ret = nft_chain_user_rename(h, chain, *table, newname);
break;
case CMD_SET_POLICY:
ret = nft_chain_set(h, *table, chain, policy, NULL);
if (ret < 0)
xtables_error(PARAMETER_PROBLEM, "Wrong policy `%s'\n",
policy);
break;
default:
/* We should never reach this... */
exit_tryhelp(2);
}
/* if (verbose > 1)
dump_entries(*handle);*/
return ret;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xshared.h"
#include "xtables-multi.h"
static const struct subcommand multi_subcommands[] = {
{"iptables-xml", iptables_xml_main},
{"xml", iptables_xml_main},
{"iptables", xtables_ip4_main},
{"iptables-compat", xtables_ip4_main},
{"main4", xtables_ip4_main},
{"save4", xtables_ip4_save_main},
{"restore4", xtables_ip4_restore_main},
{"iptables-save", xtables_ip4_save_main},
{"iptables-restore", xtables_ip4_restore_main},
{"iptables-compat-save", xtables_ip4_save_main},
{"iptables-compat-restore", xtables_ip4_restore_main},
{"ip6tables", xtables_ip6_main},
{"ip6tables-compat", xtables_ip6_main},
{"main6", xtables_ip6_main},
{"save6", xtables_ip6_save_main},
{"restore6", xtables_ip6_restore_main},
{"ip6tables-save", xtables_ip6_save_main},
{"ip6tables-restore", xtables_ip6_restore_main},
{"ip6tables-compat-save", xtables_ip6_save_main},
{"ip6tables-compat-restore", xtables_ip6_restore_main},
{"arptables", xtables_arp_main},
{"arptables-compat", xtables_arp_main},
{"ebtables-compat", xtables_eb_main},
{NULL},
};
int main(int argc, char **argv)
{
return subcmd_main(argc, argv, multi_subcommands);
}
%{
/*
* (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* 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 software has been sponsored by Sophos Astaro <http://www.sophos.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <libiptc/linux_list.h>
#include <libnftnl/table.h>
#include <libnftnl/chain.h>
#include <netinet/in.h>
#include <linux/netfilter.h>
extern char *yytext;
extern int yylineno;
static LIST_HEAD(xtables_stack);
struct stack_elem {
struct list_head head;
int token;
size_t size;
char data[];
};
static void *stack_push(int token, size_t size)
{
struct stack_elem *e;
e = calloc(1, sizeof(struct stack_elem) + size);
e->token = token;
e->size = size;
list_add(&e->head, &xtables_stack);
return e->data;
}
static struct stack_elem *stack_pop(void)
{
struct stack_elem *e;
e = list_entry(xtables_stack.next, struct stack_elem, head);
if (&e->head == &xtables_stack)
return NULL;
list_del(&e->head);
return e;
}
static inline void stack_put_i32(void *data, int value)
{
memcpy(data, &value, sizeof(int));
}
static inline void stack_put_str(void *data, const char *str)
{
memcpy(data, str, strlen(str));
}
static void stack_free(struct stack_elem *e)
{
free(e);
}
%}
%union {
int val;
char *string;
}
%token T_FAMILY
%token T_TABLE
%token T_CHAIN
%token T_HOOK
%token T_PRIO
%token <string> T_STRING
%token <val> T_INTEGER
%%
configfile :
| lines
;
lines : line
| lines line
;
line : family
;
family : T_FAMILY T_STRING '{' tables '}'
{
void *data = stack_push(T_FAMILY, strlen($2)+1);
stack_put_str(data, $2);
}
;
tables : table
| tables table
;
table : T_TABLE T_STRING '{' chains '}'
{
/* added in reverse order to pop it in order */
void *data = stack_push(T_TABLE, strlen($2)+1);
stack_put_str(data, $2);
}
;
chains : chain
| chains chain
;
chain : T_CHAIN T_STRING T_HOOK T_STRING T_PRIO T_INTEGER
{
/* added in reverse order to pop it in order */
void *data = stack_push(T_PRIO, sizeof(int32_t));
stack_put_i32(data, $6);
data = stack_push(T_HOOK, strlen($4)+1);
stack_put_str(data, $4);
data = stack_push(T_CHAIN, strlen($2)+1);
stack_put_str(data, $2);
}
;
%%
int __attribute__((noreturn))
yyerror(char *msg)
{
fprintf(stderr, "parsing config file in line (%d), symbol '%s': %s\n",
yylineno, yytext, msg);
exit(EXIT_FAILURE);
}
static int hooknametonum(const char *hookname)
{
if (strcmp(hookname, "NF_INET_LOCAL_IN") == 0)
return NF_INET_LOCAL_IN;
else if (strcmp(hookname, "NF_INET_FORWARD") == 0)
return NF_INET_FORWARD;
else if (strcmp(hookname, "NF_INET_LOCAL_OUT") == 0)
return NF_INET_LOCAL_OUT;
else if (strcmp(hookname, "NF_INET_PRE_ROUTING") == 0)
return NF_INET_PRE_ROUTING;
else if (strcmp(hookname, "NF_INET_POST_ROUTING") == 0)
return NF_INET_POST_ROUTING;
return -1;
}
static int32_t familytonumber(const char *family)
{
if (strcmp(family, "ipv4") == 0)
return AF_INET;
else if (strcmp(family, "ipv6") == 0)
return AF_INET6;
return -1;
}
int xtables_config_parse(char *filename, struct nftnl_table_list *table_list,
struct nftnl_chain_list *chain_list)
{
FILE *fp;
struct stack_elem *e;
struct nftnl_table *table = NULL;
struct nftnl_chain *chain = NULL;
int prio = 0;
int32_t family = 0;
fp = fopen(filename, "r");
if (!fp)
return -1;
yyrestart(fp);
yyparse();
fclose(fp);
for (e = stack_pop(); e != NULL; e = stack_pop()) {
switch(e->token) {
case T_FAMILY:
family = familytonumber(e->data);
if (family == -1)
return -1;
break;
case T_TABLE:
table = nftnl_table_alloc();
if (table == NULL)
return -1;
nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, family);
nftnl_table_set(table, NFTNL_TABLE_NAME, e->data);
/* This is intentionally prepending, instead of
* appending, since the elements in the stack are in
* the reverse order that chains appear in the
* configuration file.
*/
nftnl_table_list_add(table, table_list);
break;
case T_PRIO:
memcpy(&prio, e->data, sizeof(int32_t));
break;
case T_CHAIN:
chain = nftnl_chain_alloc();
if (chain == NULL)
return -1;
nftnl_chain_set(chain, NFTNL_CHAIN_TABLE,
(char *)nftnl_table_get(table, NFTNL_TABLE_NAME));
nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY,
nftnl_table_get_u32(table, NFTNL_TABLE_FAMILY));
nftnl_chain_set_s32(chain, NFTNL_CHAIN_PRIO, prio);
nftnl_chain_set(chain, NFTNL_CHAIN_NAME, e->data);
/* Intentionally prepending, instead of appending */
nftnl_chain_list_add(chain, chain_list);
break;
case T_HOOK:
nftnl_chain_set_u32(chain, NFTNL_CHAIN_HOOKNUM,
hooknametonum(e->data));
break;
default:
printf("unknown token type %d\n", e->token);
break;
}
stack_free(e);
}
return 0;
}
%{
/*
* (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* 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 software has been sponsored by Sophos Astaro <http://www.sophos.com>
*/
#include <string.h>
#include "xtables-config-parser.h"
%}
%option yylineno
%option noinput
%option nounput
ws [ \t]+
comment #.*$
nl [\n\r]
is_on [o|O][n|N]
is_off [o|O][f|F][f|F]
integer [\-\+]?[0-9]+
string [a-zA-Z][a-zA-Z0-9\.\-\_]*
%%
"family" { return T_FAMILY; }
"table" { return T_TABLE; }
"chain" { return T_CHAIN; }
"hook" { return T_HOOK; }
"prio" { return T_PRIO; }
{integer} { yylval.val = atoi(yytext); return T_INTEGER; }
{string} { yylval.string = strdup(yytext); return T_STRING; }
{comment} ;
{ws} ;
{nl} ;
<<EOF>> { yyterminate(); }
. { return yytext[0]; }
%%
int
yywrap()
{
return 1;
}
/*
* Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
*
* Based on the ipchains code by Paul Russell and Michael Neuling
*
* (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
* Paul 'Rusty' Russell <rusty@rustcorp.com.au>
* Marc Boucher <marc+nf@mbsi.ca>
* James Morris <jmorris@intercode.com.au>
* Harald Welte <laforge@gnumonks.org>
* Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* arptables -- IP firewall administration for kernels with
* firewall table (aimed for the 2.3 kernels)
*
* See the accompanying manual page arptables(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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <xtables.h>
#include <iptables.h>
#include "nft.h"
#include "xtables-multi.h"
extern struct xtables_globals ebtables_globals;
int xtables_eb_main(int argc, char *argv[])
{
int ret;
char *table = "filter";
struct nft_handle h = {
.family = NFPROTO_BRIDGE,
};
ebtables_globals.program_name = "ebtables";
ret = xtables_init_all(&ebtables_globals, NFPROTO_BRIDGE);
if (ret < 0) {
fprintf(stderr, "%s/%s Failed to initialize ebtables-compat\n",
ebtables_globals.program_name,
ebtables_globals.program_version);
exit(1);
}
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensionsb();
#endif
ret = do_commandeb(&h, argc, argv, &table);
if (ret)
ret = nft_commit(&h);
if (!ret)
fprintf(stderr, "%s\n", nft_strerror(errno));
exit(!ret);
}
/*
* ebtables.c, v2.0 July 2002
*
* Author: Bart De Schuymer
*
* This code was stongly inspired on the iptables code which is
* Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
*
* 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.
*/
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <inttypes.h>
#include <signal.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <iptables.h>
#include <xtables.h>
#include <linux/netfilter_bridge.h>
#include <linux/netfilter/nf_tables.h>
#include <ebtables/ethernetdb.h>
#include <libiptc/libxtc.h>
#include "xshared.h"
#include "nft.h"
#include "nft-bridge.h"
/*
* From include/ebtables_u.h
*/
#define EXEC_STYLE_PRG 0
#define EXEC_STYLE_DAEMON 1
#define ebt_check_option2(flags, mask) EBT_CHECK_OPTION(flags, mask)
/*
* From useful_functions.c
*/
/* 0: default
* 1: the inverse '!' of the option has already been specified */
int ebt_invert = 0;
unsigned char eb_mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0};
unsigned char eb_msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0};
unsigned char eb_mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
unsigned char eb_msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
unsigned char eb_mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
unsigned char eb_msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
unsigned char eb_mac_type_bridge_group[ETH_ALEN] = {0x01,0x80,0xc2,0,0,0};
unsigned char eb_msk_type_bridge_group[ETH_ALEN] = {255,255,255,255,255,255};
int ebt_get_mac_and_mask(const char *from, unsigned char *to,
unsigned char *mask)
{
char *p;
int i;
struct ether_addr *addr = NULL;
if (strcasecmp(from, "Unicast") == 0) {
memcpy(to, eb_mac_type_unicast, ETH_ALEN);
memcpy(mask, eb_msk_type_unicast, ETH_ALEN);
return 0;
}
if (strcasecmp(from, "Multicast") == 0) {
memcpy(to, eb_mac_type_multicast, ETH_ALEN);
memcpy(mask, eb_msk_type_multicast, ETH_ALEN);
return 0;
}
if (strcasecmp(from, "Broadcast") == 0) {
memcpy(to, eb_mac_type_broadcast, ETH_ALEN);
memcpy(mask, eb_msk_type_broadcast, ETH_ALEN);
return 0;
}
if (strcasecmp(from, "BGA") == 0) {
memcpy(to, eb_mac_type_bridge_group, ETH_ALEN);
memcpy(mask, eb_msk_type_bridge_group, ETH_ALEN);
return 0;
}
if ( (p = strrchr(from, '/')) != NULL) {
*p = '\0';
if (!(addr = ether_aton(p + 1)))
return -1;
memcpy(mask, addr, ETH_ALEN);
} else
memset(mask, 0xff, ETH_ALEN);
if (!(addr = ether_aton(from)))
return -1;
memcpy(to, addr, ETH_ALEN);
for (i = 0; i < ETH_ALEN; i++)
to[i] &= mask[i];
return 0;
}
static int ebt_check_inverse2(const char option[], int argc, char **argv)
{
if (!option)
return ebt_invert;
if (strcmp(option, "!") == 0) {
if (ebt_invert == 1)
xtables_error(PARAMETER_PROBLEM,
"Double use of '!' not allowed");
if (optind >= argc)
optarg = NULL;
else
optarg = argv[optind];
optind++;
ebt_invert = 1;
return 1;
}
return ebt_invert;
}
/*
* Glue code to use libxtables
*/
static int parse_rule_number(const char *rule)
{
unsigned int rule_nr;
if (!xtables_strtoui(rule, NULL, &rule_nr, 1, INT_MAX))
xtables_error(PARAMETER_PROBLEM,
"Invalid rule number `%s'", rule);
return rule_nr;
}
static const char *
parse_target(const char *targetname)
{
const char *ptr;
if (strlen(targetname) < 1)
xtables_error(PARAMETER_PROBLEM,
"Invalid target name (too short)");
if (strlen(targetname)+1 > EBT_CHAIN_MAXNAMELEN)
xtables_error(PARAMETER_PROBLEM,
"Invalid target '%s' (%d chars max)",
targetname, EBT_CHAIN_MAXNAMELEN);
for (ptr = targetname; *ptr; ptr++)
if (isspace(*ptr))
xtables_error(PARAMETER_PROBLEM,
"Invalid target name `%s'", targetname);
return targetname;
}
static int
append_entry(struct nft_handle *h,
const char *chain,
const char *table,
struct ebtables_command_state *cs,
int rule_nr,
bool verbose, bool append)
{
int ret = 1;
if (append)
ret = nft_rule_append(h, chain, table, cs, 0, verbose);
else
ret = nft_rule_insert(h, chain, table, cs, rule_nr, verbose);
return ret;
}
static int
delete_entry(struct nft_handle *h,
const char *chain,
const char *table,
struct ebtables_command_state *cs,
int rule_nr,
int rule_nr_end,
bool verbose)
{
int ret = 1;
if (rule_nr == -1)
ret = nft_rule_delete(h, chain, table, cs, verbose);
else {
do {
ret = nft_rule_delete_num(h, chain, table,
rule_nr, verbose);
rule_nr++;
} while (rule_nr < rule_nr_end);
}
return ret;
}
static int get_current_chain(const char *chain)
{
if (strcmp(chain, "PREROUTING") == 0)
return NF_BR_PRE_ROUTING;
else if (strcmp(chain, "INPUT") == 0)
return NF_BR_LOCAL_IN;
else if (strcmp(chain, "FORWARD") == 0)
return NF_BR_FORWARD;
else if (strcmp(chain, "OUTPUT") == 0)
return NF_BR_LOCAL_OUT;
else if (strcmp(chain, "POSTROUTING") == 0)
return NF_BR_POST_ROUTING;
return -1;
}
/*
* The original ebtables parser
*/
/* Checks whether a command has already been specified */
#define OPT_COMMANDS (flags & OPT_COMMAND || flags & OPT_ZERO)
#define OPT_COMMAND 0x01
#define OPT_TABLE 0x02
#define OPT_IN 0x04
#define OPT_OUT 0x08
#define OPT_JUMP 0x10
#define OPT_PROTOCOL 0x20
#define OPT_SOURCE 0x40
#define OPT_DEST 0x80
#define OPT_ZERO 0x100
#define OPT_LOGICALIN 0x200
#define OPT_LOGICALOUT 0x400
#define OPT_KERNELDATA 0x800 /* This value is also defined in ebtablesd.c */
#define OPT_COUNT 0x1000 /* This value is also defined in libebtc.c */
#define OPT_CNT_INCR 0x2000 /* This value is also defined in libebtc.c */
#define OPT_CNT_DECR 0x4000 /* This value is also defined in libebtc.c */
/* Default command line options. Do not mess around with the already
* assigned numbers unless you know what you are doing */
static struct option ebt_original_options[] =
{
{ "append" , required_argument, 0, 'A' },
{ "insert" , required_argument, 0, 'I' },
{ "delete" , required_argument, 0, 'D' },
{ "list" , optional_argument, 0, 'L' },
{ "Lc" , no_argument , 0, 4 },
{ "Ln" , no_argument , 0, 5 },
{ "Lx" , no_argument , 0, 6 },
{ "Lmac2" , no_argument , 0, 12 },
{ "zero" , optional_argument, 0, 'Z' },
{ "flush" , optional_argument, 0, 'F' },
{ "policy" , required_argument, 0, 'P' },
{ "in-interface" , required_argument, 0, 'i' },
{ "in-if" , required_argument, 0, 'i' },
{ "logical-in" , required_argument, 0, 2 },
{ "logical-out" , required_argument, 0, 3 },
{ "out-interface" , required_argument, 0, 'o' },
{ "out-if" , required_argument, 0, 'o' },
{ "version" , no_argument , 0, 'V' },
{ "help" , no_argument , 0, 'h' },
{ "jump" , required_argument, 0, 'j' },
{ "set-counters" , required_argument, 0, 'c' },
{ "change-counters", required_argument, 0, 'C' },
{ "proto" , required_argument, 0, 'p' },
{ "protocol" , required_argument, 0, 'p' },
{ "db" , required_argument, 0, 'b' },
{ "source" , required_argument, 0, 's' },
{ "src" , required_argument, 0, 's' },
{ "destination" , required_argument, 0, 'd' },
{ "dst" , required_argument, 0, 'd' },
{ "table" , required_argument, 0, 't' },
{ "modprobe" , required_argument, 0, 'M' },
{ "new-chain" , required_argument, 0, 'N' },
{ "rename-chain" , required_argument, 0, 'E' },
{ "delete-chain" , optional_argument, 0, 'X' },
{ "atomic-init" , no_argument , 0, 7 },
{ "atomic-commit" , no_argument , 0, 8 },
{ "atomic-file" , required_argument, 0, 9 },
{ "atomic-save" , no_argument , 0, 10 },
{ "init-table" , no_argument , 0, 11 },
{ "concurrent" , no_argument , 0, 13 },
{ 0 }
};
static void __attribute__((__noreturn__,format(printf,2,3)))
ebt_print_error(enum xtables_exittype status, const char *format, ...)
{
va_list l;
va_start(l, format);
vfprintf(stderr, format, l);
fprintf(stderr, ".\n");
va_end(l);
exit(-1);
}
struct xtables_globals ebtables_globals = {
.option_offset = 0,
.program_version = IPTABLES_VERSION,
.orig_opts = ebt_original_options,
.exit_err = ebt_print_error,
.compat_rev = nft_compatible_revision,
};
#define opts ebtables_globals.opts
#define prog_name ebtables_globals.program_name
#define prog_vers ebtables_globals.program_version
/*
* From libebtc.c
*/
/* Prints all registered extensions */
static void ebt_list_extensions(const struct xtables_target *t,
const struct xtables_rule_match *m)
{
printf("%s v%s\n", prog_name, prog_vers);
printf("Loaded userspace extensions:\n");
/*printf("\nLoaded tables:\n");
while (tbl) {
printf("%s\n", tbl->name);
tbl = tbl->next;
}*/
printf("\nLoaded targets:\n");
for (t = xtables_targets; t; t = t->next) {
printf("%s\n", t->name);
}
printf("\nLoaded matches:\n");
for (; m != NULL; m = m->next)
printf("%s\n", m->match->name);
/*printf("\nLoaded watchers:\n");
while (w) {
printf("%s\n", w->name);
w = w->next;
}*/
}
#define OPTION_OFFSET 256
static struct option *merge_options(struct option *oldopts,
const struct option *newopts,
unsigned int *options_offset)
{
unsigned int num_old, num_new, i;
struct option *merge;
if (!newopts || !oldopts || !options_offset)
return oldopts;
for (num_old = 0; oldopts[num_old].name; num_old++);
for (num_new = 0; newopts[num_new].name; num_new++);
ebtables_globals.option_offset += OPTION_OFFSET;
*options_offset = ebtables_globals.option_offset;
merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
if (!merge)
return NULL;
memcpy(merge, oldopts, num_old * sizeof(struct option));
for (i = 0; i < num_new; i++) {
merge[num_old + i] = newopts[i];
merge[num_old + i].val += *options_offset;
}
memset(merge + num_old + num_new, 0, sizeof(struct option));
/* Only free dynamically allocated stuff */
if (oldopts != ebt_original_options)
free(oldopts);
return merge;
}
/*
* More glue code.
*/
static struct xtables_target *command_jump(struct ebtables_command_state *cs,
const char *jumpto)
{
struct xtables_target *target;
size_t size;
/* XTF_TRY_LOAD (may be chain name) */
target = xtables_find_target(jumpto, XTF_TRY_LOAD);
if (!target)
return NULL;
size = XT_ALIGN(sizeof(struct xt_entry_target))
+ target->size;
target->t = xtables_calloc(1, size);
target->t->u.target_size = size;
strncpy(target->t->u.user.name, jumpto, sizeof(target->t->u.user.name));
target->t->u.user.name[sizeof(target->t->u.user.name)-1] = '\0';
target->t->u.user.revision = target->revision;
xs_init_target(target);
opts = merge_options(opts, target->extra_opts, &target->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "Can't alloc memory");
return target;
}
static void print_help(const struct xtables_target *t,
const struct xtables_rule_match *m, const char *table)
{
printf("%s %s\n", prog_name, prog_vers);
printf(
"Usage:\n"
"ebtables -[ADI] chain rule-specification [options]\n"
"ebtables -P chain target\n"
"ebtables -[LFZ] [chain]\n"
"ebtables -[NX] [chain]\n"
"ebtables -E old-chain-name new-chain-name\n\n"
"Commands:\n"
"--append -A chain : append to chain\n"
"--delete -D chain : delete matching rule from chain\n"
"--delete -D chain rulenum : delete rule at position rulenum from chain\n"
"--change-counters -C chain\n"
" [rulenum] pcnt bcnt : change counters of existing rule\n"
"--insert -I chain rulenum : insert rule at position rulenum in chain\n"
"--list -L [chain] : list the rules in a chain or in all chains\n"
"--flush -F [chain] : delete all rules in chain or in all chains\n"
"--init-table : replace the kernel table with the initial table\n"
"--zero -Z [chain] : put counters on zero in chain or in all chains\n"
"--policy -P chain target : change policy on chain to target\n"
"--new-chain -N chain : create a user defined chain\n"
"--rename-chain -E old new : rename a chain\n"
"--delete-chain -X [chain] : delete a user defined chain\n"
"--atomic-commit : update the kernel w/t table contained in <FILE>\n"
"--atomic-init : put the initial kernel table into <FILE>\n"
"--atomic-save : put the current kernel table into <FILE>\n"
"--atomic-file file : set <FILE> to file\n\n"
"Options:\n"
"--proto -p [!] proto : protocol hexadecimal, by name or LENGTH\n"
"--src -s [!] address[/mask]: source mac address\n"
"--dst -d [!] address[/mask]: destination mac address\n"
"--in-if -i [!] name[+] : network input interface name\n"
"--out-if -o [!] name[+] : network output interface name\n"
"--logical-in [!] name[+] : logical bridge input interface name\n"
"--logical-out [!] name[+] : logical bridge output interface name\n"
"--set-counters -c chain\n"
" pcnt bcnt : set the counters of the to be added rule\n"
"--modprobe -M program : try to insert modules using this program\n"
"--concurrent : use a file lock to support concurrent scripts\n"
"--version -V : print package version\n\n"
"Environment variable:\n"
/*ATOMIC_ENV_VARIABLE " : if set <FILE> (see above) will equal its value"*/
"\n\n");
for (; m != NULL; m = m->next) {
printf("\n");
m->match->help();
}
if (t != NULL) {
printf("\n");
t->help();
}
// if (table->help)
// table->help(ebt_hooknames);
}
/* Execute command L */
static int list_rules(struct nft_handle *h, const char *chain, const char *table,
int rule_nr, int verbose, int numeric, int expanded,
int linenumbers, int counters)
{
unsigned int format;
format = FMT_OPTIONS;
if (verbose)
format |= FMT_VIA;
if (numeric)
format |= FMT_NUMERIC;
if (!expanded)
format |= FMT_KILOMEGAGIGA;
if (linenumbers)
format |= FMT_LINENUMBERS;
if (!counters)
format |= FMT_NOCOUNTS;
return nft_rule_list(h, chain, table, rule_nr, format);
}
static int parse_rule_range(const char *argv, int *rule_nr, int *rule_nr_end)
{
char *colon = strchr(argv, ':'), *buffer;
if (colon) {
*colon = '\0';
if (*(colon + 1) == '\0')
*rule_nr_end = -1; /* Until the last rule */
else {
*rule_nr_end = strtol(colon + 1, &buffer, 10);
if (*buffer != '\0' || *rule_nr_end == 0)
return -1;
}
}
if (colon == argv)
*rule_nr = 1; /* Beginning with the first rule */
else {
*rule_nr = strtol(argv, &buffer, 10);
if (*buffer != '\0' || *rule_nr == 0)
return -1;
}
if (!colon)
*rule_nr_end = *rule_nr;
return 0;
}
/* Incrementing or decrementing rules in daemon mode is not supported as the
* involved code overload is not worth it (too annoying to take the increased
* counters in the kernel into account). */
static int parse_change_counters_rule(int argc, char **argv, int *rule_nr, int *rule_nr_end, int exec_style, struct ebtables_command_state *cs)
{
char *buffer;
int ret = 0;
if (optind + 1 >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')) ||
(argv[optind + 1][0] == '-' && (argv[optind + 1][1] < '0' && argv[optind + 1][1] > '9')))
xtables_error(PARAMETER_PROBLEM,
"The command -C needs at least 2 arguments");
if (optind + 2 < argc && (argv[optind + 2][0] != '-' || (argv[optind + 2][1] >= '0' && argv[optind + 2][1] <= '9'))) {
if (optind + 3 != argc)
xtables_error(PARAMETER_PROBLEM,
"No extra options allowed with -C start_nr[:end_nr] pcnt bcnt");
if (parse_rule_range(argv[optind], rule_nr, rule_nr_end))
xtables_error(PARAMETER_PROBLEM,
"Something is wrong with the rule number specification '%s'", argv[optind]);
optind++;
}
if (argv[optind][0] == '+') {
if (exec_style == EXEC_STYLE_DAEMON)
daemon_incr:
xtables_error(PARAMETER_PROBLEM,
"Incrementing rule counters (%s) not allowed in daemon mode", argv[optind]);
ret += 1;
cs->counters.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
} else if (argv[optind][0] == '-') {
if (exec_style == EXEC_STYLE_DAEMON)
daemon_decr:
xtables_error(PARAMETER_PROBLEM,
"Decrementing rule counters (%s) not allowed in daemon mode", argv[optind]);
ret += 2;
cs->counters.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
} else
cs->counters.pcnt = strtoull(argv[optind], &buffer, 10);
if (*buffer != '\0')
goto invalid;
optind++;
if (argv[optind][0] == '+') {
if (exec_style == EXEC_STYLE_DAEMON)
goto daemon_incr;
ret += 3;
cs->counters.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
} else if (argv[optind][0] == '-') {
if (exec_style == EXEC_STYLE_DAEMON)
goto daemon_decr;
ret += 6;
cs->counters.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
} else
cs->counters.bcnt = strtoull(argv[optind], &buffer, 10);
if (*buffer != '\0')
goto invalid;
optind++;
return ret;
invalid:
xtables_error(PARAMETER_PROBLEM,"Packet counter '%s' invalid", argv[optind]);
}
static int parse_iface(char *iface, char *option)
{
char *c;
if ((c = strchr(iface, '+'))) {
if (*(c + 1) != '\0') {
xtables_error(PARAMETER_PROBLEM,
"Spurious characters after '+' wildcard for '%s'", option);
return -1;
} else
*c = IF_WILDCARD;
}
return 0;
}
/* This code is very similar to iptables/xtables.c:command_match() */
static void ebt_load_match(const char *name)
{
struct xtables_match *m;
size_t size;
m = xtables_find_match(name, XTF_LOAD_MUST_SUCCEED, NULL);
if (m == NULL)
xtables_error(OTHER_PROBLEM, "Unable to load %s match", name);
size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
m->m = xtables_calloc(1, size);
m->m->u.match_size = size;
strcpy(m->m->u.user.name, m->name);
m->m->u.user.revision = m->revision;
xs_init_match(m);
opts = merge_options(opts, m->extra_opts, &m->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "Can't alloc memory");
}
static void ebt_load_watcher(const char *name)
{
struct xtables_target *watcher;
size_t size;
watcher = xtables_find_target(name, XTF_LOAD_MUST_SUCCEED);
if (!watcher)
xtables_error(OTHER_PROBLEM,
"Unable to load %s watcher", name);
size = XT_ALIGN(sizeof(struct xt_entry_target)) + watcher->size;
watcher->t = xtables_calloc(1, size);
watcher->t->u.target_size = size;
strncpy(watcher->t->u.user.name, name,
sizeof(watcher->t->u.user.name));
watcher->t->u.user.name[sizeof(watcher->t->u.user.name)-1] = '\0';
watcher->t->u.user.revision = watcher->revision;
xs_init_target(watcher);
opts = merge_options(opts, watcher->extra_opts,
&watcher->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "Can't alloc memory");
}
static void ebt_load_match_extensions(void)
{
opts = ebt_original_options;
ebt_load_match("802_3");
ebt_load_match("ip");
ebt_load_match("mark_m");
ebt_load_match("limit");
ebt_load_watcher("log");
ebt_load_watcher("nflog");
}
static void ebt_add_match(struct xtables_match *m,
struct ebtables_command_state *cs)
{
struct xtables_rule_match *i, **rule_matches = &cs->matches;
struct xtables_match *newm;
struct ebt_match *newnode;
/* match already in rule_matches, skip inclusion */
for (i = *rule_matches; i; i = i->next) {
if (strcmp(m->name, i->match->name) == 0) {
i->match->mflags |= m->mflags;
return;
}
}
newm = xtables_find_match(m->name, XTF_LOAD_MUST_SUCCEED, rule_matches);
if (newm == NULL)
xtables_error(OTHER_PROBLEM,
"Unable to add match %s", m->name);
newm->mflags = m->mflags;
/* glue code for watchers */
newnode = calloc(1, sizeof(struct ebt_match));
if (newnode == NULL)
xtables_error(OTHER_PROBLEM, "Unable to alloc memory");
newnode->ismatch = true;
newnode->u.match = newm;
if (cs->match_list == NULL)
cs->match_list = newnode;
else
cs->match_list->next = newnode;
}
static void ebt_add_watcher(struct xtables_target *watcher,
struct ebtables_command_state *cs)
{
struct ebt_match *i, *newnode;
for (i = cs->match_list; i; i = i->next) {
if (i->ismatch)
continue;
if (strcmp(i->u.watcher->name, watcher->name) == 0) {
i->u.watcher->tflags |= watcher->tflags;
return;
}
}
newnode = calloc(1, sizeof(struct ebt_match));
if (newnode == NULL)
xtables_error(OTHER_PROBLEM, "Unable to alloc memory");
newnode->u.watcher = watcher;
if (cs->match_list == NULL)
cs->match_list = newnode;
else
cs->match_list->next = newnode;
}
/* We use exec_style instead of #ifdef's because ebtables.so is a shared object. */
int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table)
{
char *buffer;
int c, i;
int zerochain = -1; /* Needed for the -Z option (we can have -Z <this> -L <that>) */
int chcounter = 0; /* Needed for -C */
int rule_nr = 0;
int rule_nr_end = 0;
int ret = 0;
unsigned int flags = 0;
struct xtables_target *t, *w;
struct xtables_match *m;
struct ebtables_command_state cs;
char command = 'h';
const char *chain = NULL;
const char *policy = NULL;
int exec_style = EXEC_STYLE_PRG;
int selected_chain = -1;
struct xtables_rule_match *xtrm_i;
struct ebt_match *match;
memset(&cs, 0, sizeof(cs));
cs.argv = argv;
if (nft_init(h, xtables_bridge) < 0)
xtables_error(OTHER_PROBLEM,
"Could not initialize nftables layer.");
h->ops = nft_family_ops_lookup(h->family);
if (h->ops == NULL)
xtables_error(PARAMETER_PROBLEM, "Unknown family");
/* manually registering ebt matches, given the original ebtables parser
* don't use '-m matchname' and the match can't loaded dinamically when
* the user calls it.
*/
ebt_load_match_extensions();
/* clear mflags in case do_commandeb gets called a second time
* (we clear the global list of all matches for security)*/
for (m = xtables_matches; m; m = m->next)
m->mflags = 0;
for (t = xtables_targets; t; t = t->next) {
t->tflags = 0;
t->used = 0;
}
/* prevent getopt to spoil our error reporting */
opterr = false;
/* Getopt saves the day */
while ((c = getopt_long(argc, argv,
"-A:D:C:I:N:E:X::L::Z::F::P:Vhi:o:j:c:p:s:d:t:M:", opts, NULL)) != -1) {
cs.c = c;
cs.invert = ebt_invert;
switch (c) {
case 'A': /* Add a rule */
case 'D': /* Delete a rule */
case 'C': /* Change counters */
case 'P': /* Define policy */
case 'I': /* Insert a rule */
case 'N': /* Make a user defined chain */
case 'E': /* Rename chain */
case 'X': /* Delete chain */
/* We allow -N chainname -P policy */
/* XXX: Not in ebtables-compat */
if (command == 'N' && c == 'P') {
command = c;
optind--; /* No table specified */
goto handle_P;
}
if (OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"Multiple commands are not allowed");
command = c;
chain = optarg;
selected_chain = get_current_chain(chain);
flags |= OPT_COMMAND;
/*if (!(replace->flags & OPT_KERNELDATA))
ebt_get_kernel_table(replace, 0);*/
/*if (optarg && (optarg[0] == '-' || !strcmp(optarg, "!")))
ebt_print_error2("No chain name specified");*/
if (c == 'N') {
ret = nft_chain_user_add(h, chain, *table);
break;
} else if (c == 'X') {
ret = nft_chain_user_del(h, chain, *table);
break;
}
if (c == 'E') {
if (optind >= argc)
xtables_error(PARAMETER_PROBLEM, "No new chain name specified");
else if (optind < argc - 1)
xtables_error(PARAMETER_PROBLEM, "No extra options allowed with -E");
else if (strlen(argv[optind]) >= NFT_CHAIN_MAXNAMELEN)
xtables_error(PARAMETER_PROBLEM, "Chain name length can't exceed %d"" characters", NFT_CHAIN_MAXNAMELEN - 1);
else if (strchr(argv[optind], ' ') != NULL)
xtables_error(PARAMETER_PROBLEM, "Use of ' ' not allowed in chain names");
ret = nft_chain_user_rename(h, chain, *table,
argv[optind]);
if (ret != 0 && errno == ENOENT)
xtables_error(PARAMETER_PROBLEM, "Chain '%s' doesn't exists", chain);
optind++;
break;
} else if (c == 'D' && optind < argc && (argv[optind][0] != '-' || (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) {
if (optind != argc - 1)
xtables_error(PARAMETER_PROBLEM,
"No extra options allowed with -D start_nr[:end_nr]");
if (parse_rule_range(argv[optind], &rule_nr, &rule_nr_end))
xtables_error(PARAMETER_PROBLEM,
"Problem with the specified rule number(s) '%s'", argv[optind]);
optind++;
} else if (c == 'C') {
if ((chcounter = parse_change_counters_rule(argc, argv, &rule_nr, &rule_nr_end, exec_style, &cs)) == -1)
return -1;
} else if (c == 'I') {
if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')))
rule_nr = 1;
else {
rule_nr = parse_rule_number(argv[optind]);
optind++;
}
} else if (c == 'P') {
handle_P:
if (optind >= argc)
xtables_error(PARAMETER_PROBLEM,
"No policy specified");
for (i = 0; i < NUM_STANDARD_TARGETS; i++)
if (!strcmp(argv[optind], nft_ebt_standard_target(i))) {
policy = argv[optind];
if (-i-1 == EBT_CONTINUE)
xtables_error(PARAMETER_PROBLEM,
"Wrong policy '%s'",
argv[optind]);
break;
}
if (i == NUM_STANDARD_TARGETS)
xtables_error(PARAMETER_PROBLEM,
"Unknown policy '%s'", argv[optind]);
optind++;
}
break;
case 'L': /* List */
case 'F': /* Flush */
case 'Z': /* Zero counters */
if (c == 'Z') {
if ((flags & OPT_ZERO) || (flags & OPT_COMMAND && command != 'L'))
print_zero:
xtables_error(PARAMETER_PROBLEM,
"Command -Z only allowed together with command -L");
flags |= OPT_ZERO;
} else {
if (flags & OPT_COMMAND)
xtables_error(PARAMETER_PROBLEM,
"Multiple commands are not allowed");
command = c;
flags |= OPT_COMMAND;
if (flags & OPT_ZERO && c != 'L')
goto print_zero;
}
#ifdef SILENT_DAEMON
if (c== 'L' && exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"-L not supported in daemon mode");
#endif
/*if (!(replace->flags & OPT_KERNELDATA))
ebt_get_kernel_table(replace, 0);
i = -1;
if (optind < argc && argv[optind][0] != '-') {
if ((i = ebt_get_chainnr(replace, argv[optind])) == -1)
ebt_print_error2("Chain '%s' doesn't exist", argv[optind]);
optind++;
}
if (i != -1) {
if (c == 'Z')
zerochain = i;
else
replace->selected_chain = i;
}*/
break;
case 'V': /* Version */
if (OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"Multiple commands are not allowed");
command = 'V';
if (exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"%s %s\n", prog_name, prog_vers);
printf("%s %s\n", prog_name, prog_vers);
exit(0);
case 'h': /* Help */
#ifdef SILENT_DAEMON
if (exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"-h not supported in daemon mode");
#endif
if (OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"Multiple commands are not allowed");
command = 'h';
/* All other arguments should be extension names */
while (optind < argc) {
/*struct ebt_u_match *m;
struct ebt_u_watcher *w;*/
if (!strcasecmp("list_extensions", argv[optind])) {
ebt_list_extensions(xtables_targets, cs.matches);
exit(0);
}
/*if ((m = ebt_find_match(argv[optind])))
ebt_add_match(new_entry, m);
else if ((w = ebt_find_watcher(argv[optind])))
ebt_add_watcher(new_entry, w);
else {*/
if (!(t = xtables_find_target(argv[optind], XTF_TRY_LOAD)))
xtables_error(PARAMETER_PROBLEM,"Extension '%s' not found", argv[optind]);
if (flags & OPT_JUMP)
xtables_error(PARAMETER_PROBLEM,"Sorry, you can only see help for one target extension at a time");
flags |= OPT_JUMP;
cs.target = t;
//}
optind++;
}
break;
case 't': /* Table */
if (OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"Please put the -t option first");
ebt_check_option2(&flags, OPT_TABLE);
if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1)
xtables_error(PARAMETER_PROBLEM,
"Table name length cannot exceed %d characters",
EBT_TABLE_MAXNAMELEN - 1);
*table = optarg;
break;
case 'i': /* Input interface */
case 2 : /* Logical input interface */
case 'o': /* Output interface */
case 3 : /* Logical output interface */
case 'j': /* Target */
case 'p': /* Net family protocol */
case 's': /* Source mac */
case 'd': /* Destination mac */
case 'c': /* Set counters */
if (!OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"No command specified");
if (command != 'A' && command != 'D' && command != 'I' && command != 'C')
xtables_error(PARAMETER_PROBLEM,
"Command and option do not match");
if (c == 'i') {
ebt_check_option2(&flags, OPT_IN);
if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
xtables_error(PARAMETER_PROBLEM,
"Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains");
if (ebt_check_inverse2(optarg, argc, argv))
cs.fw.invflags |= EBT_IIN;
if (strlen(optarg) >= IFNAMSIZ)
big_iface_length:
xtables_error(PARAMETER_PROBLEM,
"Interface name length cannot exceed %d characters",
IFNAMSIZ - 1);
xtables_parse_interface(optarg, cs.fw.in, cs.fw.in_mask);
break;
} else if (c == 2) {
ebt_check_option2(&flags, OPT_LOGICALIN);
if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
xtables_error(PARAMETER_PROBLEM,
"Use --logical-in only in INPUT, FORWARD, PREROUTING and BROUTING chains");
if (ebt_check_inverse2(optarg, argc, argv))
cs.fw.invflags |= EBT_ILOGICALIN;
if (strlen(optarg) >= IFNAMSIZ)
goto big_iface_length;
strcpy(cs.fw.logical_in, optarg);
if (parse_iface(cs.fw.logical_in, "--logical-in"))
return -1;
break;
} else if (c == 'o') {
ebt_check_option2(&flags, OPT_OUT);
if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
xtables_error(PARAMETER_PROBLEM,
"Use -o only in OUTPUT, FORWARD and POSTROUTING chains");
if (ebt_check_inverse2(optarg, argc, argv))
cs.fw.invflags |= EBT_IOUT;
if (strlen(optarg) >= IFNAMSIZ)
goto big_iface_length;
xtables_parse_interface(optarg, cs.fw.out, cs.fw.out_mask);
break;
} else if (c == 3) {
ebt_check_option2(&flags, OPT_LOGICALOUT);
if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
xtables_error(PARAMETER_PROBLEM,
"Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains");
if (ebt_check_inverse2(optarg, argc, argv))
cs.fw.invflags |= EBT_ILOGICALOUT;
if (strlen(optarg) >= IFNAMSIZ)
goto big_iface_length;
strcpy(cs.fw.logical_out, optarg);
if (parse_iface(cs.fw.logical_out, "--logical-out"))
return -1;
break;
} else if (c == 'j') {
ebt_check_option2(&flags, OPT_JUMP);
cs.jumpto = parse_target(optarg);
cs.target = command_jump(&cs, cs.jumpto);
break;
} else if (c == 's') {
ebt_check_option2(&flags, OPT_SOURCE);
if (ebt_check_inverse2(optarg, argc, argv))
cs.fw.invflags |= EBT_ISOURCE;
if (ebt_get_mac_and_mask(optarg, cs.fw.sourcemac, cs.fw.sourcemsk))
xtables_error(PARAMETER_PROBLEM, "Problem with specified source mac '%s'", optarg);
cs.fw.bitmask |= EBT_SOURCEMAC;
break;
} else if (c == 'd') {
ebt_check_option2(&flags, OPT_DEST);
if (ebt_check_inverse2(optarg, argc, argv))
cs.fw.invflags |= EBT_IDEST;
if (ebt_get_mac_and_mask(optarg, cs.fw.destmac, cs.fw.destmsk))
xtables_error(PARAMETER_PROBLEM, "Problem with specified destination mac '%s'", optarg);
cs.fw.bitmask |= EBT_DESTMAC;
break;
} else if (c == 'c') {
ebt_check_option2(&flags, OPT_COUNT);
if (ebt_check_inverse2(optarg, argc, argv))
xtables_error(PARAMETER_PROBLEM,
"Unexpected '!' after -c");
if (optind >= argc || optarg[0] == '-' || argv[optind][0] == '-')
xtables_error(PARAMETER_PROBLEM,
"Option -c needs 2 arguments");
cs.counters.pcnt = strtoull(optarg, &buffer, 10);
if (*buffer != '\0')
xtables_error(PARAMETER_PROBLEM,
"Packet counter '%s' invalid",
optarg);
cs.counters.bcnt = strtoull(argv[optind], &buffer, 10);
if (*buffer != '\0')
xtables_error(PARAMETER_PROBLEM,
"Packet counter '%s' invalid",
argv[optind]);
optind++;
break;
}
ebt_check_option2(&flags, OPT_PROTOCOL);
if (ebt_check_inverse2(optarg, argc, argv))
cs.fw.invflags |= EBT_IPROTO;
cs.fw.bitmask &= ~((unsigned int)EBT_NOPROTO);
i = strtol(optarg, &buffer, 16);
if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
xtables_error(PARAMETER_PROBLEM,
"Problem with the specified protocol");
if (*buffer != '\0') {
struct ethertypeent *ent;
if (!strcasecmp(optarg, "LENGTH")) {
cs.fw.bitmask |= EBT_802_3;
break;
}
ent = getethertypebyname(optarg);
if (!ent)
xtables_error(PARAMETER_PROBLEM,
"Problem with the specified Ethernet protocol '%s', perhaps "_PATH_ETHERTYPES " is missing", optarg);
cs.fw.ethproto = ent->e_ethertype;
} else
cs.fw.ethproto = i;
if (cs.fw.ethproto < 0x0600)
xtables_error(PARAMETER_PROBLEM,
"Sorry, protocols have values above or equal to 0x0600");
break;
case 4 : /* Lc */
#ifdef SILENT_DAEMON
if (exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"--Lc is not supported in daemon mode");
#endif
ebt_check_option2(&flags, LIST_C);
if (command != 'L')
xtables_error(PARAMETER_PROBLEM,
"Use --Lc with -L");
flags |= LIST_C;
break;
case 5 : /* Ln */
#ifdef SILENT_DAEMON
if (exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"--Ln is not supported in daemon mode");
#endif
ebt_check_option2(&flags, LIST_N);
if (command != 'L')
xtables_error(PARAMETER_PROBLEM,
"Use --Ln with -L");
if (flags & LIST_X)
xtables_error(PARAMETER_PROBLEM,
"--Lx is not compatible with --Ln");
flags |= LIST_N;
break;
case 6 : /* Lx */
#ifdef SILENT_DAEMON
if (exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"--Lx is not supported in daemon mode");
#endif
ebt_check_option2(&flags, LIST_X);
if (command != 'L')
xtables_error(PARAMETER_PROBLEM,
"Use --Lx with -L");
if (flags & LIST_N)
xtables_error(PARAMETER_PROBLEM,
"--Lx is not compatible with --Ln");
flags |= LIST_X;
break;
case 12 : /* Lmac2 */
#ifdef SILENT_DAEMON
if (exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"--Lmac2 is not supported in daemon mode");
#endif
ebt_check_option2(&flags, LIST_MAC2);
if (command != 'L')
xtables_error(PARAMETER_PROBLEM,
"Use --Lmac2 with -L");
flags |= LIST_MAC2;
break;
case 8 : /* atomic-commit */
/* if (exec_style == EXEC_STYLE_DAEMON)
ebt_print_error2("--atomic-commit is not supported in daemon mode");
replace->command = c;
if (OPT_COMMANDS)
ebt_print_error2("Multiple commands are not allowed");
replace->flags |= OPT_COMMAND;
if (!replace->filename)
ebt_print_error2("No atomic file specified");*/
/* Get the information from the file */
/*ebt_get_table(replace, 0);*/
/* We don't want the kernel giving us its counters,
* they would overwrite the counters extracted from
* the file */
/*replace->num_counters = 0;*/
/* Make sure the table will be written to the kernel */
/*free(replace->filename);
replace->filename = NULL;
break;*/
/*case 7 :*/ /* atomic-init */
/*case 10:*/ /* atomic-save */
/*case 11:*/ /* init-table */
/* if (exec_style == EXEC_STYLE_DAEMON) {
if (c == 7) {
ebt_print_error2("--atomic-init is not supported in daemon mode");
} else if (c == 10)
ebt_print_error2("--atomic-save is not supported in daemon mode");
ebt_print_error2("--init-table is not supported in daemon mode");
}
replace->command = c;
if (OPT_COMMANDS)
ebt_print_error2("Multiple commands are not allowed");
if (c != 11 && !replace->filename)
ebt_print_error2("No atomic file specified");
replace->flags |= OPT_COMMAND;
{
char *tmp = replace->filename;*/
/* Get the kernel table */
/*replace->filename = NULL;
ebt_get_kernel_table(replace, c == 10 ? 0 : 1);
replace->filename = tmp;
}
break;
case 9 :*/ /* atomic */
/*if (exec_style == EXEC_STYLE_DAEMON)
ebt_print_error2("--atomic is not supported in daemon mode");
if (OPT_COMMANDS)
ebt_print_error2("--atomic has to come before the command");*/
/* A possible memory leak here, but this is not
* executed in daemon mode */
/*replace->filename = (char *)malloc(strlen(optarg) + 1);
strcpy(replace->filename, optarg);
break;
case 13 : *//* concurrent */
/*signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
use_lockfd = 1;
break;*/
case 1 :
if (!strcmp(optarg, "!"))
ebt_check_inverse2(optarg, argc, argv);
else
xtables_error(PARAMETER_PROBLEM,
"Bad argument : '%s'", optarg);
/* ebt_ebt_check_inverse2() did optind++ */
optind--;
continue;
default:
/* Is it a target option? */
if (cs.target != NULL && cs.target->parse != NULL) {
int opt_offset = cs.target->option_offset;
if (cs.target->parse(c - opt_offset,
argv, ebt_invert,
&cs.target->tflags,
NULL, &cs.target->t))
goto check_extension;
}
/* Is it a match_option? */
for (m = xtables_matches; m; m = m->next) {
if (m->parse(c - m->option_offset, argv, ebt_invert, &m->mflags, NULL, &m->m)) {
ebt_add_match(m, &cs);
goto check_extension;
}
}
/* Is it a watcher option? */
for (w = xtables_targets; w; w = w->next) {
if (w->parse(c - w->option_offset, argv,
ebt_invert, &w->tflags,
NULL, &w->t)) {
ebt_add_watcher(w, &cs);
goto check_extension;
}
}
/*
if (w == NULL && c == '?')
ebt_print_error2("Unknown argument: '%s'", argv[optind - 1], (char)optopt, (char)c);
else if (w == NULL) {
if (!strcmp(t->name, "standard"))
ebt_print_error2("Unknown argument: don't forget the -t option");
else
ebt_print_error2("Target-specific option does not correspond with specified target");
}
if (ebt_errormsg[0] != '\0')
return -1;
if (w->used == 0) {
ebt_add_watcher(new_entry, w);
w->used = 1;
}*/
check_extension:
if (command != 'A' && command != 'I' &&
command != 'D' && command != 'C')
xtables_error(PARAMETER_PROBLEM,
"Extensions only for -A, -I, -D and -C");
}
ebt_invert = 0;
}
/* Just in case we didn't catch an error */
/*if (ebt_errormsg[0] != '\0')
return -1;
if (!(table = ebt_find_table(replace->name)))
ebt_print_error2("Bad table name");*/
if (command == 'h' && !(flags & OPT_ZERO)) {
print_help(cs.target, cs.matches, *table);
if (exec_style == EXEC_STYLE_PRG)
exit(0);
}
/* Do the final checks */
if (command == 'A' || command == 'I' ||
command == 'D' || command == 'C') {
for (xtrm_i = cs.matches; xtrm_i; xtrm_i = xtrm_i->next)
xtables_option_mfcall(xtrm_i->match);
for (match = cs.match_list; match; match = match->next) {
if (match->ismatch)
continue;
xtables_option_tfcall(match->u.watcher);
}
if (cs.target != NULL)
xtables_option_tfcall(cs.target);
}
/* So, the extensions can work with the host endian.
* The kernel does not have to do this of course */
cs.fw.ethproto = htons(cs.fw.ethproto);
if (command == 'P') {
if (selected_chain < 0) {
xtables_error(PARAMETER_PROBLEM,
"Policy %s not allowed for user defined chains",
policy);
}
if (strcmp(policy, "RETURN") == 0) {
xtables_error(PARAMETER_PROBLEM,
"Policy RETURN only allowed for user defined chains");
}
ret = nft_chain_set(h, *table, chain, policy, NULL);
if (ret < 0)
xtables_error(PARAMETER_PROBLEM, "Wrong policy");
} else if (command == 'L') {
ret = list_rules(h, chain, *table, rule_nr,
flags&OPT_VERBOSE,
flags&OPT_NUMERIC,
/*flags&OPT_EXPANDED*/0,
flags&LIST_N,
flags&LIST_C);
if (!(flags & OPT_ZERO) && exec_style == EXEC_STYLE_PRG)
exit(0);
}
if (flags & OPT_ZERO) {
selected_chain = zerochain;
ret = nft_chain_zero_counters(h, chain, *table);
} else if (command == 'F') {
ret = nft_rule_flush(h, chain, *table);
} else if (command == 'A') {
ret = append_entry(h, chain, *table, &cs, 0,
flags&OPT_VERBOSE, true);
} else if (command == 'I') {
ret = append_entry(h, chain, *table, &cs, rule_nr - 1,
flags&OPT_VERBOSE, false);
} else if (command == 'D') {
ret = delete_entry(h, chain, *table, &cs, rule_nr - 1,
rule_nr_end, flags&OPT_VERBOSE);
} /*else if (replace->command == 'C') {
ebt_change_counters(replace, new_entry, rule_nr, rule_nr_end, &(new_entry->cnt_surplus), chcounter);
if (ebt_errormsg[0] != '\0')
return -1;
}*/
/* Commands -N, -E, -X, --atomic-commit, --atomic-commit, --atomic-save,
* --init-table fall through */
/*if (ebt_errormsg[0] != '\0')
return -1;
if (table->check)
table->check(replace);
if (exec_style == EXEC_STYLE_PRG) {*//* Implies ebt_errormsg[0] == '\0' */
/*ebt_deliver_table(replace);
if (replace->nentries)
ebt_deliver_counters(replace);*/
ebt_cs_clean(&cs);
return ret;
}
......@@ -13,6 +13,10 @@
#include "ip6tables-multi.h"
#endif
#ifdef ENABLE_NFTABLES
#include "xtables-multi.h"
#endif
static const struct subcommand multi_subcommands[] = {
#ifdef ENABLE_IPV4
{"iptables", iptables_main},
......@@ -31,6 +35,15 @@ static const struct subcommand multi_subcommands[] = {
{"save6", ip6tables_save_main},
{"ip6tables-restore", ip6tables_restore_main},
{"restore6", ip6tables_restore_main},
#endif
#ifdef ENABLE_NFTABLES
{"xtables", xtables_main},
{"xtables-save", xtables_save_main},
{"xtables-restore", xtables_restore_main},
{"xtables-config", xtables_config_main},
{"xtables-events", xtables_events_main},
{"xtables-arp", xtables_arp_main},
{"xtables-ebtables", xtables_eb_main},
#endif
{NULL},
};
......
......@@ -2,5 +2,17 @@
#define _XTABLES_MULTI_H 1
extern int iptables_xml_main(int, char **);
#ifdef ENABLE_NFTABLES
extern int xtables_ip4_main(int, char **);
extern int xtables_ip4_save_main(int, char **);
extern int xtables_ip4_restore_main(int, char **);
extern int xtables_ip6_main(int, char **);
extern int xtables_ip6_save_main(int, char **);
extern int xtables_ip6_restore_main(int, char **);
extern int xtables_arp_main(int, char **);
extern int xtables_eb_main(int, char **);
extern int xtables_config_main(int, char **);
extern int xtables_events_main(int, char **);
#endif
#endif /* _XTABLES_MULTI_H */
/* Code to restore the iptables state, from file by iptables-save.
* (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
* based on previous code from Rusty Russell <rusty@linuxcare.com.au>
*
* This code is distributed under the terms of GNU GPL v2
*/
#include <getopt.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "iptables.h"
#include "xtables.h"
#include "libiptc/libiptc.h"
#include "xtables-multi.h"
#include "nft.h"
#include <libnftnl/chain.h>
#ifdef DEBUG
#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
#else
#define DEBUGP(x, args...)
#endif
static int counters = 0, verbose = 0, noflush = 0;
/* Keeping track of external matches and targets. */
static const struct option options[] = {
{.name = "counters", .has_arg = false, .val = 'c'},
{.name = "verbose", .has_arg = false, .val = 'v'},
{.name = "test", .has_arg = false, .val = 't'},
{.name = "help", .has_arg = false, .val = 'h'},
{.name = "noflush", .has_arg = false, .val = 'n'},
{.name = "modprobe", .has_arg = true, .val = 'M'},
{.name = "table", .has_arg = true, .val = 'T'},
{.name = "ipv4", .has_arg = false, .val = '4'},
{.name = "ipv6", .has_arg = false, .val = '6'},
{NULL},
};
static void print_usage(const char *name, const char *version) __attribute__((noreturn));
#define prog_name xtables_globals.program_name
static void print_usage(const char *name, const char *version)
{
fprintf(stderr, "Usage: %s [-c] [-v] [-t] [-h]\n"
" [ --counters ]\n"
" [ --verbose ]\n"
" [ --test ]\n"
" [ --help ]\n"
" [ --noflush ]\n"
" [ --table=<TABLE> ]\n"
" [ --modprobe=<command>]\n", name);
exit(1);
}
static int parse_counters(char *string, struct xt_counters *ctr)
{
unsigned long long pcnt, bcnt;
int ret;
ret = sscanf(string, "[%llu:%llu]", &pcnt, &bcnt);
ctr->pcnt = pcnt;
ctr->bcnt = bcnt;
return ret == 2;
}
/* global new argv and argc */
static char *newargv[255];
static int newargc;
/* function adding one argument to newargv, updating newargc
* returns true if argument added, false otherwise */
static int add_argv(char *what) {
DEBUGP("add_argv: %s\n", what);
if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
newargv[newargc] = strdup(what);
newargv[++newargc] = NULL;
return 1;
} else {
xtables_error(PARAMETER_PROBLEM,
"Parser cannot handle more arguments\n");
return 0;
}
}
static void free_argv(void) {
int i;
for (i = 0; i < newargc; i++)
free(newargv[i]);
}
static void add_param_to_argv(char *parsestart)
{
int quote_open = 0, escaped = 0, param_len = 0;
char param_buffer[1024], *curchar;
/* After fighting with strtok enough, here's now
* a 'real' parser. According to Rusty I'm now no
* longer a real hacker, but I can live with that */
for (curchar = parsestart; *curchar; curchar++) {
if (quote_open) {
if (escaped) {
param_buffer[param_len++] = *curchar;
escaped = 0;
continue;
} else if (*curchar == '\\') {
escaped = 1;
continue;
} else if (*curchar == '"') {
quote_open = 0;
*curchar = ' ';
} else {
param_buffer[param_len++] = *curchar;
continue;
}
} else {
if (*curchar == '"') {
quote_open = 1;
continue;
}
}
if (*curchar == ' '
|| *curchar == '\t'
|| * curchar == '\n') {
if (!param_len) {
/* two spaces? */
continue;
}
param_buffer[param_len] = '\0';
/* check if table name specified */
if (!strncmp(param_buffer, "-t", 2)
|| !strncmp(param_buffer, "--table", 8)) {
xtables_error(PARAMETER_PROBLEM,
"The -t option (seen in line %u) cannot be "
"used in xtables-restore.\n", line);
exit(1);
}
add_argv(param_buffer);
param_len = 0;
} else {
/* regular character, copy to buffer */
param_buffer[param_len++] = *curchar;
if (param_len >= sizeof(param_buffer))
xtables_error(PARAMETER_PROBLEM,
"Parameter too long!");
}
}
}
static const struct xtc_ops xtc_ops = {
.strerror = nft_strerror,
};
static int
xtables_restore_main(int family, const char *progname, int argc, char *argv[])
{
struct nft_handle h = {
.family = family,
.restore = true,
};
char buffer[10240];
int c;
char curtable[XT_TABLE_MAXNAMELEN + 1];
FILE *in;
int in_table = 0, testing = 0;
const char *tablename = NULL;
const struct xtc_ops *ops = &xtc_ops;
struct nftnl_chain_list *chain_list;
struct nftnl_chain *chain_obj;
line = 0;
xtables_globals.program_name = progname;
c = xtables_init_all(&xtables_globals, family);
if (c < 0) {
fprintf(stderr, "%s/%s Failed to initialize xtables\n",
xtables_globals.program_name,
xtables_globals.program_version);
exit(1);
}
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensions();
init_extensions4();
#endif
if (nft_init(&h, xtables_ipv4) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
strerror(errno));
exit(EXIT_FAILURE);
}
while ((c = getopt_long(argc, argv, "bcvthnM:T:46", options, NULL)) != -1) {
switch (c) {
case 'b':
fprintf(stderr, "-b/--binary option is not implemented\n");
break;
case 'c':
counters = 1;
break;
case 'v':
verbose = 1;
break;
case 't':
testing = 1;
break;
case 'h':
print_usage("xtables-restore",
IPTABLES_VERSION);
break;
case 'n':
noflush = 1;
break;
case 'M':
xtables_modprobe_program = optarg;
break;
case 'T':
tablename = optarg;
break;
case '4':
h.family = AF_INET;
break;
case '6':
h.family = AF_INET6;
xtables_set_nfproto(AF_INET6);
break;
}
}
if (optind == argc - 1) {
in = fopen(argv[optind], "re");
if (!in) {
fprintf(stderr, "Can't open %s: %s\n", argv[optind],
strerror(errno));
exit(1);
}
}
else if (optind < argc) {
fprintf(stderr, "Unknown arguments found on commandline\n");
exit(1);
}
else in = stdin;
chain_list = nft_chain_dump(&h);
if (chain_list == NULL)
xtables_error(OTHER_PROBLEM, "cannot retrieve chain list\n");
/* Grab standard input. */
while (fgets(buffer, sizeof(buffer), in)) {
int ret = 0;
line++;
if (buffer[0] == '\n')
continue;
else if (buffer[0] == '#') {
if (verbose)
fputs(buffer, stdout);
continue;
} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
if (!testing) {
/* Commit per table, although we support
* global commit at once, stick by now to
* the existing behaviour.
*/
DEBUGP("Calling commit\n");
ret = nft_commit(&h);
} else {
DEBUGP("Not calling commit, testing\n");
ret = nft_abort(&h);
}
in_table = 0;
/* Purge out unused chains in this table */
if (!testing)
nft_table_purge_chains(&h, curtable, chain_list);
} else if ((buffer[0] == '*') && (!in_table)) {
/* New table */
char *table;
table = strtok(buffer+1, " \t\n");
DEBUGP("line %u, table '%s'\n", line, table);
if (!table) {
xtables_error(PARAMETER_PROBLEM,
"%s: line %u table name invalid\n",
xt_params->program_name, line);
exit(1);
}
strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
curtable[XT_TABLE_MAXNAMELEN] = '\0';
if (tablename && (strcmp(tablename, table) != 0))
continue;
if (noflush == 0) {
DEBUGP("Cleaning all chains of table '%s'\n",
table);
nft_rule_flush(&h, NULL, table);
}
ret = 1;
in_table = 1;
} else if ((buffer[0] == ':') && (in_table)) {
/* New chain. */
char *policy, *chain = NULL;
struct xt_counters count = {};
chain = strtok(buffer+1, " \t\n");
DEBUGP("line %u, chain '%s'\n", line, chain);
if (!chain) {
xtables_error(PARAMETER_PROBLEM,
"%s: line %u chain name invalid\n",
xt_params->program_name, line);
exit(1);
}
chain_obj = nft_chain_list_find(chain_list,
curtable, chain);
/* This chain has been found, delete from list. Later
* on, unvisited chains will be purged out.
*/
if (chain_obj != NULL)
nftnl_chain_list_del(chain_obj);
if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
xtables_error(PARAMETER_PROBLEM,
"Invalid chain name `%s' "
"(%u chars max)",
chain, XT_EXTENSION_MAXNAMELEN - 1);
policy = strtok(NULL, " \t\n");
DEBUGP("line %u, policy '%s'\n", line, policy);
if (!policy) {
xtables_error(PARAMETER_PROBLEM,
"%s: line %u policy invalid\n",
xt_params->program_name, line);
exit(1);
}
if (strcmp(policy, "-") != 0) {
if (counters) {
char *ctrs;
ctrs = strtok(NULL, " \t\n");
if (!ctrs || !parse_counters(ctrs, &count))
xtables_error(PARAMETER_PROBLEM,
"invalid policy counters "
"for chain '%s'\n", chain);
}
if (nft_chain_set(&h, curtable, chain, policy, &count) < 0) {
xtables_error(OTHER_PROBLEM,
"Can't set policy `%s'"
" on `%s' line %u: %s\n",
policy, chain, line,
ops->strerror(errno));
}
DEBUGP("Setting policy of chain %s to %s\n",
chain, policy);
ret = 1;
} else {
if (nft_chain_user_add(&h, chain, curtable) < 0) {
if (errno == EEXIST)
continue;
xtables_error(PARAMETER_PROBLEM,
"cannot create chain "
"'%s' (%s)\n", chain,
strerror(errno));
}
continue;
}
} else if (in_table) {
int a;
char *ptr = buffer;
char *pcnt = NULL;
char *bcnt = NULL;
char *parsestart;
/* reset the newargv */
newargc = 0;
if (buffer[0] == '[') {
/* we have counters in our input */
ptr = strchr(buffer, ']');
if (!ptr)
xtables_error(PARAMETER_PROBLEM,
"Bad line %u: need ]\n",
line);
pcnt = strtok(buffer+1, ":");
if (!pcnt)
xtables_error(PARAMETER_PROBLEM,
"Bad line %u: need :\n",
line);
bcnt = strtok(NULL, "]");
if (!bcnt)
xtables_error(PARAMETER_PROBLEM,
"Bad line %u: need ]\n",
line);
/* start command parsing after counter */
parsestart = ptr + 1;
} else {
/* start command parsing at start of line */
parsestart = buffer;
}
add_argv(argv[0]);
add_argv("-t");
add_argv(curtable);
if (counters && pcnt && bcnt) {
add_argv("--set-counters");
add_argv((char *) pcnt);
add_argv((char *) bcnt);
}
add_param_to_argv(parsestart);
DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
newargc, curtable);
for (a = 0; a < newargc; a++)
DEBUGP("argv[%u]: %s\n", a, newargv[a]);
ret = do_commandx(&h, newargc, newargv,
&newargv[2], true);
if (ret < 0) {
ret = nft_abort(&h);
if (ret < 0) {
fprintf(stderr, "failed to abort "
"commit operation\n");
}
exit(1);
}
free_argv();
fflush(stdout);
}
if (tablename && (strcmp(tablename, curtable) != 0))
continue;
if (!ret) {
fprintf(stderr, "%s: line %u failed\n",
xt_params->program_name, line);
exit(1);
}
}
if (in_table) {
fprintf(stderr, "%s: COMMIT expected at line %u\n",
xt_params->program_name, line + 1);
exit(1);
}
fclose(in);
return 0;
}
int xtables_ip4_restore_main(int argc, char *argv[])
{
return xtables_restore_main(NFPROTO_IPV4, "iptables-restore",
argc, argv);
}
int xtables_ip6_restore_main(int argc, char *argv[])
{
return xtables_restore_main(NFPROTO_IPV6, "ip6tables-restore",
argc, argv);
}
/* Code to save the xtables state, in human readable-form. */
/* (C) 1999 by Paul 'Rusty' Russell <rusty@rustcorp.com.au> and
* (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
* (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* This code is distributed under the terms of GNU GPL v2
*
*/
#include <getopt.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <netdb.h>
#include "libiptc/libiptc.h"
#include "iptables.h"
#include "xtables-multi.h"
#include "nft.h"
#include <libnftnl/chain.h>
#ifndef NO_SHARED_LIBS
#include <dlfcn.h>
#endif
static bool show_counters = false;
static const struct option options[] = {
{.name = "counters", .has_arg = false, .val = 'c'},
{.name = "dump", .has_arg = false, .val = 'd'},
{.name = "table", .has_arg = true, .val = 't'},
{.name = "modprobe", .has_arg = true, .val = 'M'},
{.name = "ipv4", .has_arg = false, .val = '4'},
{.name = "ipv6", .has_arg = false, .val = '6'},
{NULL},
};
static int
do_output(struct nft_handle *h, const char *tablename, bool counters)
{
struct nftnl_chain_list *chain_list;
if (!tablename)
return nft_for_each_table(h, do_output, counters);
if (!nft_table_find(h, tablename)) {
printf("Table `%s' does not exist\n", tablename);
return 0;
}
chain_list = nft_chain_dump(h);
time_t now = time(NULL);
printf("# Generated by xtables-save v%s on %s",
IPTABLES_VERSION, ctime(&now));
printf("*%s\n", tablename);
/* Dump out chain names first,
* thereby preventing dependency conflicts */
nft_chain_save(h, chain_list, tablename);
nft_rule_save(h, tablename, counters);
now = time(NULL);
printf("COMMIT\n");
printf("# Completed on %s", ctime(&now));
return 1;
}
/* Format:
* :Chain name POLICY packets bytes
* rule
*/
static int
xtables_save_main(int family, const char *progname, int argc, char *argv[])
{
const char *tablename = NULL;
bool dump = false;
struct nft_handle h = {
.family = family,
};
int c;
xtables_globals.program_name = progname;
c = xtables_init_all(&xtables_globals, family);
if (c < 0) {
fprintf(stderr, "%s/%s Failed to initialize xtables\n",
xtables_globals.program_name,
xtables_globals.program_version);
exit(1);
}
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensions();
init_extensions4();
#endif
if (nft_init(&h, xtables_ipv4) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
strerror(errno));
exit(EXIT_FAILURE);
}
while ((c = getopt_long(argc, argv, "bcdt:M:46", options, NULL)) != -1) {
switch (c) {
case 'b':
fprintf(stderr, "-b/--binary option is not implemented\n");
break;
case 'c':
show_counters = true;
break;
case 't':
/* Select specific table. */
tablename = optarg;
break;
case 'M':
xtables_modprobe_program = optarg;
break;
case 'd':
dump = true;
break;
case '4':
h.family = AF_INET;
break;
case '6':
h.family = AF_INET6;
xtables_set_nfproto(AF_INET6);
break;
}
}
if (optind < argc) {
fprintf(stderr, "Unknown arguments found on commandline\n");
exit(1);
}
if (dump) {
do_output(&h, tablename, show_counters);
exit(0);
}
return !do_output(&h, tablename, show_counters);
}
int xtables_ip4_save_main(int argc, char *argv[])
{
return xtables_save_main(NFPROTO_IPV4, "iptables-save", argc, argv);
}
int xtables_ip6_save_main(int argc, char *argv[])
{
return xtables_save_main(NFPROTO_IPV6, "ip6tables-save", argc, argv);
}
/*
* Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
*
* Based on the ipchains code by Paul Russell and Michael Neuling
*
* (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
* Paul 'Rusty' Russell <rusty@rustcorp.com.au>
* Marc Boucher <marc+nf@mbsi.ca>
* James Morris <jmorris@intercode.com.au>
* Harald Welte <laforge@gnumonks.org>
* Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* iptables -- IP firewall administration for kernels with
* firewall table (aimed for the 2.3 kernels)
*
* See the accompanying manual page iptables(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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <iptables.h>
#include "xtables-multi.h"
#include "nft.h"
static int
xtables_main(int family, const char *progname, int argc, char *argv[])
{
int ret;
char *table = "filter";
struct nft_handle h = {
.family = family,
};
xtables_globals.program_name = progname;
ret = xtables_init_all(&xtables_globals, family);
if (ret < 0) {
fprintf(stderr, "%s/%s Failed to initialize xtables\n",
xtables_globals.program_name,
xtables_globals.program_version);
exit(1);
}
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensions();
init_extensions4();
#endif
if (nft_init(&h, xtables_ipv4) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
strerror(errno));
nft_fini(&h);
exit(EXIT_FAILURE);
}
ret = do_commandx(&h, argc, argv, &table, false);
if (ret)
ret = nft_commit(&h);
nft_fini(&h);
if (!ret) {
if (errno == EINVAL) {
fprintf(stderr, "iptables: %s. "
"Run `dmesg' for more information.\n",
nft_strerror(errno));
} else {
fprintf(stderr, "iptables: %s.\n",
nft_strerror(errno));
}
if (errno == EAGAIN) {
exit(RESOURCE_PROBLEM);
}
}
exit(!ret);
}
int xtables_ip4_main(int argc, char *argv[])
{
return xtables_main(NFPROTO_IPV4, "iptables", argc, argv);
}
int xtables_ip6_main(int argc, char *argv[])
{
return xtables_main(NFPROTO_IPV6, "ip6tables", argc, argv);
}
/* Code to take an iptables-style command line and do it. */
/*
* Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
*
* (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
* Paul 'Rusty' Russell <rusty@rustcorp.com.au>
* Marc Boucher <marc+nf@mbsi.ca>
* James Morris <jmorris@intercode.com.au>
* Harald Welte <laforge@gnumonks.org>
* Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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.
*/
#include <getopt.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>
#include <limits.h>
#include <unistd.h>
#include <iptables.h>
#include <xtables.h>
#include <fcntl.h>
#include "xshared.h"
#include "nft-shared.h"
#include "nft.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define NUMBER_OF_CMD 16
static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
'N', 'X', 'P', 'E', 'S', 'Z', 'C' };
#define OPT_FRAGMENT 0x00800U
#define NUMBER_OF_OPT ARRAY_SIZE(optflags)
static const char optflags[]
= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '0', 'c', 'f'};
static struct option original_opts[] = {
{.name = "append", .has_arg = 1, .val = 'A'},
{.name = "delete", .has_arg = 1, .val = 'D'},
{.name = "check", .has_arg = 1, .val = 'C'},
{.name = "insert", .has_arg = 1, .val = 'I'},
{.name = "replace", .has_arg = 1, .val = 'R'},
{.name = "list", .has_arg = 2, .val = 'L'},
{.name = "list-rules", .has_arg = 2, .val = 'S'},
{.name = "flush", .has_arg = 2, .val = 'F'},
{.name = "zero", .has_arg = 2, .val = 'Z'},
{.name = "new-chain", .has_arg = 1, .val = 'N'},
{.name = "delete-chain", .has_arg = 2, .val = 'X'},
{.name = "rename-chain", .has_arg = 1, .val = 'E'},
{.name = "policy", .has_arg = 1, .val = 'P'},
{.name = "source", .has_arg = 1, .val = 's'},
{.name = "destination", .has_arg = 1, .val = 'd'},
{.name = "src", .has_arg = 1, .val = 's'}, /* synonym */
{.name = "dst", .has_arg = 1, .val = 'd'}, /* synonym */
{.name = "protocol", .has_arg = 1, .val = 'p'},
{.name = "in-interface", .has_arg = 1, .val = 'i'},
{.name = "jump", .has_arg = 1, .val = 'j'},
{.name = "table", .has_arg = 1, .val = 't'},
{.name = "match", .has_arg = 1, .val = 'm'},
{.name = "numeric", .has_arg = 0, .val = 'n'},
{.name = "out-interface", .has_arg = 1, .val = 'o'},
{.name = "verbose", .has_arg = 0, .val = 'v'},
{.name = "wait", .has_arg = 2, .val = 'w'},
{.name = "exact", .has_arg = 0, .val = 'x'},
{.name = "fragments", .has_arg = 0, .val = 'f'},
{.name = "version", .has_arg = 0, .val = 'V'},
{.name = "help", .has_arg = 2, .val = 'h'},
{.name = "line-numbers", .has_arg = 0, .val = '0'},
{.name = "modprobe", .has_arg = 1, .val = 'M'},
{.name = "set-counters", .has_arg = 1, .val = 'c'},
{.name = "goto", .has_arg = 1, .val = 'g'},
{.name = "ipv4", .has_arg = 0, .val = '4'},
{.name = "ipv6", .has_arg = 0, .val = '6'},
{NULL},
};
void xtables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
struct xtables_globals xtables_globals = {
.option_offset = 0,
.program_version = IPTABLES_VERSION,
.orig_opts = original_opts,
.exit_err = xtables_exit_error,
.compat_rev = nft_compatible_revision,
};
/* 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 const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/* Well, it's better than "Re: Linux vs FreeBSD" */
{
/* -n -s -d -p -j -v -x -i -o --line -c -f */
/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '},
/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' '},
/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*REPLACE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '},
/*APPEND*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '},
/*LIST*/ {' ','x','x','x','x',' ',' ','x','x',' ','x','x'},
/*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*ZERO_NUM*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x',' ','x'},
/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*CHECK*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' '},
};
static const int inverse_for_options[NUMBER_OF_OPT] =
{
/* -n */ 0,
/* -s */ IPT_INV_SRCIP,
/* -d */ IPT_INV_DSTIP,
/* -p */ XT_INV_PROTO,
/* -j */ 0,
/* -v */ 0,
/* -x */ 0,
/* -i */ IPT_INV_VIA_IN,
/* -o */ IPT_INV_VIA_OUT,
/*--line*/ 0,
/* -c */ 0,
/* -f */ IPT_INV_FRAG,
};
#define opts xtables_globals.opts
#define prog_name xtables_globals.program_name
#define prog_vers xtables_globals.program_version
static void __attribute__((noreturn))
exit_tryhelp(int status)
{
if (line != -1)
fprintf(stderr, "Error occurred at line: %d\n", line);
fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
prog_name, prog_name);
xtables_free_opts(1);
exit(status);
}
static void
exit_printhelp(const struct xtables_rule_match *matches)
{
printf("%s v%s\n\n"
"Usage: %s -[ACD] chain rule-specification [options]\n"
" %s -I chain [rulenum] rule-specification [options]\n"
" %s -R chain rulenum rule-specification [options]\n"
" %s -D chain rulenum [options]\n"
" %s -[LS] [chain [rulenum]] [options]\n"
" %s -[FZ] [chain] [options]\n"
" %s -[NX] chain\n"
" %s -E old-chain-name new-chain-name\n"
" %s -P chain target [options]\n"
" %s -h (print this help information)\n\n",
prog_name, prog_vers, prog_name, prog_name,
prog_name, prog_name, prog_name, prog_name,
prog_name, prog_name, prog_name, prog_name);
printf(
"Commands:\n"
"Either long or short options are allowed.\n"
" --append -A chain Append to chain\n"
" --check -C chain Check for the existence of a rule\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 [rulenum]]\n"
" List the rules in a chain or all chains\n"
" --list-rules -S [chain [rulenum]]\n"
" Print the rules in a chain or all chains\n"
" --flush -F [chain] Delete all rules in chain or all chains\n"
" --zero -Z [chain [rulenum]]\n"
" Zero counters in chain or all chains\n"
" --new -N chain Create a new user-defined chain\n"
" --delete-chain\n"
" -X [chain] Delete a user-defined chain\n"
" --policy -P chain target\n"
" Change policy on chain to target\n"
" --rename-chain\n"
" -E old-chain new-chain\n"
" Change chain name, (moving any references)\n"
"Options:\n"
" --ipv4 -4 Nothing (line is ignored by ip6tables-restore)\n"
" --ipv6 -6 Error (line is ignored by iptables-restore)\n"
"[!] --proto -p proto protocol: by number or name, eg. `tcp'\n"
"[!] --source -s address[/mask][...]\n"
" source specification\n"
"[!] --destination -d address[/mask][...]\n"
" destination specification\n"
"[!] --in-interface -i input name[+]\n"
" network interface name ([+] for wildcard)\n"
" --jump -j target\n"
" target for rule (may load target extension)\n"
#ifdef IPT_F_GOTO
" --goto -g chain\n"
" jump to chain with no return\n"
#endif
" --match -m match\n"
" extended match (may load extension)\n"
" --numeric -n numeric output of addresses and ports\n"
"[!] --out-interface -o output name[+]\n"
" network interface name ([+] for wildcard)\n"
" --table -t table table to manipulate (default: `filter')\n"
" --verbose -v verbose mode\n"
" --line-numbers print line numbers when listing\n"
" --exact -x expand numbers (display exact values)\n"
"[!] --fragment -f match second or further fragments only\n"
" --modprobe=<command> try to insert modules using this command\n"
" --set-counters PKTS BYTES set the counter during insert/append\n"
"[!] --version -V print package version.\n");
print_extension_helps(xtables_targets, matches);
exit(0);
}
void
xtables_exit_error(enum xtables_exittype status, const char *msg, ...)
{
va_list args;
va_start(args, msg);
fprintf(stderr, "%s v%s: ", prog_name, prog_vers);
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr, "\n");
if (status == PARAMETER_PROBLEM)
exit_tryhelp(status);
if (status == VERSION_PROBLEM)
fprintf(stderr,
"Perhaps iptables or your kernel needs to be upgraded.\n");
/* On error paths, make sure that we don't leak memory */
xtables_free_opts(1);
exit(status);
}
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] == '+')
xtables_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)
xtables_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 void
add_command(unsigned int *cmd, const int newcmd, const int othercmds,
int invert)
{
if (invert)
xtables_error(PARAMETER_PROBLEM, "unexpected ! flag");
if (*cmd & (~othercmds))
xtables_error(PARAMETER_PROBLEM, "Cannot use -%c with -%c\n",
cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
*cmd |= newcmd;
}
/*
* 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.
*/
/* Christophe Burki wants `-p 6' to imply `-m tcp'. */
/* Can't be zero. */
static int
parse_rulenumber(const char *rule)
{
unsigned int rulenum;
if (!xtables_strtoui(rule, NULL, &rulenum, 1, INT_MAX))
xtables_error(PARAMETER_PROBLEM,
"Invalid rule number `%s'", rule);
return rulenum;
}
static const char *
parse_target(const char *targetname)
{
const char *ptr;
if (strlen(targetname) < 1)
xtables_error(PARAMETER_PROBLEM,
"Invalid target name (too short)");
if (strlen(targetname) >= XT_EXTENSION_MAXNAMELEN)
xtables_error(PARAMETER_PROBLEM,
"Invalid target name `%s' (%u chars max)",
targetname, XT_EXTENSION_MAXNAMELEN - 1);
for (ptr = targetname; *ptr; ptr++)
if (isspace(*ptr))
xtables_error(PARAMETER_PROBLEM,
"Invalid target name `%s'", targetname);
return targetname;
}
static void
set_option(unsigned int *options, unsigned int option, uint8_t *invflg,
int invert)
{
if (*options & option)
xtables_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
opt2char(option));
*options |= option;
if (invert) {
unsigned int i;
for (i = 0; 1 << i != option; i++);
if (!inverse_for_options[i])
xtables_error(PARAMETER_PROBLEM,
"cannot have ! before -%c",
opt2char(option));
*invflg |= inverse_for_options[i];
}
}
static int
add_entry(const char *chain,
const char *table,
struct iptables_command_state *cs,
int rulenum, int family,
const struct addr_mask s,
const struct addr_mask d,
bool verbose, struct nft_handle *h, bool append)
{
unsigned int i, j;
int ret = 1;
for (i = 0; i < s.naddrs; i++) {
if (family == AF_INET) {
cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
for (j = 0; j < d.naddrs; j++) {
cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;
if (append) {
ret = nft_rule_append(h, chain, table,
cs, 0,
verbose);
} else {
ret = nft_rule_insert(h, chain, table,
cs, rulenum,
verbose);
}
}
} else if (family == AF_INET6) {
memcpy(&cs->fw6.ipv6.src,
&s.addr.v6[i], sizeof(struct in6_addr));
memcpy(&cs->fw6.ipv6.smsk,
&s.mask.v6[i], sizeof(struct in6_addr));
for (j = 0; j < d.naddrs; j++) {
memcpy(&cs->fw6.ipv6.dst,
&d.addr.v6[j], sizeof(struct in6_addr));
memcpy(&cs->fw6.ipv6.dmsk,
&d.mask.v6[j], sizeof(struct in6_addr));
if (append) {
ret = nft_rule_append(h, chain, table,
cs, 0,
verbose);
} else {
ret = nft_rule_insert(h, chain, table,
cs, rulenum,
verbose);
}
}
}
}
return ret;
}
static int
replace_entry(const char *chain, const char *table,
struct iptables_command_state *cs,
unsigned int rulenum,
int family,
const struct addr_mask s,
const struct addr_mask d,
bool verbose, struct nft_handle *h)
{
if (family == AF_INET) {
cs->fw.ip.src.s_addr = s.addr.v4->s_addr;
cs->fw.ip.dst.s_addr = d.addr.v4->s_addr;
cs->fw.ip.smsk.s_addr = s.mask.v4->s_addr;
cs->fw.ip.dmsk.s_addr = d.mask.v4->s_addr;
} else if (family == AF_INET6) {
memcpy(&cs->fw6.ipv6.src, s.addr.v6, sizeof(struct in6_addr));
memcpy(&cs->fw6.ipv6.dst, d.addr.v6, sizeof(struct in6_addr));
memcpy(&cs->fw6.ipv6.smsk, s.mask.v6, sizeof(struct in6_addr));
memcpy(&cs->fw6.ipv6.dmsk, d.mask.v6, sizeof(struct in6_addr));
} else
return 1;
return nft_rule_replace(h, chain, table, cs, rulenum, verbose);
}
static int
delete_entry(const char *chain, const char *table,
struct iptables_command_state *cs,
int family,
const struct addr_mask s,
const struct addr_mask d,
bool verbose,
struct nft_handle *h)
{
unsigned int i, j;
int ret = 1;
for (i = 0; i < s.naddrs; i++) {
if (family == AF_INET) {
cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
for (j = 0; j < d.naddrs; j++) {
cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;
ret = nft_rule_delete(h, chain,
table, cs, verbose);
}
} else if (family == AF_INET6) {
memcpy(&cs->fw6.ipv6.src,
&s.addr.v6[i], sizeof(struct in6_addr));
memcpy(&cs->fw6.ipv6.smsk,
&s.mask.v6[i], sizeof(struct in6_addr));
for (j = 0; j < d.naddrs; j++) {
memcpy(&cs->fw6.ipv6.dst,
&d.addr.v6[j], sizeof(struct in6_addr));
memcpy(&cs->fw6.ipv6.dmsk,
&d.mask.v6[j], sizeof(struct in6_addr));
ret = nft_rule_delete(h, chain,
table, cs, verbose);
}
}
}
return ret;
}
static int
check_entry(const char *chain, const char *table,
struct iptables_command_state *cs,
int family,
const struct addr_mask s,
const struct addr_mask d,
bool verbose, struct nft_handle *h)
{
unsigned int i, j;
int ret = 1;
for (i = 0; i < s.naddrs; i++) {
if (family == AF_INET) {
cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
for (j = 0; j < d.naddrs; j++) {
cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;
ret = nft_rule_check(h, chain,
table, cs, verbose);
}
} else if (family == AF_INET6) {
memcpy(&cs->fw6.ipv6.src,
&s.addr.v6[i], sizeof(struct in6_addr));
memcpy(&cs->fw6.ipv6.smsk,
&s.mask.v6[i], sizeof(struct in6_addr));
for (j = 0; j < d.naddrs; j++) {
memcpy(&cs->fw6.ipv6.dst,
&d.addr.v6[j], sizeof(struct in6_addr));
memcpy(&cs->fw6.ipv6.dmsk,
&d.mask.v6[j], sizeof(struct in6_addr));
ret = nft_rule_check(h, chain,
table, cs, verbose);
}
}
}
return ret;
}
static int
list_entries(struct nft_handle *h, const char *chain, const char *table,
int rulenum, int verbose, int numeric, int expanded,
int linenumbers)
{
unsigned int format;
format = FMT_OPTIONS;
if (!verbose)
format |= FMT_NOCOUNTS;
else
format |= FMT_VIA;
if (numeric)
format |= FMT_NUMERIC;
if (!expanded)
format |= FMT_KILOMEGAGIGA;
if (linenumbers)
format |= FMT_LINENUMBERS;
return nft_rule_list(h, chain, table, rulenum, format);
}
static int
list_rules(struct nft_handle *h, const char *chain, const char *table,
int rulenum, int counters)
{
if (counters)
counters = -1; /* iptables -c format */
nft_rule_list_save(h, chain, table, rulenum, counters);
/* iptables does not return error if rule number not found */
return 1;
}
static void command_jump(struct iptables_command_state *cs)
{
size_t size;
set_option(&cs->options, OPT_JUMP, &cs->fw.ip.invflags, cs->invert);
cs->jumpto = parse_target(optarg);
/* TRY_LOAD (may be chain name) */
cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
if (cs->target == NULL)
return;
size = XT_ALIGN(sizeof(struct xt_entry_target))
+ cs->target->size;
cs->target->t = xtables_calloc(1, size);
cs->target->t->u.target_size = size;
if (cs->target->real_name == NULL) {
strcpy(cs->target->t->u.user.name, cs->jumpto);
} else {
/* Alias support for userspace side */
strcpy(cs->target->t->u.user.name, cs->target->real_name);
if (!(cs->target->ext_flags & XTABLES_EXT_ALIAS))
fprintf(stderr, "Notice: The %s target is converted into %s target "
"in rule listing and saving.\n",
cs->jumpto, cs->target->real_name);
}
cs->target->t->u.user.revision = cs->target->revision;
xs_init_target(cs->target);
if (cs->target->x6_options != NULL)
opts = xtables_options_xfrm(xtables_globals.orig_opts, opts,
cs->target->x6_options,
&cs->target->option_offset);
else
opts = xtables_merge_options(xtables_globals.orig_opts, opts,
cs->target->extra_opts,
&cs->target->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
}
static void command_match(struct iptables_command_state *cs)
{
struct xtables_match *m;
size_t size;
if (cs->invert)
xtables_error(PARAMETER_PROBLEM,
"unexpected ! flag before --match");
m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, &cs->matches);
size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
m->m = xtables_calloc(1, size);
m->m->u.match_size = size;
if (m->real_name == NULL) {
strcpy(m->m->u.user.name, m->name);
} else {
strcpy(m->m->u.user.name, m->real_name);
if (!(m->ext_flags & XTABLES_EXT_ALIAS))
fprintf(stderr, "Notice: the %s match is converted into %s match "
"in rule listing and saving.\n", m->name, m->real_name);
}
m->m->u.user.revision = m->revision;
xs_init_match(m);
if (m == m->next)
return;
/* Merge options for non-cloned matches */
if (m->x6_options != NULL)
opts = xtables_options_xfrm(xtables_globals.orig_opts, opts,
m->x6_options, &m->option_offset);
else if (m->extra_opts != NULL)
opts = xtables_merge_options(xtables_globals.orig_opts, opts,
m->extra_opts, &m->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
}
int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
bool restore)
{
struct iptables_command_state cs;
int verbose = 0;
int wait = 0;
const char *chain = NULL;
const char *policy = NULL, *newname = NULL;
unsigned int rulenum = 0, command = 0;
int ret = 1;
struct xtables_match *m;
struct xtables_rule_match *matchp;
struct xtables_target *t;
struct xtables_args args = {
.family = h->family,
};
memset(&cs, 0, sizeof(cs));
cs.jumpto = "";
cs.argv = argv;
/* re-set optind to 0 in case do_command4 gets called
* a second time */
optind = 0;
/* clear mflags in case do_command4 gets called a second time
* (we clear the global list of all matches for security)*/
for (m = xtables_matches; m; m = m->next)
m->mflags = 0;
for (t = xtables_targets; t; t = t->next) {
t->tflags = 0;
t->used = 0;
}
/* Suppress error messages: we may add new options if we
demand-load a protocol. */
opterr = 0;
h->ops = nft_family_ops_lookup(h->family);
if (h->ops == NULL)
xtables_error(PARAMETER_PROBLEM, "Unknown family");
opts = xt_params->orig_opts;
while ((cs.c = getopt_long(argc, argv,
"-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvw::nt:m:xc:g:46",
opts, NULL)) != -1) {
switch (cs.c) {
/*
* Command selection
*/
case 'A':
add_command(&command, CMD_APPEND, CMD_NONE,
cs.invert);
chain = optarg;
break;
case 'C':
add_command(&command, CMD_CHECK, CMD_NONE,
cs.invert);
chain = optarg;
break;
case 'D':
add_command(&command, CMD_DELETE, CMD_NONE,
cs.invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!') {
rulenum = parse_rulenumber(argv[optind++]);
command = CMD_DELETE_NUM;
}
break;
case 'R':
add_command(&command, CMD_REPLACE, CMD_NONE,
cs.invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
rulenum = parse_rulenumber(argv[optind++]);
else
xtables_error(PARAMETER_PROBLEM,
"-%c requires a rule number",
cmd2char(CMD_REPLACE));
break;
case 'I':
add_command(&command, CMD_INSERT, CMD_NONE,
cs.invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
rulenum = parse_rulenumber(argv[optind++]);
else rulenum = 1;
break;
case 'L':
add_command(&command, CMD_LIST,
CMD_ZERO | CMD_ZERO_NUM, cs.invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
chain = argv[optind++];
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
rulenum = parse_rulenumber(argv[optind++]);
break;
case 'S':
add_command(&command, CMD_LIST_RULES,
CMD_ZERO|CMD_ZERO_NUM, cs.invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
chain = argv[optind++];
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
rulenum = parse_rulenumber(argv[optind++]);
break;
case 'F':
add_command(&command, CMD_FLUSH, CMD_NONE,
cs.invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
chain = argv[optind++];
break;
case 'Z':
add_command(&command, CMD_ZERO, CMD_LIST|CMD_LIST_RULES,
cs.invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
chain = argv[optind++];
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!') {
rulenum = parse_rulenumber(argv[optind++]);
command = CMD_ZERO_NUM;
}
break;
case 'N':
if (optarg && (*optarg == '-' || *optarg == '!'))
xtables_error(PARAMETER_PROBLEM,
"chain name not allowed to start "
"with `%c'\n", *optarg);
if (xtables_find_target(optarg, XTF_TRY_LOAD))
xtables_error(PARAMETER_PROBLEM,
"chain name may not clash "
"with target name\n");
add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
cs.invert);
chain = optarg;
break;
case 'X':
add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
cs.invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
chain = argv[optind++];
break;
case 'E':
add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
cs.invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
newname = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM,
"-%c requires old-chain-name and "
"new-chain-name",
cmd2char(CMD_RENAME_CHAIN));
break;
case 'P':
add_command(&command, CMD_SET_POLICY, CMD_NONE,
cs.invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
policy = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM,
"-%c requires a chain and a policy",
cmd2char(CMD_SET_POLICY));
break;
case 'h':
if (!optarg)
optarg = argv[optind];
/* iptables -p icmp -h */
if (!cs.matches && cs.protocol)
xtables_find_match(cs.protocol,
XTF_TRY_LOAD, &cs.matches);
exit_printhelp(cs.matches);
/*
* Option selection
*/
case 'p':
set_option(&cs.options, OPT_PROTOCOL, &args.invflags,
cs.invert);
/* Canonicalize into lower case */
for (cs.protocol = optarg; *cs.protocol; cs.protocol++)
*cs.protocol = tolower(*cs.protocol);
cs.protocol = optarg;
args.proto = xtables_parse_protocol(cs.protocol);
if (args.proto == 0 && (args.invflags & XT_INV_PROTO))
xtables_error(PARAMETER_PROBLEM,
"rule would never match protocol");
/* This needs to happen here to parse extensions */
h->ops->proto_parse(&cs, &args);
break;
case 's':
set_option(&cs.options, OPT_SOURCE, &args.invflags,
cs.invert);
args.shostnetworkmask = optarg;
break;
case 'd':
set_option(&cs.options, OPT_DESTINATION,
&args.invflags, cs.invert);
args.dhostnetworkmask = optarg;
break;
#ifdef IPT_F_GOTO
case 'g':
set_option(&cs.options, OPT_JUMP, &args.invflags,
cs.invert);
args.goto_set = true;
cs.jumpto = parse_target(optarg);
break;
#endif
case 'j':
command_jump(&cs);
break;
case 'i':
if (*optarg == '\0')
xtables_error(PARAMETER_PROBLEM,
"Empty interface is likely to be "
"undesired");
set_option(&cs.options, OPT_VIANAMEIN, &args.invflags,
cs.invert);
xtables_parse_interface(optarg,
args.iniface,
args.iniface_mask);
break;
case 'o':
if (*optarg == '\0')
xtables_error(PARAMETER_PROBLEM,
"Empty interface is likely to be "
"undesired");
set_option(&cs.options, OPT_VIANAMEOUT, &args.invflags,
cs.invert);
xtables_parse_interface(optarg,
args.outiface,
args.outiface_mask);
break;
case 'f':
if (args.family == AF_INET6) {
xtables_error(PARAMETER_PROBLEM,
"`-f' is not supported in IPv6, "
"use -m frag instead");
}
set_option(&cs.options, OPT_FRAGMENT, &args.invflags,
cs.invert);
args.flags |= IPT_F_FRAG;
break;
case 'v':
if (!verbose)
set_option(&cs.options, OPT_VERBOSE,
&args.invflags, cs.invert);
verbose++;
break;
case 'm':
command_match(&cs);
break;
case 'n':
set_option(&cs.options, OPT_NUMERIC, &args.invflags,
cs.invert);
break;
case 't':
if (cs.invert)
xtables_error(PARAMETER_PROBLEM,
"unexpected ! flag before --table");
*table = optarg;
break;
case 'x':
set_option(&cs.options, OPT_EXPANDED, &args.invflags,
cs.invert);
break;
case 'V':
if (cs.invert)
printf("Not %s ;-)\n", prog_vers);
else
printf("%s v%s\n",
prog_name, prog_vers);
exit(0);
case 'w':
if (restore) {
xtables_error(PARAMETER_PROBLEM,
"You cannot use `-w' from "
"iptables-restore");
}
if (optarg) {
if (sscanf(optarg, "%i", &wait) != 1)
xtables_error(PARAMETER_PROBLEM,
"wait seconds not numeric");
} else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
if (sscanf(argv[optind++], "%i", &wait) != 1)
xtables_error(PARAMETER_PROBLEM,
"wait seconds not numeric");
break;
case '0':
set_option(&cs.options, OPT_LINENUMBERS,
&args.invflags, cs.invert);
break;
case 'M':
xtables_modprobe_program = optarg;
break;
case 'c':
set_option(&cs.options, OPT_COUNTERS, &args.invflags,
cs.invert);
args.pcnt = optarg;
args.bcnt = strchr(args.pcnt + 1, ',');
if (args.bcnt)
args.bcnt++;
if (!args.bcnt && optind < argc &&
argv[optind][0] != '-' &&
argv[optind][0] != '!')
args.bcnt = argv[optind++];
if (!args.bcnt)
xtables_error(PARAMETER_PROBLEM,
"-%c requires packet and byte counter",
opt2char(OPT_COUNTERS));
if (sscanf(args.pcnt, "%llu", &args.pcnt_cnt) != 1)
xtables_error(PARAMETER_PROBLEM,
"-%c packet counter not numeric",
opt2char(OPT_COUNTERS));
if (sscanf(args.bcnt, "%llu", &args.bcnt_cnt) != 1)
xtables_error(PARAMETER_PROBLEM,
"-%c byte counter not numeric",
opt2char(OPT_COUNTERS));
break;
case '4':
if (args.family != AF_INET)
exit_tryhelp(2);
h->ops = nft_family_ops_lookup(args.family);
break;
case '6':
args.family = AF_INET6;
xtables_set_nfproto(AF_INET6);
h->ops = nft_family_ops_lookup(args.family);
if (h->ops == NULL)
xtables_error(PARAMETER_PROBLEM,
"Unknown family");
break;
case 1: /* non option */
if (optarg[0] == '!' && optarg[1] == '\0') {
if (cs.invert)
xtables_error(PARAMETER_PROBLEM,
"multiple consecutive ! not"
" allowed");
cs.invert = TRUE;
optarg[0] = '\0';
continue;
}
fprintf(stderr, "Bad argument `%s'\n", optarg);
exit_tryhelp(2);
default:
if (command_default(&cs, &xtables_globals) == 1)
/* cf. ip6tables.c */
continue;
break;
}
cs.invert = FALSE;
}
if (strcmp(*table, "nat") == 0 &&
((policy != NULL && strcmp(policy, "DROP") == 0) ||
(cs.jumpto != NULL && strcmp(cs.jumpto, "DROP") == 0)))
xtables_error(PARAMETER_PROBLEM,
"\nThe \"nat\" table is not intended for filtering, "
"the use of DROP is therefore inhibited.\n\n");
for (matchp = cs.matches; matchp; matchp = matchp->next)
xtables_option_mfcall(matchp->match);
if (cs.target != NULL)
xtables_option_tfcall(cs.target);
/* Fix me: must put inverse options checking here --MN */
if (optind < argc)
xtables_error(PARAMETER_PROBLEM,
"unknown arguments found on commandline");
if (!command)
xtables_error(PARAMETER_PROBLEM, "no command specified");
if (cs.invert)
xtables_error(PARAMETER_PROBLEM,
"nothing appropriate following !");
/* Set only if required, needed by xtables-restore */
if (h->family == AF_UNSPEC)
h->family = args.family;
h->ops->post_parse(command, &cs, &args);
if (command == CMD_REPLACE &&
(args.s.naddrs != 1 || args.d.naddrs != 1))
xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
"specify a unique address");
generic_opt_check(command, cs.options);
if (chain != NULL && strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
xtables_error(PARAMETER_PROBLEM,
"chain name `%s' too long (must be under %u chars)",
chain, XT_EXTENSION_MAXNAMELEN);
if (command == CMD_APPEND
|| command == CMD_DELETE
|| command == CMD_CHECK
|| command == CMD_INSERT
|| command == CMD_REPLACE) {
if (strcmp(chain, "PREROUTING") == 0
|| strcmp(chain, "INPUT") == 0) {
/* -o not valid with incoming packets. */
if (cs.options & OPT_VIANAMEOUT)
xtables_error(PARAMETER_PROBLEM,
"Can't use -%c with %s\n",
opt2char(OPT_VIANAMEOUT),
chain);
}
if (strcmp(chain, "POSTROUTING") == 0
|| strcmp(chain, "OUTPUT") == 0) {
/* -i not valid with outgoing packets */
if (cs.options & OPT_VIANAMEIN)
xtables_error(PARAMETER_PROBLEM,
"Can't use -%c with %s\n",
opt2char(OPT_VIANAMEIN),
chain);
}
/*
* Contrary to what iptables does, we assume that any jumpto
* is a custom chain jumps (if no target is found). Later on,
* nf_table will spot the error if the chain does not exists.
*/
}
switch (command) {
case CMD_APPEND:
ret = add_entry(chain, *table, &cs, 0, h->family,
args.s, args.d, cs.options&OPT_VERBOSE,
h, true);
break;
case CMD_DELETE:
ret = delete_entry(chain, *table, &cs, h->family,
args.s, args.d, cs.options&OPT_VERBOSE, h);
break;
case CMD_DELETE_NUM:
ret = nft_rule_delete_num(h, chain, *table, rulenum - 1, verbose);
break;
case CMD_CHECK:
ret = check_entry(chain, *table, &cs, h->family,
args.s, args.d, cs.options&OPT_VERBOSE, h);
break;
case CMD_REPLACE:
ret = replace_entry(chain, *table, &cs, rulenum - 1,
h->family, args.s, args.d,
cs.options&OPT_VERBOSE, h);
break;
case CMD_INSERT:
ret = add_entry(chain, *table, &cs, rulenum - 1, h->family,
args.s, args.d, cs.options&OPT_VERBOSE, h,
false);
break;
case CMD_FLUSH:
ret = nft_rule_flush(h, chain, *table);
break;
case CMD_ZERO:
ret = nft_chain_zero_counters(h, chain, *table);
break;
case CMD_ZERO_NUM:
ret = nft_rule_zero_counters(h, chain, *table, rulenum - 1);
break;
case CMD_LIST:
case CMD_LIST|CMD_ZERO:
case CMD_LIST|CMD_ZERO_NUM:
ret = list_entries(h, chain, *table,
rulenum,
cs.options&OPT_VERBOSE,
cs.options&OPT_NUMERIC,
cs.options&OPT_EXPANDED,
cs.options&OPT_LINENUMBERS);
if (ret && (command & CMD_ZERO))
ret = nft_chain_zero_counters(h, chain, *table);
if (ret && (command & CMD_ZERO_NUM))
ret = nft_rule_zero_counters(h, chain, *table,
rulenum - 1);
break;
case CMD_LIST_RULES:
case CMD_LIST_RULES|CMD_ZERO:
case CMD_LIST_RULES|CMD_ZERO_NUM:
ret = list_rules(h, chain, *table, rulenum, cs.options&OPT_VERBOSE);
if (ret && (command & CMD_ZERO))
ret = nft_chain_zero_counters(h, chain, *table);
if (ret && (command & CMD_ZERO_NUM))
ret = nft_rule_zero_counters(h, chain, *table,
rulenum - 1);
break;
case CMD_NEW_CHAIN:
ret = nft_chain_user_add(h, chain, *table);
break;
case CMD_DELETE_CHAIN:
ret = nft_chain_user_del(h, chain, *table);
break;
case CMD_RENAME_CHAIN:
ret = nft_chain_user_rename(h, chain, *table, newname);
break;
case CMD_SET_POLICY:
ret = nft_chain_set(h, *table, chain, policy, NULL);
if (ret < 0)
xtables_error(PARAMETER_PROBLEM, "Wrong policy `%s'\n",
policy);
break;
default:
/* We should never reach this... */
exit_tryhelp(2);
}
/* if (verbose > 1)
dump_entries(*handle); */
xtables_rule_matches_free(&cs.matches);
if (h->family == AF_INET) {
free(args.s.addr.v4);
free(args.s.mask.v4);
free(args.d.addr.v4);
free(args.d.mask.v4);
} else if (h->family == AF_INET6) {
free(args.s.addr.v6);
free(args.s.mask.v6);
free(args.d.addr.v6);
free(args.d.mask.v6);
}
xtables_free_opts(1);
return ret;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment