/* * Copyright (c) 2013-2015 Pablo Neira Ayuso * Copyright (c) 2015 Arturo Borrero Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 (or any * later) as published by the Free Software Foundation. */ #include #include #include #include #include #include /* for isspace */ #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBXTABLES #include static void *xt_entry_alloc(const struct xt_stmt *xt, uint32_t af); #endif void xt_stmt_xlate(const struct stmt *stmt, struct output_ctx *octx) { static const char *typename[NFT_XT_MAX] = { [NFT_XT_MATCH] = "match", [NFT_XT_TARGET] = "target", [NFT_XT_WATCHER] = "watcher", }; int rc = 0; #ifdef HAVE_LIBXTABLES struct xt_xlate *xl = xt_xlate_alloc(10240); struct xtables_target *tg; struct xt_entry_target *t; struct xtables_match *mt; struct xt_entry_match *m; size_t size; void *entry; xtables_set_nfproto(stmt->xt.family); entry = xt_entry_alloc(&stmt->xt, stmt->xt.family); switch (stmt->xt.type) { case NFT_XT_MATCH: mt = xtables_find_match(stmt->xt.name, XTF_TRY_LOAD, NULL); if (!mt) { fprintf(stderr, "XT match %s not found\n", stmt->xt.name); return; } size = XT_ALIGN(sizeof(*m)) + stmt->xt.infolen; m = xzalloc(size); memcpy(&m->data, stmt->xt.info, stmt->xt.infolen); m->u.match_size = size; m->u.user.revision = stmt->xt.rev; if (mt->xlate) { struct xt_xlate_mt_params params = { .ip = entry, .match = m, .numeric = 1, }; rc = mt->xlate(xl, ¶ms); } xfree(m); break; case NFT_XT_WATCHER: case NFT_XT_TARGET: tg = xtables_find_target(stmt->xt.name, XTF_TRY_LOAD); if (!tg) { fprintf(stderr, "XT target %s not found\n", stmt->xt.name); return; } size = XT_ALIGN(sizeof(*t)) + stmt->xt.infolen; t = xzalloc(size); memcpy(&t->data, stmt->xt.info, stmt->xt.infolen); t->u.target_size = size; t->u.user.revision = stmt->xt.rev; strcpy(t->u.user.name, tg->name); if (tg->xlate) { struct xt_xlate_tg_params params = { .ip = entry, .target = t, .numeric = 1, }; rc = tg->xlate(xl, ¶ms); } xfree(t); break; } if (rc == 1) nft_print(octx, "%s", xt_xlate_get(xl)); xt_xlate_free(xl); xfree(entry); #endif if (!rc) nft_print(octx, "xt %s %s", typename[stmt->xt.type], stmt->xt.name); } void xt_stmt_destroy(struct stmt *stmt) { xfree(stmt->xt.name); xfree(stmt->xt.info); } #ifdef HAVE_LIBXTABLES static void *xt_entry_alloc(const struct xt_stmt *xt, uint32_t af) { union nft_entry { struct ipt_entry ipt; struct ip6t_entry ip6t; struct arpt_entry arpt; struct ebt_entry ebt; } *entry; entry = xmalloc(sizeof(union nft_entry)); switch (af) { case NFPROTO_IPV4: entry->ipt.ip.proto = xt->proto; break; case NFPROTO_IPV6: entry->ip6t.ipv6.proto = xt->proto; break; case NFPROTO_BRIDGE: entry->ebt.ethproto = xt->proto; break; case NFPROTO_ARP: entry->arpt.arp.arhln_mask = 0xff; entry->arpt.arp.arhln = 6; break; default: break; } return entry; } static uint32_t xt_proto(const struct proto_ctx *pctx) { const struct proto_desc *desc = NULL; if (pctx->family == NFPROTO_BRIDGE) { desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc; if (desc == NULL) return 0; if (strcmp(desc->name, "ip") == 0) return __constant_htons(ETH_P_IP); if (strcmp(desc->name, "ip6") == 0) return __constant_htons(ETH_P_IPV6); return 0; } desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc; if (desc == NULL) return 0; if (strcmp(desc->name, "tcp") == 0) return IPPROTO_TCP; else if (strcmp(desc->name, "udp") == 0) return IPPROTO_UDP; else if (strcmp(desc->name, "udplite") == 0) return IPPROTO_UDPLITE; else if (strcmp(desc->name, "sctp") == 0) return IPPROTO_SCTP; else if (strcmp(desc->name, "dccp") == 0) return IPPROTO_DCCP; else if (strcmp(desc->name, "esp") == 0) return IPPROTO_ESP; else if (strcmp(desc->name, "ah") == 0) return IPPROTO_AH; return 0; } #endif /* * Delinearization */ void netlink_parse_match(struct netlink_parse_ctx *ctx, const struct location *loc, const struct nftnl_expr *nle) { const char *mtinfo; struct stmt *stmt; uint32_t mt_len; mtinfo = nftnl_expr_get(nle, NFTNL_EXPR_MT_INFO, &mt_len); stmt = xt_stmt_alloc(loc); stmt->xt.name = strdup(nftnl_expr_get_str(nle, NFTNL_EXPR_MT_NAME)); stmt->xt.type = NFT_XT_MATCH; stmt->xt.rev = nftnl_expr_get_u32(nle, NFTNL_EXPR_MT_REV); stmt->xt.family = ctx->table->handle.family; stmt->xt.infolen = mt_len; stmt->xt.info = xmalloc(mt_len); memcpy(stmt->xt.info, mtinfo, mt_len); ctx->table->has_xt_stmts = true; rule_stmt_append(ctx->rule, stmt); } void netlink_parse_target(struct netlink_parse_ctx *ctx, const struct location *loc, const struct nftnl_expr *nle) { const void *tginfo; struct stmt *stmt; uint32_t tg_len; tginfo = nftnl_expr_get(nle, NFTNL_EXPR_TG_INFO, &tg_len); stmt = xt_stmt_alloc(loc); stmt->xt.name = strdup(nftnl_expr_get_str(nle, NFTNL_EXPR_TG_NAME)); stmt->xt.type = NFT_XT_TARGET; stmt->xt.rev = nftnl_expr_get_u32(nle, NFTNL_EXPR_TG_REV); stmt->xt.family = ctx->table->handle.family; stmt->xt.infolen = tg_len; stmt->xt.info = xmalloc(tg_len); memcpy(stmt->xt.info, tginfo, tg_len); ctx->table->has_xt_stmts = true; rule_stmt_append(ctx->rule, stmt); } #ifdef HAVE_LIBXTABLES static bool is_watcher(uint32_t family, struct stmt *stmt) { if (family != NFPROTO_BRIDGE || stmt->xt.type != NFT_XT_TARGET) return false; /* this has to be hardcoded :-( */ if (strcmp(stmt->xt.name, "log") == 0) return true; else if (strcmp(stmt->xt.name, "nflog") == 0) return true; return false; } void stmt_xt_postprocess(struct rule_pp_ctx *rctx, struct stmt *stmt, struct rule *rule) { struct dl_proto_ctx *dl = dl_proto_ctx(rctx); if (is_watcher(dl->pctx.family, stmt)) stmt->xt.type = NFT_XT_WATCHER; stmt->xt.proto = xt_proto(&dl->pctx); } static int nft_xt_compatible_revision(const char *name, uint8_t rev, int opt) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq, type, family; struct nfgenmsg *nfg; int ret = 0; switch (opt) { case IPT_SO_GET_REVISION_MATCH: family = NFPROTO_IPV4; type = 0; break; case IPT_SO_GET_REVISION_TARGET: family = NFPROTO_IPV4; type = 1; break; case IP6T_SO_GET_REVISION_MATCH: family = NFPROTO_IPV6; type = 0; break; case IP6T_SO_GET_REVISION_TARGET: family = NFPROTO_IPV6; type = 1; break; default: /* No revision support, assume ok */ return 1; } nlh = mnl_nlmsg_put_header(buf); nlh->nlmsg_type = (NFNL_SUBSYS_NFT_COMPAT << 8) | NFNL_MSG_COMPAT_GET; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlh->nlmsg_seq = seq = time(NULL); nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); nfg->nfgen_family = family; nfg->version = NFNETLINK_V0; nfg->res_id = 0; mnl_attr_put_strz(nlh, NFTA_COMPAT_NAME, name); mnl_attr_put_u32(nlh, NFTA_COMPAT_REV, htonl(rev)); mnl_attr_put_u32(nlh, NFTA_COMPAT_TYPE, htonl(type)); nl = mnl_socket_open(NETLINK_NETFILTER); if (nl == NULL) return 0; if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) goto err; portid = mnl_socket_get_portid(nl); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) goto err; ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); if (ret == -1) goto err; ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); if (ret == -1) goto err; err: mnl_socket_close(nl); return ret < 0 ? 0 : 1; } static struct option original_opts[] = { { NULL }, }; static struct xtables_globals xt_nft_globals = { .program_name = "nft", .program_version = PACKAGE_VERSION, .orig_opts = original_opts, .compat_rev = nft_xt_compatible_revision, }; void xt_init(void) { /* Default to IPv4, but this changes in runtime */ xtables_init_all(&xt_nft_globals, NFPROTO_IPV4); } #endif