Send connman mailing list submissions to
[email protected]
To subscribe or unsubscribe via the World Wide Web, visit
https://lists.01.org/mailman/listinfo/connman
or, via email, send a message with subject or body 'help' to
[email protected]
You can reach the person managing the list at
[email protected]
When replying, please edit your Subject line so it is more specific
than "Re: Contents of connman digest..."
Today's Topics:
1. [PATCH v4 6/6] firewall-nftables: Add nftable support for
firewall (Daniel Wagner)
2. [PATCH v4 4/6] firewall: Rename firewall.c to
firewall-iptables.c (Daniel Wagner)
----------------------------------------------------------------------
Message: 1
Date: Thu, 18 Aug 2016 11:06:17 +0200
From: Daniel Wagner <[email protected]>
To: [email protected]
Cc: Daniel Wagner <[email protected]>
Subject: [PATCH v4 6/6] firewall-nftables: Add nftable support for
firewall
Message-ID: <[email protected]>
From: Daniel Wagner <[email protected]>
A straight forward implementation for the firewall API. It is based
on the libntfnl examples and Daniel Macks' systemd implementation.
There are some comment how to use/extend this code, that is
'nft --debug' your friend. Also check the libnftnl examples for more
details.
---
src/firewall-nftables.c | 1088 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 1080 insertions(+), 8 deletions(-)
diff --git a/src/firewall-nftables.c b/src/firewall-nftables.c
index df578fa..bc6d875 100644
--- a/src/firewall-nftables.c
+++ b/src/firewall-nftables.c
@@ -19,63 +19,1135 @@
*
*/
+/*
+ * This file is based on the libnftnl examples:
+ * https://git.netfilter.org/libnftnl/tree/examples
+ * by Pablo Neira Ayuso. and inspiration from systemd nft implemention
+ *
https://github.com/zonque/systemd/blob/rfc-nftnl/src/shared/firewall-util.c
+ * by Daniel Mack.
+ */
+
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
#include <errno.h>
+#include <stdint.h>
+#include <alloca.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include <sys/types.h>
+#include <pwd.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_nat.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftnl/table.h>
+#include <libnftnl/chain.h>
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include <glib.h>
#include "connman.h"
+#define CONNMAN_TABLE "connman"
+#define CONNMAN_CHAIN_NAT_PRE "nat-prerouting"
+#define CONNMAN_CHAIN_NAT_POST "nat-postrouting"
+#define CONNMAN_CHAIN_FILTER_OUTPUT "filter-output"
+
+static bool debug_enabled = true;
+
+struct firewall_handle {
+ uint64_t handle;
+ const char *chain;
+};
+
+struct firewall_context {
+ struct firewall_handle rule;
+};
+
+struct nftables_info {
+ struct firewall_handle ct;
+ unsigned int mark_ref;
+};
+static struct nftables_info *nft_info;
+
+enum callback_return_type {
+ CALLBACK_RETURN_NONE = 0,
+ CALLBACK_RETURN_HANDLE,
+ CALLBACK_RETURN_BYTE_COUNTER,
+ _CALLBACK_RETURN_MAX,
+};
+
+struct callback_data {
+ enum callback_return_type type;
+ uint64_t value;
+ bool success;
+};
+
+static void debug_netlink_dump_rule(struct nftnl_rule *nlr)
+{
+ char buf[4096];
+
+ if (!debug_enabled)
+ return;
+
+ nftnl_rule_snprintf(buf, sizeof(buf), nlr, 0, 0);
+ fprintf(stdout, "%s\n", buf);
+}
+
+static void debug_mnl_dump_rule(const void *req, size_t req_size)
+{
+ if (!debug_enabled)
+ return;
+
+ mnl_nlmsg_fprintf(stdout, req, req_size, 0);
+ printf("\n");
+}
+
+static int rule_expr_cb(struct nftnl_expr *expr, void *data) {
+
+ struct callback_data *cb = data;
+ const char *name;
+
+ name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
+
+ if (strcmp(name, "counter")) {
+ cb->value = nftnl_expr_get_u64(expr, NFTNL_EXPR_CTR_BYTES);
+ cb->success = true;
+ }
+
+ return 0;
+}
+
+static int rule_cb(const struct nlmsghdr *nlh, int event,
+ struct callback_data *cb)
+{
+ struct nftnl_rule *rule;
+
+ rule = nftnl_rule_alloc();
+ if (!rule)
+ return MNL_CB_OK;
+
+ if (nftnl_rule_nlmsg_parse(nlh, rule) < 0)
+ goto out;
+
+ switch (cb->type) {
+ case CALLBACK_RETURN_HANDLE:
+ cb->value = nftnl_rule_get_u64(rule, NFTNL_RULE_HANDLE);
+ cb->success = true;
+ break;
+
+ case CALLBACK_RETURN_BYTE_COUNTER:
+ nftnl_expr_foreach(rule, rule_expr_cb, cb);
+ break;
+
+ default:
+ DBG("unhandled callback type %d\n", cb->type);
+ break;
+ }
+
+out:
+ nftnl_rule_free(rule);
+ return MNL_CB_STOP;
+}
+
+static int chain_cb(const struct nlmsghdr *nlh, int event,
+ struct callback_data *cb)
+{
+ struct nftnl_chain *chain;
+
+ chain = nftnl_chain_alloc();
+ if (!chain)
+ return MNL_CB_OK;
+
+ if (nftnl_chain_nlmsg_parse(nlh, chain) < 0)
+ goto out;
+
+ switch (cb->type) {
+ case CALLBACK_RETURN_HANDLE:
+ cb->value = nftnl_chain_get_u64(chain, NFTNL_CHAIN_HANDLE);
+ cb->success = true;
+ break;
+
+ default:
+ DBG("unhandled callback type %d\n", cb->type);
+ break;
+ }
+
+out:
+ nftnl_chain_free(chain);
+ return MNL_CB_OK;
+}
+
+static const char *event_to_str(enum nf_tables_msg_types type)
+{
+ const char *table[] = {
+ "NFT_MSG_NEWTABLE",
+ "NFT_MSG_GETTABLE",
+ "NFT_MSG_DELTABLE",
+ "NFT_MSG_NEWCHAIN",
+ "NFT_MSG_GETCHAIN",
+ "NFT_MSG_DELCHAIN",
+ "NFT_MSG_NEWRULE",
+ "NFT_MSG_GETRULE",
+ "NFT_MSG_DELRULE",
+ "NFT_MSG_NEWSET",
+ "NFT_MSG_GETSET",
+ "NFT_MSG_DELSET",
+ "NFT_MSG_NEWSETELEM",
+ "NFT_MSG_GETSETELEM",
+ "NFT_MSG_DELSETELEM",
+ "NFT_MSG_NEWGEN",
+ "NFT_MSG_GETGEN",
+ "NFT_MSG_TRACE"
+ };
+
+ if (type < sizeof(table)/sizeof(table[0]))
+ return table[type];
+
+ return "unknown";
+}
+
+static int events_cb(const struct nlmsghdr *nlh, void *data)
+{
+ int event = NFNL_MSG_TYPE(nlh->nlmsg_type);
+ struct callback_data *cb = data;
+ int err = MNL_CB_OK;
+
+ if (!cb || cb->type == CALLBACK_RETURN_NONE)
+ return err;
+
+ DBG("handle event %s", event_to_str(event));
+
+ switch(event) {
+ case NFT_MSG_NEWCHAIN:
+ err = chain_cb(nlh, event, cb);
+ break;
+
+ case NFT_MSG_NEWRULE:
+ err = rule_cb(nlh, event, cb);
+ break;
+ default:
+ DBG("unhandled event type %s", event_to_str(event));
+ break;
+ }
+
+ return err;
+}
+
+static int socket_open_and_bind(struct mnl_socket **n)
+{
+
+ struct mnl_socket *nl = NULL;
+ int err;
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (!nl)
+ return -errno;
+
+ err = mnl_socket_bind(nl, 1 << (NFNLGRP_NFTABLES-1),
+ MNL_SOCKET_AUTOPID);
+ if (err < 0) {
+ mnl_socket_close(nl);
+ return -errno;
+ }
+
+ *n = nl;
+ return 0;
+}
+
+static int send_and_dispatch(struct mnl_socket *nl, const void *req,
+ size_t req_size, enum callback_return_type callback_type,
+ uint64_t *callback_value)
+{
+ struct callback_data cb = {};
+ uint32_t portid;
+ int err;
+
+ debug_mnl_dump_rule(req, req_size);
+
+ err = mnl_socket_sendto(nl, req, req_size);
+ if (err < 0)
+ return -errno;
+
+ portid = mnl_socket_get_portid(nl);
+ cb.type = callback_type;
+
+ for (;;) {
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+
+ err = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (err <= 0)
+ break;
+
+ err = mnl_cb_run(buf, err, 0, portid, events_cb, &cb);
+ if (err <= 0)
+ break;
+ }
+
+ if (err < 0)
+ return -errno;
+
+ if (callback_type == CALLBACK_RETURN_NONE)
+ return 0;
+
+ if (cb.success) {
+ if (callback_value)
+ *callback_value = cb.value;
+
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static void put_batch_headers(char *buf, uint16_t type, uint32_t seq)
+{
+
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfg;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = type;
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+ nlh->nlmsg_seq = seq;
+
+ nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_INET;
+ nfg->version = NFNETLINK_V0;
+ nfg->res_id = NFNL_SUBSYS_NFTABLES;
+}
+
+static int add_payload(struct nftnl_rule *rule, uint32_t base,
+ uint32_t dreg, uint32_t offset, uint32_t len)
+{
+ struct nftnl_expr *expr;
+
+ expr = nftnl_expr_alloc("payload");
+ if (!expr)
+ return -ENOMEM;
+
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_BASE, base);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_DREG, dreg);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_LEN, len);
+
+ nftnl_rule_add_expr(rule, expr);
+
+ return 0;
+}
+
+static int add_bitwise(struct nftnl_rule *rule, int reg, const void *mask,
+ size_t len)
+{
+ struct nftnl_expr *expr;
+ uint8_t *xor;
+
+ expr = nftnl_expr_alloc("bitwise");
+ if (!expr)
+ return -ENOMEM;
+
+ xor = alloca(len);
+ memset(xor, 0, len);
+
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, reg);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, reg);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, len);
+ nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_MASK, mask, len);
+ nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_XOR, xor, len);
+
+ nftnl_rule_add_expr(rule, expr);
+
+ return 0;
+}
+
+static int add_cmp(struct nftnl_rule *rule, uint32_t sreg, uint32_t op,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr *expr;
+
+ expr = nftnl_expr_alloc("cmp");
+ if (!expr)
+ return -ENOMEM;
+
+ nftnl_expr_set_u32(expr, NFT_EXPR_CMP_SREG, sreg);
+ nftnl_expr_set_u32(expr, NFT_EXPR_CMP_OP, op);
+ nftnl_expr_set(expr, NFT_EXPR_CMP_DATA, data, data_len);
+
+ nftnl_rule_add_expr(rule, expr);
+
+ return 0;
+}
+
+static int table_cmd(struct mnl_socket *nl, struct nftnl_table *t,
+ uint16_t cmd, uint16_t family, uint16_t type)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct mnl_nlmsg_batch *batch;
+ struct nlmsghdr *nlh;
+ uint32_t seq = 0;
+ int err;
+
+ batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
+ nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
+ mnl_nlmsg_batch_next(batch);
+
+ nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ cmd, family, type, seq++);
+ nftnl_table_nlmsg_build_payload(nlh, t);
+ nftnl_table_free(t);
+ mnl_nlmsg_batch_next(batch);
+
+ nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
+ mnl_nlmsg_batch_next(batch);
+
+ /* The current table commands do not support any callback returns. */
+ err = send_and_dispatch(nl, mnl_nlmsg_batch_head(batch),
+ mnl_nlmsg_batch_size(batch), 0, NULL);
+ if (err < 0)
+ return err;
+
+ mnl_nlmsg_batch_stop(batch);
+
+ return 0;
+}
+
+static int chain_cmd(struct mnl_socket *nl, struct nftnl_chain *chain,
+ uint16_t cmd, int family, uint16_t type,
+ enum callback_return_type cb_type, uint64_t *cb_val)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct mnl_nlmsg_batch *batch;
+ struct nlmsghdr *nlh;
+ uint32_t seq = 0;
+ int err;
+
+ batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
+ nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
+ mnl_nlmsg_batch_next(batch);
+
+ nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ cmd, family, type, seq++);
+ nftnl_chain_nlmsg_build_payload(nlh, chain);
+ nftnl_chain_free(chain);
+ mnl_nlmsg_batch_next(batch);
+
+ nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
+ mnl_nlmsg_batch_next(batch);
+
+ err = send_and_dispatch(nl, mnl_nlmsg_batch_head(batch),
+ mnl_nlmsg_batch_size(batch), cb_type, cb_val);
+ if (err < 0)
+ return err;
+
+ mnl_nlmsg_batch_stop(batch);
+
+ return 0;
+}
+
+static int rule_cmd(struct mnl_socket *nl, struct nftnl_rule *rule,
+ uint16_t cmd, uint16_t family, uint16_t type,
+ enum callback_return_type callback_type,
+ uint64_t *callback_value)
+{
+
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct mnl_nlmsg_batch *batch;
+ struct nlmsghdr *nlh;
+ uint32_t seq = 0;
+ int err;
+
+ debug_netlink_dump_rule(rule);
+
+ batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
+ put_batch_headers(mnl_nlmsg_batch_current(batch),
+ NFNL_MSG_BATCH_BEGIN, seq++);
+ mnl_nlmsg_batch_next(batch);
+
+ nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ cmd, family, type, seq++);
+ nftnl_rule_nlmsg_build_payload(nlh, rule);
+ mnl_nlmsg_batch_next(batch);
+
+ put_batch_headers(mnl_nlmsg_batch_current(batch),
+ NFNL_MSG_BATCH_END, seq++);
+ mnl_nlmsg_batch_next(batch);
+
+ err = send_and_dispatch(nl, mnl_nlmsg_batch_head(batch),
+ mnl_nlmsg_batch_size(batch),
+ callback_type, callback_value);
+ mnl_nlmsg_batch_stop(batch);
+
+ return err;
+}
+
+static int rule_delete(struct firewall_handle *handle)
+{
+ struct nftnl_rule *rule;
+ struct mnl_socket *nl;
+ int err;
+
+ DBG("");
+
+ rule = nftnl_rule_alloc();
+ if (!rule)
+ return -ENOMEM;
+
+ nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set(rule, NFTNL_RULE_CHAIN, handle->chain);
+ nftnl_rule_set_u64(rule, NFTNL_RULE_HANDLE, handle->handle);
+
+ err = socket_open_and_bind(&nl);
+ if (err < 0) {
+ nftnl_rule_free(rule);
+ return err;
+ }
+
+ err = rule_cmd(nl, rule, NFT_MSG_DELRULE, NFPROTO_IPV4,
+ NLM_F_ACK, 0, NULL);
+ nftnl_rule_free(rule);
+ mnl_socket_close(nl);
+
+ return err;
+}
+
struct firewall_context *__connman_firewall_create(void)
{
- return NULL;
+ struct firewall_context *ctx;
+
+ DBG("");
+
+ ctx = g_new0(struct firewall_context, 1);
+
+ return ctx;
}
void __connman_firewall_destroy(struct firewall_context *ctx)
{
+ DBG("");
+
+ g_free(ctx);
+}
+
+static int build_rule_nat(const char *address, unsigned char prefixlen,
+ const char *interface, struct nftnl_rule **res)
+{
+ struct nftnl_rule *rule;
+ struct in_addr ipv4_addr, ipv4_mask;
+ struct nftnl_expr *expr;
+ int err;
+
+ /*
+ * # nft --debug netlink add rule connman nat-postrouting \
+ * oifname eth0 ip saddr 10.10.0.0/24 masquerade
+ *
+ * ip connman nat-postrouting
+ * [ meta load oifname => reg 1 ]
+ * [ cmp eq reg 1 0x30687465 0x00000000 0x00000000 0x00000000 ]
+ * [ payload load 4b @ network header + 12 => reg 1 ]
+ * [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ]
+ * [ cmp eq reg 1 0x00000a0a ]
+ * [ masq ]
+ */
+
+ rule = nftnl_rule_alloc();
+ if (!rule)
+ return -ENOMEM;
+
+ nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST);
+
+ /* family ipv4 */
+ nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4);
+
+ /* oifname */
+ expr = nftnl_expr_alloc("meta");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFT_EXPR_META_KEY, NFT_META_OIFNAME);
+ nftnl_expr_set_u32(expr, NFT_EXPR_META_DREG, NFT_REG_1);
+ nftnl_rule_add_expr(rule, expr);
+ err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, interface,
+ strlen(interface) + 1);
+ if (err < 0)
+ goto err;
+
+ /* source */
+ ipv4_mask.s_addr = htonl((0xffffffff << (32 - prefixlen)) & 0xffffffff);
+ ipv4_addr.s_addr = inet_addr(address);
+ ipv4_addr.s_addr &= ipv4_mask.s_addr;
+
+ err = add_payload(rule, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
+ offsetof(struct iphdr, saddr), sizeof(struct in_addr));
+ if (err < 0)
+ goto err;
+ err = add_bitwise(rule, NFT_REG_1, &ipv4_mask.s_addr,
+ sizeof(struct in_addr));
+ if (err < 0)
+ goto err;
+ err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &ipv4_addr.s_addr,
+ sizeof(struct in_addr));
+ if (err < 0)
+ goto err;
+
+ /* masquerade */
+ expr = nftnl_expr_alloc("masq");
+ if (!expr)
+ goto err;
+ nftnl_rule_add_expr(rule, expr);
+
+ *res = rule;
+ return 0;
+
+err:
+ nftnl_rule_free(rule);
+ return -ENOMEM;
}
int __connman_firewall_enable_nat(struct firewall_context *ctx,
char *address, unsigned char prefixlen,
char *interface)
{
- return -EPROTONOSUPPORT;
+ struct mnl_socket *nl;
+ struct nftnl_rule *rule;
+ int err;
+
+ DBG("address %s/%d interface %s", address, (int)prefixlen, interface);
+
+ err = socket_open_and_bind(&nl);
+ if (err < 0)
+ return err;
+
+ err = build_rule_nat(address, prefixlen, interface, &rule);
+ if (err)
+ goto out;
+
+ ctx->rule.chain = CONNMAN_CHAIN_NAT_POST;
+ err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4,
+ NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK,
+ CALLBACK_RETURN_HANDLE, &ctx->rule.handle);
+ nftnl_rule_free(rule);
+out:
+ mnl_socket_close(nl);
+ return err;
}
int __connman_firewall_disable_nat(struct firewall_context *ctx)
{
- return -EPROTONOSUPPORT;
+ return rule_delete(&ctx->rule);
+}
+
+static int build_rule_snat(int index, const char *address,
+ struct nftnl_rule **res)
+{
+ struct nftnl_rule *rule;
+ struct nftnl_expr *expr;
+ uint32_t snat;
+ int err;
+
+ /*
+ * # nft --debug netlink add rule connman nat-postrouting \
+ * oif eth0 snat 1.2.3.4
+ * ip connman nat-postrouting
+ * [ meta load oif => reg 1 ]
+ * [ cmp eq reg 1 0x0000000b ]
+ * [ immediate reg 1 0x04030201 ]
+ * [ nat snat ip addr_min reg 1 addr_max reg 0 ]
+ */
+
+ rule = nftnl_rule_alloc();
+ if (!rule)
+ return -ENOMEM;
+
+ nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST);
+
+ /* IOF */
+ expr = nftnl_expr_alloc("meta");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFT_EXPR_META_KEY, NFT_META_OIF);
+ nftnl_expr_set_u32(expr, NFT_EXPR_META_DREG, NFT_REG_1);
+ nftnl_rule_add_expr(rule, expr);
+ err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &index, sizeof(index));
+ if (err < 0)
+ goto err;
+
+ /* snat */
+ expr = nftnl_expr_alloc("immediate");
+ if (!expr)
+ goto err;
+ snat = inet_addr(address);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_1);
+ nftnl_expr_set(expr, NFTNL_EXPR_IMM_DATA, &snat, sizeof(snat));
+ nftnl_rule_add_expr(rule, expr);
+
+ expr = nftnl_expr_alloc("nat");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_NAT_TYPE, NFT_NAT_SNAT);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_NAT_FAMILY, NFPROTO_IPV4);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_NAT_REG_ADDR_MIN, NFT_REG_1);
+ nftnl_rule_add_expr(rule, expr);
+
+ *res = rule;
+ return 0;
+
+err:
+ nftnl_rule_free(rule);
+ return -ENOMEM;
}
int __connman_firewall_enable_snat(struct firewall_context *ctx,
int index, const char *ifname, const char *addr)
{
- return -EPROTONOSUPPORT;
+ struct nftnl_rule *rule;
+ struct mnl_socket *nl;
+ int err;
+
+ DBG("");
+
+ err = socket_open_and_bind(&nl);
+ if (err < 0)
+ return err;
+
+ err = build_rule_snat(index, addr, &rule);
+ if (err)
+ goto out;
+
+ ctx->rule.chain = CONNMAN_CHAIN_NAT_POST;
+ err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4,
+ NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK,
+ CALLBACK_RETURN_HANDLE, &ctx->rule.handle);
+ nftnl_rule_free(rule);
+out:
+ mnl_socket_close(nl);
+ return err;
}
int __connman_firewall_disable_snat(struct firewall_context *ctx)
{
- return -EPROTONOSUPPORT;
+ DBG("");
+
+ return rule_delete(&ctx->rule);
+}
+
+static int build_rule_ct(struct nftnl_rule **res)
+{
+ struct nftnl_rule *rule;
+ struct nftnl_expr *expr;
+
+ /*
+ *
http://wiki.nftables.org/wiki-nftables/index.php/Setting_packet_metainformation
+ *
http://wiki.nftables.org/wiki-nftables/index.php/Matching_packet_metainformation
+ *
+ * # nft --debug netlink add rule connman filter-output \
+ * ct mark set mark
+ *
+ * ip connman filter-output
+ * [ meta load mark => reg 1 ]
+ * [ ct set mark with reg 1 ]
+ */
+
+ rule = nftnl_rule_alloc();
+ if (!rule)
+ return -ENOMEM;
+
+ nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_FILTER_OUTPUT);
+
+ expr = nftnl_expr_alloc("meta");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_MARK);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, NFT_REG_1);
+ nftnl_rule_add_expr(rule, expr);
+
+ expr = nftnl_expr_alloc("ct");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_CT_KEY, NFT_CT_MARK);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_CT_SREG, NFT_REG_1);
+ nftnl_rule_add_expr(rule, expr);
+
+ *res = rule;
+ return 0;
+
+err:
+ nftnl_rule_free(rule);
+ return -ENOMEM;
+}
+
+static int ct_enable(void)
+{
+ struct nftnl_rule *rule;
+ struct mnl_socket *nl;
+ int err;
+
+ DBG("");
+
+ if (nft_info->mark_ref > 0)
+ return 0;
+
+ err = socket_open_and_bind(&nl);
+ if (err < 0)
+ return err;
+
+ err = build_rule_ct(&rule);
+ if (err < 0)
+ goto out;
+
+ nft_info->ct.chain = CONNMAN_CHAIN_FILTER_OUTPUT;
+ err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4,
+ NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK,
+ CALLBACK_RETURN_HANDLE, &nft_info->ct.handle);
+ nftnl_rule_free(rule);
+
+ if (!err)
+ nft_info->mark_ref++;
+out:
+ mnl_socket_close(nl);
+ return err;
+}
+
+static int ct_disable(void)
+{
+ nft_info->mark_ref--;
+ if (nft_info->mark_ref > 0)
+ return 0;
+
+ return rule_delete(&nft_info->ct);
+}
+
+static int build_rule_marking(uid_t uid, uint32_t mark, struct nftnl_rule
**res)
+{
+ struct nftnl_rule *rule;
+ struct nftnl_expr *expr;
+ int err;
+
+ /*
+ *
http://wiki.nftables.org/wiki-nftables/index.php/Setting_packet_metainformation
+ *
http://wiki.nftables.org/wiki-nftables/index.php/Matching_packet_metainformation
+ *
+ * # nft --debug netlink add rule connman filter-output \
+ * meta skuid wagi mark set 1234
+ *
+ * ip connman filter-output
+ * [ meta load skuid => reg 1 ]
+ * [ cmp eq reg 1 0x000003e8 ]
+ * [ immediate reg 1 0x000004d2 ]
+ * [ meta set mark with reg 1 ]
+ */
+
+ rule = nftnl_rule_alloc();
+ if (!rule)
+ return -ENOMEM;
+
+ nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_FILTER_OUTPUT);
+
+ expr = nftnl_expr_alloc("meta");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_SKUID);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, NFT_REG_1);
+ nftnl_rule_add_expr(rule, expr);
+ err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &uid, sizeof(uid));
+ if (err < 0)
+ goto err;
+
+ expr = nftnl_expr_alloc("immediate");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_1);
+ nftnl_expr_set(expr, NFTNL_EXPR_IMM_DATA, &mark, sizeof(mark));
+ nftnl_rule_add_expr(rule, expr);
+
+ expr = nftnl_expr_alloc("meta");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_MARK);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_SREG, NFT_REG_1);
+ nftnl_rule_add_expr(rule, expr);
+
+ *res = rule;
+ return 0;
+
+err:
+ return -ENOMEM;
}
int __connman_firewall_enable_marking(struct firewall_context *ctx,
enum connman_session_id_type id_type,
char *id, uint32_t mark)
{
- return -EPROTONOSUPPORT;
+ struct nftnl_rule *rule;
+ struct mnl_socket *nl;
+ struct passwd *pw;
+ uid_t uid;
+ int err;
+
+ DBG("");
+
+
+ if (id_type != CONNMAN_SESSION_ID_TYPE_UID)
+ return -ENOTSUP;
+
+ pw = getpwnam(id);
+ if (!pw)
+ return -EINVAL;
+ uid = pw->pw_uid;
+
+ err = ct_enable();
+ if (err)
+ return err;
+
+ err = socket_open_and_bind(&nl);
+ if (err < 0)
+ return err;
+
+ err = build_rule_marking(uid, mark, &rule);
+ if (err < 0)
+ goto out;
+
+ ctx->rule.chain = CONNMAN_CHAIN_FILTER_OUTPUT;
+ err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4,
+ NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK,
+ CALLBACK_RETURN_HANDLE, &ctx->rule.handle);
+
+ nftnl_rule_free(rule);
+out:
+ if (err)
+ ct_disable();
+ mnl_socket_close(nl);
+ return err;
}
int __connman_firewall_disable_marking(struct firewall_context *ctx)
{
- return -EPROTONOSUPPORT;
+ int err;
+
+ DBG("");
+
+ err = rule_delete(&ctx->rule);
+ ct_disable();
+ return err;
+}
+
+static struct nftnl_table *build_table(const char *name, uint16_t family)
+{
+ struct nftnl_table *table;
+
+ table = nftnl_table_alloc();
+ if (!table)
+ return NULL;
+
+ nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, family);
+ nftnl_table_set_str(table, NFTNL_TABLE_NAME, name);
+
+ return table;
+}
+
+
+static struct nftnl_chain *build_chain(const char *name, const char *table,
+ const char *type, int hooknum, int prio)
+{
+ struct nftnl_chain *chain;
+
+ chain = nftnl_chain_alloc();
+ if (!chain)
+ return NULL;
+
+ nftnl_chain_set(chain, NFTNL_CHAIN_TABLE, table);
+ nftnl_chain_set(chain, NFTNL_CHAIN_NAME, name);
+
+ if (type)
+ nftnl_chain_set_str(chain, NFTNL_CHAIN_TYPE, type);
+
+ if (hooknum >= 0)
+ nftnl_chain_set_u32(chain, NFTNL_CHAIN_HOOKNUM, hooknum);
+
+ if (prio >= 0)
+ nftnl_chain_set_u32(chain, NFTNL_CHAIN_PRIO, prio);
+
+ return chain;
+}
+
+static int create_table_and_chains(struct nftables_info *nft_info)
+{
+ struct mnl_socket *nl;
+ struct nftnl_table *table;
+ struct nftnl_chain *chain;
+ int err;
+
+
+ DBG("");
+
+ err = socket_open_and_bind(&nl);
+ if (err < 0)
+ return err;
+
+ /*
+ * Add table
+ * http://wiki.nftables.org/wiki-nftables/index.php/Configuring_tables
+ */
+
+ /*
+ * # nft add table connman
+ */
+ table = build_table(CONNMAN_TABLE, NFPROTO_IPV4);
+ if (!table) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = table_cmd(nl, table, NFT_MSG_NEWTABLE, NFPROTO_IPV4,
+ NLM_F_CREATE|NLM_F_ACK);
+ if (err < 0)
+ goto out;
+
+ /*
+ * Add basic chains
+ * http://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains
+ */
+
+ /*
+ * # nft add chain connman nat-prerouting \
+ * { type nat hook prerouting priortiy 0 ; }
+ */
+ chain = build_chain(CONNMAN_CHAIN_NAT_PRE, CONNMAN_TABLE,
+ "nat", NF_INET_PRE_ROUTING, 0);
+ if (!chain) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = chain_cmd(nl, chain, NFT_MSG_NEWCHAIN,
+ NFPROTO_IPV4, NLM_F_CREATE | NLM_F_ACK,
+ CALLBACK_RETURN_NONE, NULL);
+ if (err < 0)
+ goto out;
+
+ /*
+ * # nft add chain connman nat-postrouting \
+ * { type nat hook postrouting priortiy 0 ; }
+ */
+ chain = build_chain(CONNMAN_CHAIN_NAT_POST, CONNMAN_TABLE,
+ "nat", NF_INET_POST_ROUTING, 0);
+ if (!chain) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = chain_cmd(nl, chain, NFT_MSG_NEWCHAIN,
+ NFPROTO_IPV4, NLM_F_CREATE | NLM_F_ACK,
+ CALLBACK_RETURN_NONE, NULL);
+ if (err < 0)
+ goto out;
+
+ /*
+ * # nft add chain connman filter-output \
+ * { type filter hook output priority 0 ; }
+ */
+ chain = build_chain(CONNMAN_CHAIN_FILTER_OUTPUT, CONNMAN_TABLE,
+ "filter", NF_INET_LOCAL_OUT, 0);
+ if (!chain) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = chain_cmd(nl, chain, NFT_MSG_NEWCHAIN,
+ NFPROTO_IPV4, NLM_F_CREATE | NLM_F_ACK,
+ CALLBACK_RETURN_NONE, NULL);
+ if (err < 0)
+ goto out;
+
+out:
+ if (err)
+ connman_warn("Failed to create basic chains: %s",
+ strerror(-err));
+ mnl_socket_close(nl);
+ return err;
+}
+
+static int cleanup_table_and_chains(void)
+{
+ struct nftnl_table *table;
+ struct mnl_socket *nl;
+ int err;
+
+ DBG("");
+
+ err = socket_open_and_bind(&nl);
+ if (err < 0)
+ return -ENOMEM;
+
+ /*
+ * Cleanup everythying in one go. There is little point in
+ * step-by-step removal of rules and chains if you can get it
+ * as simple as this.
+ */
+ /*
+ * # nft delete table connman
+ */
+ table = build_table(CONNMAN_TABLE, NFPROTO_IPV4);
+ err = table_cmd(nl, table, NFT_MSG_DELTABLE, NFPROTO_IPV4, NLM_F_ACK);
+
+ mnl_socket_close(nl);
+ return err;
}
int __connman_firewall_init(void)
{
- return 0;
+ int err;
+
+ DBG("");
+
+ if (getenv("CONNMAN_NFTABLES_DEBUG"))
+ debug_enabled = true;
+
+ /*
+ * EAFNOSUPPORT is return whenever the nf_tables_ipv4 hasn't been
+ * loaded yet. ENOENT is return in case the table is missing.
+ */
+ err = cleanup_table_and_chains();
+ if (err < 0 && (err != EAFNOSUPPORT && err != -ENOENT))
+ return err;
+
+ nft_info = g_new0(struct nftables_info, 1);
+ err = create_table_and_chains(nft_info);
+ if (err) {
+ g_free(nft_info);
+ nft_info = NULL;
+ }
+
+ return err;
}
void __connman_firewall_cleanup(void)
{
+ int err;
+
+ DBG("");
+
+ err = cleanup_table_and_chains();
+ if (err < 0)
+ printf("cleanup table and chains failed with '%s' %d\n",
+ strerror(-err), err);
+
+ g_free(nft_info);
+ nft_info = NULL;
}
--
2.7.4
------------------------------
Message: 2
Date: Thu, 18 Aug 2016 11:06:15 +0200
From: Daniel Wagner <[email protected]>
To: [email protected]
Cc: Daniel Wagner <[email protected]>
Subject: [PATCH v4 4/6] firewall: Rename firewall.c to
firewall-iptables.c
Message-ID: <[email protected]>
From: Daniel Wagner <[email protected]>
There is little point in keeping the orignal firewall implementation.
It is designed based on iptables.c API. For nftables it is just
better to start from fresh. The gives the necessary freedom to really
exploit the nftables API without fearing to break the working iptables
implementation. That should also make testing considerable more simpler.
---
Makefile.am | 38 ++-
configure.ac | 1 +
src/firewall-iptables.c | 622 ++++++++++++++++++++++++++++++++++++++++++++++++
src/firewall.c | 622 ------------------------------------------------
4 files changed, 650 insertions(+), 633 deletions(-)
create mode 100644 src/firewall-iptables.c
delete mode 100644 src/firewall.c
diff --git a/Makefile.am b/Makefile.am
index d70725c..48188f9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -120,13 +120,13 @@ src_connmand_SOURCES = $(gdhcp_sources) $(gweb_sources)
$(backtrace_sources) \
src/storage.c src/dbus.c src/config.c \
src/technology.c src/counter.c src/ntp.c \
src/session.c src/tethering.c src/wpad.c src/wispr.c \
- src/stats.c src/iptables.c src/dnsproxy.c src/6to4.c \
+ src/stats.c src/dnsproxy.c src/6to4.c \
src/ippool.c src/bridge.c src/nat.c src/ipaddress.c \
- src/inotify.c src/firewall.c src/ipv6pd.c src/peer.c \
+ src/inotify.c src/ipv6pd.c src/peer.c \
src/peer_service.c src/machine.c src/util.c
src_connmand_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
- @GLIB_LIBS@ @DBUS_LIBS@ @XTABLES_LIBS@ @GNUTLS_LIBS@ \
+ @GLIB_LIBS@ @DBUS_LIBS@ @GNUTLS_LIBS@ \
-lresolv -ldl -lrt
src_connmand_LDFLAGS = -Wl,--export-dynamic \
@@ -137,6 +137,11 @@ src_connmand_wait_online_SOURCES =
src/connmand-wait-online.c
src_connmand_wait_online_LDADD = gdbus/libgdbus-internal.la \
@GLIB_LIBS@ @DBUS_LIBS@
+if XTABLES
+src_connmand_SOURCES += src/iptables.c src/firewall-iptables.c
+src_connmand_LDADD += @XTABLES_LIBS@
+endif
+
if VPN
vpn_plugin_LTLIBRARIES =
@@ -206,7 +211,7 @@ build_vpn_plugindir = $(vpn_plugindir)
endif
endif
-AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @XTABLES_CFLAGS@ \
+AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ \
@GNUTLS_CFLAGS@ $(builtin_cflags) \
-DCONNMAN_PLUGIN_BUILTIN \
-DSTATEDIR=\""$(statedir)"\" \
@@ -223,7 +228,7 @@ else
AM_CPPFLAGS = -I$(builddir)/include -I$(builddir)/src -I$(srcdir)/gdbus
endif
-src_connmand_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @XTABLES_CFLAGS@ \
+src_connmand_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ \
@GNUTLS_CFLAGS@ $(builtin_cflags) \
-DCONNMAN_PLUGIN_BUILTIN \
-DSTATEDIR=\""$(statedir)"\" \
@@ -252,6 +257,11 @@ vpn_connman_vpnd_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ \
endif
+if XTABLES
+AM_CFLAGS += @XTABLES_CFLAGS@
+src_connmand_CFLAGS += @XTABLES_CFLAGS@
+endif
+
EXTRA_DIST += vpn/vpn-dbus.conf vpn/vpn-polkit.conf
script_DATA =
@@ -297,9 +307,9 @@ noinst_PROGRAMS += tools/supplicant-test \
tools/dhcp-test tools/dhcp-server-test \
tools/addr-test tools/web-test tools/resolv-test \
tools/dbus-test tools/polkit-test \
- tools/iptables-test tools/tap-test tools/wpad-test \
+ tools/tap-test tools/wpad-test \
tools/stats-tool tools/private-network-test \
- tools/session-test tools/iptables-unit \
+ tools/session-test \
tools/dnsproxy-test tools/netlink-test
tools_supplicant_test_SOURCES = tools/supplicant-test.c \
@@ -330,9 +340,6 @@ tools_dbus_test_LDADD = gdbus/libgdbus-internal.la
@GLIB_LIBS@ @DBUS_LIBS@
tools_polkit_test_LDADD = @DBUS_LIBS@
-tools_iptables_test_SOURCES = $(backtrace_sources) src/log.c src/iptables.c
tools/iptables-test.c
-tools_iptables_test_LDADD = @GLIB_LIBS@ @XTABLES_LIBS@ -ldl
-
tools_private_network_test_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
tools_session_test_SOURCES = $(backtrace_sources) src/log.c src/dbus.c
src/error.c \
@@ -341,12 +348,21 @@ tools_session_test_SOURCES = $(backtrace_sources)
src/log.c src/dbus.c src/error
tools_session_test_LDADD = gdbus/libgdbus-internal.la \
@GLIB_LIBS@ @DBUS_LIBS@ -ldl
+if XTABLES
+noinst_PROGRAMS += tools/iptables-test tools/iptables-unit
+
+tools_iptables_test_SOURCES = $(backtrace_sources) src/log.c src/iptables.c \
+ tools/iptables-test.c
+tools_iptables_test_LDADD = @GLIB_LIBS@ @XTABLES_LIBS@ -ldl
+
tools_iptables_unit_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @XTABLES_CFLAGS@ \
-DIPTABLES_SAVE=\""${IPTABLES_SAVE}"\"
tools_iptables_unit_SOURCES = $(backtrace_sources) src/log.c \
- src/iptables.c src/firewall.c src/nat.c tools/iptables-unit.c
+ src/iptables.c src/firewall-iptables.c src/nat.c \
+ tools/iptables-unit.c
tools_iptables_unit_LDADD = gdbus/libgdbus-internal.la \
@GLIB_LIBS@ @DBUS_LIBS@ @XTABLES_LIBS@ -ldl
+endif
tools_dnsproxy_test_SOURCES = tools/dnsproxy-test.c
tools_dnsproxy_test_LDADD = @GLIB_LIBS@
diff --git a/configure.ac b/configure.ac
index 6e66ab3..86b9516 100644
--- a/configure.ac
+++ b/configure.ac
@@ -269,6 +269,7 @@ PKG_CHECK_MODULES(XTABLES, xtables >= 1.4.11, dummy=yes,
AC_MSG_ERROR(Xtables library is required))
AC_SUBST(XTABLES_CFLAGS)
AC_SUBST(XTABLES_LIBS)
+AM_CONDITIONAL(XTABLES, test "${XTABLES}" != "no")
AC_ARG_ENABLE(test, AC_HELP_STRING([--enable-test],
[enable test/example scripts]), [enable_test=${enableval}])
diff --git a/src/firewall-iptables.c b/src/firewall-iptables.c
new file mode 100644
index 0000000..c58efc1
--- /dev/null
+++ b/src/firewall-iptables.c
@@ -0,0 +1,622 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2013,2015 BMW Car IT GmbH.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <xtables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+#include "connman.h"
+
+#define CHAIN_PREFIX "connman-"
+
+static const char *builtin_chains[] = {
+ [NF_IP_PRE_ROUTING] = "PREROUTING",
+ [NF_IP_LOCAL_IN] = "INPUT",
+ [NF_IP_FORWARD] = "FORWARD",
+ [NF_IP_LOCAL_OUT] = "OUTPUT",
+ [NF_IP_POST_ROUTING] = "POSTROUTING",
+};
+
+struct connman_managed_table {
+ char *name;
+ unsigned int chains[NF_INET_NUMHOOKS];
+};
+
+struct fw_rule {
+ bool enabled;
+ char *table;
+ char *chain;
+ char *rule_spec;
+};
+
+struct firewall_context {
+ GList *rules;
+};
+
+static GSList *managed_tables;
+static struct firewall_context *connmark_ctx;
+static unsigned int connmark_ref;
+
+static int chain_to_index(const char *chain_name)
+{
+ if (!g_strcmp0(builtin_chains[NF_IP_PRE_ROUTING], chain_name))
+ return NF_IP_PRE_ROUTING;
+ if (!g_strcmp0(builtin_chains[NF_IP_LOCAL_IN], chain_name))
+ return NF_IP_LOCAL_IN;
+ if (!g_strcmp0(builtin_chains[NF_IP_FORWARD], chain_name))
+ return NF_IP_FORWARD;
+ if (!g_strcmp0(builtin_chains[NF_IP_LOCAL_OUT], chain_name))
+ return NF_IP_LOCAL_OUT;
+ if (!g_strcmp0(builtin_chains[NF_IP_POST_ROUTING], chain_name))
+ return NF_IP_POST_ROUTING;
+
+ return -1;
+}
+
+static int managed_chain_to_index(const char *chain_name)
+{
+ if (!g_str_has_prefix(chain_name, CHAIN_PREFIX))
+ return -1;
+
+ return chain_to_index(chain_name + strlen(CHAIN_PREFIX));
+}
+
+static int insert_managed_chain(const char *table_name, int id)
+{
+ char *rule, *managed_chain;
+ int err;
+
+ managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX,
+ builtin_chains[id]);
+
+ err = __connman_iptables_new_chain(table_name, managed_chain);
+ if (err < 0)
+ goto out;
+
+ rule = g_strdup_printf("-j %s", managed_chain);
+ err = __connman_iptables_insert(table_name, builtin_chains[id], rule);
+ g_free(rule);
+ if (err < 0) {
+ __connman_iptables_delete_chain(table_name, managed_chain);
+ goto out;
+ }
+
+out:
+ g_free(managed_chain);
+
+ return err;
+}
+
+static int delete_managed_chain(const char *table_name, int id)
+{
+ char *rule, *managed_chain;
+ int err;
+
+ managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX,
+ builtin_chains[id]);
+
+ rule = g_strdup_printf("-j %s", managed_chain);
+ err = __connman_iptables_delete(table_name, builtin_chains[id], rule);
+ g_free(rule);
+
+ if (err < 0)
+ goto out;
+
+ err = __connman_iptables_delete_chain(table_name, managed_chain);
+
+out:
+ g_free(managed_chain);
+
+ return err;
+}
+
+static int insert_managed_rule(const char *table_name,
+ const char *chain_name,
+ const char *rule_spec)
+{
+ struct connman_managed_table *mtable = NULL;
+ GSList *list;
+ char *chain;
+ int id, err;
+
+ id = chain_to_index(chain_name);
+ if (id < 0) {
+ /* This chain is not managed */
+ chain = g_strdup(chain_name);
+ goto out;
+ }
+
+ for (list = managed_tables; list; list = list->next) {
+ mtable = list->data;
+
+ if (g_strcmp0(mtable->name, table_name) == 0)
+ break;
+
+ mtable = NULL;
+ }
+
+ if (!mtable) {
+ mtable = g_new0(struct connman_managed_table, 1);
+ mtable->name = g_strdup(table_name);
+
+ managed_tables = g_slist_prepend(managed_tables, mtable);
+ }
+
+ if (mtable->chains[id] == 0) {
+ DBG("table %s add managed chain for %s",
+ table_name, chain_name);
+
+ err = insert_managed_chain(table_name, id);
+ if (err < 0)
+ return err;
+ }
+
+ mtable->chains[id]++;
+ chain = g_strdup_printf("%s%s", CHAIN_PREFIX, chain_name);
+
+out:
+ err = __connman_iptables_append(table_name, chain, rule_spec);
+
+ g_free(chain);
+
+ return err;
+ }
+
+static int delete_managed_rule(const char *table_name,
+ const char *chain_name,
+ const char *rule_spec)
+ {
+ struct connman_managed_table *mtable = NULL;
+ GSList *list;
+ int id, err;
+ char *managed_chain;
+
+ id = chain_to_index(chain_name);
+ if (id < 0) {
+ /* This chain is not managed */
+ return __connman_iptables_delete(table_name, chain_name,
+ rule_spec);
+ }
+
+ managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, chain_name);
+
+ err = __connman_iptables_delete(table_name, managed_chain,
+ rule_spec);
+
+ for (list = managed_tables; list; list = list->next) {
+ mtable = list->data;
+
+ if (g_strcmp0(mtable->name, table_name) == 0)
+ break;
+
+ mtable = NULL;
+ }
+
+ if (!mtable) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ mtable->chains[id]--;
+ if (mtable->chains[id] > 0)
+ goto out;
+
+ DBG("table %s remove managed chain for %s",
+ table_name, chain_name);
+
+ err = delete_managed_chain(table_name, id);
+
+ out:
+ g_free(managed_chain);
+
+ return err;
+}
+
+static void cleanup_managed_table(gpointer user_data)
+{
+ struct connman_managed_table *table = user_data;
+
+ g_free(table->name);
+ g_free(table);
+}
+
+static void cleanup_fw_rule(gpointer user_data)
+{
+ struct fw_rule *rule = user_data;
+
+ g_free(rule->rule_spec);
+ g_free(rule->chain);
+ g_free(rule->table);
+ g_free(rule);
+}
+
+struct firewall_context *__connman_firewall_create(void)
+{
+ struct firewall_context *ctx;
+
+ ctx = g_new0(struct firewall_context, 1);
+
+ return ctx;
+}
+
+void __connman_firewall_destroy(struct firewall_context *ctx)
+{
+ g_list_free_full(ctx->rules, cleanup_fw_rule);
+ g_free(ctx);
+}
+
+static int enable_rule(struct fw_rule *rule)
+{
+ int err;
+
+ if (rule->enabled)
+ return -EALREADY;
+
+ DBG("%s %s %s", rule->table, rule->chain, rule->rule_spec);
+
+ err = insert_managed_rule(rule->table, rule->chain, rule->rule_spec);
+ if (err < 0)
+ return err;
+
+ err = __connman_iptables_commit(rule->table);
+ if (err < 0)
+ return err;
+
+ rule->enabled = true;
+
+ return 0;
+}
+
+static int disable_rule(struct fw_rule *rule)
+{
+ int err;
+
+ if (!rule->enabled)
+ return -EALREADY;
+
+ err = delete_managed_rule(rule->table, rule->chain, rule->rule_spec);
+ if (err < 0) {
+ connman_error("Cannot remove previously installed "
+ "iptables rules: %s", strerror(-err));
+ return err;
+ }
+
+ err = __connman_iptables_commit(rule->table);
+ if (err < 0) {
+ connman_error("Cannot remove previously installed "
+ "iptables rules: %s", strerror(-err));
+ return err;
+ }
+
+ rule->enabled = false;
+
+ return 0;
+}
+
+static void firewall_add_rule(struct firewall_context *ctx,
+ const char *table,
+ const char *chain,
+ const char *rule_fmt, ...)
+{
+ va_list args;
+ char *rule_spec;
+ struct fw_rule *rule;
+
+ va_start(args, rule_fmt);
+
+ rule_spec = g_strdup_vprintf(rule_fmt, args);
+
+ va_end(args);
+
+ rule = g_new0(struct fw_rule, 1);
+
+ rule->enabled = false;
+ rule->table = g_strdup(table);
+ rule->chain = g_strdup(chain);
+ rule->rule_spec = rule_spec;
+
+ ctx->rules = g_list_append(ctx->rules, rule);
+}
+
+static void firewall_remove_rules(struct firewall_context *ctx)
+{
+ struct fw_rule *rule;
+ GList *list;
+
+ for (list = g_list_last(ctx->rules); list;
+ list = g_list_previous(list)) {
+ rule = list->data;
+
+ ctx->rules = g_list_remove(ctx->rules, rule);
+ cleanup_fw_rule(rule);
+ }
+}
+
+static int firewall_enable_rules(struct firewall_context *ctx)
+{
+ struct fw_rule *rule;
+ GList *list;
+ int err = -ENOENT;
+
+ for (list = g_list_first(ctx->rules); list; list = g_list_next(list)) {
+ rule = list->data;
+
+ err = enable_rule(rule);
+ if (err < 0)
+ break;
+ }
+
+ return err;
+}
+
+static int firewall_disable_rules(struct firewall_context *ctx)
+{
+ struct fw_rule *rule;
+ GList *list;
+ int e;
+ int err = -ENOENT;
+
+ for (list = g_list_last(ctx->rules); list;
+ list = g_list_previous(list)) {
+ rule = list->data;
+
+ e = disable_rule(rule);
+
+ /* Report last error back */
+ if (e == 0 && err == -ENOENT)
+ err = 0;
+ else if (e < 0)
+ err = e;
+ }
+
+ return err;
+}
+
+int __connman_firewall_enable_nat(struct firewall_context *ctx,
+ char *address, unsigned char prefixlen,
+ char *interface)
+{
+ char *cmd;
+ int err;
+
+ cmd = g_strdup_printf("-s %s/%d -o %s -j MASQUERADE",
+ address, prefixlen, interface);
+
+ firewall_add_rule(ctx, "nat", "POSTROUTING", cmd);
+ g_free(cmd);
+ err = firewall_enable_rules(ctx);
+ if (err)
+ firewall_remove_rules(ctx);
+ return err;
+}
+
+int __connman_firewall_disable_nat(struct firewall_context *ctx)
+{
+ int err;
+
+ err = firewall_disable_rules(ctx);
+ if (err < 0) {
+ DBG("could not disable NAT rule");
+ return err;
+ }
+
+ firewall_remove_rules(ctx);
+ return 0;
+}
+
+int __connman_firewall_enable_snat(struct firewall_context *ctx,
+ int index, const char *ifname,
+ const char *addr)
+{
+ int err;
+
+ firewall_add_rule(ctx, "nat", "POSTROUTING",
+ "-o %s -j SNAT --to-source %s",
+ ifname, addr);
+
+ err = firewall_enable_rules(ctx);
+ if (err)
+ firewall_remove_rules(ctx);
+ return err;
+}
+
+int __connman_firewall_disable_snat(struct firewall_context *ctx)
+{
+ int err;
+
+ err = firewall_disable_rules(ctx);
+ if (err < 0) {
+ DBG("could not disable SNAT rule");
+ return err;
+ }
+
+ firewall_remove_rules(ctx);
+ return 0;
+}
+
+static int firewall_enable_connmark(void)
+{
+ int err;
+
+ if (connmark_ref > 0) {
+ connmark_ref++;
+ return 0;
+ }
+
+ connmark_ctx = __connman_firewall_create();
+
+ firewall_add_rule(connmark_ctx, "mangle", "INPUT",
+ "-j CONNMARK --restore-mark");
+ firewall_add_rule(connmark_ctx, "mangle", "POSTROUTING",
+ "-j CONNMARK --save-mark");
+ err = firewall_enable_rules(connmark_ctx);
+ if (err) {
+ __connman_firewall_destroy(connmark_ctx);
+ connmark_ctx = NULL;
+ return err;
+ }
+ connmark_ref++;
+ return 0;
+}
+
+static void firewall_disable_connmark(void)
+{
+ connmark_ref--;
+ if (connmark_ref > 0)
+ return;
+
+ firewall_disable_rules(connmark_ctx);
+ __connman_firewall_destroy(connmark_ctx);
+ connmark_ctx = NULL;
+}
+
+int __connman_firewall_enable_marking(struct firewall_context *ctx,
+ enum connman_session_id_type id_type,
+ char *id, uint32_t mark)
+{
+ int err;
+
+ err = firewall_enable_connmark();
+ if (err)
+ return err;
+
+ switch (id_type) {
+ case CONNMAN_SESSION_ID_TYPE_UID:
+ firewall_add_rule(ctx, "mangle", "OUTPUT",
+ "-m owner --uid-owner %s -j MARK --set-mark %d",
+ id, mark);
+ break;
+ case CONNMAN_SESSION_ID_TYPE_GID:
+ firewall_add_rule(ctx, "mangle", "OUTPUT",
+ "-m owner --gid-owner %s -j MARK --set-mark %d",
+ id, mark);
+ break;
+ case CONNMAN_SESSION_ID_TYPE_LSM:
+ default:
+ return -EINVAL;
+ }
+
+ return firewall_enable_rules(ctx);
+}
+
+int __connman_firewall_disable_marking(struct firewall_context *ctx)
+{
+ firewall_disable_connmark();
+ return firewall_disable_rules(ctx);
+}
+
+static void iterate_chains_cb(const char *chain_name, void *user_data)
+{
+ GSList **chains = user_data;
+ int id;
+
+ id = managed_chain_to_index(chain_name);
+ if (id < 0)
+ return;
+
+ *chains = g_slist_prepend(*chains, GINT_TO_POINTER(id));
+}
+
+static void flush_table(const char *table_name)
+{
+ GSList *chains = NULL, *list;
+ char *rule, *managed_chain;
+ int id, err;
+
+ __connman_iptables_iterate_chains(table_name, iterate_chains_cb,
+ &chains);
+
+ for (list = chains; list; list = list->next) {
+ id = GPOINTER_TO_INT(list->data);
+
+ managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX,
+ builtin_chains[id]);
+
+ rule = g_strdup_printf("-j %s", managed_chain);
+ err = __connman_iptables_delete(table_name,
+ builtin_chains[id], rule);
+ if (err < 0) {
+ connman_warn("Failed to delete jump rule '%s': %s",
+ rule, strerror(-err));
+ }
+ g_free(rule);
+
+ err = __connman_iptables_flush_chain(table_name, managed_chain);
+ if (err < 0) {
+ connman_warn("Failed to flush chain '%s': %s",
+ managed_chain, strerror(-err));
+ }
+ err = __connman_iptables_delete_chain(table_name,
managed_chain);
+ if (err < 0) {
+ connman_warn("Failed to delete chain '%s': %s",
+ managed_chain, strerror(-err));
+ }
+
+ g_free(managed_chain);
+ }
+
+ err = __connman_iptables_commit(table_name);
+ if (err < 0) {
+ connman_warn("Failed to flush table '%s': %s",
+ table_name, strerror(-err));
+ }
+
+ g_slist_free(chains);
+}
+
+static void flush_all_tables(void)
+{
+ /* Flush the tables ConnMan might have modified
+ * But do so if only ConnMan has done something with
+ * iptables */
+
+ if (!g_file_test("/proc/net/ip_tables_names",
+ G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
+ return;
+ }
+
+ flush_table("filter");
+ flush_table("mangle");
+ flush_table("nat");
+}
+
+int __connman_firewall_init(void)
+{
+ DBG("");
+
+ __connman_iptables_init();
+ flush_all_tables();
+
+ return 0;
+}
+
+void __connman_firewall_cleanup(void)
+{
+ DBG("");
+
+ g_slist_free_full(managed_tables, cleanup_managed_table);
+ __connman_iptables_cleanup();
+}
diff --git a/src/firewall.c b/src/firewall.c
deleted file mode 100644
index c58efc1..0000000
--- a/src/firewall.c
+++ /dev/null
@@ -1,622 +0,0 @@
-/*
- *
- * Connection Manager
- *
- * Copyright (C) 2013,2015 BMW Car IT GmbH.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <errno.h>
-
-#include <xtables.h>
-#include <linux/netfilter_ipv4/ip_tables.h>
-
-#include "connman.h"
-
-#define CHAIN_PREFIX "connman-"
-
-static const char *builtin_chains[] = {
- [NF_IP_PRE_ROUTING] = "PREROUTING",
- [NF_IP_LOCAL_IN] = "INPUT",
- [NF_IP_FORWARD] = "FORWARD",
- [NF_IP_LOCAL_OUT] = "OUTPUT",
- [NF_IP_POST_ROUTING] = "POSTROUTING",
-};
-
-struct connman_managed_table {
- char *name;
- unsigned int chains[NF_INET_NUMHOOKS];
-};
-
-struct fw_rule {
- bool enabled;
- char *table;
- char *chain;
- char *rule_spec;
-};
-
-struct firewall_context {
- GList *rules;
-};
-
-static GSList *managed_tables;
-static struct firewall_context *connmark_ctx;
-static unsigned int connmark_ref;
-
-static int chain_to_index(const char *chain_name)
-{
- if (!g_strcmp0(builtin_chains[NF_IP_PRE_ROUTING], chain_name))
- return NF_IP_PRE_ROUTING;
- if (!g_strcmp0(builtin_chains[NF_IP_LOCAL_IN], chain_name))
- return NF_IP_LOCAL_IN;
- if (!g_strcmp0(builtin_chains[NF_IP_FORWARD], chain_name))
- return NF_IP_FORWARD;
- if (!g_strcmp0(builtin_chains[NF_IP_LOCAL_OUT], chain_name))
- return NF_IP_LOCAL_OUT;
- if (!g_strcmp0(builtin_chains[NF_IP_POST_ROUTING], chain_name))
- return NF_IP_POST_ROUTING;
-
- return -1;
-}
-
-static int managed_chain_to_index(const char *chain_name)
-{
- if (!g_str_has_prefix(chain_name, CHAIN_PREFIX))
- return -1;
-
- return chain_to_index(chain_name + strlen(CHAIN_PREFIX));
-}
-
-static int insert_managed_chain(const char *table_name, int id)
-{
- char *rule, *managed_chain;
- int err;
-
- managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX,
- builtin_chains[id]);
-
- err = __connman_iptables_new_chain(table_name, managed_chain);
- if (err < 0)
- goto out;
-
- rule = g_strdup_printf("-j %s", managed_chain);
- err = __connman_iptables_insert(table_name, builtin_chains[id], rule);
- g_free(rule);
- if (err < 0) {
- __connman_iptables_delete_chain(table_name, managed_chain);
- goto out;
- }
-
-out:
- g_free(managed_chain);
-
- return err;
-}
-
-static int delete_managed_chain(const char *table_name, int id)
-{
- char *rule, *managed_chain;
- int err;
-
- managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX,
- builtin_chains[id]);
-
- rule = g_strdup_printf("-j %s", managed_chain);
- err = __connman_iptables_delete(table_name, builtin_chains[id], rule);
- g_free(rule);
-
- if (err < 0)
- goto out;
-
- err = __connman_iptables_delete_chain(table_name, managed_chain);
-
-out:
- g_free(managed_chain);
-
- return err;
-}
-
-static int insert_managed_rule(const char *table_name,
- const char *chain_name,
- const char *rule_spec)
-{
- struct connman_managed_table *mtable = NULL;
- GSList *list;
- char *chain;
- int id, err;
-
- id = chain_to_index(chain_name);
- if (id < 0) {
- /* This chain is not managed */
- chain = g_strdup(chain_name);
- goto out;
- }
-
- for (list = managed_tables; list; list = list->next) {
- mtable = list->data;
-
- if (g_strcmp0(mtable->name, table_name) == 0)
- break;
-
- mtable = NULL;
- }
-
- if (!mtable) {
- mtable = g_new0(struct connman_managed_table, 1);
- mtable->name = g_strdup(table_name);
-
- managed_tables = g_slist_prepend(managed_tables, mtable);
- }
-
- if (mtable->chains[id] == 0) {
- DBG("table %s add managed chain for %s",
- table_name, chain_name);
-
- err = insert_managed_chain(table_name, id);
- if (err < 0)
- return err;
- }
-
- mtable->chains[id]++;
- chain = g_strdup_printf("%s%s", CHAIN_PREFIX, chain_name);
-
-out:
- err = __connman_iptables_append(table_name, chain, rule_spec);
-
- g_free(chain);
-
- return err;
- }
-
-static int delete_managed_rule(const char *table_name,
- const char *chain_name,
- const char *rule_spec)
- {
- struct connman_managed_table *mtable = NULL;
- GSList *list;
- int id, err;
- char *managed_chain;
-
- id = chain_to_index(chain_name);
- if (id < 0) {
- /* This chain is not managed */
- return __connman_iptables_delete(table_name, chain_name,
- rule_spec);
- }
-
- managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, chain_name);
-
- err = __connman_iptables_delete(table_name, managed_chain,
- rule_spec);
-
- for (list = managed_tables; list; list = list->next) {
- mtable = list->data;
-
- if (g_strcmp0(mtable->name, table_name) == 0)
- break;
-
- mtable = NULL;
- }
-
- if (!mtable) {
- err = -ENOENT;
- goto out;
- }
-
- mtable->chains[id]--;
- if (mtable->chains[id] > 0)
- goto out;
-
- DBG("table %s remove managed chain for %s",
- table_name, chain_name);
-
- err = delete_managed_chain(table_name, id);
-
- out:
- g_free(managed_chain);
-
- return err;
-}
-
-static void cleanup_managed_table(gpointer user_data)
-{
- struct connman_managed_table *table = user_data;
-
- g_free(table->name);
- g_free(table);
-}
-
-static void cleanup_fw_rule(gpointer user_data)
-{
- struct fw_rule *rule = user_data;
-
- g_free(rule->rule_spec);
- g_free(rule->chain);
- g_free(rule->table);
- g_free(rule);
-}
-
-struct firewall_context *__connman_firewall_create(void)
-{
- struct firewall_context *ctx;
-
- ctx = g_new0(struct firewall_context, 1);
-
- return ctx;
-}
-
-void __connman_firewall_destroy(struct firewall_context *ctx)
-{
- g_list_free_full(ctx->rules, cleanup_fw_rule);
- g_free(ctx);
-}
-
-static int enable_rule(struct fw_rule *rule)
-{
- int err;
-
- if (rule->enabled)
- return -EALREADY;
-
- DBG("%s %s %s", rule->table, rule->chain, rule->rule_spec);
-
- err = insert_managed_rule(rule->table, rule->chain, rule->rule_spec);
- if (err < 0)
- return err;
-
- err = __connman_iptables_commit(rule->table);
- if (err < 0)
- return err;
-
- rule->enabled = true;
-
- return 0;
-}
-
-static int disable_rule(struct fw_rule *rule)
-{
- int err;
-
- if (!rule->enabled)
- return -EALREADY;
-
- err = delete_managed_rule(rule->table, rule->chain, rule->rule_spec);
- if (err < 0) {
- connman_error("Cannot remove previously installed "
- "iptables rules: %s", strerror(-err));
- return err;
- }
-
- err = __connman_iptables_commit(rule->table);
- if (err < 0) {
- connman_error("Cannot remove previously installed "
- "iptables rules: %s", strerror(-err));
- return err;
- }
-
- rule->enabled = false;
-
- return 0;
-}
-
-static void firewall_add_rule(struct firewall_context *ctx,
- const char *table,
- const char *chain,
- const char *rule_fmt, ...)
-{
- va_list args;
- char *rule_spec;
- struct fw_rule *rule;
-
- va_start(args, rule_fmt);
-
- rule_spec = g_strdup_vprintf(rule_fmt, args);
-
- va_end(args);
-
- rule = g_new0(struct fw_rule, 1);
-
- rule->enabled = false;
- rule->table = g_strdup(table);
- rule->chain = g_strdup(chain);
- rule->rule_spec = rule_spec;
-
- ctx->rules = g_list_append(ctx->rules, rule);
-}
-
-static void firewall_remove_rules(struct firewall_context *ctx)
-{
- struct fw_rule *rule;
- GList *list;
-
- for (list = g_list_last(ctx->rules); list;
- list = g_list_previous(list)) {
- rule = list->data;
-
- ctx->rules = g_list_remove(ctx->rules, rule);
- cleanup_fw_rule(rule);
- }
-}
-
-static int firewall_enable_rules(struct firewall_context *ctx)
-{
- struct fw_rule *rule;
- GList *list;
- int err = -ENOENT;
-
- for (list = g_list_first(ctx->rules); list; list = g_list_next(list)) {
- rule = list->data;
-
- err = enable_rule(rule);
- if (err < 0)
- break;
- }
-
- return err;
-}
-
-static int firewall_disable_rules(struct firewall_context *ctx)
-{
- struct fw_rule *rule;
- GList *list;
- int e;
- int err = -ENOENT;
-
- for (list = g_list_last(ctx->rules); list;
- list = g_list_previous(list)) {
- rule = list->data;
-
- e = disable_rule(rule);
-
- /* Report last error back */
- if (e == 0 && err == -ENOENT)
- err = 0;
- else if (e < 0)
- err = e;
- }
-
- return err;
-}
-
-int __connman_firewall_enable_nat(struct firewall_context *ctx,
- char *address, unsigned char prefixlen,
- char *interface)
-{
- char *cmd;
- int err;
-
- cmd = g_strdup_printf("-s %s/%d -o %s -j MASQUERADE",
- address, prefixlen, interface);
-
- firewall_add_rule(ctx, "nat", "POSTROUTING", cmd);
- g_free(cmd);
- err = firewall_enable_rules(ctx);
- if (err)
- firewall_remove_rules(ctx);
- return err;
-}
-
-int __connman_firewall_disable_nat(struct firewall_context *ctx)
-{
- int err;
-
- err = firewall_disable_rules(ctx);
- if (err < 0) {
- DBG("could not disable NAT rule");
- return err;
- }
-
- firewall_remove_rules(ctx);
- return 0;
-}
-
-int __connman_firewall_enable_snat(struct firewall_context *ctx,
- int index, const char *ifname,
- const char *addr)
-{
- int err;
-
- firewall_add_rule(ctx, "nat", "POSTROUTING",
- "-o %s -j SNAT --to-source %s",
- ifname, addr);
-
- err = firewall_enable_rules(ctx);
- if (err)
- firewall_remove_rules(ctx);
- return err;
-}
-
-int __connman_firewall_disable_snat(struct firewall_context *ctx)
-{
- int err;
-
- err = firewall_disable_rules(ctx);
- if (err < 0) {
- DBG("could not disable SNAT rule");
- return err;
- }
-
- firewall_remove_rules(ctx);
- return 0;
-}
-
-static int firewall_enable_connmark(void)
-{
- int err;
-
- if (connmark_ref > 0) {
- connmark_ref++;
- return 0;
- }
-
- connmark_ctx = __connman_firewall_create();
-
- firewall_add_rule(connmark_ctx, "mangle", "INPUT",
- "-j CONNMARK --restore-mark");
- firewall_add_rule(connmark_ctx, "mangle", "POSTROUTING",
- "-j CONNMARK --save-mark");
- err = firewall_enable_rules(connmark_ctx);
- if (err) {
- __connman_firewall_destroy(connmark_ctx);
- connmark_ctx = NULL;
- return err;
- }
- connmark_ref++;
- return 0;
-}
-
-static void firewall_disable_connmark(void)
-{
- connmark_ref--;
- if (connmark_ref > 0)
- return;
-
- firewall_disable_rules(connmark_ctx);
- __connman_firewall_destroy(connmark_ctx);
- connmark_ctx = NULL;
-}
-
-int __connman_firewall_enable_marking(struct firewall_context *ctx,
- enum connman_session_id_type id_type,
- char *id, uint32_t mark)
-{
- int err;
-
- err = firewall_enable_connmark();
- if (err)
- return err;
-
- switch (id_type) {
- case CONNMAN_SESSION_ID_TYPE_UID:
- firewall_add_rule(ctx, "mangle", "OUTPUT",
- "-m owner --uid-owner %s -j MARK --set-mark %d",
- id, mark);
- break;
- case CONNMAN_SESSION_ID_TYPE_GID:
- firewall_add_rule(ctx, "mangle", "OUTPUT",
- "-m owner --gid-owner %s -j MARK --set-mark %d",
- id, mark);
- break;
- case CONNMAN_SESSION_ID_TYPE_LSM:
- default:
- return -EINVAL;
- }
-
- return firewall_enable_rules(ctx);
-}
-
-int __connman_firewall_disable_marking(struct firewall_context *ctx)
-{
- firewall_disable_connmark();
- return firewall_disable_rules(ctx);
-}
-
-static void iterate_chains_cb(const char *chain_name, void *user_data)
-{
- GSList **chains = user_data;
- int id;
-
- id = managed_chain_to_index(chain_name);
- if (id < 0)
- return;
-
- *chains = g_slist_prepend(*chains, GINT_TO_POINTER(id));
-}
-
-static void flush_table(const char *table_name)
-{
- GSList *chains = NULL, *list;
- char *rule, *managed_chain;
- int id, err;
-
- __connman_iptables_iterate_chains(table_name, iterate_chains_cb,
- &chains);
-
- for (list = chains; list; list = list->next) {
- id = GPOINTER_TO_INT(list->data);
-
- managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX,
- builtin_chains[id]);
-
- rule = g_strdup_printf("-j %s", managed_chain);
- err = __connman_iptables_delete(table_name,
- builtin_chains[id], rule);
- if (err < 0) {
- connman_warn("Failed to delete jump rule '%s': %s",
- rule, strerror(-err));
- }
- g_free(rule);
-
- err = __connman_iptables_flush_chain(table_name, managed_chain);
- if (err < 0) {
- connman_warn("Failed to flush chain '%s': %s",
- managed_chain, strerror(-err));
- }
- err = __connman_iptables_delete_chain(table_name,
managed_chain);
- if (err < 0) {
- connman_warn("Failed to delete chain '%s': %s",
- managed_chain, strerror(-err));
- }
-
- g_free(managed_chain);
- }
-
- err = __connman_iptables_commit(table_name);
- if (err < 0) {
- connman_warn("Failed to flush table '%s': %s",
- table_name, strerror(-err));
- }
-
- g_slist_free(chains);
-}
-
-static void flush_all_tables(void)
-{
- /* Flush the tables ConnMan might have modified
- * But do so if only ConnMan has done something with
- * iptables */
-
- if (!g_file_test("/proc/net/ip_tables_names",
- G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
- return;
- }
-
- flush_table("filter");
- flush_table("mangle");
- flush_table("nat");
-}
-
-int __connman_firewall_init(void)
-{
- DBG("");
-
- __connman_iptables_init();
- flush_all_tables();
-
- return 0;
-}
-
-void __connman_firewall_cleanup(void)
-{
- DBG("");
-
- g_slist_free_full(managed_tables, cleanup_managed_table);
- __connman_iptables_cleanup();
-}
--
2.7.4
------------------------------
Subject: Digest Footer
_______________________________________________
connman mailing list
[email protected]
https://lists.01.org/mailman/listinfo/connman
------------------------------
End of connman Digest, Vol 10, Issue 21
***************************************