Commit dab1e98e authored by Arturo Borrero Gonzalez's avatar Arturo Borrero Gonzalez
Browse files

New upstream version 1.8.1

parent f1f129da
......@@ -276,7 +276,7 @@ iptables_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);
fprintf(stderr, "%s v%s (legacy): ", prog_name, prog_vers);
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr, "\n");
......@@ -405,27 +405,6 @@ parse_chain(const char *chainname)
"Invalid chain name `%s'", chainname);
}
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)
......@@ -502,19 +481,23 @@ print_match(const struct xt_entry_match *m,
const struct ipt_ip *ip,
int numeric)
{
const struct xtables_match *match =
xtables_find_match(m->u.user.name, XTF_TRY_LOAD, NULL);
const char *name = m->u.user.name;
const int revision = m->u.user.revision;
struct xtables_match *match, *mt;
match = xtables_find_match(name, XTF_TRY_LOAD, NULL);
if (match) {
if (match->print && m->u.user.revision == match->revision)
match->print(ip, m, numeric);
mt = xtables_find_match_revision(name, XTF_TRY_LOAD,
match, revision);
if (mt && mt->print)
mt->print(ip, m, numeric);
else if (match->print)
printf("%s%s ", match->name, unsupported_rev);
else
printf("%s ", match->name);
} else {
if (m->u.user.name[0])
printf("UNKNOWN match `%s' ", m->u.user.name);
if (name[0])
printf("UNKNOWN match `%s' ", name);
}
/* Don't stop iterating. */
return 0;
......@@ -528,10 +511,9 @@ print_firewall(const struct ipt_entry *fw,
unsigned int format,
struct xtc_handle *const handle)
{
const struct xtables_target *target = NULL;
struct xtables_target *target, *tg;
const struct xt_entry_target *t;
uint8_t flags;
char buf[BUFSIZ];
if (!iptc_is_chain(targname, handle))
target = xtables_find_target(targname, XTF_TRY_LOAD);
......@@ -570,59 +552,9 @@ print_firewall(const struct ipt_entry *fw,
fputc(' ', stdout);
}
if (format & FMT_VIA) {
char iface[IFNAMSIZ+2];
if (fw->ip.invflags & IPT_INV_VIA_IN) {
iface[0] = '!';
iface[1] = '\0';
}
else iface[0] = '\0';
if (fw->ip.iniface[0] != '\0') {
strcat(iface, fw->ip.iniface);
}
else if (format & FMT_NUMERIC) strcat(iface, "*");
else strcat(iface, "any");
printf(FMT(" %-6s ","in %s "), iface);
print_ifaces(fw->ip.iniface, fw->ip.outiface, fw->ip.invflags, format);
if (fw->ip.invflags & IPT_INV_VIA_OUT) {
iface[0] = '!';
iface[1] = '\0';
}
else iface[0] = '\0';
if (fw->ip.outiface[0] != '\0') {
strcat(iface, fw->ip.outiface);
}
else if (format & FMT_NUMERIC) strcat(iface, "*");
else strcat(iface, "any");
printf(FMT("%-6s ","out %s "), iface);
}
fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC))
printf(FMT("%-19s ","%s "), "anywhere");
else {
if (format & FMT_NUMERIC)
strcpy(buf, xtables_ipaddr_to_numeric(&fw->ip.src));
else
strcpy(buf, xtables_ipaddr_to_anyname(&fw->ip.src));
strcat(buf, xtables_ipmask_to_numeric(&fw->ip.smsk));
printf(FMT("%-19s ","%s "), buf);
}
fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
printf(FMT("%-19s ","-> %s"), "anywhere");
else {
if (format & FMT_NUMERIC)
strcpy(buf, xtables_ipaddr_to_numeric(&fw->ip.dst));
else
strcpy(buf, xtables_ipaddr_to_anyname(&fw->ip.dst));
strcat(buf, xtables_ipmask_to_numeric(&fw->ip.dmsk));
printf(FMT("%-19s ","-> %s"), buf);
}
print_ipv4_addresses(fw, format);
if (format & FMT_NOTABLE)
fputs(" ", stdout);
......@@ -635,9 +567,13 @@ print_firewall(const struct ipt_entry *fw,
IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
if (target) {
if (target->print && t->u.user.revision == target->revision)
const int revision = t->u.user.revision;
tg = xtables_find_target_revision(targname, XTF_TRY_LOAD,
target, revision);
if (tg && tg->print)
/* Print the target information. */
target->print(&fw->ip, t, format & FMT_NUMERIC);
tg->print(&fw->ip, t, format & FMT_NUMERIC);
else if (target->print)
printf(" %s%s", target->name, unsupported_rev);
} else if (t->u.target_size != sizeof(*t))
......@@ -1025,23 +961,28 @@ print_iface(char letter, const char *iface, const unsigned char *mask,
static int print_match_save(const struct xt_entry_match *e,
const struct ipt_ip *ip)
{
const struct xtables_match *match =
xtables_find_match(e->u.user.name, XTF_TRY_LOAD, NULL);
const char *name = e->u.user.name;
const int revision = e->u.user.revision;
struct xtables_match *match, *mt, *mt2;
match = xtables_find_match(name, XTF_TRY_LOAD, NULL);
if (match) {
printf(" -m %s",
match->alias ? match->alias(e) : e->u.user.name);
mt = mt2 = xtables_find_match_revision(name, XTF_TRY_LOAD,
match, revision);
if (!mt2)
mt2 = match;
printf(" -m %s", mt2->alias ? mt2->alias(e) : name);
/* some matches don't provide a save function */
if (match->save && e->u.user.revision == match->revision)
match->save(ip, e);
if (mt && mt->save)
mt->save(ip, e);
else if (match->save)
printf(unsupported_rev);
} else {
if (e->u.match_size) {
fprintf(stderr,
"Can't find library for match `%s'\n",
e->u.user.name);
name);
exit(1);
}
}
......@@ -1114,9 +1055,8 @@ void print_rule4(const struct ipt_entry *e,
e->ip.invflags & IPT_INV_FRAG ? " !" : "");
/* Print matchinfo part */
if (e->target_offset) {
if (e->target_offset)
IPT_MATCH_ITERATE(e, print_match_save, &e->ip);
}
/* print counters for iptables -R */
if (counters < 0)
......@@ -1126,18 +1066,25 @@ void print_rule4(const struct ipt_entry *e,
target_name = iptc_get_target(e, h);
t = ipt_get_target((struct ipt_entry *)e);
if (t->u.user.name[0]) {
const struct xtables_target *target =
xtables_find_target(t->u.user.name, XTF_TRY_LOAD);
const char *name = t->u.user.name;
const int revision = t->u.user.revision;
struct xtables_target *target, *tg, *tg2;
target = xtables_find_target(name, XTF_TRY_LOAD);
if (!target) {
fprintf(stderr, "Can't find library for target `%s'\n",
t->u.user.name);
name);
exit(1);
}
printf(" -j %s", target->alias ? target->alias(t) : target_name);
if (target->save && t->u.user.revision == target->revision)
target->save(&e->ip, t);
tg = tg2 = xtables_find_target_revision(name, XTF_TRY_LOAD,
target, revision);
if (!tg2)
tg2 = target;
printf(" -j %s", tg2->alias ? tg2->alias(t) : target_name);
if (tg && tg->save)
tg->save(&e->ip, t);
else if (target->save)
printf(unsupported_rev);
else {
......@@ -1148,7 +1095,7 @@ void print_rule4(const struct ipt_entry *e,
sizeof(struct xt_entry_target)) {
fprintf(stderr, "Target `%s' is missing "
"save function\n",
t->u.user.name);
name);
exit(1);
}
}
......@@ -1243,90 +1190,13 @@ generate_entry(const struct ipt_entry *fw,
return e;
}
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(iptables_globals.orig_opts, opts,
cs->target->x6_options,
&cs->target->option_offset);
else
opts = xtables_merge_options(iptables_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(iptables_globals.orig_opts, opts,
m->x6_options, &m->option_offset);
else if (m->extra_opts != NULL)
opts = xtables_merge_options(iptables_globals.orig_opts, opts,
m->extra_opts, &m->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
}
int do_command4(int argc, char *argv[], char **table,
struct xtc_handle **handle, bool restore)
{
struct iptables_command_state cs;
struct iptables_command_state cs = {
.jumpto = "",
.argv = argv,
};
struct ipt_entry *e = NULL;
unsigned int nsaddrs = 0, ndaddrs = 0;
struct in_addr *saddrs = NULL, *smasks = NULL;
......@@ -1348,10 +1218,6 @@ int do_command4(int argc, char *argv[], char **table,
struct xtables_target *t;
unsigned long long cnt;
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;
......@@ -1393,8 +1259,7 @@ int do_command4(int argc, char *argv[], char **table,
add_command(&command, CMD_DELETE, CMD_NONE,
cs.invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!') {
if (xs_has_arg(argc, argv)) {
rulenum = parse_rulenumber(argv[optind++]);
command = CMD_DELETE_NUM;
}
......@@ -1404,8 +1269,7 @@ int do_command4(int argc, char *argv[], char **table,
add_command(&command, CMD_REPLACE, CMD_NONE,
cs.invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
if (xs_has_arg(argc, argv))
rulenum = parse_rulenumber(argv[optind++]);
else
xtables_error(PARAMETER_PROBLEM,
......@@ -1417,8 +1281,7 @@ int do_command4(int argc, char *argv[], char **table,
add_command(&command, CMD_INSERT, CMD_NONE,
cs.invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
if (xs_has_arg(argc, argv))
rulenum = parse_rulenumber(argv[optind++]);
else rulenum = 1;
break;
......@@ -1427,11 +1290,9 @@ int do_command4(int argc, char *argv[], char **table,
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] != '!')
else if (xs_has_arg(argc, argv))
chain = argv[optind++];
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
if (xs_has_arg(argc, argv))
rulenum = parse_rulenumber(argv[optind++]);
break;
......@@ -1439,11 +1300,9 @@ int do_command4(int argc, char *argv[], char **table,
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] != '!')
else if (xs_has_arg(argc, argv))
chain = argv[optind++];
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
if (xs_has_arg(argc, argv))
rulenum = parse_rulenumber(argv[optind++]);
break;
......@@ -1451,8 +1310,7 @@ int do_command4(int argc, char *argv[], char **table,
add_command(&command, CMD_FLUSH, CMD_NONE,
cs.invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
else if (xs_has_arg(argc, argv))
chain = argv[optind++];
break;
......@@ -1460,11 +1318,9 @@ int do_command4(int argc, char *argv[], char **table,
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] != '!')
else if (xs_has_arg(argc, argv))
chain = argv[optind++];
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!') {
if (xs_has_arg(argc, argv)) {
rulenum = parse_rulenumber(argv[optind++]);
command = CMD_ZERO_NUM;
}
......@@ -1481,8 +1337,7 @@ int do_command4(int argc, char *argv[], char **table,
add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
cs.invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
else if (xs_has_arg(argc, argv))
chain = argv[optind++];
break;
......@@ -1490,8 +1345,7 @@ int do_command4(int argc, char *argv[], char **table,
add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
cs.invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
if (xs_has_arg(argc, argv))
newname = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM,
......@@ -1504,8 +1358,7 @@ int do_command4(int argc, char *argv[], char **table,
add_command(&command, CMD_SET_POLICY, CMD_NONE,
cs.invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
if (xs_has_arg(argc, argv))
policy = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM,
......@@ -1561,11 +1414,13 @@ int do_command4(int argc, char *argv[], char **table,
set_option(&cs.options, OPT_JUMP, &cs.fw.ip.invflags,
cs.invert);
cs.fw.ip.flags |= IPT_F_GOTO;
cs.jumpto = parse_target(optarg);
cs.jumpto = xt_parse_target(optarg);
break;
#endif
case 'j':
set_option(&cs.options, OPT_JUMP, &cs.fw.ip.invflags,
cs.invert);
command_jump(&cs);
break;
......@@ -1613,16 +1468,7 @@ int do_command4(int argc, char *argv[], char **table,
"You cannot use `-w' from "
"iptables-restore");
}
wait = -1;
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");
wait = parse_wait_time(argc, argv);
break;
case 'W':
......@@ -1631,14 +1477,7 @@ int do_command4(int argc, char *argv[], char **table,
"You cannot use `-W' from "
"iptables-restore");
}
if (optarg)
parse_wait_interval(optarg, &wait_interval);
else if (optind < argc &&
argv[optind][0] != '-' &&
argv[optind][0] != '!')
parse_wait_interval(argv[optind++],
&wait_interval);
parse_wait_interval(argc, argv, &wait_interval);
wait_interval_set = true;
break;
......@@ -1667,7 +1506,7 @@ int do_command4(int argc, char *argv[], char **table,
if (cs.invert)
printf("Not %s ;-)\n", prog_vers);
else
printf("%s v%s\n",
printf("%s v%s (legacy)\n",
prog_name, prog_vers);
exit(0);
......@@ -1688,8 +1527,7 @@ int do_command4(int argc, char *argv[], char **table,
bcnt = strchr(pcnt + 1, ',');
if (bcnt)
bcnt++;
if (!bcnt && optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
if (!bcnt && xs_has_arg(argc, argv))
bcnt = argv[optind++];
if (!bcnt)
xtables_error(PARAMETER_PROBLEM,
......@@ -1796,15 +1634,8 @@ int do_command4(int argc, char *argv[], char **table,
generic_opt_check(command, cs.options);
/* Attempt to acquire the xtables lock */
if (!restore && !xtables_lock(wait, &wait_interval)) {
fprintf(stderr, "Another app is currently holding the xtables lock. ");
if (wait == 0)
fprintf(stderr, "Perhaps you want to use the -w option?\n");
else
fprintf(stderr, "Stopped waiting after %ds.\n", wait);
xtables_free_opts(1);
exit(RESOURCE_PROBLEM);
}
if (!restore)
xtables_lock_or_exit(wait, &wait_interval);
/* only allocate handle if we weren't called with a handle */
if (!*handle)
......
......@@ -92,7 +92,7 @@ static char *
mask_to_dotted(const struct in_addr *mask)
{
int i;
static char buf[20];
static char buf[22];
u_int32_t maskaddr, bits;
maskaddr = ntohl(mask->s_addr);
......@@ -109,7 +109,7 @@ mask_to_dotted(const struct in_addr *mask)
sprintf(buf, "/%d", i);
else
/* mask was not a decent combination of 1's and 0's */
sprintf(buf, "/%s", addr_to_dotted(mask));
snprintf(buf, sizeof(buf), "/%s", addr_to_dotted(mask));
return buf;
}
......@@ -139,8 +139,8 @@ static void print_mac_and_mask(const unsigned char *mac, const unsigned char *ma
static int nft_arp_add(struct nftnl_rule *r, void *data)
{
struct arptables_command_state *cs = data;
struct arpt_entry *fw = &cs->fw;
struct iptables_command_state *cs = data;
struct arpt_entry *fw = &cs->arp;
uint32_t op;
int ret = 0;
......@@ -260,8 +260,8 @@ static uint16_t ipt_to_arpt_flags(uint8_t invflags)
static void nft_arp_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
void *data)
{
struct arptables_command_state *cs = data;
struct arpt_entry *fw = &cs->fw;
struct iptables_command_state *cs = data;
struct arpt_entry *fw = &cs->arp;
uint8_t flags = 0;
parse_meta(e, ctx->meta.key, fw->arp.iniface, fw->arp.iniface_mask,
......@@ -271,17 +271,10 @@ static void nft_arp_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
fw->arp.invflags |= ipt_to_arpt_flags(flags);
}
static void nft_arp_parse_target(struct xtables_target *target, void *data)
{
struct arptables_command_state *cs = data;
cs->target = target;
}
static void nft_arp_parse_immediate(const char *jumpto, bool nft_goto,
void *data)
{
struct arptables_command_state *cs = data;
struct iptables_command_state *cs = data;
cs->jumpto = jumpto;
}
......@@ -294,8 +287,8 @@ static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask)
static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
struct nftnl_expr *e, void *data)
{
struct arptables_command_state *cs = data;
struct arpt_entry *fw = &cs->fw;
struct iptables_command_state *cs = data;
struct arpt_entry *fw = &cs->arp;
struct in_addr addr;
unsigned short int ar_hrd, ar_pro, ar_op, ar_hln;
bool inv;
......@@ -330,9 +323,6 @@ static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
fw->arp.invflags |= ARPT_INV_ARPOP;
break;
default:
if (fw->arp.arhln < 0)
break;
if (ctx->payload.offset == sizeof(struct arphdr) +
fw->arp.arhln) {
get_cmp_data(e, &addr, sizeof(addr), &inv);
......@@ -365,14 +355,14 @@ static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
}
}
void nft_rule_to_arptables_command_state(struct nftnl_rule *r,
struct arptables_command_state *cs)
static void nft_arp_rule_to_cs(const 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_arp = cs,
.cs = cs,
.family = family,
};
......@@ -387,7 +377,7 @@ void nft_rule_to_arptables_command_state(struct nftnl_rule *r,
nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
if (strcmp(name, "counter") == 0)
nft_parse_counter(expr, &ctx.state.cs_arp->fw.counters);
nft_parse_counter(expr, &ctx.cs->arp.counters);
else if (strcmp(name, "payload") == 0)
nft_parse_payload(&ctx, expr);
else if (strcmp(name, "meta") == 0)
......@@ -418,10 +408,11 @@ void nft_rule_to_arptables_command_state(struct nftnl_rule *r,
static void nft_arp_print_header(unsigned int format, const char *chain,
const char *pol,
const struct xt_counters *counters,
bool basechain, uint32_t refs)
bool basechain, uint32_t refs,
uint32_t entries)
{
printf("Chain %s", chain);
if (pol) {
if (basechain && pol) {
printf(" (policy %s", pol);
if (!(format & FMT_NOCOUNTS)) {
fputc(' ', stdout);
......@@ -436,7 +427,8 @@ static void nft_arp_print_header(unsigned int format, const char *chain,
}
}
static void print_fw_details(struct arpt_entry *fw, unsigned int format)
static void nft_arp_print_rule_details(const struct arpt_entry *fw,
unsigned int format)
{
char buf[BUFSIZ];
char iface[IFNAMSIZ+2];
......@@ -542,6 +534,7 @@ after_devdst:
if (tmp <= NUMOPCODES && !(format & FMT_NUMERIC))
printf("--opcode %s", opcodes[tmp-1]);
else
printf("--opcode %d", tmp);
if (fw->arp.arpop_mask != 65535)
printf("/%d", ntohs(fw->arp.arpop_mask));
......@@ -578,36 +571,48 @@ after_devdst:
}
static void
nft_arp_print_firewall(struct nftnl_rule *r, unsigned int num,
unsigned int format)
__nft_arp_save_rule(const void *data, unsigned int format)
{
struct arptables_command_state cs = {};
nft_rule_to_arptables_command_state(r, &cs);
const struct iptables_command_state *cs = data;
if (format & FMT_LINENUMBERS)
printf("%u ", num);
nft_arp_print_rule_details(&cs->arp, format);
print_fw_details(&cs.fw, format);
if (cs.jumpto != NULL && strcmp(cs.jumpto, "") != 0) {
printf("-j %s", cs.jumpto);
} else if (cs.target) {
printf("-j %s", cs.target->name);
cs.target->print(&cs.fw, cs.target->t, format & FMT_NUMERIC);
if (cs->jumpto != NULL && strcmp(cs->jumpto, "") != 0) {
printf("-j %s", cs->jumpto);
} else if (cs->target) {
printf("-j %s", cs->target->name);
cs->target->print(&cs->arp, cs->target->t, format & FMT_NUMERIC);
}
if (!(format & FMT_NOCOUNTS)) {
printf(", pcnt=");
xtables_print_num(cs.fw.counters.pcnt, format);
xtables_print_num(cs->arp.counters.pcnt, format);
printf("-- bcnt=");
xtables_print_num(cs.fw.counters.bcnt, format);
xtables_print_num(cs->arp.counters.bcnt, format);
}
if (!(format & FMT_NONEWLINE))
fputc('\n', stdout);
}
static void
nft_arp_save_rule(const void *data, unsigned int format)
{
__nft_arp_save_rule(data, format | FMT_NUMERIC);
}
static void
nft_arp_print_rule(struct nftnl_rule *r, unsigned int num, unsigned int format)
{
struct iptables_command_state cs = {};
if (format & FMT_LINENUMBERS)
printf("%u ", num);
nft_arp_rule_to_cs(r, &cs);
__nft_arp_save_rule(&cs, format);
}
static bool nft_arp_is_same(const void *data_a,
const void *data_b)
{
......@@ -637,24 +642,31 @@ static bool nft_arp_is_same(const void *data_a,
static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
void *data)
{
const struct arptables_command_state *cs = data;
struct arptables_command_state this = {};
const struct iptables_command_state *cs = data;
struct iptables_command_state this = {};
/* Delete by matching rule case */
nft_rule_to_arptables_command_state(r, &this);
nft_arp_rule_to_cs(r, &this);
if (!nft_arp_is_same(cs, &this))
if (!nft_arp_is_same(&cs->arp, &this.arp))
return false;
if (!compare_targets(cs->target, this.target))
return false;
if (strcmp(cs->jumpto, this.jumpto) != 0)
if (this.jumpto && strcmp(cs->jumpto, this.jumpto) != 0)
return false;
return true;
}
static void nft_arp_save_chain(const struct nftnl_chain *c, const char *policy)
{
const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
printf(":%s %s\n", chain, policy ?: "-");
}
struct nft_family_ops nft_family_ops_arp = {
.add = nft_arp_add,
.is_same = nft_arp_is_same,
......@@ -663,10 +675,13 @@ struct nft_family_ops nft_family_ops_arp = {
.parse_payload = nft_arp_parse_payload,
.parse_immediate = nft_arp_parse_immediate,
.print_header = nft_arp_print_header,
.print_firewall = nft_arp_print_firewall,
.save_firewall = NULL,
.print_rule = nft_arp_print_rule,
.save_rule = nft_arp_save_rule,
.save_counters = NULL,
.save_chain = nft_arp_save_chain,
.post_parse = NULL,
.rule_to_cs = nft_arp_rule_to_cs,
.clear_cs = nft_clear_iptables_command_state,
.rule_find = nft_arp_rule_find,
.parse_target = nft_arp_parse_target,
.parse_target = nft_ipv46_parse_target,
};
......@@ -4,13 +4,4 @@
extern char *opcodes[];
#define NUMOPCODES 9
struct arptables_command_state {
struct arpt_entry fw;
struct xtables_target *target;
const char *jumpto;
};
void nft_rule_to_arptables_command_state(struct nftnl_rule *r,
struct arptables_command_state *cs);
#endif
......@@ -16,22 +16,30 @@
#include <xtables.h>
#include <libiptc/libxtc.h>
#include <linux/netfilter/nf_tables.h>
#include <ebtables/ethernetdb.h>
#include "nft-shared.h"
#include "nft-bridge.h"
#include "nft.h"
void ebt_cs_clean(struct ebtables_command_state *cs)
void ebt_cs_clean(struct iptables_command_state *cs)
{
struct ebt_match *m, *nm;
xtables_rule_matches_free(&cs->matches);
for (m = cs->match_list; m;) {
if (!m->ismatch) {
struct xtables_target *target = m->u.watcher;
if (target->t) {
free(target->t);
target->t = NULL;
}
if (target == target->next)
free(target);
}
nm = m->next;
if (!m->ismatch)
free(m->u.watcher->t);
free(m);
m = nm;
}
......@@ -54,10 +62,16 @@ static void ebt_print_mac(const unsigned char *mac)
printf("%s", ether_ntoa((struct ether_addr *) mac));
}
static bool mac_all_ones(const unsigned char *mac)
{
static const char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
return memcmp(mac, hlpmsk, sizeof(hlpmsk)) == 0;
}
/* Put the mac address into 6 (ETH_ALEN) bytes returns 0 on success. */
static void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask)
{
char hlpmsk[6] = {};
if (!memcmp(mac, eb_mac_type_unicast, 6) &&
!memcmp(mask, eb_msk_type_unicast, 6))
......@@ -73,29 +87,13 @@ static void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char
printf("BGA");
else {
ebt_print_mac(mac);
if (memcmp(mask, hlpmsk, 6)) {
if (!mac_all_ones(mask)) {
printf("/");
ebt_print_mac(mask);
}
}
}
static uint16_t ipt_to_ebt_flags(uint8_t invflags)
{
uint16_t result = 0;
if (invflags & IPT_INV_VIA_IN)
result |= EBT_IIN;
if (invflags & IPT_INV_VIA_OUT)
result |= EBT_IOUT;
if (invflags & IPT_INV_PROTO)
result |= EBT_IPROTO;
return result;
}
static void add_logical_iniface(struct nftnl_rule *r, char *iface, uint32_t op)
{
int iface_len;
......@@ -125,7 +123,7 @@ static void add_logical_outiface(struct nftnl_rule *r, char *iface, uint32_t op)
/* TODO: Use generic add_action() once we convert this to use
* iptables_command_state.
*/
static int _add_action(struct nftnl_rule *r, struct ebtables_command_state *cs)
static int _add_action(struct nftnl_rule *r, struct iptables_command_state *cs)
{
int ret = 0;
......@@ -153,11 +151,10 @@ static int _add_action(struct nftnl_rule *r, struct ebtables_command_state *cs)
static int nft_bridge_add(struct nftnl_rule *r, void *data)
{
struct ebtables_command_state *cs = data;
struct iptables_command_state *cs = data;
struct ebt_match *iter;
struct ebt_entry *fw = &cs->fw;
struct ebt_entry *fw = &cs->eb;
uint32_t op;
char *addr;
if (fw->in[0] != '\0') {
op = nft_invflags2cmp(fw->invflags, EBT_IIN);
......@@ -179,30 +176,32 @@ static int nft_bridge_add(struct nftnl_rule *r, void *data)
add_logical_outiface(r, fw->logical_out, op);
}
addr = ether_ntoa((struct ether_addr *) fw->sourcemac);
if (strcmp(addr, "0:0:0:0:0:0") != 0) {
if (fw->bitmask & EBT_ISOURCE) {
op = nft_invflags2cmp(fw->invflags, EBT_ISOURCE);
add_payload(r, offsetof(struct ethhdr, h_source), 6,
NFT_PAYLOAD_LL_HEADER);
if (!mac_all_ones(fw->sourcemsk))
add_bitwise(r, fw->sourcemsk, 6);
add_cmp_ptr(r, op, fw->sourcemac, 6);
}
addr = ether_ntoa((struct ether_addr *) fw->destmac);
if (strcmp(addr, "0:0:0:0:0:0") != 0) {
if (fw->bitmask & EBT_IDEST) {
op = nft_invflags2cmp(fw->invflags, EBT_IDEST);
add_payload(r, offsetof(struct ethhdr, h_dest), 6,
NFT_PAYLOAD_LL_HEADER);
if (!mac_all_ones(fw->destmsk))
add_bitwise(r, fw->destmsk, 6);
add_cmp_ptr(r, op, fw->destmac, 6);
}
if (fw->ethproto != 0) {
if ((fw->bitmask & EBT_NOPROTO) == 0) {
op = nft_invflags2cmp(fw->invflags, EBT_IPROTO);
add_payload(r, offsetof(struct ethhdr, h_proto), 2,
NFT_PAYLOAD_LL_HEADER);
add_cmp_u16(r, fw->ethproto, op);
}
add_compat(r, fw->ethproto, fw->invflags);
add_compat(r, fw->ethproto, fw->invflags & EBT_IPROTO);
for (iter = cs->match_list; iter; iter = iter->next) {
if (iter->ismatch) {
......@@ -223,62 +222,44 @@ static int nft_bridge_add(struct nftnl_rule *r, void *data)
static void nft_bridge_parse_meta(struct nft_xt_ctx *ctx,
struct nftnl_expr *e, void *data)
{
struct ebtables_command_state *cs = data;
struct ebt_entry *fw = &cs->fw;
uint8_t flags = 0;
int iface = 0;
const void *ifname;
uint32_t len;
iface = parse_meta(e, ctx->meta.key, fw->in, fw->in_mask,
fw->out, fw->out_mask, &flags);
if (!iface)
goto out;
struct iptables_command_state *cs = data;
struct ebt_entry *fw = &cs->eb;
uint8_t invflags = 0;
char iifname[IFNAMSIZ] = {}, oifname[IFNAMSIZ] = {};
parse_meta(e, ctx->meta.key, iifname, NULL, oifname, NULL, &invflags);
switch (ctx->meta.key) {
case NFT_META_BRI_IIFNAME:
ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
flags |= IPT_INV_VIA_IN;
memcpy(fw->logical_in, ifname, len);
if (fw->logical_in[len] == '\0')
memset(fw->in_mask, 0xff, len);
else {
fw->logical_in[len] = '+';
fw->logical_in[len+1] = '\0';
memset(fw->in_mask, 0xff, len + 1);
}
if (invflags & IPT_INV_VIA_IN)
cs->eb.invflags |= EBT_ILOGICALIN;
snprintf(fw->logical_in, sizeof(fw->logical_in), "%s", iifname);
break;
case NFT_META_IIFNAME:
if (invflags & IPT_INV_VIA_IN)
cs->eb.invflags |= EBT_IIN;
snprintf(fw->in, sizeof(fw->in), "%s", iifname);
break;
case NFT_META_BRI_OIFNAME:
ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
flags |= IPT_INV_VIA_OUT;
memcpy(fw->logical_out, ifname, len);
if (fw->logical_out[len] == '\0')
memset(fw->out_mask, 0xff, len);
else {
fw->logical_out[len] = '+';
fw->logical_out[len+1] = '\0';
memset(fw->out_mask, 0xff, len + 1);
}
if (invflags & IPT_INV_VIA_OUT)
cs->eb.invflags |= EBT_ILOGICALOUT;
snprintf(fw->logical_out, sizeof(fw->logical_out), "%s", oifname);
break;
case NFT_META_OIFNAME:
if (invflags & IPT_INV_VIA_OUT)
cs->eb.invflags |= EBT_IOUT;
snprintf(fw->out, sizeof(fw->out), "%s", oifname);
break;
default:
break;
}
out:
fw->invflags |= ipt_to_ebt_flags(flags);
}
static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx,
struct nftnl_expr *e, void *data)
{
struct ebtables_command_state *cs = data;
struct ebt_entry *fw = &cs->fw;
struct iptables_command_state *cs = data;
struct ebt_entry *fw = &cs->eb;
unsigned char addr[ETH_ALEN];
unsigned short int ethproto;
bool inv;
......@@ -291,6 +272,14 @@ static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx,
fw->destmac[i] = addr[i];
if (inv)
fw->invflags |= EBT_IDEST;
if (ctx->flags & NFT_XT_CTX_BITWISE) {
memcpy(fw->destmsk, ctx->bitwise.mask, ETH_ALEN);
ctx->flags &= ~NFT_XT_CTX_BITWISE;
} else {
memset(&fw->destmsk, 0xff, ETH_ALEN);
}
fw->bitmask |= EBT_IDEST;
break;
case offsetof(struct ethhdr, h_source):
get_cmp_data(e, addr, sizeof(addr), &inv);
......@@ -298,12 +287,20 @@ static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx,
fw->sourcemac[i] = addr[i];
if (inv)
fw->invflags |= EBT_ISOURCE;
if (ctx->flags & NFT_XT_CTX_BITWISE) {
memcpy(fw->sourcemsk, ctx->bitwise.mask, ETH_ALEN);
ctx->flags &= ~NFT_XT_CTX_BITWISE;
} else {
memset(&fw->sourcemsk, 0xff, ETH_ALEN);
}
fw->bitmask |= EBT_ISOURCE;
break;
case offsetof(struct ethhdr, h_proto):
get_cmp_data(e, &ethproto, sizeof(ethproto), &inv);
fw->ethproto = ethproto;
if (inv)
fw->invflags |= EBT_IPROTO;
fw->bitmask &= ~EBT_NOPROTO;
break;
}
}
......@@ -311,7 +308,7 @@ static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx,
static void nft_bridge_parse_immediate(const char *jumpto, bool nft_goto,
void *data)
{
struct ebtables_command_state *cs = data;
struct iptables_command_state *cs = data;
cs->jumpto = jumpto;
}
......@@ -339,14 +336,14 @@ static void parse_watcher(void *object, struct ebt_match **match_list,
static void nft_bridge_parse_match(struct xtables_match *m, void *data)
{
struct ebtables_command_state *cs = data;
struct iptables_command_state *cs = data;
parse_watcher(m, &cs->match_list, true);
}
static void nft_bridge_parse_target(struct xtables_target *t, void *data)
{
struct ebtables_command_state *cs = data;
struct iptables_command_state *cs = data;
/* harcoded names :-( */
if (strcmp(t->name, "log") == 0 ||
......@@ -358,66 +355,17 @@ static void nft_bridge_parse_target(struct xtables_target *t, void *data)
cs->target = t;
}
void nft_rule_to_ebtables_command_state(struct nftnl_rule *r,
struct ebtables_command_state *cs)
static void nft_rule_to_ebtables_command_state(const 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_eb = cs,
.family = family,
};
iter = nftnl_expr_iter_create(r);
if (iter == NULL)
return;
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, &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->jumpto != NULL)
return;
if (cs->target != NULL && cs->target->name != NULL)
cs->target = xtables_find_target(cs->target->name, XTF_TRY_LOAD);
else
cs->jumpto = "CONTINUE";
cs->eb.bitmask = EBT_NOPROTO;
nft_rule_to_iptables_command_state(r, cs);
}
static void print_iface(const char *iface)
static void print_iface(const char *option, const char *name, bool invert)
{
char *c;
if ((c = strchr(iface, IF_WILDCARD)))
*c = '+';
printf("%s ", iface);
if (c)
*c = IF_WILDCARD;
if (*name)
printf("%s%s %s ", invert ? "! " : "", option, name);
}
static void nft_bridge_print_table_header(const char *tablename)
......@@ -428,124 +376,147 @@ static void nft_bridge_print_table_header(const char *tablename)
static void nft_bridge_print_header(unsigned int format, const char *chain,
const char *pol,
const struct xt_counters *counters,
bool basechain, uint32_t refs)
bool basechain, uint32_t refs, uint32_t entries)
{
printf("Bridge chain: %s, entries: %u, policy: %s\n",
chain, refs, basechain ? pol : "RETURN");
chain, entries, basechain ? pol : "RETURN");
}
static void nft_bridge_print_firewall(struct nftnl_rule *r, unsigned int num,
static void print_matches_and_watchers(const struct iptables_command_state *cs,
unsigned int format)
{
struct xtables_match *matchp;
struct xtables_target *watcherp;
struct xtables_match *matchp;
struct ebt_match *m;
struct ebtables_command_state cs = {};
char *addr;
nft_rule_to_ebtables_command_state(r, &cs);
for (m = cs->match_list; m; m = m->next) {
if (m->ismatch) {
matchp = m->u.match;
if (matchp->print != NULL) {
matchp->print(&cs->eb, matchp->m,
format & FMT_NUMERIC);
}
} else {
watcherp = m->u.watcher;
if (watcherp->print != NULL) {
watcherp->print(&cs->eb, watcherp->t,
format & FMT_NUMERIC);
}
}
}
}
static void print_mac(char option, const unsigned char *mac,
const unsigned char *mask,
bool invert)
{
printf("-%c ", option);
if (invert)
printf("! ");
ebt_print_mac_and_mask(mac, mask);
printf(" ");
}
if (format & FMT_LINENUMBERS)
printf("%d ", num);
static void print_protocol(uint16_t ethproto, bool invert, unsigned int bitmask)
{
struct xt_ethertypeent *ent;
/* Dont print anything about the protocol if no protocol was
* specified, obviously this means any protocol will do. */
if (cs.fw.ethproto != 0) {
if (bitmask & EBT_NOPROTO)
return;
printf("-p ");
if (cs.fw.invflags & EBT_IPROTO)
if (invert)
printf("! ");
if (cs.fw.bitmask & EBT_802_3)
printf("Length ");
else {
struct ethertypeent *ent;
ent = getethertypebynumber(ntohs(cs.fw.ethproto));
if (bitmask & EBT_802_3) {
printf("length ");
return;
}
ent = xtables_getethertypebynumber(ntohs(ethproto));
if (!ent)
printf("0x%x ", ntohs(cs.fw.ethproto));
printf("0x%x ", ntohs(ethproto));
else
printf("%s ", ent->e_name);
}
}
}
addr = ether_ntoa((struct ether_addr *) cs.fw.sourcemac);
if (strcmp(addr, "0:0:0:0:0:0") != 0) {
printf("-s ");
if (cs.fw.invflags & EBT_ISOURCE)
printf("! ");
ebt_print_mac_and_mask(cs.fw.sourcemac, cs.fw.sourcemsk);
printf(" ");
}
static void nft_bridge_save_rule(const void *data, unsigned int format)
{
const struct iptables_command_state *cs = data;
if (cs->eb.ethproto)
print_protocol(cs->eb.ethproto, cs->eb.invflags & EBT_IPROTO,
cs->eb.bitmask);
if (cs->eb.bitmask & EBT_ISOURCE)
print_mac('s', cs->eb.sourcemac, cs->eb.sourcemsk,
cs->eb.invflags & EBT_ISOURCE);
if (cs->eb.bitmask & EBT_IDEST)
print_mac('d', cs->eb.destmac, cs->eb.destmsk,
cs->eb.invflags & EBT_IDEST);
print_iface("-i", cs->eb.in, cs->eb.invflags & EBT_IIN);
print_iface("--logical-in", cs->eb.logical_in,
cs->eb.invflags & EBT_ILOGICALIN);
print_iface("-o", cs->eb.out, cs->eb.invflags & EBT_IOUT);
print_iface("--logical-out", cs->eb.logical_out,
cs->eb.invflags & EBT_ILOGICALOUT);
print_matches_and_watchers(cs, format);
addr = ether_ntoa((struct ether_addr *) cs.fw.destmac);
if (strcmp(addr, "0:0:0:0:0:0") != 0) {
printf("-d ");
if (cs.fw.invflags & EBT_IDEST)
printf("! ");
ebt_print_mac_and_mask(cs.fw.destmac, cs.fw.destmsk);
printf(" ");
}
printf("-j ");
if (cs.fw.in[0] != '\0') {
printf("-i ");
if (cs.fw.invflags & EBT_IIN)
printf("! ");
print_iface(cs.fw.in);
if (cs->jumpto != NULL) {
if (strcmp(cs->jumpto, "") != 0)
printf("%s", cs->jumpto);
else
printf("CONTINUE");
}
if (cs.fw.logical_in[0] != '\0') {
printf("--logical-in ");
if (cs.fw.invflags & EBT_ILOGICALIN)
printf("! ");
print_iface(cs.fw.logical_in);
if (cs->target != NULL && cs->target->print != NULL) {
printf(" ");
cs->target->print(&cs->fw, cs->target->t, format & FMT_NUMERIC);
}
if (cs.fw.logical_out[0] != '\0') {
printf("--logical-out ");
if (cs.fw.invflags & EBT_ILOGICALOUT)
printf("! ");
print_iface(cs.fw.logical_out);
}
if (!(format & FMT_NOCOUNTS)) {
const char *counter_fmt;
if (cs.fw.out[0] != '\0') {
printf("-o ");
if (cs.fw.invflags & EBT_IOUT)
printf("! ");
print_iface(cs.fw.out);
}
if (format & FMT_EBT_SAVE)
counter_fmt = " -c %"PRIu64" %"PRIu64"";
else
counter_fmt = " , pcnt = %"PRIu64" -- bcnt = %"PRIu64"";
for (m = cs.match_list; m; m = m->next) {
if (m->ismatch) {
matchp = m->u.match;
if (matchp->print != NULL) {
matchp->print(&cs.fw, matchp->m,
format & FMT_NUMERIC);
printf(counter_fmt,
(uint64_t)cs->counters.pcnt,
(uint64_t)cs->counters.bcnt);
}
} else {
watcherp = m->u.watcher;
if (watcherp->print != NULL) {
watcherp->print(&cs.fw, watcherp->t,
format & FMT_NUMERIC);
}
}
}
printf("-j ");
if (cs.jumpto != NULL)
printf("%s", cs.jumpto);
else if (cs.target != NULL && cs.target->print != NULL)
cs.target->print(&cs.fw, cs.target->t, format & FMT_NUMERIC);
if (!(format & FMT_NOCOUNTS))
printf(" , pcnt = %"PRIu64" -- bcnt = %"PRIu64"",
(uint64_t)cs.counters.pcnt, (uint64_t)cs.counters.bcnt);
if (!(format & FMT_NONEWLINE))
fputc('\n', stdout);
}
static void nft_bridge_print_rule(struct nftnl_rule *r, unsigned int num,
unsigned int format)
{
struct iptables_command_state cs = {};
if (format & FMT_LINENUMBERS)
printf("%d ", num);
nft_rule_to_ebtables_command_state(r, &cs);
nft_bridge_save_rule(&cs, format);
ebt_cs_clean(&cs);
}
static void nft_bridge_save_chain(const struct nftnl_chain *c,
const char *policy)
{
const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
printf(":%s %s\n", chain, policy ?: "ACCEPT");
}
static bool nft_bridge_is_same(const void *data_a, const void *data_b)
{
const struct ebt_entry *a = data_a;
......@@ -587,21 +558,14 @@ static bool nft_bridge_is_same(const void *data_a, const void *data_b)
}
}
return is_same_interfaces((char *)a->in,
(char *)a->out,
a->in_mask,
a->out_mask,
(char *)b->in,
(char *)b->out,
b->in_mask,
b->out_mask);
return strcmp(a->in, b->in) == 0 && strcmp(a->out, b->out) == 0;
}
static bool nft_bridge_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
void *data)
{
struct ebtables_command_state *cs = data;
struct ebtables_command_state this = {};
struct iptables_command_state *cs = data;
struct iptables_command_state this = {};
nft_rule_to_ebtables_command_state(r, &this);
......@@ -628,6 +592,168 @@ static bool nft_bridge_rule_find(struct nft_family_ops *ops, struct nftnl_rule *
return true;
}
static int xlate_ebmatches(const struct iptables_command_state *cs, struct xt_xlate *xl)
{
int ret = 1, numeric = cs->options & OPT_NUMERIC;
struct ebt_match *m;
for (m = cs->match_list; m; m = m->next) {
if (m->ismatch) {
struct xtables_match *matchp = m->u.match;
struct xt_xlate_mt_params mt_params = {
.ip = (const void *)&cs->eb,
.numeric = numeric,
.escape_quotes = false,
.match = matchp->m,
};
if (!matchp->xlate)
return 0;
ret = matchp->xlate(xl, &mt_params);
} else {
struct xtables_target *watcherp = m->u.watcher;
struct xt_xlate_tg_params wt_params = {
.ip = (const void *)&cs->eb,
.numeric = numeric,
.escape_quotes = false,
.target = watcherp->t,
};
if (!watcherp->xlate)
return 0;
ret = watcherp->xlate(xl, &wt_params);
}
if (!ret)
break;
}
return ret;
}
static int xlate_ebaction(const struct iptables_command_state *cs, struct xt_xlate *xl)
{
int ret = 1, numeric = cs->options & OPT_NUMERIC;
/* If no target at all, add nothing (default to continue) */
if (cs->target != NULL) {
/* Standard target? */
if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
xt_xlate_add(xl, " accept");
else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
xt_xlate_add(xl, " drop");
else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
xt_xlate_add(xl, " return");
else if (cs->target->xlate) {
xt_xlate_add(xl, " ");
struct xt_xlate_tg_params params = {
.ip = (const void *)&cs->eb,
.target = cs->target->t,
.numeric = numeric,
};
ret = cs->target->xlate(xl, &params);
}
else
return 0;
} else if (cs->jumpto == NULL) {
} else if (strlen(cs->jumpto) > 0)
xt_xlate_add(xl, " jump %s", cs->jumpto);
return ret;
}
static void xlate_mac(struct xt_xlate *xl, const unsigned char *mac)
{
int i;
xt_xlate_add(xl, "%02x", mac[0]);
for (i=1; i < ETH_ALEN; i++)
xt_xlate_add(xl, ":%02x", mac[i]);
}
static void nft_bridge_xlate_mac(struct xt_xlate *xl, const char *type, bool invert,
const unsigned char *mac, const unsigned char *mask)
{
char one_msk[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
xt_xlate_add(xl, "ether %s %s", type, invert ? "!= " : "");
xlate_mac(xl, mac);
if (memcmp(mask, one_msk, ETH_ALEN)) {
int i;
xt_xlate_add(xl, " and ");
xlate_mac(xl, mask);
xt_xlate_add(xl, " == %02x", mac[0] & mask[0]);
for (i=1; i < ETH_ALEN; i++)
xt_xlate_add(xl, ":%02x", mac[i] & mask[i]);
}
xt_xlate_add(xl, " ");
}
static int nft_bridge_xlate(const void *data, struct xt_xlate *xl)
{
const struct iptables_command_state *cs = data;
int ret;
xlate_ifname(xl, "iifname", cs->eb.in,
cs->eb.invflags & EBT_IIN);
xlate_ifname(xl, "meta ibrname", cs->eb.logical_in,
cs->eb.invflags & EBT_ILOGICALIN);
xlate_ifname(xl, "oifname", cs->eb.out,
cs->eb.invflags & EBT_IOUT);
xlate_ifname(xl, "meta obrname", cs->eb.logical_out,
cs->eb.invflags & EBT_ILOGICALOUT);
if ((cs->eb.bitmask & EBT_NOPROTO) == 0) {
const char *implicit = NULL;
switch (ntohs(cs->eb.ethproto)) {
case ETH_P_IP:
implicit = "ip";
break;
case ETH_P_IPV6:
implicit = "ip6";
break;
case ETH_P_8021Q:
implicit = "vlan";
break;
default:
break;
}
if (!implicit || !xlate_find_match(cs, implicit))
xt_xlate_add(xl, "ether type %s0x%x ",
cs->eb.invflags & EBT_IPROTO ? "!= " : "",
ntohs(cs->eb.ethproto));
}
if (cs->eb.bitmask & EBT_802_3)
return 0;
if (cs->eb.bitmask & EBT_ISOURCE)
nft_bridge_xlate_mac(xl, "saddr", cs->eb.invflags & EBT_ISOURCE,
cs->eb.sourcemac, cs->eb.sourcemsk);
if (cs->eb.bitmask & EBT_IDEST)
nft_bridge_xlate_mac(xl, "daddr", cs->eb.invflags & EBT_IDEST,
cs->eb.destmac, cs->eb.destmsk);
ret = xlate_ebmatches(cs, xl);
if (ret == 0)
return ret;
/* Always add counters per rule, as in ebtables */
xt_xlate_add(xl, "counter");
ret = xlate_ebaction(cs, xl);
return ret;
}
struct nft_family_ops nft_family_ops_bridge = {
.add = nft_bridge_add,
.is_same = nft_bridge_is_same,
......@@ -639,9 +765,13 @@ struct nft_family_ops nft_family_ops_bridge = {
.parse_target = nft_bridge_parse_target,
.print_table_header = nft_bridge_print_table_header,
.print_header = nft_bridge_print_header,
.print_firewall = nft_bridge_print_firewall,
.save_firewall = NULL,
.print_rule = nft_bridge_print_rule,
.save_rule = nft_bridge_save_rule,
.save_counters = NULL,
.save_chain = nft_bridge_save_chain,
.post_parse = NULL,
.rule_to_cs = nft_rule_to_ebtables_command_state,
.clear_cs = ebt_cs_clean,
.rule_find = nft_bridge_rule_find,
.xlate = nft_bridge_xlate,
};
......@@ -15,9 +15,6 @@
#define LIST_X 0x10
#define LIST_MAC2 0x20
/* Be backwards compatible, so don't use '+' in kernel */
#define IF_WILDCARD 1
extern unsigned char eb_mac_type_unicast[ETH_ALEN];
extern unsigned char eb_msk_type_unicast[ETH_ALEN];
extern unsigned char eb_mac_type_multicast[ETH_ALEN];
......@@ -70,56 +67,7 @@ int ebt_get_mac_and_mask(const char *from, unsigned char *to, unsigned char *mas
*/
#define EBT_VERDICT_BITS 0x0000000F
/* Fake ebt_entry */
struct ebt_entry {
/* this needs to be the first field */
unsigned int bitmask;
unsigned int invflags;
uint16_t ethproto;
/* the physical in-dev */
char in[IFNAMSIZ];
/* the logical in-dev */
char logical_in[IFNAMSIZ];
/* the physical out-dev */
char out[IFNAMSIZ];
/* the logical out-dev */
char logical_out[IFNAMSIZ];
unsigned char sourcemac[ETH_ALEN];
unsigned char sourcemsk[ETH_ALEN];
unsigned char destmac[ETH_ALEN];
unsigned char destmsk[ETH_ALEN];
unsigned char in_mask[IFNAMSIZ];
unsigned char out_mask[IFNAMSIZ];
};
/* trick for ebtables-compat, since watchers are targets */
struct ebt_match {
struct ebt_match *next;
union {
struct xtables_match *match;
struct xtables_target *watcher;
} u;
bool ismatch;
};
struct ebtables_command_state {
struct ebt_entry fw;
struct xtables_target *target;
struct xtables_rule_match *matches;
struct ebt_match *match_list;
const char *jumpto;
struct xt_counters counters;
int invert;
int c;
char **argv;
int proto_used;
char *protocol;
unsigned int options;
};
void nft_rule_to_ebtables_command_state(struct nftnl_rule *r,
struct ebtables_command_state *cs);
struct nftnl_rule;
static const char *ebt_standard_targets[NUM_STANDARD_TARGETS] = {
"ACCEPT",
......@@ -130,7 +78,7 @@ static const char *ebt_standard_targets[NUM_STANDARD_TARGETS] = {
static inline const char *nft_ebt_standard_target(unsigned int num)
{
if (num > NUM_STANDARD_TARGETS)
if (num >= NUM_STANDARD_TARGETS)
return NULL;
return ebt_standard_targets[num];
......@@ -166,6 +114,13 @@ static inline const char *ebt_target_name(unsigned int verdict)
*flags |= mask; \
}) \
void ebt_cs_clean(struct ebtables_command_state *cs);
void ebt_cs_clean(struct iptables_command_state *cs);
void ebt_load_match_extensions(void);
void ebt_add_match(struct xtables_match *m,
struct iptables_command_state *cs);
void ebt_add_watcher(struct xtables_target *watcher,
struct iptables_command_state *cs);
int ebt_command_default(struct iptables_command_state *cs);
struct xtables_target *ebt_command_jump(const char *jumpto);
#endif
......@@ -45,8 +45,7 @@ static int nft_ipv4_add(struct nftnl_rule *r, void *data)
if (cs->fw.ip.proto != 0) {
op = nft_invflags2cmp(cs->fw.ip.invflags, XT_INV_PROTO);
add_proto(r, offsetof(struct iphdr, protocol), 1,
cs->fw.ip.proto, op);
add_l4proto(r, cs->fw.ip.proto, op);
}
if (cs->fw.ip.src.s_addr != 0) {
......@@ -65,22 +64,26 @@ static int nft_ipv4_add(struct nftnl_rule *r, void *data)
add_payload(r, offsetof(struct iphdr, frag_off), 2,
NFT_PAYLOAD_NETWORK_HEADER);
/* get the 13 bits that contain the fragment offset */
add_bitwise_u16(r, 0x1fff, !0x1fff);
add_bitwise_u16(r, 0x1fff, 0);
/* if offset is non-zero, this is a fragment */
op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_FRAG);
op = NFT_CMP_NEQ;
if (cs->fw.ip.invflags & IPT_INV_FRAG)
op = NFT_CMP_EQ;
add_cmp_u16(r, 0, op);
}
add_compat(r, cs->fw.ip.proto, cs->fw.ip.invflags);
add_compat(r, cs->fw.ip.proto, cs->fw.ip.invflags & XT_INV_PROTO);
for (matchp = cs->matches; matchp; matchp = matchp->next) {
/* Use nft built-in comments support instead of comment match */
if (strcmp(matchp->match->name, "comment") == 0) {
ret = add_comment(r, (char *)matchp->match->m->data);
if (ret < 0)
return ret;
goto try_match;
} else {
try_match:
ret = add_match(r, matchp->match->m);
if (ret < 0)
return ret;
......@@ -168,6 +171,16 @@ static void nft_ipv4_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
{
struct iptables_command_state *cs = data;
switch (ctx->meta.key) {
case NFT_META_L4PROTO:
cs->fw.ip.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
cs->fw.ip.invflags |= XT_INV_PROTO;
return;
default:
break;
}
parse_meta(e, ctx->meta.key, cs->fw.ip.iniface, cs->fw.ip.iniface_mask,
cs->fw.ip.outiface, cs->fw.ip.outiface_mask,
&cs->fw.ip.invflags);
......@@ -221,6 +234,7 @@ static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx,
break;
case offsetof(struct iphdr, frag_off):
cs->fw.ip.flags |= IPT_F_FRAG;
inv = false;
get_frag(ctx, e, &inv);
if (inv)
cs->fw.ip.invflags |= IPT_INV_FRAG;
......@@ -242,44 +256,6 @@ static void nft_ipv4_parse_immediate(const char *jumpto, bool nft_goto,
cs->fw.ip.flags |= IPT_F_GOTO;
}
static void nft_ipv4_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_ipv4_addr(const struct iptables_command_state *cs,
unsigned int format)
{
char buf[BUFSIZ];
fputc(cs->fw.ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
if (cs->fw.ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC))
printf(FMT("%-19s ","%s "), "anywhere");
else {
if (format & FMT_NUMERIC)
strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.src));
else
strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.src));
strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.smsk));
printf(FMT("%-19s ","%s "), buf);
}
fputc(cs->fw.ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
if (cs->fw.ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
printf(FMT("%-19s ","-> %s"), "anywhere");
else {
if (format & FMT_NUMERIC)
strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.dst));
else
strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.dst));
strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.dmsk));
printf(FMT("%-19s ","-> %s"), buf);
}
}
static void print_fragment(unsigned int flags, unsigned int invflags,
unsigned int format)
{
......@@ -293,20 +269,19 @@ static void print_fragment(unsigned int flags, unsigned int invflags,
fputc(' ', stdout);
}
static void nft_ipv4_print_firewall(struct nftnl_rule *r, unsigned int num,
static void nft_ipv4_print_rule(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.fw.ip.flags,
cs.fw.ip.invflags, cs.fw.ip.proto,
num, format);
print_rule_details(&cs, cs.jumpto, cs.fw.ip.flags,
cs.fw.ip.invflags, cs.fw.ip.proto, num, format);
print_fragment(cs.fw.ip.flags, cs.fw.ip.invflags, format);
print_ifaces(cs.fw.ip.iniface, cs.fw.ip.outiface, cs.fw.ip.invflags,
format);
print_ipv4_addr(&cs, format);
print_ipv4_addresses(&cs.fw, format);
if (format & FMT_NOTABLE)
fputs(" ", stdout);
......@@ -320,6 +295,8 @@ static void nft_ipv4_print_firewall(struct nftnl_rule *r, unsigned int num,
if (!(format & FMT_NONEWLINE))
fputc('\n', stdout);
xtables_rule_matches_free(&cs.matches);
}
static void save_ipv4_addr(char letter, const struct in_addr *addr,
......@@ -332,11 +309,16 @@ static void save_ipv4_addr(char letter, const struct in_addr *addr,
mask_to_str(mask));
}
static void nft_ipv4_save_firewall(const void *data, unsigned int format)
static void nft_ipv4_save_rule(const void *data, unsigned int format)
{
const struct iptables_command_state *cs = data;
save_firewall_details(cs, cs->fw.ip.invflags, cs->fw.ip.proto,
save_ipv4_addr('s', &cs->fw.ip.src, cs->fw.ip.smsk.s_addr,
cs->fw.ip.invflags & IPT_INV_SRCIP);
save_ipv4_addr('d', &cs->fw.ip.dst, cs->fw.ip.dmsk.s_addr,
cs->fw.ip.invflags & IPT_INV_DSTIP);
save_rule_details(cs, cs->fw.ip.invflags, cs->fw.ip.proto,
cs->fw.ip.iniface, cs->fw.ip.iniface_mask,
cs->fw.ip.outiface, cs->fw.ip.outiface_mask);
......@@ -346,19 +328,8 @@ static void nft_ipv4_save_firewall(const void *data, unsigned int format)
printf("-f ");
}
save_ipv4_addr('s', &cs->fw.ip.src, cs->fw.ip.smsk.s_addr,
cs->fw.ip.invflags & IPT_INV_SRCIP);
save_ipv4_addr('d', &cs->fw.ip.dst, cs->fw.ip.dmsk.s_addr,
cs->fw.ip.invflags & IPT_INV_DSTIP);
save_matches_and_target(cs->matches, cs->target,
cs->jumpto, cs->fw.ip.flags, &cs->fw);
if (cs->target == NULL && strlen(cs->jumpto) > 0) {
printf("-%c %s", cs->fw.ip.flags & IPT_F_GOTO ? 'g' : 'j',
cs->jumpto);
}
printf("\n");
save_matches_and_target(cs, cs->fw.ip.flags & IPT_F_GOTO,
&cs->fw, format);
}
static void nft_ipv4_proto_parse(struct iptables_command_state *cs,
......@@ -416,28 +387,6 @@ static void nft_ipv4_post_parse(int command,
" source or destination IP addresses");
}
static void nft_ipv4_parse_target(struct xtables_target *t, void *data)
{
struct iptables_command_state *cs = data;
cs->target = t;
}
static bool nft_ipv4_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_ipv4_save_counters(const void *data)
{
const struct iptables_command_state *cs = data;
save_counters(cs->counters.pcnt, cs->counters.bcnt);
}
static int nft_ipv4_xlate(const void *data, struct xt_xlate *xl)
{
const struct iptables_command_state *cs = data;
......@@ -450,23 +399,25 @@ static int nft_ipv4_xlate(const void *data, struct xt_xlate *xl)
cs->fw.ip.invflags & IPT_INV_VIA_OUT);
if (cs->fw.ip.flags & IPT_F_FRAG) {
xt_xlate_add(xl, "ip frag-off %s%x ",
xt_xlate_add(xl, "ip frag-off & 0x1fff %s%x ",
cs->fw.ip.invflags & IPT_INV_FRAG? "" : "!= ", 0);
}
if (cs->fw.ip.proto != 0) {
const struct protoent *pent =
getprotobynumber(cs->fw.ip.proto);
char protonum[strlen("255") + 1];
char protonum[sizeof("65535")];
const char *name = protonum;
if (!xlate_find_match(cs, pent->p_name)) {
snprintf(protonum, sizeof(protonum), "%u",
cs->fw.ip.proto);
protonum[sizeof(protonum) - 1] = '\0';
if (!pent || !xlate_find_match(cs, pent->p_name)) {
if (pent)
name = pent->p_name;
xt_xlate_add(xl, "ip protocol %s%s ",
cs->fw.ip.invflags & IPT_INV_PROTO ?
"!= " : "",
pent ? pent->p_name : protonum);
"!= " : "", name);
}
}
......@@ -488,7 +439,7 @@ static int nft_ipv4_xlate(const void *data, struct xt_xlate *xl)
return ret;
/* Always add counters per rule, as in iptables */
xt_xlate_add(xl, "counter ");
xt_xlate_add(xl, "counter");
ret = xlate_action(cs, !!(cs->fw.ip.flags & IPT_F_GOTO), xl);
comment = xt_xlate_get_comment(xl);
......@@ -504,13 +455,16 @@ struct nft_family_ops nft_family_ops_ipv4 = {
.parse_meta = nft_ipv4_parse_meta,
.parse_payload = nft_ipv4_parse_payload,
.parse_immediate = nft_ipv4_parse_immediate,
.print_header = nft_ipv4_print_header,
.print_firewall = nft_ipv4_print_firewall,
.save_firewall = nft_ipv4_save_firewall,
.save_counters = nft_ipv4_save_counters,
.print_header = print_header,
.print_rule = nft_ipv4_print_rule,
.save_rule = nft_ipv4_save_rule,
.save_counters = save_counters,
.save_chain = nft_ipv46_save_chain,
.proto_parse = nft_ipv4_proto_parse,
.post_parse = nft_ipv4_post_parse,
.parse_target = nft_ipv4_parse_target,
.rule_find = nft_ipv4_rule_find,
.parse_target = nft_ipv46_parse_target,
.rule_to_cs = nft_rule_to_iptables_command_state,
.clear_cs = nft_clear_iptables_command_state,
.rule_find = nft_ipv46_rule_find,
.xlate = nft_ipv4_xlate,
};
......@@ -44,8 +44,7 @@ static int nft_ipv6_add(struct nftnl_rule *r, void *data)
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);
add_l4proto(r, cs->fw6.ipv6.proto, op);
}
if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src)) {
......@@ -60,15 +59,16 @@ static int nft_ipv6_add(struct nftnl_rule *r, void *data)
&cs->fw6.ipv6.dst, &cs->fw6.ipv6.dmsk,
sizeof(struct in6_addr), op);
}
add_compat(r, cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags);
add_compat(r, cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags & XT_INV_PROTO);
for (matchp = cs->matches; matchp; matchp = matchp->next) {
/* Use nft built-in comments support instead of comment match */
if (strcmp(matchp->match->name, "comment") == 0) {
ret = add_comment(r, (char *)matchp->match->m->data);
if (ret < 0)
return ret;
goto try_match;
} else {
try_match:
ret = add_match(r, matchp->match->m);
if (ret < 0)
return ret;
......@@ -114,6 +114,16 @@ static void nft_ipv6_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
{
struct iptables_command_state *cs = data;
switch (ctx->meta.key) {
case NFT_META_L4PROTO:
cs->fw6.ipv6.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
cs->fw6.ipv6.invflags |= XT_INV_PROTO;
return;
default:
break;
}
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);
......@@ -140,7 +150,7 @@ static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx,
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));
memset(&cs->fw6.ipv6.smsk, 0xff, sizeof(struct in6_addr));
}
if (inv)
......@@ -153,7 +163,7 @@ static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx,
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));
memset(&cs->fw6.ipv6.dmsk, 0xff, sizeof(struct in6_addr));
}
if (inv)
......@@ -161,7 +171,6 @@ static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx,
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 |= IP6T_INV_PROTO;
......@@ -182,64 +191,24 @@ static void nft_ipv6_parse_immediate(const char *jumpto, bool 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 & IP6T_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 & IP6T_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,
static void nft_ipv6_print_rule(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,
print_rule_details(&cs, cs.jumpto, cs.fw6.ipv6.flags,
cs.fw6.ipv6.invflags, cs.fw6.ipv6.proto,
num, format);
if (format & FMT_OPTIONS) {
if (format & FMT_NOTABLE)
fputs("opt ", stdout);
fputs(" ", stdout);
}
print_ifaces(cs.fw6.ipv6.iniface, cs.fw6.ipv6.outiface,
cs.fw6.ipv6.invflags, format);
print_ipv6_addr(&cs, format);
print_ipv6_addresses(&cs.fw6, format);
if (format & FMT_NOTABLE)
fputs(" ", stdout);
......@@ -251,42 +220,45 @@ static void nft_ipv6_print_firewall(struct nftnl_rule *r, unsigned int num,
if (!(format & FMT_NONEWLINE))
fputc('\n', stdout);
xtables_rule_matches_free(&cs.matches);
}
static void save_ipv6_addr(char letter, const struct in6_addr *addr,
const struct in6_addr *mask,
int invert)
{
char addr_str[INET6_ADDRSTRLEN];
int l = xtables_ip6mask_to_cidr(mask);
if (!invert && IN6_IS_ADDR_UNSPECIFIED(addr))
if (!invert && l == 0)
return;
inet_ntop(AF_INET6, addr, addr_str, INET6_ADDRSTRLEN);
printf("%s-%c %s ", invert ? "! " : "", letter, addr_str);
printf("%s-%c %s",
invert ? " !" : "", letter,
inet_ntop(AF_INET6, addr, addr_str, sizeof(addr_str)));
if (l == -1)
printf("/%s ", inet_ntop(AF_INET6, mask, addr_str, sizeof(addr_str)));
else
printf("/%d ", l);
}
static void nft_ipv6_save_firewall(const void *data, unsigned int format)
static void nft_ipv6_save_rule(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,
save_ipv6_addr('s', &cs->fw6.ipv6.src, &cs->fw6.ipv6.smsk,
cs->fw6.ipv6.invflags & IP6T_INV_SRCIP);
save_ipv6_addr('d', &cs->fw6.ipv6.dst,
save_ipv6_addr('d', &cs->fw6.ipv6.dst, &cs->fw6.ipv6.dmsk,
cs->fw6.ipv6.invflags & IP6T_INV_DSTIP);
save_matches_and_target(cs->matches, cs->target,
cs->jumpto, cs->fw6.ipv6.flags, &cs->fw6);
save_rule_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);
if (cs->target == NULL && strlen(cs->jumpto) > 0) {
printf("-%c %s", cs->fw6.ipv6.flags & IP6T_F_GOTO ? 'g' : 'j',
cs->jumpto);
}
printf("\n");
save_matches_and_target(cs, cs->fw6.ipv6.flags & IP6T_F_GOTO,
&cs->fw6, format);
}
/* These are invalid numbers as upper layer protocol */
......@@ -315,9 +287,6 @@ static void nft_ipv6_proto_parse(struct iptables_command_state *cs,
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.
......@@ -364,28 +333,6 @@ static void nft_ipv6_post_parse(int command, struct iptables_command_state *cs,
" 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);
}
static void xlate_ipv6_addr(const char *selector, const struct in6_addr *addr,
const struct in6_addr *mask,
int invert, struct xt_xlate *xl)
......@@ -414,17 +361,20 @@ static int nft_ipv6_xlate(const void *data, struct xt_xlate *xl)
if (cs->fw6.ipv6.proto != 0) {
const struct protoent *pent =
getprotobynumber(cs->fw6.ipv6.proto);
char protonum[strlen("255") + 1];
char protonum[sizeof("65535")];
const char *name = protonum;
if (!xlate_find_match(cs, pent->p_name)) {
snprintf(protonum, sizeof(protonum), "%u",
cs->fw6.ipv6.proto);
protonum[sizeof(protonum) - 1] = '\0';
if (!pent || !xlate_find_match(cs, pent->p_name)) {
if (pent)
name = pent->p_name;
xt_xlate_add(xl, "meta l4proto %s%s ",
cs->fw6.ipv6.invflags & IP6T_INV_PROTO ?
"!= " : "",
pent ? pent->p_name : protonum);
"!= " : "", name);
}
}
xlate_ipv6_addr("ip6 saddr", &cs->fw6.ipv6.src, &cs->fw6.ipv6.smsk,
......@@ -437,7 +387,7 @@ static int nft_ipv6_xlate(const void *data, struct xt_xlate *xl)
return ret;
/* Always add counters per rule, as in iptables */
xt_xlate_add(xl, "counter ");
xt_xlate_add(xl, "counter");
ret = xlate_action(cs, !!(cs->fw6.ipv6.flags & IP6T_F_GOTO), xl);
comment = xt_xlate_get_comment(xl);
......@@ -453,13 +403,16 @@ struct nft_family_ops nft_family_ops_ipv6 = {
.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,
.print_header = print_header,
.print_rule = nft_ipv6_print_rule,
.save_rule = nft_ipv6_save_rule,
.save_counters = save_counters,
.save_chain = nft_ipv46_save_chain,
.proto_parse = nft_ipv6_proto_parse,
.post_parse = nft_ipv6_post_parse,
.parse_target = nft_ipv6_parse_target,
.rule_find = nft_ipv6_rule_find,
.parse_target = nft_ipv46_parse_target,
.rule_to_cs = nft_rule_to_iptables_command_state,
.clear_cs = nft_clear_iptables_command_state,
.rule_find = nft_ipv46_rule_find,
.xlate = nft_ipv6_xlate,
};
......@@ -16,10 +16,13 @@
#include <stdbool.h>
#include <netdb.h>
#include <errno.h>
#include <inttypes.h>
#include <xtables.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/xt_comment.h>
#include <linux/netfilter/xt_limit.h>
#include <libmnl/libmnl.h>
#include <libnftnl/rule.h>
......@@ -83,7 +86,7 @@ void add_bitwise_u16(struct nftnl_rule *r, int mask, int xor)
nftnl_rule_add_expr(r, expr);
}
static void add_bitwise(struct nftnl_rule *r, uint8_t *mask, size_t len)
void add_bitwise(struct nftnl_rule *r, uint8_t *mask, size_t len)
{
struct nftnl_expr *expr;
uint32_t xor[4] = { 0 };
......@@ -138,9 +141,10 @@ void add_iniface(struct nftnl_rule *r, char *iface, uint32_t op)
iface_len = strlen(iface);
add_meta(r, NFT_META_IIFNAME);
if (iface[iface_len - 1] == '+')
if (iface[iface_len - 1] == '+') {
if (iface_len > 1)
add_cmp_ptr(r, op, iface, iface_len - 1);
else
} else
add_cmp_ptr(r, op, iface, iface_len + 1);
}
......@@ -151,16 +155,27 @@ void add_outiface(struct nftnl_rule *r, char *iface, uint32_t op)
iface_len = strlen(iface);
add_meta(r, NFT_META_OIFNAME);
if (iface[iface_len - 1] == '+')
if (iface[iface_len - 1] == '+') {
if (iface_len > 1)
add_cmp_ptr(r, op, iface, iface_len - 1);
else
} 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)
{
const char *m = mask;
int i;
add_payload(r, offset, len, NFT_PAYLOAD_NETWORK_HEADER);
for (i = 0; i < len; i++) {
if (m[i] != 0xff)
break;
}
if (i != len)
add_bitwise(r, mask, len);
add_cmp_ptr(r, op, data, len);
......@@ -173,6 +188,12 @@ void add_proto(struct nftnl_rule *r, int offset, size_t len,
add_cmp_u8(r, proto, op);
}
void add_l4proto(struct nftnl_rule *r, uint8_t proto, uint32_t op)
{
add_meta(r, NFT_META_L4PROTO);
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,
......@@ -207,6 +228,30 @@ bool is_same_interfaces(const char *a_iniface, const char *a_outiface,
return true;
}
static void parse_ifname(const char *name, unsigned int len, char *dst, unsigned char *mask)
{
if (len == 0)
return;
memcpy(dst, name, len);
if (name[len - 1] == '\0') {
if (mask)
memset(mask, 0xff, len);
return;
}
if (len >= IFNAMSIZ)
return;
/* wildcard */
dst[len++] = '+';
if (len >= IFNAMSIZ)
return;
dst[len++] = 0;
if (mask)
memset(mask, 0xff, len + 1);
}
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)
......@@ -234,35 +279,21 @@ int parse_meta(struct nftnl_expr *e, uint8_t key, char *iniface,
memset(outiface_mask, 0xff, strlen(outiface)+1);
break;
case NFT_META_BRI_IIFNAME:
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);
}
parse_ifname(ifname, len, iniface, iniface_mask);
break;
case NFT_META_BRI_OIFNAME:
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);
}
parse_ifname(ifname, len, outiface, outiface_mask);
break;
default:
return -1;
......@@ -271,22 +302,6 @@ int parse_meta(struct nftnl_expr *e, uint8_t key, char *iniface,
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;
......@@ -296,7 +311,7 @@ void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
struct xt_entry_target *t;
size_t size;
struct nft_family_ops *ops;
void *data = nft_get_data(ctx);
void *data = ctx->cs;
target = xtables_find_target(targname, XTF_TRY_LOAD);
if (target == NULL)
......@@ -304,11 +319,7 @@ void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
size = XT_ALIGN(sizeof(struct xt_entry_target)) + tg_len;
t = calloc(1, size);
if (t == NULL) {
fprintf(stderr, "OOM");
exit(EXIT_FAILURE);
}
t = xtables_calloc(1, size);
memcpy(&t->data, targinfo, tg_len);
t->u.target_size = size;
t->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
......@@ -333,10 +344,8 @@ void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
switch (ctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
matches = &ctx->state.cs->matches;
break;
case NFPROTO_BRIDGE:
matches = &ctx->state.cs_eb->matches;
matches = &ctx->cs->matches;
break;
default:
fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
......@@ -348,12 +357,7 @@ void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
if (match == NULL)
return;
m = calloc(1, sizeof(struct xt_entry_match) + mt_len);
if (m == NULL) {
fprintf(stderr, "OOM");
exit(EXIT_FAILURE);
}
m = xtables_calloc(1, sizeof(struct xt_entry_match) + mt_len);
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);
......@@ -363,7 +367,7 @@ void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
ops = nft_family_ops_lookup(ctx->family);
if (ops->parse_match != NULL)
ops->parse_match(match, nft_get_data(ctx));
ops->parse_match(match, ctx->cs);
}
void print_proto(uint16_t proto, int invert)
......@@ -394,10 +398,54 @@ void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv)
*inv = false;
}
static void nft_meta_set_to_target(struct nft_xt_ctx *ctx)
{
const struct nft_family_ops *ops;
struct xtables_target *target;
struct xt_entry_target *t;
unsigned int size;
const char *targname;
switch (ctx->meta.key) {
case NFT_META_NFTRACE:
if (ctx->immediate.data[0] == 0)
return;
targname = "TRACE";
break;
default:
return;
}
target = xtables_find_target(targname, XTF_TRY_LOAD);
if (target == NULL)
return;
size = XT_ALIGN(sizeof(struct xt_entry_target)) + target->size;
t = xtables_calloc(1, size);
t->u.target_size = size;
t->u.user.revision = target->revision;
strcpy(t->u.user.name, targname);
target->t = t;
ops = nft_family_ops_lookup(ctx->family);
ops->parse_target(target, ctx->cs);
}
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);
if (nftnl_expr_is_set(e, NFTNL_EXPR_META_SREG) &&
(ctx->flags & NFT_XT_CTX_IMMEDIATE) &&
nftnl_expr_get_u32(e, NFTNL_EXPR_META_SREG) == ctx->immediate.reg) {
ctx->flags &= ~NFT_XT_CTX_IMMEDIATE;
nft_meta_set_to_target(ctx);
return;
}
ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
ctx->flags |= NFT_XT_CTX_META;
}
......@@ -427,7 +475,7 @@ 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)
{
struct nft_family_ops *ops = nft_family_ops_lookup(ctx->family);
void *data = nft_get_data(ctx);
void *data = ctx->cs;
uint32_t reg;
reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
......@@ -453,13 +501,30 @@ 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)
{
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);
void *data = ctx->cs;
int verdict;
if (nftnl_expr_is_set(e, NFTNL_EXPR_IMM_DATA)) {
const void *imm_data;
uint32_t len;
imm_data = nftnl_expr_get_data(e, NFTNL_EXPR_IMM_DATA, &len);
if (len > sizeof(ctx->immediate.data))
return;
memcpy(ctx->immediate.data, imm_data, len);
ctx->immediate.len = len;
ctx->immediate.reg = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_DREG);
ctx->flags |= NFT_XT_CTX_IMMEDIATE;
return;
}
verdict = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_VERDICT);
/* Standard target? */
switch(verdict) {
case NF_ACCEPT:
......@@ -473,6 +538,7 @@ void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
break;;
case NFT_GOTO:
nft_goto = true;
/* fall through */
case NFT_JUMP:
jumpto = chain;
break;
......@@ -482,14 +548,57 @@ void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
ops->parse_immediate(jumpto, nft_goto, data);
}
void nft_rule_to_iptables_command_state(struct nftnl_rule *r,
static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
{
__u32 burst = nftnl_expr_get_u32(e, NFTNL_EXPR_LIMIT_BURST);
__u64 unit = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_UNIT);
__u64 rate = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_RATE);
struct xtables_rule_match **matches;
struct xtables_match *match;
struct nft_family_ops *ops;
struct xt_rateinfo *rinfo;
size_t size;
switch (ctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
case NFPROTO_BRIDGE:
matches = &ctx->cs->matches;
break;
default:
fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
ctx->family);
exit(EXIT_FAILURE);
}
match = xtables_find_match("limit", XTF_TRY_LOAD, matches);
if (match == NULL)
return;
size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size;
match->m = xtables_calloc(1, size);
match->m->u.match_size = size;
strcpy(match->m->u.user.name, match->name);
match->m->u.user.revision = match->revision;
xs_init_match(match);
rinfo = (void *)match->m->data;
rinfo->avg = XT_LIMIT_SCALE * unit / rate;
rinfo->burst = burst;
ops = nft_family_ops_lookup(ctx->family);
if (ops->parse_match != NULL)
ops->parse_match(match, ctx->cs);
}
void nft_rule_to_iptables_command_state(const 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,
.cs = cs,
.family = family,
};
......@@ -504,7 +613,7 @@ void nft_rule_to_iptables_command_state(struct nftnl_rule *r,
nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
if (strcmp(name, "counter") == 0)
nft_parse_counter(expr, &ctx.state.cs->counters);
nft_parse_counter(expr, &ctx.cs->counters);
else if (strcmp(name, "payload") == 0)
nft_parse_payload(&ctx, expr);
else if (strcmp(name, "meta") == 0)
......@@ -519,6 +628,8 @@ void nft_rule_to_iptables_command_state(struct nftnl_rule *r,
nft_parse_match(&ctx, expr);
else if (strcmp(name, "target") == 0)
nft_parse_target(&ctx, expr);
else if (strcmp(name, "limit") == 0)
nft_parse_limit(&ctx, expr);
expr = nftnl_expr_iter_next(iter);
}
......@@ -527,7 +638,7 @@ void nft_rule_to_iptables_command_state(struct nftnl_rule *r,
if (nftnl_rule_is_set(r, NFTNL_RULE_USERDATA)) {
const void *data;
uint32_t len;
uint32_t len, size;
struct xtables_match *match;
struct xt_entry_match *m;
......@@ -537,14 +648,12 @@ void nft_rule_to_iptables_command_state(struct nftnl_rule *r,
if (match == NULL)
return;
m = calloc(1, sizeof(struct xt_entry_match) + len);
if (m == NULL) {
fprintf(stderr, "OOM");
exit(EXIT_FAILURE);
}
size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size;
m = xtables_calloc(1, size);
memcpy(&m->data, get_comment(data, len), len);
m->u.match_size = len + XT_ALIGN(sizeof(struct xt_entry_match));
strncpy((char *)m->data, get_comment(data, len),
match->size - 1);
m->u.match_size = size;
m->u.user.revision = 0;
strcpy(m->u.user.name, match->name);
......@@ -559,9 +668,16 @@ void nft_rule_to_iptables_command_state(struct nftnl_rule *r,
cs->jumpto = "";
}
void nft_clear_iptables_command_state(struct iptables_command_state *cs)
{
xtables_rule_matches_free(&cs->matches);
if (cs->target)
free(cs->target->t);
}
void print_header(unsigned int format, const char *chain, const char *pol,
const struct xt_counters *counters, bool basechain,
uint32_t refs)
uint32_t refs, uint32_t entries)
{
printf("Chain %s", chain);
if (basechain) {
......@@ -603,7 +719,7 @@ void print_header(unsigned int format, const char *chain, const char *pol,
printf("\n");
}
void print_firewall_details(const struct iptables_command_state *cs,
void print_rule_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)
......@@ -630,45 +746,6 @@ void print_firewall_details(const struct iptables_command_state *cs,
}
}
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)
{
......@@ -693,7 +770,7 @@ print_iface(char letter, const char *iface, const unsigned char *mask, int inv)
printf(" ");
}
void save_firewall_details(const struct iptables_command_state *cs,
void save_rule_details(const struct iptables_command_state *cs,
uint8_t invflags, uint16_t proto,
const char *iniface,
unsigned const char *iniface_mask,
......@@ -722,19 +799,31 @@ void save_firewall_details(const struct iptables_command_state *cs,
}
}
void save_counters(uint64_t pcnt, uint64_t bcnt)
void save_counters(const void *data)
{
const struct iptables_command_state *cs = data;
printf("[%llu:%llu] ", (unsigned long long)cs->counters.pcnt,
(unsigned long long)cs->counters.bcnt);
}
void nft_ipv46_save_chain(const struct nftnl_chain *c, const char *policy)
{
printf("[%llu:%llu] ", (unsigned long long)pcnt,
(unsigned long long)bcnt);
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);
printf(":%s %s [%"PRIu64":%"PRIu64"]\n",
chain, policy ?: "-", pkts, bytes);
}
void save_matches_and_target(struct xtables_rule_match *m,
struct xtables_target *target,
const char *jumpto, uint8_t flags, const void *fw)
void save_matches_and_target(const struct iptables_command_state *cs,
bool goto_flag, const void *fw,
unsigned int format)
{
struct xtables_rule_match *matchp;
for (matchp = m; matchp; matchp = matchp->next) {
for (matchp = cs->matches; matchp; matchp = matchp->next) {
if (matchp->match->alias) {
printf("-m %s",
matchp->match->alias(matchp->match->m));
......@@ -748,15 +837,24 @@ void save_matches_and_target(struct xtables_rule_match *m,
printf(" ");
}
if (target != NULL) {
if (target->alias) {
printf("-j %s", target->alias(target->t));
if ((format & (FMT_NOCOUNTS | FMT_C_COUNTS)) == FMT_C_COUNTS)
printf("-c %llu %llu ",
(unsigned long long)cs->counters.pcnt,
(unsigned long long)cs->counters.bcnt);
if (cs->target != NULL) {
if (cs->target->alias) {
printf("-j %s", cs->target->alias(cs->target->t));
} else
printf("-j %s", jumpto);
printf("-j %s", cs->jumpto);
if (target->save != NULL)
target->save(fw, target->t);
if (cs->target->save != NULL)
cs->target->save(fw, cs->target->t);
} else if (strlen(cs->jumpto) > 0) {
printf("-%c %s", goto_flag ? 'g' : 'j', cs->jumpto);
}
printf("\n");
}
void print_matches_and_target(struct iptables_command_state *cs,
......@@ -838,7 +936,9 @@ 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))
if (tg1 == NULL || tg2 == NULL)
return false;
if (tg1->userspacesize != tg2->userspacesize)
return false;
if (strcmp(tg1->t->u.user.name, tg2->t->u.user.name) != 0)
......@@ -850,16 +950,23 @@ bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2)
return true;
}
void nft_ipv46_parse_target(struct xtables_target *t, void *data)
{
struct iptables_command_state *cs = data;
cs->target = t;
}
bool nft_ipv46_rule_find(struct nft_family_ops *ops,
struct nftnl_rule *r, struct iptables_command_state *cs)
struct nftnl_rule *r, void *data)
{
struct iptables_command_state this = {};
struct iptables_command_state *cs = data, 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);
nft_rule_print_save(r, NFT_RULE_APPEND, 0);
#endif
if (!ops->is_same(cs, &this))
return false;
......@@ -881,3 +988,32 @@ bool nft_ipv46_rule_find(struct nft_family_ops *ops,
return true;
}
void nft_check_xt_legacy(int family, bool is_ipt_save)
{
static const char tables6[] = "/proc/net/ip6_tables_names";
static const char tables4[] = "/proc/net/ip_tables_names";
const char *prefix = "ip";
FILE *fp = NULL;
char buf[1024];
switch (family) {
case NFPROTO_IPV4:
fp = fopen(tables4, "r");
break;
case NFPROTO_IPV6:
fp = fopen(tables6, "r");
prefix = "ip6";
break;
default:
break;
}
if (!fp)
return;
if (fgets(buf, sizeof(buf), fp))
fprintf(stderr, "# Warning: %stables-legacy tables present, use %stables-legacy%s to see them\n",
prefix, prefix, is_ipt_save ? "-save" : "");
fclose(fp);
}
......@@ -5,17 +5,15 @@
#include <libnftnl/rule.h>
#include <libnftnl/expr.h>
#include <libnftnl/chain.h>
#include <linux/netfilter_arp/arp_tables.h>
#include "xshared.h"
#if 0
#define DEBUGP(x, args...) fprintf(stdout, x, ## args)
#ifdef DEBUG
#define NLDEBUG
#define DEBUG_DEL
#else
#define DEBUGP(x, args...)
#endif
/*
......@@ -43,14 +41,11 @@ enum {
NFT_XT_CTX_PAYLOAD = (1 << 0),
NFT_XT_CTX_META = (1 << 1),
NFT_XT_CTX_BITWISE = (1 << 2),
NFT_XT_CTX_IMMEDIATE = (1 << 3),
};
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;
......@@ -63,6 +58,10 @@ struct nft_xt_ctx {
struct {
uint32_t key;
} meta;
struct {
uint32_t data[4];
uint32_t len, reg;
} immediate;
struct {
uint32_t mask[4];
uint32_t xor[4];
......@@ -89,17 +88,21 @@ struct nft_family_ops {
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,
uint32_t refs, uint32_t entries);
void (*print_rule)(struct nftnl_rule *r, unsigned int num,
unsigned int format);
void (*save_firewall)(const void *data, unsigned int format);
void (*save_rule)(const void *data, unsigned int format);
void (*save_counters)(const void *data);
void (*save_chain)(const struct nftnl_chain *c, const char *policy);
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);
void (*rule_to_cs)(const struct nftnl_rule *r,
struct iptables_command_state *cs);
void (*clear_cs)(struct iptables_command_state *cs);
bool (*rule_find)(struct nft_family_ops *ops, struct nftnl_rule *r,
void *data);
int (*xlate)(const void *data, struct xt_xlate *xl);
......@@ -107,6 +110,7 @@ struct nft_family_ops {
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(struct nftnl_rule *r, uint8_t *mask, size_t len);
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);
......@@ -118,6 +122,7 @@ 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_l4proto(struct nftnl_rule *r, 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,
......@@ -140,36 +145,36 @@ 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,
void nft_rule_to_iptables_command_state(const struct nftnl_rule *r,
struct iptables_command_state *cs);
void nft_clear_iptables_command_state(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,
uint32_t refs, uint32_t entries);
void print_rule_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,
void save_rule_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);
void save_counters(const void *data);
void nft_ipv46_save_chain(const struct nftnl_chain *c, const char *policy);
void save_matches_and_target(const struct iptables_command_state *cs,
bool goto_flag, const void *fw,
unsigned int format);
struct nft_family_ops *nft_family_ops_lookup(int family);
struct nft_handle;
void nft_ipv46_parse_target(struct xtables_target *t, void *data);
bool nft_ipv46_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
struct iptables_command_state *cs);
void *data);
bool compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2);
bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2);
......@@ -223,9 +228,9 @@ struct nft_xt_cmd_parse {
unsigned int command;
unsigned int rulenum;
char *table;
char *chain;
char *newname;
char *policy;
const char *chain;
const char *newname;
const char *policy;
bool restore;
int verbose;
};
......@@ -238,6 +243,7 @@ struct nft_xt_restore_parse {
FILE *in;
int testing;
const char *tablename;
bool commit;
};
struct nftnl_chain_list;
......@@ -245,17 +251,18 @@ struct nftnl_chain_list;
struct nft_xt_restore_cb {
void (*table_new)(struct nft_handle *h, const char *table);
struct nftnl_chain_list *(*chain_list)(struct nft_handle *h);
int (*chains_purge)(struct nft_handle *h, const char *table,
struct nftnl_chain_list *clist);
void (*chain_del)(struct nftnl_chain_list *clist, const char *curtable,
const char *chain);
int (*chain_user_flush)(struct nft_handle *h,
struct nftnl_chain_list *clist,
const char *table, const char *chain);
int (*chain_set)(struct nft_handle *h, const char *table,
const char *chain, const char *policy,
const struct xt_counters *counters);
int (*chain_user_add)(struct nft_handle *h, const char *chain,
const char *table);
int (*rule_flush)(struct nft_handle *h, const char *chain, const char *table);
int (*table_flush)(struct nft_handle *h, const char *table);
int (*do_command)(struct nft_handle *h, int argc, char *argv[],
char **table, bool restore);
......@@ -269,4 +276,5 @@ void xtables_restore_parse(struct nft_handle *h,
struct nft_xt_restore_cb *cb,
int argc, char *argv[]);
void nft_check_xt_legacy(int family, bool is_ipt_save);
#endif
......@@ -19,6 +19,7 @@
#include <time.h>
#include <stdarg.h>
#include <inttypes.h>
#include <assert.h>
#include <xtables.h>
#include <libiptc/libxtc.h>
......@@ -37,6 +38,8 @@
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_tables_compat.h>
#include <linux/netfilter/xt_limit.h>
#include <libmnl/libmnl.h>
#include <libnftnl/table.h>
#include <libnftnl/chain.h>
......@@ -44,6 +47,7 @@
#include <libnftnl/expr.h>
#include <libnftnl/set.h>
#include <libnftnl/udata.h>
#include <libnftnl/batch.h>
#include <netinet/in.h> /* inet_ntoa */
#include <arpa/inet.h>
......@@ -60,7 +64,7 @@ int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
void *data)
{
int ret;
char buf[MNL_SOCKET_BUFFER_SIZE];
char buf[16536];
if (mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len) < 0)
return -1;
......@@ -80,13 +84,7 @@ int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
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;
};
#define NFT_NLMSG_MAXSIZE (UINT16_MAX + getpagesize())
/* selected batch page is 256 Kbytes long to load ruleset of
* half a million rules without hitting -EMSGSIZE due to large
......@@ -94,44 +92,74 @@ struct batch_page {
*/
#define BATCH_PAGE_SIZE getpagesize() * 32
static struct mnl_nlmsg_batch *mnl_nftnl_batch_alloc(void)
static struct nftnl_batch *mnl_batch_init(void)
{
static char *buf;
struct nftnl_batch *batch;
/* libmnl needs higher buffer to handle batch overflows */
buf = malloc(BATCH_PAGE_SIZE + getpagesize());
if (buf == NULL)
batch = nftnl_batch_alloc(BATCH_PAGE_SIZE, NFT_NLMSG_MAXSIZE);
if (batch == NULL)
return NULL;
return mnl_nlmsg_batch_start(buf, BATCH_PAGE_SIZE);
return batch;
}
static struct mnl_nlmsg_batch *
mnl_nftnl_batch_page_add(struct mnl_nlmsg_batch *batch)
static void mnl_nft_batch_continue(struct nftnl_batch *batch)
{
struct batch_page *batch_page;
assert(nftnl_batch_update(batch) >= 0);
}
batch_page = malloc(sizeof(struct batch_page));
if (batch_page == NULL)
return NULL;
static uint32_t mnl_batch_begin(struct nftnl_batch *batch, uint32_t seqnum)
{
nftnl_batch_begin(nftnl_batch_buffer(batch), seqnum);
mnl_nft_batch_continue(batch);
return seqnum;
}
static void mnl_batch_end(struct nftnl_batch *batch, uint32_t seqnum)
{
nftnl_batch_end(nftnl_batch_buffer(batch), seqnum);
mnl_nft_batch_continue(batch);
}
static void mnl_batch_reset(struct nftnl_batch *batch)
{
nftnl_batch_free(batch);
}
struct mnl_err {
struct list_head head;
int err;
uint32_t seqnum;
};
static void mnl_err_list_node_add(struct list_head *err_list, int error,
int seqnum)
{
struct mnl_err *err = malloc(sizeof(struct mnl_err));
batch_page->batch = batch;
list_add_tail(&batch_page->head, &batch_page_list);
batch_num_pages++;
err->seqnum = seqnum;
err->err = error;
list_add_tail(&err->head, err_list);
}
return mnl_nftnl_batch_alloc();
static void mnl_err_list_free(struct mnl_err *err)
{
list_del(&err->head);
free(err);
}
static int nlbuffsiz;
static void mnl_nft_set_sndbuffer(const struct mnl_socket *nl)
static void mnl_set_sndbuffer(const struct mnl_socket *nl,
struct nftnl_batch *batch)
{
int newbuffsiz;
if (batch_num_pages * BATCH_PAGE_SIZE <= nlbuffsiz)
if (nftnl_batch_iovec_len(batch) * BATCH_PAGE_SIZE <= nlbuffsiz)
return;
newbuffsiz = batch_num_pages * BATCH_PAGE_SIZE;
newbuffsiz = nftnl_batch_iovec_len(batch) * BATCH_PAGE_SIZE;
/* Rise sender buffer length to avoid hitting -EMSGSIZE */
if (setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUFFORCE,
......@@ -141,56 +169,32 @@ static void mnl_nft_set_sndbuffer(const struct mnl_socket *nl)
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 ssize_t mnl_nft_socket_sendmsg(const struct mnl_socket *nf_sock,
struct nftnl_batch *batch)
{
static const struct sockaddr_nl snl = {
.nl_family = AF_NETLINK
};
struct iovec iov[batch_num_pages];
uint32_t iov_len = nftnl_batch_iovec_len(batch);
struct iovec iov[iov_len];
struct msghdr msg = {
.msg_name = (struct sockaddr *) &snl,
.msg_namelen = sizeof(snl),
.msg_iov = iov,
.msg_iovlen = batch_num_pages,
.msg_iovlen = iov_len,
};
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();
mnl_set_sndbuffer(nf_sock, batch);
nftnl_batch_iovec(batch, iov, iov_len);
return ret;
return sendmsg(mnl_socket_get_fd(nf_sock), &msg, 0);
}
static int mnl_nftnl_batch_talk(struct nft_handle *h)
static int mnl_batch_talk(const struct mnl_socket *nf_sock,
struct nftnl_batch *batch, struct list_head *err_list)
{
int ret, fd = mnl_socket_get_fd(h->nl);
const struct mnl_socket *nl = nf_sock;
int ret, fd = mnl_socket_get_fd(nl), portid = mnl_socket_get_portid(nl);
char rcv_buf[MNL_SOCKET_BUFFER_SIZE];
fd_set readfds;
struct timeval tv = {
......@@ -199,7 +203,7 @@ static int mnl_nftnl_batch_talk(struct nft_handle *h)
};
int err = 0;
ret = mnl_nft_socket_sendmsg(h->nl);
ret = mnl_nft_socket_sendmsg(nf_sock, batch);
if (ret == -1)
return -1;
......@@ -212,16 +216,18 @@ static int mnl_nftnl_batch_talk(struct nft_handle *h)
return -1;
while (ret > 0 && FD_ISSET(fd, &readfds)) {
ret = mnl_socket_recvfrom(h->nl, rcv_buf, sizeof(rcv_buf));
struct nlmsghdr *nlh = (struct nlmsghdr *)rcv_buf;
ret = mnl_socket_recvfrom(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 = mnl_cb_run(rcv_buf, ret, 0, portid, NULL, NULL);
/* Continue on error, make sure we get all acknowledgments */
if (ret == -1) {
mnl_err_list_node_add(err_list, errno, nlh->nlmsg_seq);
err = -1;
}
ret = select(fd+1, &readfds, NULL, NULL, &tv);
if (ret == -1)
......@@ -230,31 +236,19 @@ static int mnl_nftnl_batch_talk(struct nft_handle *h)
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);
return err;
}
enum obj_update_type {
NFT_COMPAT_TABLE_ADD,
NFT_COMPAT_TABLE_FLUSH,
NFT_COMPAT_CHAIN_ADD,
NFT_COMPAT_CHAIN_USER_ADD,
NFT_COMPAT_CHAIN_USER_DEL,
NFT_COMPAT_CHAIN_USER_FLUSH,
NFT_COMPAT_CHAIN_UPDATE,
NFT_COMPAT_CHAIN_RENAME,
NFT_COMPAT_CHAIN_ZERO,
NFT_COMPAT_RULE_APPEND,
NFT_COMPAT_RULE_INSERT,
NFT_COMPAT_RULE_REPLACE,
......@@ -270,14 +264,82 @@ enum obj_action {
struct obj_update {
struct list_head head;
enum obj_update_type type;
unsigned int seq;
union {
struct nftnl_table *table;
struct nftnl_chain *chain;
struct nftnl_rule *rule;
void *ptr;
};
struct {
unsigned int lineno;
} error;
};
static int mnl_append_error(const struct nft_handle *h,
const struct obj_update *o,
const struct mnl_err *err,
char *buf, unsigned int len)
{
static const char *type_name[] = {
[NFT_COMPAT_TABLE_ADD] = "TABLE_ADD",
[NFT_COMPAT_TABLE_FLUSH] = "TABLE_FLUSH",
[NFT_COMPAT_CHAIN_ADD] = "CHAIN_ADD",
[NFT_COMPAT_CHAIN_USER_ADD] = "CHAIN_USER_ADD",
[NFT_COMPAT_CHAIN_USER_DEL] = "CHAIN_USER_DEL",
[NFT_COMPAT_CHAIN_USER_FLUSH] = "CHAIN_USER_FLUSH",
[NFT_COMPAT_CHAIN_UPDATE] = "CHAIN_UPDATE",
[NFT_COMPAT_CHAIN_RENAME] = "CHAIN_RENAME",
[NFT_COMPAT_RULE_APPEND] = "RULE_APPEND",
[NFT_COMPAT_RULE_INSERT] = "RULE_INSERT",
[NFT_COMPAT_RULE_REPLACE] = "RULE_REPLACE",
[NFT_COMPAT_RULE_DELETE] = "RULE_DELETE",
[NFT_COMPAT_RULE_FLUSH] = "RULE_FLUSH",
};
char errmsg[256];
char tcr[128];
if (o->error.lineno)
snprintf(errmsg, sizeof(errmsg), "\nline %u: %s failed (%s)",
o->error.lineno, type_name[o->type], strerror(err->err));
else
snprintf(errmsg, sizeof(errmsg), " %s failed (%s)",
type_name[o->type], strerror(err->err));
switch (o->type) {
case NFT_COMPAT_TABLE_ADD:
case NFT_COMPAT_TABLE_FLUSH:
snprintf(tcr, sizeof(tcr), "table %s",
nftnl_table_get_str(o->table, NFTNL_TABLE_NAME));
break;
case NFT_COMPAT_CHAIN_ADD:
case NFT_COMPAT_CHAIN_ZERO:
case NFT_COMPAT_CHAIN_USER_ADD:
case NFT_COMPAT_CHAIN_USER_DEL:
case NFT_COMPAT_CHAIN_USER_FLUSH:
case NFT_COMPAT_CHAIN_UPDATE:
case NFT_COMPAT_CHAIN_RENAME:
snprintf(tcr, sizeof(tcr), "chain %s",
nftnl_chain_get_str(o->chain, NFTNL_CHAIN_NAME));
break;
case NFT_COMPAT_RULE_APPEND:
case NFT_COMPAT_RULE_INSERT:
case NFT_COMPAT_RULE_REPLACE:
case NFT_COMPAT_RULE_DELETE:
case NFT_COMPAT_RULE_FLUSH:
snprintf(tcr, sizeof(tcr), "rule in chain %s",
nftnl_rule_get_str(o->rule, NFTNL_RULE_CHAIN));
#if 0
{
nft_rule_print_save(o->rule, NFT_RULE_APPEND, FMT_NOCOUNTS);
}
#endif
break;
}
return snprintf(buf, len, "%s: %s", errmsg, tcr);
}
static int batch_add(struct nft_handle *h, enum obj_update_type type, void *ptr)
{
struct obj_update *obj;
......@@ -287,6 +349,7 @@ static int batch_add(struct nft_handle *h, enum obj_update_type type, void *ptr)
return -1;
obj->ptr = ptr;
obj->error.lineno = h->error.lineno;
obj->type = type;
list_add_tail(&obj->head, &h->obj_list);
h->obj_list_num++;
......@@ -454,12 +517,6 @@ struct builtin_table xtables_arp[TABLES_MAX] = {
.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",
......@@ -521,30 +578,6 @@ struct builtin_table xtables_bridge[TABLES_MAX] = {
},
};
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)
{
......@@ -560,13 +593,7 @@ static int nft_table_builtin_add(struct nft_handle *h,
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;
}
......@@ -591,29 +618,6 @@ nft_chain_builtin_alloc(struct builtin_table *table,
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)
......@@ -624,14 +628,11 @@ static void nft_chain_builtin_add(struct nft_handle *h,
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 *
struct builtin_table *
nft_table_builtin_find(struct nft_handle *h, const char *table)
{
int i;
......@@ -652,7 +653,7 @@ nft_table_builtin_find(struct nft_handle *h, const char *table)
}
/* find if built-in chain already exists */
static struct builtin_chain *
struct builtin_chain *
nft_chain_builtin_find(struct builtin_table *t, const char *chain)
{
int i;
......@@ -676,7 +677,7 @@ static void nft_chain_builtin_init(struct nft_handle *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++) {
for (i=0; i < NF_INET_NUMHOOKS && table->chains[i].name != NULL; i++) {
c = nft_chain_list_find(list, table->name,
table->chains[i].name);
......@@ -685,28 +686,27 @@ static void nft_chain_builtin_init(struct nft_handle *h,
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;
}
if (t == NULL)
return -1;
if (t->initialized)
return 0;
if (nft_table_builtin_add(h, t) < 0)
return -1;
nft_chain_builtin_init(h, t);
out:
return ret;
t->initialized = true;
return 0;
}
static bool nft_chain_builtin(struct nftnl_chain *c)
......@@ -717,47 +717,20 @@ static bool nft_chain_builtin(struct nftnl_chain *c)
return nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM) != NULL;
}
static bool mnl_batch_supported(struct nft_handle *h)
static int nft_restart(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_socket_close(h->nl);
mnl_nlmsg_batch_reset(h->batch);
h->nl = mnl_socket_open(NETLINK_NETFILTER);
if (h->nl == NULL)
return -1;
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;
if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0)
return -1;
ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
}
h->portid = mnl_socket_get_portid(h->nl);
/* 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;
return 0;
}
int nft_init(struct nft_handle *h, struct builtin_table *t)
......@@ -766,35 +739,77 @@ int nft_init(struct nft_handle *h, struct builtin_table *t)
if (h->nl == NULL)
return -1;
if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0)
if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0) {
mnl_socket_close(h->nl);
return -1;
}
h->portid = mnl_socket_get_portid(h->nl);
h->tables = t;
INIT_LIST_HEAD(&h->obj_list);
INIT_LIST_HEAD(&h->err_list);
return 0;
}
static int __flush_rule_cache(struct nftnl_rule *r, void *data)
{
const char *tablename = data;
h->batch = mnl_nftnl_batch_alloc();
h->batch_support = mnl_batch_supported(h);
if (!strcmp(nftnl_rule_get_str(r, NFTNL_RULE_TABLE), tablename)) {
nftnl_rule_list_del(r);
nftnl_rule_free(r);
}
return 0;
}
static void flush_rule_cache(struct nft_handle *h)
static void flush_rule_cache(struct nft_handle *h, const char *tablename)
{
if (!h->rule_cache)
return;
if (tablename) {
nftnl_rule_list_foreach(h->rule_cache, __flush_rule_cache,
(void *)tablename);
} else {
nftnl_rule_list_free(h->rule_cache);
h->rule_cache = NULL;
}
}
static int __flush_chain_cache(struct nftnl_chain *c, void *data)
{
const char *tablename = data;
if (!strcmp(nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE), tablename)) {
nftnl_chain_list_del(c);
nftnl_chain_free(c);
}
return 0;
}
static void flush_chain_cache(struct nft_handle *h, const char *tablename)
{
if (!h->chain_cache)
return;
if (tablename) {
nftnl_chain_list_foreach(h->chain_cache, __flush_chain_cache,
(void *)tablename);
} else {
nftnl_chain_list_free(h->chain_cache);
h->chain_cache = NULL;
}
}
void nft_fini(struct nft_handle *h)
{
flush_rule_cache(h);
flush_chain_cache(h, NULL);
flush_rule_cache(h, NULL);
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)
......@@ -802,7 +817,7 @@ 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);
nftnl_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
......@@ -818,8 +833,12 @@ static struct nftnl_chain *nft_chain_new(struct nft_handle *h,
struct builtin_chain *_c;
_t = nft_table_builtin_find(h, table);
if (!_t) {
errno = ENXIO;
return NULL;
}
/* 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);
......@@ -856,14 +875,13 @@ int nft_chain_set(struct nft_handle *h, const char *table,
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);
else
errno = EINVAL;
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;
......@@ -886,11 +904,50 @@ static int __add_match(struct nftnl_expr *e, struct xt_entry_match *m)
return 0;
}
static int add_nft_limit(struct nftnl_rule *r, struct xt_entry_match *m)
{
struct xt_rateinfo *rinfo = (void *)m->data;
static const uint32_t mult[] = {
XT_LIMIT_SCALE*24*60*60, /* day */
XT_LIMIT_SCALE*60*60, /* hour */
XT_LIMIT_SCALE*60, /* min */
XT_LIMIT_SCALE, /* sec */
};
struct nftnl_expr *expr;
int i;
expr = nftnl_expr_alloc("limit");
if (!expr)
return -ENOMEM;
for (i = 1; i < ARRAY_SIZE(mult); i++) {
if (rinfo->avg > mult[i] ||
mult[i] / rinfo->avg < mult[i] % rinfo->avg)
break;
}
nftnl_expr_set_u32(expr, NFTNL_EXPR_LIMIT_TYPE, NFT_LIMIT_PKTS);
nftnl_expr_set_u32(expr, NFTNL_EXPR_LIMIT_FLAGS, 0);
nftnl_expr_set_u64(expr, NFTNL_EXPR_LIMIT_RATE,
mult[i - 1] / rinfo->avg);
nftnl_expr_set_u64(expr, NFTNL_EXPR_LIMIT_UNIT,
mult[i - 1] / XT_LIMIT_SCALE);
nftnl_expr_set_u32(expr, NFTNL_EXPR_LIMIT_BURST, rinfo->burst);
nftnl_rule_add_expr(r, expr);
return 0;
}
int add_match(struct nftnl_rule *r, struct xt_entry_match *m)
{
struct nftnl_expr *expr;
int ret;
if (!strcmp(m->u.user.name, "limit"))
return add_nft_limit(r, m);
expr = nftnl_expr_alloc("match");
if (expr == NULL)
return -ENOMEM;
......@@ -919,11 +976,36 @@ static int __add_target(struct nftnl_expr *e, struct xt_entry_target *t)
return 0;
}
static int add_meta_nftrace(struct nftnl_rule *r)
{
struct nftnl_expr *expr;
expr = nftnl_expr_alloc("immediate");
if (expr == NULL)
return -ENOMEM;
nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG32_01);
nftnl_expr_set_u8(expr, NFTNL_EXPR_IMM_DATA, 1);
nftnl_rule_add_expr(r, expr);
expr = nftnl_expr_alloc("meta");
if (expr == NULL)
return -ENOMEM;
nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_NFTRACE);
nftnl_expr_set_u32(expr, NFTNL_EXPR_META_SREG, NFT_REG32_01);
nftnl_rule_add_expr(r, expr);
return 0;
}
int add_target(struct nftnl_rule *r, struct xt_entry_target *t)
{
struct nftnl_expr *expr;
int ret;
if (strcmp(t->u.user.name, "TRACE") == 0)
return add_meta_nftrace(r);
expr = nftnl_expr_alloc("target");
if (expr == NULL)
return -ENOMEM;
......@@ -996,7 +1078,7 @@ 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);
nftnl_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
......@@ -1027,13 +1109,21 @@ enum udata_type {
int add_comment(struct nftnl_rule *r, const char *comment)
{
struct nftnl_udata_buf *udata;
uint32_t len;
if (nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len))
return -EALREADY;
udata = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
if (!udata)
return -ENOMEM;
if (strnlen(comment, 255) == 255)
return -ENOSPC;
if (!nftnl_udata_put_strz(udata, UDATA_TYPE_COMMENT, comment))
return -ENOMEM;
nftnl_rule_set_data(r, NFTNL_RULE_USERDATA,
nftnl_udata_buf_data(udata),
nftnl_udata_buf_len(udata));
......@@ -1105,6 +1195,8 @@ err:
return NULL;
}
static struct nftnl_rule_list *nft_rule_list_get(struct nft_handle *h);
int
nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
void *data, uint64_t handle, bool verbose)
......@@ -1128,26 +1220,36 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
} else
type = NFT_COMPAT_RULE_APPEND;
if (batch_rule_add(h, type, r) < 0)
if (batch_rule_add(h, type, r) < 0) {
nftnl_rule_free(r);
return 0;
}
if (verbose)
h->ops->print_rule(r, 0, FMT_PRINT_RULE);
if (!nft_rule_list_get(h))
return 0;
nftnl_rule_list_add_tail(r, h->rule_cache);
flush_rule_cache(h);
return 1;
}
void
nft_rule_print_save(const void *data,
struct nftnl_rule *r, enum nft_rule_print type,
nft_rule_print_save(const 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 iptables_command_state cs = {};
struct nft_family_ops *ops;
ops = nft_family_ops_lookup(family);
ops->rule_to_cs(r, &cs);
if (!(format & FMT_NOCOUNTS) && ops->save_counters)
ops->save_counters(data);
if (!(format & (FMT_NOCOUNTS | FMT_C_COUNTS)) && ops->save_counters)
ops->save_counters(&cs);
/* print chain name */
switch(type) {
......@@ -1159,9 +1261,11 @@ nft_rule_print_save(const void *data,
break;
}
if (ops->save_firewall)
ops->save_firewall(data, format);
if (ops->save_rule)
ops->save_rule(&cs, format);
if (ops->clear_cs)
ops->clear_cs(&cs);
}
static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
......@@ -1187,10 +1291,14 @@ err:
static struct nftnl_chain_list *nftnl_chain_list_get(struct nft_handle *h)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
char buf[16536];
struct nlmsghdr *nlh;
struct nftnl_chain_list *list;
int ret;
if (h->chain_cache)
return h->chain_cache;
retry:
list = nftnl_chain_list_alloc();
if (list == NULL) {
errno = ENOMEM;
......@@ -1200,7 +1308,14 @@ static struct nftnl_chain_list *nftnl_chain_list_get(struct nft_handle *h)
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);
ret = mnl_talk(h, nlh, nftnl_chain_list_cb, list);
if (ret < 0 && errno == EINTR) {
assert(nft_restart(h) >= 0);
nftnl_chain_list_free(list);
goto retry;
}
h->chain_cache = list;
return list;
}
......@@ -1215,32 +1330,15 @@ static const char *policy_name[NF_ACCEPT+1] = {
[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 nft_family_ops *ops;
struct nftnl_chain *c;
ops = nft_family_ops_lookup(h->family);
iter = nftnl_chain_list_iter_create(list);
if (iter == NULL)
return 0;
......@@ -1249,19 +1347,26 @@ int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list,
while (c != NULL) {
const char *chain_table =
nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
bool basechain = false;
const char *policy = NULL;
if (strcmp(table, chain_table) != 0)
goto next;
basechain = nft_chain_builtin(c);
nft_chain_print_save(c, basechain);
if (nft_chain_builtin(c)) {
uint32_t pol = NF_ACCEPT;
if (nftnl_chain_get(c, NFTNL_CHAIN_POLICY))
pol = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
policy = policy_name[pol];
}
if (ops->save_chain)
ops->save_chain(c, policy);
next:
c = nftnl_chain_list_iter_next(iter);
}
nftnl_chain_list_iter_destroy(iter);
nftnl_chain_list_free(list);
return 1;
}
......@@ -1290,7 +1395,7 @@ err:
static struct nftnl_rule_list *nft_rule_list_get(struct nft_handle *h)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
char buf[16536];
struct nlmsghdr *nlh;
struct nftnl_rule_list *list;
int ret;
......@@ -1298,6 +1403,7 @@ static struct nftnl_rule_list *nft_rule_list_get(struct nft_handle *h)
if (h->rule_cache)
return h->rule_cache;
retry:
list = nftnl_rule_list_alloc();
if (list == NULL)
return 0;
......@@ -1307,15 +1413,21 @@ static struct nftnl_rule_list *nft_rule_list_get(struct nft_handle *h)
ret = mnl_talk(h, nlh, nftnl_rule_list_cb, list);
if (ret < 0) {
if (errno == EINTR) {
assert(nft_restart(h) >= 0);
nftnl_rule_list_free(list);
return NULL;
goto retry;
}
h->rule_cache = list;
nftnl_rule_list_free(list);
return NULL;
}
h->rule_cache = list;
return list;
}
int nft_rule_save(struct nft_handle *h, const char *table, bool counters)
int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format)
{
struct nftnl_rule_list *list;
struct nftnl_rule_list_iter *iter;
......@@ -1333,15 +1445,11 @@ int nft_rule_save(struct nft_handle *h, const char *table, bool counters)
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);
nft_rule_print_save(r, NFT_RULE_APPEND, format);
next:
r = nftnl_rule_list_iter_next(iter);
......@@ -1369,24 +1477,73 @@ __nft_rule_flush(struct nft_handle *h, const char *table, const char *chain)
nftnl_rule_free(r);
}
int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table)
struct chain_user_flush_data {
struct nft_handle *handle;
const char *table;
const char *chain;
};
static int __nft_chain_user_flush(struct nftnl_chain *c, void *data)
{
int ret;
const char *table_name = nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
const char *chain_name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
struct chain_user_flush_data *d = data;
struct nft_handle *h = d->handle;
const char *table = d->table;
const char *chain = d->chain;
if (strcmp(table, table_name) != 0)
return 0;
if (strcmp(chain, chain_name) != 0)
return 0;
if (!nftnl_chain_is_set(c, NFTNL_CHAIN_HOOKNUM))
__nft_rule_flush(h, table, chain);
return 0;
}
int nft_chain_user_flush(struct nft_handle *h, struct nftnl_chain_list *list,
const char *table, const char *chain)
{
struct chain_user_flush_data d = {
.handle = h,
.table = table,
.chain = chain,
};
nft_fn = nft_chain_user_flush;
nftnl_chain_list_foreach(list, __nft_chain_user_flush, &d);
return 1;
}
int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
bool verbose)
{
int ret = 0;
struct nftnl_chain_list *list;
struct nftnl_chain_list_iter *iter;
struct nftnl_chain *c;
if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
nft_xt_builtin_init(h, table);
nft_fn = nft_rule_flush;
list = nftnl_chain_list_get(h);
if (list == NULL) {
ret = 0;
ret = 1;
goto err;
}
iter = nftnl_chain_list_iter_create(list);
if (iter == NULL)
if (iter == NULL) {
ret = 1;
goto err;
}
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
......@@ -1401,6 +1558,9 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table)
if (chain != NULL && strcmp(chain, chain_name) != 0)
goto next;
if (verbose)
fprintf(stdout, "Flushing chain `%s'\n", chain_name);
__nft_rule_flush(h, table_name, chain_name);
if (chain != NULL)
......@@ -1408,12 +1568,9 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table)
next:
c = nftnl_chain_list_iter_next(iter);
}
nftnl_chain_list_iter_destroy(iter);
flush_rule_cache(h);
flush_rule_cache(h, table);
err:
nftnl_chain_list_free(list);
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
......@@ -1436,37 +1593,23 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl
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);
}
nft_chain_dump(h);
nftnl_chain_list_add(c, h->chain_cache);
/* 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);
}
/* From linux/netlink.h */
#ifndef NLM_F_NONREC
#define NLM_F_NONREC 0x100 /* Do not delete recursively */
#endif
int nft_chain_user_del(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, bool verbose)
{
struct nftnl_chain_list *list;
struct nftnl_chain_list_iter *iter;
......@@ -1474,6 +1617,8 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *tabl
int ret = 0;
int deleted_ctr = 0;
nft_fn = nft_chain_user_del;
list = nftnl_chain_list_get(h);
if (list == NULL)
goto err;
......@@ -1499,15 +1644,16 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *tabl
if (chain != NULL && strcmp(chain, chain_name) != 0)
goto next;
if (h->batch_support)
if (verbose)
fprintf(stdout, "Deleting chain `%s'\n", chain);
ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_DEL, c);
else
ret = __nft_chain_del(h, c);
if (ret < 0)
break;
deleted_ctr++;
nftnl_chain_list_del(c);
if (chain != NULL)
break;
......@@ -1517,11 +1663,9 @@ next:
nftnl_chain_list_iter_destroy(iter);
err:
if (!h->batch_support)
nftnl_chain_list_free(list);
/* chain not found */
if (deleted_ctr == 0) {
if (chain != NULL && deleted_ctr == 0) {
ret = -1;
errno = ENOENT;
}
......@@ -1575,6 +1719,21 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
return nft_chain_list_find(list, table, chain);
}
bool nft_chain_exists(struct nft_handle *h,
const char *table, const char *chain)
{
struct builtin_table *t = nft_table_builtin_find(h, table);
/* xtables does not support custom tables */
if (!t)
return false;
if (nft_chain_builtin_find(t, chain))
return true;
return !!nft_chain_find(h, table, chain);
}
int nft_chain_user_rename(struct nft_handle *h,const char *chain,
const char *table, const char *newname)
{
......@@ -1608,19 +1767,7 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain,
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;
......@@ -1649,10 +1796,12 @@ err:
static struct nftnl_table_list *nftnl_table_list_get(struct nft_handle *h)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
char buf[16536];
struct nlmsghdr *nlh;
struct nftnl_table_list *list;
int ret;
retry:
list = nftnl_table_list_alloc();
if (list == NULL)
return 0;
......@@ -1660,7 +1809,12 @@ static struct nftnl_table_list *nftnl_table_list_get(struct nft_handle *h)
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);
ret = mnl_talk(h, nlh, nftnl_table_list_cb, list);
if (ret < 0 && errno == EINTR) {
assert(nft_restart(h) >= 0);
nftnl_table_list_free(list);
goto retry;
}
return list;
}
......@@ -1685,12 +1839,15 @@ bool nft_table_find(struct nft_handle *h, const char *tablename)
const char *this_tablename =
nftnl_table_get(t, NFTNL_TABLE_NAME);
if (strcmp(tablename, this_tablename) == 0)
return true;
if (strcmp(tablename, this_tablename) == 0) {
ret = true;
break;
}
t = nftnl_table_list_iter_next(iter);
}
nftnl_table_list_iter_destroy(iter);
nftnl_table_list_free(list);
err:
......@@ -1701,20 +1858,17 @@ 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;
}
if (list == NULL)
return -1;
iter = nftnl_table_list_iter_create(list);
if (iter == NULL)
return 0;
return -1;
t = nftnl_table_list_iter_next(iter);
while (t != NULL) {
......@@ -1726,44 +1880,89 @@ int nft_for_each_table(struct nft_handle *h,
t = nftnl_table_list_iter_next(iter);
}
nftnl_table_list_iter_destroy(iter);
nftnl_table_list_free(list);
err:
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
return 0;
}
int nft_table_purge_chains(struct nft_handle *h, const char *this_table,
struct nftnl_chain_list *chain_list)
static int __nft_table_flush(struct nft_handle *h, const char *table)
{
struct nftnl_chain_list_iter *iter;
struct nftnl_chain *chain_obj;
struct builtin_table *_t;
struct nftnl_table *t;
t = nftnl_table_alloc();
if (t == NULL)
return -1;
nftnl_table_set_str(t, NFTNL_TABLE_NAME, table);
batch_table_add(h, NFT_COMPAT_TABLE_FLUSH, t);
_t = nft_table_builtin_find(h, table);
assert(_t);
_t->initialized = false;
flush_chain_cache(h, table);
flush_rule_cache(h, table);
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);
int nft_table_flush(struct nft_handle *h, const char *table)
{
struct nftnl_table_list_iter *iter;
struct nftnl_table_list *list;
struct nftnl_table *t;
int ret = 0;
if (strcmp(this_table, table) != 0)
goto next;
nft_fn = nft_table_flush;
if (nft_chain_builtin(chain_obj))
goto next;
list = nftnl_table_list_get(h);
if (list == NULL) {
ret = -1;
goto err_out;
}
if ( __nft_chain_del(h, chain_obj) < 0) {
if (errno != EBUSY)
return -1;
iter = nftnl_table_list_iter_create(list);
if (iter == NULL) {
ret = -1;
goto err_table_list;
}
t = nftnl_table_list_iter_next(iter);
while (t != NULL) {
const char *table_name =
nftnl_table_get_str(t, NFTNL_TABLE_NAME);
if (strcmp(table_name, table) != 0)
goto next;
ret = __nft_table_flush(h, table);
if (ret < 0)
goto err_table_iter;
next:
chain_obj = nftnl_chain_list_iter_next(iter);
t = nftnl_table_list_iter_next(iter);
}
nftnl_chain_list_iter_destroy(iter);
return 0;
if (!h->rule_cache) {
h->rule_cache = nftnl_rule_list_alloc();
if (h->rule_cache == NULL)
return -1;
}
err_table_iter:
nftnl_table_list_iter_destroy(iter);
err_table_list:
nftnl_table_list_free(list);
err_out:
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
void nft_table_new(struct nft_handle *h, const char *table)
{
if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
nft_xt_builtin_init(h, table);
}
static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule_list *list,
......@@ -1832,7 +2031,7 @@ 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;
struct nftnl_rule *r;
nft_fn = nft_rule_check;
......@@ -1840,11 +2039,15 @@ int nft_rule_check(struct nft_handle *h, const char *chain,
if (list == NULL)
return 0;
ret = nft_rule_find(h, list, chain, table, data, -1) ? 1 : 0;
if (ret == 0)
r = nft_rule_find(h, list, chain, table, data, -1);
if (r == NULL) {
errno = ENOENT;
return 0;
}
if (verbose)
h->ops->print_rule(r, 0, FMT_PRINT_RULE);
return ret;
return 1;
}
int nft_rule_delete(struct nft_handle *h, const char *chain,
......@@ -1865,15 +2068,15 @@ int nft_rule_delete(struct nft_handle *h, const char *chain,
ret =__nft_rule_del(h, list, r);
if (ret < 0)
errno = ENOMEM;
if (verbose)
h->ops->print_rule(r, 0, FMT_PRINT_RULE);
} else
errno = ENOENT;
flush_rule_cache(h);
return ret;
}
static int
static struct nftnl_rule *
nft_rule_add(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *cs,
uint64_t handle, bool verbose)
......@@ -1882,25 +2085,27 @@ nft_rule_add(struct nft_handle *h, const char *chain,
r = nft_rule_new(h, chain, table, cs);
if (r == NULL)
return 0;
return NULL;
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 NULL;
}
flush_rule_cache(h);
return 1;
if (verbose)
h->ops->print_rule(r, 0, FMT_PRINT_RULE);
return r;
}
int nft_rule_insert(struct nft_handle *h, const char *chain,
const char *table, void *data, int rulenum, bool verbose)
{
struct nftnl_rule *r, *new_rule;
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 */
......@@ -1921,11 +2126,9 @@ int nft_rule_insert(struct nft_handle *h, const char *chain,
*/
r = nft_rule_find(h, list, chain, table, data,
rulenum - 1);
if (r != NULL) {
flush_rule_cache(h);
if (r != NULL)
return nft_rule_append(h, chain, table, data,
0, verbose);
}
errno = ENOENT;
goto err;
......@@ -1933,13 +2136,21 @@ int nft_rule_insert(struct nft_handle *h, const char *chain,
handle = nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE);
DEBUGP("adding after rule handle %"PRIu64"\n", handle);
flush_rule_cache(h);
} else {
nft_rule_list_get(h);
}
return nft_rule_add(h, chain, table, data, handle, verbose);
new_rule = nft_rule_add(h, chain, table, data, handle, verbose);
if (!new_rule)
goto err;
if (handle)
nftnl_rule_list_insert_at(new_rule, r);
else
nftnl_rule_list_add(new_rule, h->rule_cache);
return 1;
err:
flush_rule_cache(h);
return 0;
}
......@@ -1958,8 +2169,6 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain,
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)
......@@ -1967,8 +2176,6 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain,
} else
errno = ENOENT;
flush_rule_cache(h);
return ret;
}
......@@ -1991,14 +2198,14 @@ int nft_rule_replace(struct nft_handle *h, const char *chain,
(unsigned long long)
nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE));
nftnl_rule_list_del(r);
ret = nft_rule_append(h, chain, table, data,
nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE),
verbose);
} else
errno = ENOENT;
flush_rule_cache(h);
return ret;
}
......@@ -2011,7 +2218,7 @@ __nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
struct nftnl_rule_list *list;
struct nftnl_rule_list_iter *iter;
struct nftnl_rule *r;
int rule_ctr = 0, ret = 0;
int rule_ctr = 0;
list = nft_rule_list_get(h);
if (list == NULL)
......@@ -2019,7 +2226,7 @@ __nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
iter = nftnl_rule_list_iter_create(list);
if (iter == NULL)
goto err;
return 0;
r = nftnl_rule_list_iter_next(iter);
while (r != NULL) {
......@@ -2040,21 +2247,51 @@ __nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
}
cb(r, rule_ctr, format);
if (rulenum > 0 && rule_ctr == rulenum) {
ret = 1;
if (rulenum > 0)
break;
}
next:
r = nftnl_rule_list_iter_next(iter);
}
nftnl_rule_list_iter_destroy(iter);
err:
if (ret == 0)
errno = ENOENT;
return 1;
}
return ret;
static int nft_rule_count(struct nft_handle *h,
const char *chain, const char *table)
{
struct nftnl_rule_list_iter *iter;
struct nftnl_rule_list *list;
struct nftnl_rule *r;
int rule_ctr = 0;
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);
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++;
next:
r = nftnl_rule_list_iter_next(iter);
}
nftnl_rule_list_iter_destroy(iter);
return rule_ctr;
}
int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
......@@ -2072,15 +2309,22 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
/* Force table and chain creation, otherwise first iptables -L
* lists no table/chains.
*/
if (!list_empty(&h->obj_list))
if (!list_empty(&h->obj_list)) {
nft_commit(h);
flush_chain_cache(h, NULL);
}
}
ops = nft_family_ops_lookup(h->family);
if (!nft_is_table_compatible(h, table)) {
xtables_error(OTHER_PROBLEM, "table `%s' is incompatible, use 'nft' tool.\n", table);
return 0;
}
if (chain && rulenum) {
__nft_rule_list(h, chain, table,
rulenum, format, ops->print_firewall);
rulenum, format, ops->print_rule);
return 1;
}
......@@ -2090,7 +2334,7 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
if (iter == NULL)
goto err;
if (ops->print_table_header)
if (!chain && ops->print_table_header)
ops->print_table_header(table);
c = nftnl_chain_list_iter_next(iter);
......@@ -2108,37 +2352,44 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
.bcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES),
};
bool basechain = false;
uint32_t entries;
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)
if (chain) {
if (strcmp(chain, chain_name) != 0)
goto next;
else if (ops->print_table_header)
ops->print_table_header(table);
}
if (found)
printf("\n");
entries = nft_rule_count(h, chain_name, table);
ops->print_header(format, chain_name, policy_name[policy],
&ctrs, basechain, refs);
&ctrs, basechain, refs - entries, entries);
__nft_rule_list(h, chain_name, table,
rulenum, format, ops->print_firewall);
rulenum, format, ops->print_rule);
found = true;
/* 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);
if (chain && !found)
return 0;
return 1;
}
......@@ -2146,11 +2397,7 @@ err:
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));
nft_rule_print_save(r, NFT_RULE_APPEND, format);
}
static int
......@@ -2205,8 +2452,26 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
{
struct nftnl_chain_list *list;
struct nftnl_chain_list_iter *iter;
unsigned int format = 0;
struct nftnl_chain *c;
int ret = 1;
int ret = 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);
/* Force table and chain creation, otherwise first iptables -L
* lists no table/chains.
*/
if (!list_empty(&h->obj_list)) {
nft_commit(h);
flush_chain_cache(h, NULL);
}
}
if (!nft_is_table_compatible(h, table)) {
xtables_error(OTHER_PROBLEM, "table `%s' is incompatible, use 'nft' tool.\n", table);
return 0;
}
list = nft_chain_dump(h);
......@@ -2219,6 +2484,11 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
if (iter == NULL)
goto err;
if (counters < 0)
format = FMT_C_COUNTS;
else if (counters == 0)
format = FMT_NOCOUNTS;
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
const char *chain_table =
......@@ -2232,7 +2502,7 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
goto next;
ret = __nft_rule_list(h, chain_name, table, rulenum,
counters ? 0 : FMT_NOCOUNTS, list_save);
format, list_save);
/* we printed the chain we wanted, stop processing. */
if (chain)
......@@ -2243,8 +2513,6 @@ next:
nftnl_chain_list_iter_destroy(iter);
err:
nftnl_chain_list_free(list);
return ret;
}
......@@ -2278,8 +2546,6 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
false);
error:
flush_rule_cache(h);
return ret;
}
......@@ -2289,10 +2555,9 @@ static void nft_compat_table_batch_add(struct nft_handle *h, uint16_t type,
{
struct nlmsghdr *nlh;
nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
nlh = nftnl_table_nlmsg_build_hdr(nftnl_batch_buffer(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,
......@@ -2301,11 +2566,10 @@ static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type,
{
struct nlmsghdr *nlh;
nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
nlh = nftnl_chain_nlmsg_build_hdr(nftnl_batch_buffer(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,
......@@ -2314,96 +2578,162 @@ static void nft_compat_rule_batch_add(struct nft_handle *h, uint16_t type,
{
struct nlmsghdr *nlh;
nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
nlh = nftnl_rule_nlmsg_build_hdr(nftnl_batch_buffer(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 void batch_obj_del(struct nft_handle *h, struct obj_update *o)
{
switch (o->type) {
case NFT_COMPAT_TABLE_ADD:
case NFT_COMPAT_TABLE_FLUSH:
nftnl_table_free(o->table);
break;
case NFT_COMPAT_CHAIN_ZERO:
case NFT_COMPAT_CHAIN_USER_ADD:
break;
case NFT_COMPAT_CHAIN_ADD:
case NFT_COMPAT_CHAIN_USER_DEL:
case NFT_COMPAT_CHAIN_USER_FLUSH:
case NFT_COMPAT_CHAIN_UPDATE:
case NFT_COMPAT_CHAIN_RENAME:
nftnl_chain_free(o->chain);
break;
case NFT_COMPAT_RULE_APPEND:
case NFT_COMPAT_RULE_INSERT:
case NFT_COMPAT_RULE_REPLACE:
case NFT_COMPAT_RULE_DELETE:
break;
case NFT_COMPAT_RULE_FLUSH:
nftnl_rule_free(o->rule);
break;
}
h->obj_list_num--;
list_del(&o->head);
free(o);
}
static int nft_action(struct nft_handle *h, int action)
{
struct obj_update *n, *tmp;
struct mnl_err *err, *ne;
unsigned int buflen, i, len;
bool show_errors = true;
char errmsg[1024];
uint32_t seq = 1;
int ret = 0;
mnl_nftnl_batch_begin(h->batch, seq++);
h->batch = mnl_batch_init();
list_for_each_entry_safe(n, tmp, &h->obj_list, head) {
mnl_batch_begin(h->batch, seq++);
list_for_each_entry(n, &h->obj_list, head) {
n->seq = seq++;
switch (n->type) {
case NFT_COMPAT_TABLE_ADD:
nft_compat_table_batch_add(h, NFT_MSG_NEWTABLE,
NLM_F_CREATE, seq++,
NLM_F_CREATE, n->seq,
n->table);
break;
case NFT_COMPAT_TABLE_FLUSH:
nft_compat_table_batch_add(h, NFT_MSG_DELTABLE,
0,
n->seq, n->table);
break;
case NFT_COMPAT_CHAIN_ADD:
case NFT_COMPAT_CHAIN_ZERO:
nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
NLM_F_CREATE, seq++,
NLM_F_CREATE, n->seq,
n->chain);
break;
case NFT_COMPAT_CHAIN_USER_ADD:
nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
NLM_F_EXCL, seq++,
NLM_F_EXCL, n->seq,
n->chain);
break;
case NFT_COMPAT_CHAIN_USER_DEL:
nft_compat_chain_batch_add(h, NFT_MSG_DELCHAIN,
0, seq++, n->chain);
NLM_F_NONREC, n->seq,
n->chain);
break;
case NFT_COMPAT_CHAIN_USER_FLUSH:
nft_compat_chain_batch_add(h, NFT_MSG_DELCHAIN,
0, n->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);
n->seq, n->chain);
break;
case NFT_COMPAT_CHAIN_RENAME:
nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN, 0,
seq++, n->chain);
n->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);
n->seq, n->rule);
break;
case NFT_COMPAT_RULE_INSERT:
nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
NLM_F_CREATE, seq++,
NLM_F_CREATE, n->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);
n->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);
n->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);
mnl_nft_batch_continue(h->batch);
}
switch (action) {
case NFT_COMPAT_COMMIT:
mnl_nftnl_batch_end(h->batch, seq++);
mnl_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_batch_talk(h->nl, h->batch, &h->err_list);
i = 0;
buflen = sizeof(errmsg);
list_for_each_entry_safe(n, tmp, &h->obj_list, head) {
list_for_each_entry_safe(err, ne, &h->err_list, head) {
if (err->seqnum > n->seq)
break;
ret = mnl_nftnl_batch_talk(h);
if (err->seqnum == n->seq && show_errors) {
if (n->error.lineno == 0)
show_errors = false;
len = mnl_append_error(h, n, err, errmsg + i, buflen);
if (len > 0 && len <= buflen) {
buflen -= len;
i += len;
}
}
mnl_err_list_free(err);
}
batch_obj_del(h, n);
}
mnl_nlmsg_batch_reset(h->batch);
mnl_batch_reset(h->batch);
if (i)
xtables_error(RESOURCE_PROBLEM, "%s", errmsg);
return ret == 0 ? 1 : 0;
}
......@@ -2421,16 +2751,29 @@ int nft_abort(struct nft_handle *h)
int nft_compatible_revision(const char *name, uint8_t rev, int opt)
{
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
char buf[16536];
struct nlmsghdr *nlh;
uint32_t portid, seq, type;
uint32_t portid, seq, type = 0;
uint32_t pf = AF_INET;
int ret = 0;
if (opt == IPT_SO_GET_REVISION_MATCH ||
opt == IP6T_SO_GET_REVISION_MATCH)
type = 0;
else
switch (opt) {
case IPT_SO_GET_REVISION_MATCH:
break;
case IP6T_SO_GET_REVISION_MATCH:
pf = AF_INET6;
break;
case IPT_SO_GET_REVISION_TARGET:
type = 1;
break;
case IP6T_SO_GET_REVISION_TARGET:
type = 1;
pf = AF_INET6;
break;
default:
/* No revision support (arp, ebtables), assume latest version ok */
return 1;
}
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = (NFNL_SUBSYS_NFT_COMPAT << 8) | NFNL_MSG_COMPAT_GET;
......@@ -2438,7 +2781,7 @@ int nft_compatible_revision(const char *name, uint8_t rev, int opt)
nlh->nlmsg_seq = seq = time(NULL);
struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
nfg->nfgen_family = AF_INET;
nfg->nfgen_family = pf;
nfg->version = NFNETLINK_V0;
nfg->res_id = 0;
......@@ -2491,19 +2834,19 @@ const char *nft_strerror(int err)
{ 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_insert, ENOENT, "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" },
{ nft_rule_delete_num, ENOENT, "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" },
{ nft_chain_set, ENXIO, "Bad table name" },
{ NULL, ELOOP, "Loop found in table" },
{ NULL, EPERM, "Permission denied (you must be root)" },
{ NULL, 0, "Incompatible with this kernel" },
{ NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
......@@ -2533,11 +2876,11 @@ static void xtables_config_perror(uint32_t flags, const char *fmt, ...)
va_end(args);
}
int nft_xtables_config_load(struct nft_handle *h, const char *filename,
static 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 *table_list = NULL;
struct nftnl_chain_list *chain_list = NULL;
struct nftnl_table_list_iter *titer = NULL;
struct nftnl_chain_list_iter *citer = NULL;
struct nftnl_table *table;
......@@ -2545,8 +2888,8 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename,
uint32_t table_family, chain_family;
bool found = false;
if (h->restore)
return 0;
table_list = nftnl_table_list_alloc();
chain_list = nftnl_chain_list_alloc();
if (xtables_config_parse(filename, table_list, chain_list) < 0) {
if (errno == ENOENT) {
......@@ -2626,6 +2969,8 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename,
nftnl_chain_list_iter_destroy(citer);
nftnl_chain_list_free(chain_list);
h->config_done = 1;
return 0;
err:
......@@ -2637,11 +2982,22 @@ err:
if (citer != NULL)
nftnl_chain_list_iter_destroy(citer);
h->config_done = -1;
return -1;
}
int nft_xtables_config_load(struct nft_handle *h, const char *filename,
uint32_t flags)
{
if (!h->config_done)
return __nft_xtables_config_load(h, filename, flags);
return h->config_done;
}
int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
const char *table)
const char *table, bool verbose)
{
struct nftnl_chain_list *list;
struct nftnl_chain_list_iter *iter;
......@@ -2669,23 +3025,15 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
if (chain != NULL && strcmp(chain, chain_name) != 0)
goto next;
if (verbose)
fprintf(stdout, "Zeroing chain `%s'\n", chain_name);
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);
}
ret = batch_chain_add(h, NFT_COMPAT_CHAIN_ZERO, c);
if (chain != NULL)
break;
......@@ -2693,9 +3041,6 @@ next:
c = nftnl_chain_list_iter_next(iter);
}
if (!h->batch_support)
nftnl_chain_list_free(list);
nftnl_chain_list_iter_destroy(iter);
err:
......@@ -2725,8 +3070,9 @@ static const char *supported_exprs[NFT_COMPAT_EXPR_MAX] = {
};
static int nft_is_expr_compatible(const char *name)
static int nft_is_expr_compatible(const struct nftnl_expr *expr)
{
const char *name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
int i;
for (i = 0; i < NFT_COMPAT_EXPR_MAX; i++) {
......@@ -2734,59 +3080,76 @@ static int nft_is_expr_compatible(const char *name)
return 0;
}
if (!strcmp(name, "limit") &&
nftnl_expr_get_u32(expr, NFTNL_EXPR_LIMIT_TYPE) == NFT_LIMIT_PKTS &&
nftnl_expr_get_u32(expr, NFTNL_EXPR_LIMIT_FLAGS) == 0)
return 0;
return 1;
}
static int nft_is_rule_compatible(struct nftnl_rule *rule)
static bool nft_is_rule_compatible(struct nftnl_rule *rule)
{
struct nftnl_expr_iter *iter;
struct nftnl_expr *expr;
int ret = 0;
bool compatible = false;
iter = nftnl_expr_iter_create(rule);
if (iter == NULL)
return -1;
return false;
expr = nftnl_expr_iter_next(iter);
while (expr != NULL) {
const char *name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
if (nft_is_expr_compatible(name) == 0) {
if (nft_is_expr_compatible(expr) == 0) {
expr = nftnl_expr_iter_next(iter);
continue;
}
ret = 1;
compatible = true;
break;
}
nftnl_expr_iter_destroy(iter);
return ret;
return compatible;
}
static int nft_is_chain_compatible(const char *table, const char *chain)
static int nft_is_chain_compatible(const struct nft_handle *h,
const struct nftnl_chain *chain)
{
const char *cur_table;
const char *table, *name, *type, *cur_table;
struct builtin_chain *chains;
int i, j;
int i, j, prio;
enum nf_inet_hooks hook;
table = nftnl_chain_get(chain, NFTNL_CHAIN_TABLE);
name = nftnl_chain_get(chain, NFTNL_CHAIN_NAME);
type = nftnl_chain_get(chain, NFTNL_CHAIN_TYPE);
prio = nftnl_chain_get_u32(chain, NFTNL_CHAIN_PRIO);
hook = nftnl_chain_get_u32(chain, NFTNL_CHAIN_HOOKNUM);
for (i = 0; i < TABLES_MAX; i++) {
cur_table = xtables_ipv4[i].name;
chains = xtables_ipv4[i].chains;
cur_table = h->tables[i].name;
chains = h->tables[i].chains;
if (strcmp(table, cur_table) != 0)
if (!cur_table || strcmp(table, cur_table) != 0)
continue;
for (j = 0; j < NF_INET_NUMHOOKS && chains[j].name; j++) {
if (strcmp(chain, chains[j].name) == 0)
if (strcmp(name, chains[j].name) != 0)
continue;
if (strcmp(type, chains[j].type) == 0 &&
prio == chains[j].prio &&
hook == chains[j].hook)
return 0;
break;
}
}
return 1;
}
static int nft_are_chains_compatible(struct nft_handle *h)
static int nft_are_chains_compatible(struct nft_handle *h, const char *tablename)
{
struct nftnl_chain_list *list;
struct nftnl_chain_list_iter *iter;
......@@ -2803,104 +3166,62 @@ static int nft_are_chains_compatible(struct nft_handle *h)
chain = nftnl_chain_list_iter_next(iter);
while (chain != NULL) {
if (!nft_chain_builtin(chain))
goto next;
const char *chain_table;
const char *table = nftnl_chain_get(chain, NFTNL_CHAIN_TABLE);
const char *name = nftnl_chain_get(chain, NFTNL_CHAIN_NAME);
chain_table = nftnl_chain_get_str(chain, NFTNL_CHAIN_TABLE);
if (nft_is_chain_compatible(table, name) == 1) {
ret = 1;
break;
}
if (strcmp(chain_table, tablename) ||
!nft_chain_builtin(chain))
goto next;
ret = nft_is_chain_compatible(h, chain);
if (ret != 0)
break;
next:
chain = nftnl_chain_list_iter_next(iter);
}
nftnl_chain_list_iter_destroy(iter);
nftnl_chain_list_free(list);
return ret;
}
static int nft_is_table_compatible(const char *name)
{
int i;
for (i = 0; i < TABLES_MAX; i++) {
if (strcmp(xtables_ipv4[i].name, name) == 0)
return 0;
}
return 1;
}
static int nft_are_tables_compatible(struct nft_handle *h)
{
struct nftnl_table_list *list;
struct nftnl_table_list_iter *iter;
struct nftnl_table *table;
int ret = 0;
list = nftnl_table_list_get(h);
if (list == NULL)
return -1;
iter = nftnl_table_list_iter_create(list);
if (iter == NULL)
return -1;
table = nftnl_table_list_iter_next(iter);
while (table != NULL) {
const char *name = nftnl_table_get(table, NFTNL_TABLE_NAME);
if (nft_is_table_compatible(name) == 0) {
table = nftnl_table_list_iter_next(iter);
continue;
}
ret = 1;
break;
}
nftnl_table_list_iter_destroy(iter);
nftnl_table_list_free(list);
return ret;
}
int nft_is_ruleset_compatible(struct nft_handle *h)
bool nft_is_table_compatible(struct nft_handle *h, const char *tablename)
{
struct nftnl_rule_list *list;
struct nftnl_rule_list_iter *iter;
struct nftnl_rule *rule;
int ret = 0;
ret = nft_are_tables_compatible(h);
if (ret != 0)
return ret;
if (!nft_table_builtin_find(h, tablename))
return false;
ret = nft_are_chains_compatible(h);
ret = nft_are_chains_compatible(h, tablename);
if (ret != 0)
return ret;
return false;
list = nft_rule_list_get(h);
if (list == NULL)
return -1;
return true;
iter = nftnl_rule_list_iter_create(list);
if (iter == NULL)
return -1;
return true;
rule = nftnl_rule_list_iter_next(iter);
while (rule != NULL) {
const char *table = nftnl_rule_get_str(rule, NFTNL_RULE_TABLE);
if (strcmp(table, tablename))
goto next_rule;
ret = nft_is_rule_compatible(rule);
if (ret != 0)
break;
next_rule:
rule = nftnl_rule_list_iter_next(iter);
}
nftnl_rule_list_iter_destroy(iter);
return ret;
return ret == 0;
}
......@@ -32,12 +32,19 @@ struct nft_handle {
uint32_t seq;
struct list_head obj_list;
int obj_list_num;
struct mnl_nlmsg_batch *batch;
struct nftnl_batch *batch;
struct list_head err_list;
struct nft_family_ops *ops;
struct builtin_table *tables;
struct nftnl_chain_list *chain_cache;
struct nftnl_rule_list *rule_cache;
bool restore;
bool batch_support;
int8_t config_done;
/* meta data, for error reporting */
struct {
unsigned int lineno;
} error;
};
extern struct builtin_table xtables_ipv4[TABLES_MAX];
......@@ -56,25 +63,30 @@ void nft_fini(struct nft_handle *h);
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);
int nft_table_flush(struct nft_handle *h, const char *table);
void nft_table_new(struct nft_handle *h, const char *table);
struct builtin_table *nft_table_builtin_find(struct nft_handle *h, const char *table);
/*
* 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_del(struct nft_handle *h, const char *chain, const char *table, bool verbose);
int nft_chain_user_flush(struct nft_handle *h, struct nftnl_chain_list *list,
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);
int nft_chain_zero_counters(struct nft_handle *h, const char *chain, const char *table, bool verbose);
struct builtin_chain *nft_chain_builtin_find(struct builtin_table *t, const char *chain);
bool nft_chain_exists(struct nft_handle *h, const char *table, const char *chain);
/*
* Operations with rule-set.
......@@ -89,8 +101,8 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain, const char *tab
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_save(struct nft_handle *h, const char *table, unsigned int format);
int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table, bool verbose);
int nft_rule_zero_counters(struct nft_handle *h, const char *chain, const char *table, int rulenum);
/*
......@@ -110,8 +122,7 @@ enum nft_rule_print {
NFT_RULE_DEL,
};
void nft_rule_print_save(const void *data,
struct nftnl_rule *r, enum nft_rule_print type,
void nft_rule_print_save(const struct nftnl_rule *r, enum nft_rule_print type,
unsigned int format);
uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag);
......@@ -135,9 +146,12 @@ 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);
int nft_init_arp(struct nft_handle *h, const char *pname);
int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table, bool restore);
/* For xtables-eb.c */
int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table);
int nft_init_eb(struct nft_handle *h, const char *pname);
int ebt_get_current_chain(const char *chain);
int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table, bool restore);
/*
* Parse config for tables and chain helper functions
......@@ -182,6 +196,6 @@ int nft_arp_rule_insert(struct nft_handle *h, const char *chain,
void nft_rule_to_arpt_entry(struct nftnl_rule *r, struct arpt_entry *fw);
int nft_is_ruleset_compatible(struct nft_handle *h);
bool nft_is_table_compatible(struct nft_handle *h, const char *name);
#endif
To run the test suite (as root):
$ cd iptables/tests/shell
# ./run-tests.sh
Test files are executable files with the pattern <<name_N>> , where N is the
expected return code of the executable. Since they are located with `find',
test-files can be spreaded in any sub-directories.
You can turn on a verbose execution by calling:
# ./run-tests.sh -v
And to run test suite for pariticular test files:
# ./run-tests.sh <PATH_OF_TESTFILES>
Also, test-files will receive the environment variable $XT_MULTI which contains
the path to the old iptables (xtables-legacy-multi) or new iptables (xtables-nft-multi)
binary being tested.
#!/bin/bash
#configuration
TESTDIR="./$(dirname $0)/"
RETURNCODE_SEPARATOR="_"
XTABLES_NFT_MULTI="$(dirname $0)/../../xtables-nft-multi"
XTABLES_LEGACY_MULTI="$(dirname $0)/../../xtables-legacy-multi"
export XTABLES_LIBDIR=${TESTDIR}/../../../extensions
msg_error() {
echo "E: $1 ..." >&2
exit 1
}
msg_warn() {
echo "W: $1" >&2
}
msg_info() {
echo "I: $1"
}
if [ "$(id -u)" != "0" ] ; then
msg_error "this requires root!"
fi
if [ ! -d "$TESTDIR" ] ; then
msg_error "missing testdir $TESTDIR"
fi
if [ "$1" == "-v" ] ; then
VERBOSE=y
shift
fi
for arg in "$@"; do
if grep ^.*${RETURNCODE_SEPARATOR}[0-9]\\+$ <<< $arg >/dev/null ; then
SINGLE+=" $arg"
VERBOSE=y
else
msg_error "unknown parameter '$arg'"
fi
done
find_tests() {
if [ ! -z "$SINGLE" ] ; then
echo $SINGLE
return
fi
find ${TESTDIR} -executable -regex \
.*${RETURNCODE_SEPARATOR}[0-9]+ | sort
}
ok=0
failed=0
do_test() {
testfile="$1"
xtables_multi="$2"
rc_spec=`echo $(basename ${testfile}) | cut -d _ -f2-`
msg_info "[EXECUTING] $testfile"
if [ "$VERBOSE" = "y" ]; then
XT_MULTI=$xtables_multi unshare -n ${testfile}
rc_got=$?
else
XT_MULTI=$xtables_multi unshare -n ${testfile} > /dev/null 2>&1
rc_got=$?
echo -en "\033[1A\033[K" # clean the [EXECUTING] foobar line
fi
if [ "$rc_got" == "$rc_spec" ] ; then
msg_info "[OK] $testfile"
((ok++))
else
((failed++))
msg_warn "[FAILED] $testfile: expected $rc_spec but got $rc_got"
fi
}
echo ""
for testfile in $(find_tests);do
do_test "$testfile" "$XTABLES_LEGACY_MULTI"
done
msg_info "legacy results: [OK] $ok [FAILED] $failed [TOTAL] $((ok+failed))"
legacy_ok=$ok
legacy_fail=$failed
ok=0
failed=0
for testfile in $(find_tests);do
do_test "$testfile" "$XTABLES_NFT_MULTI"
done
msg_info "nft results: [OK] $ok [FAILED] $failed [TOTAL] $((ok+failed))"
ok=$((legacy_ok+ok))
failed=$((legacy_fail+failed))
msg_info "combined results: [OK] $ok [FAILED] $failed [TOTAL] $((ok+failed))"
exit 0
#!/bin/bash
set -e
#set -x
# there is no legacy backend to test
[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
# fill arptables manually
$XT_MULTI arptables -F
$XT_MULTI arptables -A INPUT -s 10.0.0.0/8 -j ACCEPT
$XT_MULTI arptables -A INPUT -d 192.168.123.1 -j ACCEPT
#$XT_MULTI arptables -A INPUT --source-mac fe:ed:ba:be:00:01 -j ACCEPT
#$XT_MULTI arptables -A INPUT --destination-mac fe:ed:ba:be:00:01 -j ACCEPT
$XT_MULTI arptables -N foo
$XT_MULTI arptables -A foo -i lo -j ACCEPT
$XT_MULTI arptables -A foo -l 6 -j ACCEPT
$XT_MULTI arptables -A foo --opcode Request -j ACCEPT
$XT_MULTI arptables -A foo --h-type 1 --proto-type 0x800 -j ACCEPT
$XT_MULTI arptables -A foo -l 6 --h-type 1 --proto-type 0x800 -i lo --opcode Request -j ACCEPT
$XT_MULTI arptables -A INPUT -j foo
$XT_MULTI arptables -A INPUT
$XT_MULTI arptables -A OUTPUT -o lo -j ACCEPT
$XT_MULTI arptables -A OUTPUT -o eth134 -j mangle --mangle-ip-s 10.0.0.1
$XT_MULTI arptables -A OUTPUT -o eth432 -j CLASSIFY --set-class feed:babe
$XT_MULTI arptables -A OUTPUT -o eth432 --opcode Request -j CLASSIFY --set-class feed:babe
$XT_MULTI arptables -P OUTPUT DROP
# compare against stored arptables dump
DUMP='*filter
:INPUT ACCEPT
:OUTPUT DROP
:foo -
-A INPUT -s 10.0.0.0/8 -j ACCEPT
-A INPUT -d 192.168.123.1 -j ACCEPT
-A INPUT -j foo
-A INPUT
-A OUTPUT -o lo -j ACCEPT
-A OUTPUT -o eth134 -j mangle --mangle-ip-s 10.0.0.1
-A OUTPUT -o eth432 -j CLASSIFY --set-class feed:babe
-A OUTPUT -o eth432 --opcode 1 -j CLASSIFY --set-class feed:babe
-A foo -i lo -j ACCEPT
-A foo --h-length 6 -j ACCEPT
-A foo --opcode 1 -j ACCEPT
-A foo --h-type 1 --proto-type 0x800 -j ACCEPT
-A foo -i lo --h-length 6 --opcode 1 --h-type 1 --proto-type 0x800 -j ACCEPT
'
diff -u <(echo -e "$DUMP") <($XT_MULTI arptables-save)
# make sure dump can be restored and check it didn't change
$XT_MULTI arptables -F
$XT_MULTI arptables-restore <<<$DUMP
diff -u <(echo -e "$DUMP") <($XT_MULTI arptables-save)
#!/bin/bash
set -e
# there is no legacy backend to test
[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
# arptables-restore reuses preloaded targets and matches, make sure defaults
# apply to consecutive rules using the same target/match as a previous one
DUMP='*filter
:OUTPUT ACCEPT
-A OUTPUT -j mangle --mangle-ip-s 10.0.0.1
-A OUTPUT -j mangle --mangle-ip-d 10.0.0.2
'
# note how mangle-ip-s is unset in second rule
EXPECT='*filter
:INPUT ACCEPT
:OUTPUT ACCEPT
-A OUTPUT -j mangle --mangle-ip-s 10.0.0.1
-A OUTPUT -j mangle --mangle-ip-d 10.0.0.2
'
$XT_MULTI arptables -F
$XT_MULTI arptables-restore <<<$DUMP
diff -u <(echo -e "$EXPECT") <($XT_MULTI arptables-save | grep -v '^#')
#!/bin/bash
set -x
$XT_MULTI iptables -t filter -N c1 || exit 0
$XT_MULTI iptables -t filter -N c1 || exit 1
$XT_MULTI ip6tables -t filter -N c1 || exit 0
$XT_MULTI ip6tables -t filter -N c1 || exit 1
echo "E: Duplicate chains" >&2
exit 0
#!/bin/bash
set -e
$XT_MULTI iptables -N c1
$XT_MULTI ip6tables -N c1
$XT_MULTI iptables -N c2
$XT_MULTI ip6tables -N c2
#!/bin/bash
$XT_MULTI iptables -N c1 || exit 0
$XT_MULTI iptables -N c2 || exit 0
$XT_MULTI iptables -E c1 c2 || exit 1
$XT_MULTI ip6tables -N c1 || exit 0
$XT_MULTI ip6tables -N c2 || exit 0
$XT_MULTI ip6tables -E c1 c2 || exit 1
echo "E: Renamed with existing chain" >&2
exit 0
#!/bin/sh
set -x
case "$XT_MULTI" in
*/xtables-nft-multi)
for t in filter nat;do
$XT_MULTI ebtables -t $t -L || exit 1
$XT_MULTI ebtables -t $t -X || exit 1
$XT_MULTI ebtables -t $t -F || exit 1
done
for t in broute foobar ;do
$XT_MULTI ebtables -t $t -L &&
$XT_MULTI ebtables -t $t -X &&
$XT_MULTI ebtables -t $t -F
if [ $? -eq 0 ]; then
echo "Expect nonzero return for unsupported table"
exit 1
fi
done
$XT_MULTI ebtables -t filter -N FOO || exit 1
$XT_MULTI ebtables -t filter -N FOO
if [ $? -eq 0 ]; then
echo "Duplicate chain FOO"
$XT_MULTI ebtables -t filter -L
exit 1
fi
$XT_MULTI ebtables -L FOO | grep -q 'entries: 0'
if [ $? -ne 0 ]; then
echo "Unexpected entries count in empty unreferenced chain"
$XT_MULTI ebtables -L
exit 1
fi
$XT_MULTI ebtables -A FORWARD -j FOO
$XT_MULTI ebtables -L FORWARD | grep -q 'entries: 1'
if [ $? -ne 0 ]; then
echo "Unexpected entries count in FORWARD chain"
$XT_MULTI ebtables -L
exit 1
fi
$XT_MULTI ebtables -L FOO | grep -q 'entries: 0'
if [ $? -ne 0 ]; then
echo "Unexpected entries count in empty referenced chain"
$XT_MULTI ebtables -L
exit 1
fi
$XT_MULTI ebtables -A FOO -j ACCEPT
$XT_MULTI ebtables -L FOO | grep -q 'entries: 1'
if [ $? -ne 0 ]; then
echo "Unexpected entries count in non-empty referenced chain"
$XT_MULTI ebtables -L
exit 1
fi
$XT_MULTI ebtables -t filter -N BAR || exit 1
$XT_MULTI ebtables -t filter -N BAZ || exit 1
$XT_MULTI ebtables -t filter -L | grep -q FOO || exit 1
$XT_MULTI ebtables -t filter -L | grep -q BAR || exit 1
$XT_MULTI ebtables -t filter -L | grep -q BAZ || exit 1
$XT_MULTI ebtables -t filter -L BAZ || exit 1
$XT_MULTI ebtables -t filter -X BAZ || exit 1
$XT_MULTI ebtables -t filter -L BAZ | grep -q BAZ
if [ $? -eq 0 ]; then
echo "Deleted chain -L BAZ ok, expected failure"
$XT_MULTI ebtables -t filter -L
exit 1
fi
$XT_MULTI ebtables -t $t -F || exit 0
;;
*)
echo "skip $XT_MULTI"
;;
esac
#!/bin/bash
set -e
#set -x
# there is no legacy backend to test
[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
# fill ebtables manually
$XT_MULTI ebtables --init-table
$XT_MULTI ebtables -A INPUT -p IPv4 -i lo -j ACCEPT
$XT_MULTI ebtables -P FORWARD DROP
$XT_MULTI ebtables -A OUTPUT -s ff:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff -j DROP
$XT_MULTI ebtables -N foo
$XT_MULTI ebtables -A foo --802_3-sap 0x23 -j ACCEPT
$XT_MULTI ebtables -A foo --802_3-sap 0xaa --802_3-type 0x1337 -j ACCEPT
#$XT_MULTI ebtables -A foo --among-dst fe:ed:ba:be:00:01,fe:ed:ba:be:00:02,fe:ed:ba:be:00:03 -j ACCEPT
$XT_MULTI ebtables -A foo -p ARP --arp-gratuitous -j ACCEPT
$XT_MULTI ebtables -A foo -p ARP --arp-opcode Request -j ACCEPT
$XT_MULTI ebtables -A foo -p ARP --arp-ip-src 10.0.0.1 -j ACCEPT
$XT_MULTI ebtables -A foo -p ARP --arp-ip-dst 10.0.0.0/8 -j ACCEPT
$XT_MULTI ebtables -A foo -p ARP --arp-mac-src fe:ed:ba:be:00:01 -j ACCEPT
$XT_MULTI ebtables -A foo -p ARP --arp-mac-dst fe:ed:ba:be:00:01/ff:ff:ff:00:00:00 -j ACCEPT
$XT_MULTI ebtables -A foo -p IPv4 --ip-src 10.0.0.1 -j ACCEPT
$XT_MULTI ebtables -A foo -p IPv4 --ip-dst 10.0.0.0/8 -j ACCEPT
$XT_MULTI ebtables -A foo -p IPv4 --ip-tos 0x10 -j ACCEPT
$XT_MULTI ebtables -A foo -p IPv4 --ip-protocol tcp -j ACCEPT
#$XT_MULTI ebtables -A foo -p IPv4 --ip-sport 23 -j ACCEPT
#$XT_MULTI ebtables -A foo -p IPv4 --ip-dport 1024:4096 -j ACCEPT
$XT_MULTI ebtables -A foo -p IPv6 --ip6-src feed:babe::1 -j ACCEPT
$XT_MULTI ebtables -A foo -p IPv6 --ip6-dst feed:babe::/64 -j ACCEPT
$XT_MULTI ebtables -A foo -p IPv6 --ip6-proto tcp -j ACCEPT
#$XT_MULTI ebtables -A foo -p IPv6 --ip6-sport 23 -j ACCEPT
#$XT_MULTI ebtables -A foo -p IPv6 --ip6-dport 1024:4096 -j ACCEPT
$XT_MULTI ebtables -A foo --limit 100 --limit-burst 42 -j ACCEPT
$XT_MULTI ebtables -A foo --log
$XT_MULTI ebtables -A foo --mark-set 0x23 --mark-target ACCEPT
$XT_MULTI ebtables -A foo --nflog
$XT_MULTI ebtables -A foo --pkttype-type multicast -j ACCEPT
$XT_MULTI ebtables -A foo --stp-type config -j ACCEPT
#$XT_MULTI ebtables -A foo --vlan-id 42 -j ACCEPT
$XT_MULTI ebtables -A foo --802_3-sap 0x23 --limit 100 -j ACCEPT
$XT_MULTI ebtables -A foo --pkttype-type multicast --log
$XT_MULTI ebtables -A foo --pkttype-type multicast --limit 100 -j ACCEPT
$XT_MULTI ebtables -A FORWARD -j foo
$XT_MULTI ebtables -t nat -A PREROUTING --redirect-target ACCEPT
#$XT_MULTI ebtables -t nat -A PREROUTING --to-src fe:ed:ba:be:00:01
$XT_MULTI ebtables -t nat -A OUTPUT -j ACCEPT
$XT_MULTI ebtables -t nat -P OUTPUT DROP
$XT_MULTI ebtables -t nat -A POSTROUTING -j ACCEPT
#$XT_MULTI ebtables -t nat -A POSTROUTING --to-dst fe:ed:ba:be:00:01 --dnat-target ACCEPT
# compare against stored ebtables dump
DUMP='*filter
:INPUT ACCEPT
:FORWARD DROP
:OUTPUT ACCEPT
:foo ACCEPT
-A INPUT -p IPv4 -i lo -j ACCEPT
-A FORWARD -j foo
-A OUTPUT -s Broadcast -j DROP
-A foo --802_3-sap 0x23 -j ACCEPT
-A foo --802_3-sap 0xaa --802_3-type 0x1337 -j ACCEPT
-A foo -p ARP --arp-gratuitous -j ACCEPT
-A foo -p ARP --arp-op Request -j ACCEPT
-A foo -p ARP --arp-ip-src 10.0.0.1 -j ACCEPT
-A foo -p ARP --arp-ip-dst 10.0.0.0/8 -j ACCEPT
-A foo -p ARP --arp-mac-src fe:ed:ba:be:0:1 -j ACCEPT
-A foo -p ARP --arp-mac-dst fe:ed:ba:0:0:0/ff:ff:ff:0:0:0 -j ACCEPT
-A foo -p IPv4 --ip-src 10.0.0.1 -j ACCEPT
-A foo -p IPv4 --ip-dst 10.0.0.0/8 -j ACCEPT
-A foo -p IPv4 --ip-tos 0x10 -j ACCEPT
-A foo -p IPv4 --ip-proto tcp -j ACCEPT
-A foo -p IPv6 --ip6-src feed:babe::1 -j ACCEPT
-A foo -p IPv6 --ip6-dst feed:babe::/64 -j ACCEPT
-A foo -p IPv6 --ip6-proto tcp -j ACCEPT
-A foo --limit 100/sec --limit-burst 42 -j ACCEPT
-A foo --log-level notice --log-prefix "" -j CONTINUE
-A foo -j mark --mark-set 0x23 --mark-target ACCEPT
-A foo --nflog-group 1 -j CONTINUE
-A foo --pkttype-type multicast -j ACCEPT
-A foo --stp-type config -j ACCEPT
-A foo --802_3-sap 0x23 --limit 100/sec --limit-burst 5 -j ACCEPT
-A foo --pkttype-type multicast --log-level notice --log-prefix "" -j CONTINUE
-A foo --pkttype-type multicast --limit 100/sec --limit-burst 5 -j ACCEPT
*nat
:PREROUTING ACCEPT
:OUTPUT DROP
:POSTROUTING ACCEPT
-A PREROUTING -j redirect
-A OUTPUT -j ACCEPT
-A POSTROUTING -j ACCEPT
'
diff -u <(echo -e "$DUMP") <($XT_MULTI ebtables-save | grep -v '^#')
# make sure dump can be restored and check it didn't change
$XT_MULTI ebtables --init-table
$XT_MULTI ebtables-restore <<<$DUMP
diff -u <(echo -e "$DUMP") <($XT_MULTI ebtables-save | grep -v '^#')
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