Merge "Fix SIGSEV on freeing server domains list" into tizen
[platform/upstream/connman.git] / src / firewall-nftables.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2016  BMW Car IT GmbH.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 /*
23  * This file is based on the libnftnl examples:
24  *   https://git.netfilter.org/libnftnl/tree/examples
25  * by Pablo Neira Ayuso. and inspiration from systemd nft implementation
26  *   https://github.com/zonque/systemd/blob/rfc-nftnl/src/shared/firewall-util.c
27  * by Daniel Mack.
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include <stdlib.h>
35 #include <time.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <stdint.h>
39 #include <alloca.h>
40 #include <netinet/in.h>
41 #include <netinet/ip.h>
42
43 #include <sys/types.h>
44 #include <pwd.h>
45
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49
50 #include <linux/netfilter.h>
51 #include <linux/netfilter/nfnetlink.h>
52 #include <linux/netfilter/nf_nat.h>
53 #include <linux/netfilter/nf_tables.h>
54
55 #include <libmnl/libmnl.h>
56 #include <libnftnl/table.h>
57 #include <libnftnl/chain.h>
58 #include <libnftnl/rule.h>
59 #include <libnftnl/expr.h>
60
61 #include <glib.h>
62
63 #include "connman.h"
64
65 #define CONNMAN_TABLE "connman"
66 #define CONNMAN_CHAIN_NAT_PRE "nat-prerouting"
67 #define CONNMAN_CHAIN_NAT_POST "nat-postrouting"
68 #define CONNMAN_CHAIN_ROUTE_OUTPUT "route-output"
69
70 static bool debug_enabled = false;
71
72 struct firewall_handle {
73         uint64_t handle;
74         const char *chain;
75 };
76
77 struct firewall_context {
78         struct firewall_handle rule;
79 };
80
81 struct nftables_info {
82         struct firewall_handle ct;
83 };
84
85 static struct nftables_info *nft_info;
86
87 enum callback_return_type {
88         CALLBACK_RETURN_NONE = 0,
89         CALLBACK_RETURN_HANDLE,
90         CALLBACK_RETURN_BYTE_COUNTER,
91         _CALLBACK_RETURN_MAX,
92 };
93
94 struct callback_data {
95         enum callback_return_type type;
96         uint64_t value;
97         bool success;
98 };
99
100 static void debug_netlink_dump_rule(struct nftnl_rule *nlr)
101 {
102         char buf[4096];
103
104         if (!debug_enabled)
105                 return;
106
107         nftnl_rule_snprintf(buf, sizeof(buf), nlr, 0, 0);
108         fprintf(stdout, "%s\n", buf);
109 }
110
111 static void debug_mnl_dump_rule(const void *req, size_t req_size)
112 {
113         if (!debug_enabled)
114                 return;
115
116         mnl_nlmsg_fprintf(stdout, req, req_size, 0);
117         printf("\n");
118 }
119
120 static int rule_expr_cb(struct nftnl_expr *expr, void *data) {
121
122         struct callback_data *cb = data;
123         const char *name;
124
125         name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
126
127         if (strcmp(name, "counter")) {
128                 cb->value = nftnl_expr_get_u64(expr, NFTNL_EXPR_CTR_BYTES);
129                 cb->success = true;
130         }
131
132         return 0;
133 }
134
135 static int rule_cb(const struct nlmsghdr *nlh, int event,
136                 struct callback_data *cb)
137 {
138         struct nftnl_rule *rule;
139
140         rule = nftnl_rule_alloc();
141         if (!rule)
142                 return MNL_CB_OK;
143
144         if (nftnl_rule_nlmsg_parse(nlh, rule) < 0)
145                 goto out;
146
147         switch (cb->type) {
148         case CALLBACK_RETURN_HANDLE:
149                 cb->value = nftnl_rule_get_u64(rule, NFTNL_RULE_HANDLE);
150                 cb->success = true;
151                 break;
152
153         case CALLBACK_RETURN_BYTE_COUNTER:
154                 nftnl_expr_foreach(rule, rule_expr_cb, cb);
155                 break;
156
157         default:
158                 DBG("unhandled callback type %d\n", cb->type);
159                 break;
160         }
161
162 out:
163         nftnl_rule_free(rule);
164         return MNL_CB_STOP;
165 }
166
167 static int chain_cb(const struct nlmsghdr *nlh, int event,
168                         struct callback_data *cb)
169 {
170         struct nftnl_chain *chain;
171
172         chain = nftnl_chain_alloc();
173         if (!chain)
174                 return MNL_CB_OK;
175
176         if (nftnl_chain_nlmsg_parse(nlh, chain) < 0)
177                 goto out;
178
179         switch (cb->type) {
180         case CALLBACK_RETURN_HANDLE:
181                 cb->value = nftnl_chain_get_u64(chain, NFTNL_CHAIN_HANDLE);
182                 cb->success = true;
183                 break;
184
185         default:
186                 DBG("unhandled callback type %d\n", cb->type);
187                 break;
188         }
189
190 out:
191         nftnl_chain_free(chain);
192         return MNL_CB_OK;
193 }
194
195 static const char *event_to_str(enum nf_tables_msg_types type)
196 {
197         const char *table[] = {
198                 "NFT_MSG_NEWTABLE",
199                 "NFT_MSG_GETTABLE",
200                 "NFT_MSG_DELTABLE",
201                 "NFT_MSG_NEWCHAIN",
202                 "NFT_MSG_GETCHAIN",
203                 "NFT_MSG_DELCHAIN",
204                 "NFT_MSG_NEWRULE",
205                 "NFT_MSG_GETRULE",
206                 "NFT_MSG_DELRULE",
207                 "NFT_MSG_NEWSET",
208                 "NFT_MSG_GETSET",
209                 "NFT_MSG_DELSET",
210                 "NFT_MSG_NEWSETELEM",
211                 "NFT_MSG_GETSETELEM",
212                 "NFT_MSG_DELSETELEM",
213                 "NFT_MSG_NEWGEN",
214                 "NFT_MSG_GETGEN",
215                 "NFT_MSG_TRACE"
216         };
217
218         if (type < sizeof(table)/sizeof(table[0]))
219                 return table[type];
220
221         return "unknown";
222 }
223
224 static int events_cb(const struct nlmsghdr *nlh, void *data)
225 {
226         int event = NFNL_MSG_TYPE(nlh->nlmsg_type);
227         struct callback_data *cb = data;
228         int err = MNL_CB_OK;
229
230         if (!cb || cb->type == CALLBACK_RETURN_NONE)
231                 return err;
232
233         DBG("handle event %s", event_to_str(event));
234
235         switch(event) {
236         case NFT_MSG_NEWCHAIN:
237                 err = chain_cb(nlh, event, cb);
238                 break;
239
240         case NFT_MSG_NEWRULE:
241                 err = rule_cb(nlh, event, cb);
242                 break;
243         default:
244                 DBG("unhandled event type %s", event_to_str(event));
245                 break;
246         }
247
248         return err;
249 }
250
251 static int socket_open_and_bind(struct mnl_socket **n)
252 {
253
254         struct mnl_socket *nl = NULL;
255         int err;
256
257         nl = mnl_socket_open(NETLINK_NETFILTER);
258         if (!nl)
259                 return -errno;
260
261         err = mnl_socket_bind(nl, 1 << (NFNLGRP_NFTABLES-1),
262                                 MNL_SOCKET_AUTOPID);
263         if (err < 0) {
264                 err = errno;
265                 mnl_socket_close(nl);
266                 return -err;
267         }
268
269         *n = nl;
270         return 0;
271 }
272
273 static int send_and_dispatch(struct mnl_socket *nl, const void *req,
274                 size_t req_size, enum callback_return_type callback_type,
275                 uint64_t *callback_value)
276 {
277         struct callback_data cb = {};
278         uint32_t portid;
279         int err;
280
281         debug_mnl_dump_rule(req, req_size);
282
283         err = mnl_socket_sendto(nl, req, req_size);
284         if (err < 0)
285                 return -errno;
286
287         portid = mnl_socket_get_portid(nl);
288         cb.type = callback_type;
289
290         for (;;) {
291                 char buf[MNL_SOCKET_BUFFER_SIZE];
292
293                 err = mnl_socket_recvfrom(nl, buf, sizeof(buf));
294                 if (err <= 0)
295                         break;
296
297                 err = mnl_cb_run(buf, err, 0, portid, events_cb, &cb);
298                 if (err <= 0)
299                         break;
300         }
301
302         if (err < 0)
303                 return -errno;
304
305         if (callback_type == CALLBACK_RETURN_NONE)
306                 return 0;
307
308         if (cb.success) {
309                 if (callback_value)
310                         *callback_value = cb.value;
311
312                 return 0;
313         }
314
315         return -ENOENT;
316 }
317
318 static void put_batch_headers(char *buf, uint16_t type, uint32_t seq)
319 {
320
321         struct nlmsghdr *nlh;
322         struct nfgenmsg *nfg;
323
324         nlh = mnl_nlmsg_put_header(buf);
325         nlh->nlmsg_type = type;
326         nlh->nlmsg_flags = NLM_F_REQUEST;
327         nlh->nlmsg_seq = seq;
328
329         nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
330         nfg->nfgen_family = AF_INET;
331         nfg->version = NFNETLINK_V0;
332         nfg->res_id = NFNL_SUBSYS_NFTABLES;
333 }
334
335 static int add_payload(struct nftnl_rule *rule, uint32_t base,
336                         uint32_t dreg, uint32_t offset, uint32_t len)
337 {
338         struct nftnl_expr *expr;
339
340         expr = nftnl_expr_alloc("payload");
341         if (!expr)
342                 return -ENOMEM;
343
344         nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_BASE, base);
345         nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_DREG, dreg);
346         nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
347         nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_LEN, len);
348
349         nftnl_rule_add_expr(rule, expr);
350
351         return 0;
352 }
353
354 static int add_bitwise(struct nftnl_rule *rule, int reg, const void *mask,
355                         size_t len)
356 {
357         struct nftnl_expr *expr;
358         uint8_t *xor;
359
360         expr = nftnl_expr_alloc("bitwise");
361         if (!expr)
362                 return -ENOMEM;
363
364         xor = alloca(len);
365         memset(xor, 0, len);
366
367         nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, reg);
368         nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, reg);
369         nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, len);
370         nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_MASK, mask, len);
371         nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_XOR, xor, len);
372
373         nftnl_rule_add_expr(rule, expr);
374
375         return 0;
376 }
377
378 static int add_cmp(struct nftnl_rule *rule, uint32_t sreg, uint32_t op,
379                         const void *data, uint32_t data_len)
380 {
381         struct nftnl_expr *expr;
382
383         expr = nftnl_expr_alloc("cmp");
384         if (!expr)
385                 return -ENOMEM;
386
387         nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_SREG, sreg);
388         nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_OP, op);
389         nftnl_expr_set(expr, NFTNL_EXPR_CMP_DATA, data, data_len);
390
391         nftnl_rule_add_expr(rule, expr);
392
393         return 0;
394 }
395
396 static int table_cmd(struct mnl_socket *nl, struct nftnl_table *t,
397                 uint16_t cmd, uint16_t family, uint16_t type)
398 {
399         char buf[MNL_SOCKET_BUFFER_SIZE];
400         struct mnl_nlmsg_batch *batch;
401         struct nlmsghdr *nlh;
402         uint32_t seq = 0;
403         int err;
404
405         bzero(buf, sizeof(buf));
406
407         batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
408         nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
409         mnl_nlmsg_batch_next(batch);
410
411         nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
412                                 cmd, family, type, seq++);
413         nftnl_table_nlmsg_build_payload(nlh, t);
414         nftnl_table_free(t);
415         mnl_nlmsg_batch_next(batch);
416
417         nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
418         mnl_nlmsg_batch_next(batch);
419
420         /* The current table commands do not support any callback returns. */
421         err = send_and_dispatch(nl, mnl_nlmsg_batch_head(batch),
422                                 mnl_nlmsg_batch_size(batch), 0, NULL);
423
424         mnl_nlmsg_batch_stop(batch);
425         return err;
426 }
427
428 static int chain_cmd(struct mnl_socket *nl, struct nftnl_chain *chain,
429                 uint16_t cmd, int family, uint16_t type,
430                 enum callback_return_type cb_type, uint64_t *cb_val)
431 {
432         char buf[MNL_SOCKET_BUFFER_SIZE];
433         struct mnl_nlmsg_batch *batch;
434         struct nlmsghdr *nlh;
435         uint32_t seq = 0;
436         int err;
437
438         bzero(buf, sizeof(buf));
439
440         batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
441         nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
442         mnl_nlmsg_batch_next(batch);
443
444         nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
445                                         cmd, family, type, seq++);
446         nftnl_chain_nlmsg_build_payload(nlh, chain);
447         nftnl_chain_free(chain);
448         mnl_nlmsg_batch_next(batch);
449
450         nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
451         mnl_nlmsg_batch_next(batch);
452
453         err = send_and_dispatch(nl, mnl_nlmsg_batch_head(batch),
454                         mnl_nlmsg_batch_size(batch), cb_type, cb_val);
455
456         mnl_nlmsg_batch_stop(batch);
457         return err;
458 }
459
460 static int rule_cmd(struct mnl_socket *nl, struct nftnl_rule *rule,
461                         uint16_t cmd, uint16_t family, uint16_t type,
462                         enum callback_return_type callback_type,
463                         uint64_t *callback_value)
464 {
465
466         char buf[MNL_SOCKET_BUFFER_SIZE];
467         struct mnl_nlmsg_batch *batch;
468         struct nlmsghdr *nlh;
469         uint32_t seq = 0;
470         int err;
471
472         bzero(buf, sizeof(buf));
473
474         debug_netlink_dump_rule(rule);
475
476         batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
477         put_batch_headers(mnl_nlmsg_batch_current(batch),
478                                 NFNL_MSG_BATCH_BEGIN, seq++);
479         mnl_nlmsg_batch_next(batch);
480
481         nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
482                                         cmd, family, type, seq++);
483         nftnl_rule_nlmsg_build_payload(nlh, rule);
484         mnl_nlmsg_batch_next(batch);
485
486         put_batch_headers(mnl_nlmsg_batch_current(batch),
487                                 NFNL_MSG_BATCH_END, seq++);
488         mnl_nlmsg_batch_next(batch);
489
490         err = send_and_dispatch(nl, mnl_nlmsg_batch_head(batch),
491                                 mnl_nlmsg_batch_size(batch),
492                                 callback_type, callback_value);
493         mnl_nlmsg_batch_stop(batch);
494
495         return err;
496 }
497
498 static int rule_delete(struct firewall_handle *handle)
499 {
500         struct nftnl_rule *rule;
501         struct mnl_socket *nl;
502         int err;
503
504         DBG("");
505
506         rule = nftnl_rule_alloc();
507         if (!rule)
508                 return -ENOMEM;
509
510         nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
511         nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, handle->chain);
512         nftnl_rule_set_u64(rule, NFTNL_RULE_HANDLE, handle->handle);
513
514         err = socket_open_and_bind(&nl);
515         if (err < 0) {
516                 nftnl_rule_free(rule);
517                 return err;
518         }
519
520         err = rule_cmd(nl, rule, NFT_MSG_DELRULE, NFPROTO_IPV4,
521                         NLM_F_ACK, 0, NULL);
522         nftnl_rule_free(rule);
523         mnl_socket_close(nl);
524
525         return err;
526 }
527
528 struct firewall_context *__connman_firewall_create(void)
529 {
530         struct firewall_context *ctx;
531
532         DBG("");
533
534         ctx = g_new0(struct firewall_context, 1);
535
536         return ctx;
537 }
538
539 void __connman_firewall_destroy(struct firewall_context *ctx)
540 {
541         DBG("");
542
543         g_free(ctx);
544 }
545
546 static int build_rule_nat(const char *address, unsigned char prefixlen,
547                                 const char *interface, struct nftnl_rule **res)
548 {
549         struct nftnl_rule *rule;
550         struct in_addr ipv4_addr, ipv4_mask;
551         struct nftnl_expr *expr;
552         int err;
553
554         /*
555          * # nft --debug netlink add rule connman nat-postrouting       \
556          *      oifname eth0 ip saddr 10.10.0.0/24 masquerade
557          *
558          *      ip connman nat-postrouting
559          *        [ meta load oifname => reg 1 ]
560          *        [ cmp eq reg 1 0x30687465 0x00000000 0x00000000 0x00000000 ]
561          *        [ payload load 4b @ network header + 12 => reg 1 ]
562          *        [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ]
563          *        [ cmp eq reg 1 0x00000a0a ]
564          *        [ masq ]
565          */
566
567         rule = nftnl_rule_alloc();
568         if (!rule)
569                 return -ENOMEM;
570
571         nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
572         nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST);
573
574         /* family ipv4 */
575         nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4);
576
577         /* oifname */
578         expr = nftnl_expr_alloc("meta");
579         if (!expr)
580                 goto err;
581         nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_OIFNAME);
582         nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, NFT_REG_1);
583         nftnl_rule_add_expr(rule, expr);
584         err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, interface,
585                         strlen(interface) + 1);
586         if (err < 0)
587                 goto err;
588
589         /* source */
590         ipv4_mask.s_addr = htonl((0xffffffff << (32 - prefixlen)) & 0xffffffff);
591         ipv4_addr.s_addr = inet_addr(address);
592         ipv4_addr.s_addr &= ipv4_mask.s_addr;
593
594         err = add_payload(rule, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
595                         offsetof(struct iphdr, saddr), sizeof(struct in_addr));
596         if (err < 0)
597                 goto err;
598         err = add_bitwise(rule, NFT_REG_1, &ipv4_mask.s_addr,
599                                 sizeof(struct in_addr));
600         if (err < 0)
601                 goto err;
602         err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &ipv4_addr.s_addr,
603                         sizeof(struct in_addr));
604         if (err < 0)
605                 goto err;
606
607         /* masquerade */
608         expr = nftnl_expr_alloc("masq");
609         if (!expr)
610                 goto err;
611         nftnl_rule_add_expr(rule, expr);
612
613         *res = rule;
614         return 0;
615
616 err:
617         nftnl_rule_free(rule);
618         return -ENOMEM;
619 }
620
621 int __connman_firewall_enable_nat(struct firewall_context *ctx,
622                                         char *address, unsigned char prefixlen,
623                                         char *interface)
624 {
625         struct mnl_socket *nl;
626         struct nftnl_rule *rule;
627         int err;
628
629         DBG("address %s/%d interface %s", address, (int)prefixlen, interface);
630
631         err = socket_open_and_bind(&nl);
632         if (err < 0)
633                 return err;
634
635         err = build_rule_nat(address, prefixlen, interface, &rule);
636         if (err)
637                 goto out;
638
639         ctx->rule.chain = CONNMAN_CHAIN_NAT_POST;
640         err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4,
641                         NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK,
642                         CALLBACK_RETURN_HANDLE, &ctx->rule.handle);
643         nftnl_rule_free(rule);
644 out:
645         mnl_socket_close(nl);
646         return err;
647 }
648
649 int __connman_firewall_disable_nat(struct firewall_context *ctx)
650 {
651         return rule_delete(&ctx->rule);
652 }
653
654 static int build_rule_snat(int index, const char *address,
655                                 struct nftnl_rule **res)
656 {
657         struct nftnl_rule *rule;
658         struct nftnl_expr *expr;
659         uint32_t snat;
660         int err;
661
662         /*
663          * # nft --debug netlink add rule connman nat-postrouting \
664          *      oif eth0 snat 1.2.3.4
665          *      ip connman nat-postrouting
666          *        [ meta load oif => reg 1 ]
667          *        [ cmp eq reg 1 0x0000000b ]
668          *        [ immediate reg 1 0x04030201 ]
669          *        [ nat snat ip addr_min reg 1 addr_max reg 0 ]
670          */
671
672         rule = nftnl_rule_alloc();
673         if (!rule)
674                 return -ENOMEM;
675
676         nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
677         nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST);
678
679         /* OIF */
680         expr = nftnl_expr_alloc("meta");
681         if (!expr)
682                 goto err;
683         nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_OIF);
684         nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, NFT_REG_1);
685         nftnl_rule_add_expr(rule, expr);
686         err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &index, sizeof(index));
687         if (err < 0)
688                 goto err;
689
690         /* snat */
691         expr = nftnl_expr_alloc("immediate");
692         if (!expr)
693                 goto err;
694         snat = inet_addr(address);
695         nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_1);
696         nftnl_expr_set(expr, NFTNL_EXPR_IMM_DATA, &snat, sizeof(snat));
697         nftnl_rule_add_expr(rule, expr);
698
699         expr = nftnl_expr_alloc("nat");
700         if (!expr)
701                 goto err;
702         nftnl_expr_set_u32(expr, NFTNL_EXPR_NAT_TYPE, NFT_NAT_SNAT);
703         nftnl_expr_set_u32(expr, NFTNL_EXPR_NAT_FAMILY, NFPROTO_IPV4);
704         nftnl_expr_set_u32(expr, NFTNL_EXPR_NAT_REG_ADDR_MIN, NFT_REG_1);
705         nftnl_rule_add_expr(rule, expr);
706
707         *res = rule;
708         return 0;
709
710 err:
711         nftnl_rule_free(rule);
712         return -ENOMEM;
713 }
714
715 int __connman_firewall_enable_snat(struct firewall_context *ctx,
716                                 int index, const char *ifname, const char *addr)
717 {
718         struct nftnl_rule *rule;
719         struct mnl_socket *nl;
720         int err;
721
722         DBG("");
723
724         err = socket_open_and_bind(&nl);
725         if (err < 0)
726                 return err;
727
728         err = build_rule_snat(index, addr, &rule);
729         if (err)
730                 goto out;
731
732         ctx->rule.chain = CONNMAN_CHAIN_NAT_POST;
733         err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4,
734                         NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK,
735                         CALLBACK_RETURN_HANDLE, &ctx->rule.handle);
736         nftnl_rule_free(rule);
737 out:
738         mnl_socket_close(nl);
739         return err;
740 }
741
742 int __connman_firewall_disable_snat(struct firewall_context *ctx)
743 {
744         DBG("");
745
746         return rule_delete(&ctx->rule);
747 }
748
749 static int build_rule_marking(uid_t uid, uint32_t mark, struct nftnl_rule **res)
750 {
751         struct nftnl_rule *rule;
752         struct nftnl_expr *expr;
753         int err;
754
755         /*
756          * http://wiki.nftables.org/wiki-nftables/index.php/Setting_packet_metainformation
757          * http://wiki.nftables.org/wiki-nftables/index.php/Matching_packet_metainformation
758          *
759          * # nft --debug netlink add rule connman route-output  \
760          *      meta skuid wagi mark set 1234
761          *
762          *      ip connman route-output
763          *        [ meta load skuid => reg 1 ]
764          *        [ cmp eq reg 1 0x000003e8 ]
765          *        [ immediate reg 1 0x000004d2 ]
766          *        [ meta set mark with reg 1 ]
767          */
768
769         rule = nftnl_rule_alloc();
770         if (!rule)
771                 return -ENOMEM;
772
773         nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
774         nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_ROUTE_OUTPUT);
775
776         expr = nftnl_expr_alloc("meta");
777         if (!expr)
778                 goto err;
779         nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_SKUID);
780         nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, NFT_REG_1);
781         nftnl_rule_add_expr(rule, expr);
782         err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &uid, sizeof(uid));
783         if (err < 0)
784                 goto err;
785
786         expr = nftnl_expr_alloc("immediate");
787         if (!expr)
788                 goto err;
789         nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_1);
790         nftnl_expr_set(expr, NFTNL_EXPR_IMM_DATA, &mark, sizeof(mark));
791         nftnl_rule_add_expr(rule, expr);
792
793         expr = nftnl_expr_alloc("meta");
794         if (!expr)
795                 goto err;
796         nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_MARK);
797         nftnl_expr_set_u32(expr, NFTNL_EXPR_META_SREG, NFT_REG_1);
798         nftnl_rule_add_expr(rule, expr);
799
800         *res = rule;
801         return 0;
802
803 err:
804         return -ENOMEM;
805 }
806
807 static int build_rule_src_ip(const char *src_ip, uint32_t mark, struct nftnl_rule **res)
808 {
809         struct nftnl_rule *rule;
810         struct nftnl_expr *expr;
811         int err;
812         in_addr_t s_addr;
813
814         /*
815          * # nft --debug netlink add rule connman route-output \
816          *      ip saddr 192.168.10.31 mark set 1234
817          *
818          *      ip connman route-output
819          *        [ payload load 4b @ network header + 12 => reg 1 ]
820          *        [ cmp eq reg 1 0x1f0aa8c0 ]
821          *        [ immediate reg 1 0x000004d2 ]
822          *        [ meta set mark with reg 1 ]
823          */
824
825         rule = nftnl_rule_alloc();
826         if (!rule)
827                 return -ENOMEM;
828
829         nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
830         nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_ROUTE_OUTPUT);
831
832         /* family ipv4 */
833         nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4);
834
835         /* source IP */
836         err = add_payload(rule, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
837                         offsetof(struct iphdr, saddr), sizeof(struct in_addr));
838         if (err < 0)
839                 goto err;
840
841         s_addr = inet_addr(src_ip);
842         err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &s_addr, sizeof(s_addr));
843         if (err < 0)
844                 goto err;
845
846         expr = nftnl_expr_alloc("immediate");
847         if (!expr)
848                 goto err;
849         nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_1);
850         nftnl_expr_set(expr, NFTNL_EXPR_IMM_DATA, &mark, sizeof(mark));
851         nftnl_rule_add_expr(rule, expr);
852
853         expr = nftnl_expr_alloc("meta");
854         if (!expr)
855                 goto err;
856         nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_MARK);
857         nftnl_expr_set_u32(expr, NFTNL_EXPR_META_SREG, NFT_REG_1);
858         nftnl_rule_add_expr(rule, expr);
859
860         *res = rule;
861         return 0;
862
863 err:
864         return -ENOMEM;
865 }
866
867 int __connman_firewall_enable_marking(struct firewall_context *ctx,
868                                         enum connman_session_id_type id_type,
869                                         char *id, const char *src_ip,
870                                         uint32_t mark)
871 {
872         struct nftnl_rule *rule;
873         struct mnl_socket *nl;
874         struct passwd *pw;
875         uid_t uid;
876         int err;
877
878         DBG("");
879
880         if (id_type == CONNMAN_SESSION_ID_TYPE_UID) {
881                 pw = getpwnam(id);
882                 if (!pw)
883                         return -EINVAL;
884                 uid = pw->pw_uid;
885         }
886         else if (!src_ip)
887                 return -ENOTSUP;
888
889         err = socket_open_and_bind(&nl);
890         if (err < 0)
891                 return err;
892
893         if (id_type == CONNMAN_SESSION_ID_TYPE_UID) {
894                 err = build_rule_marking(uid, mark, &rule);
895                 if (err < 0)
896                         goto out;
897
898                 ctx->rule.chain = CONNMAN_CHAIN_ROUTE_OUTPUT;
899                 err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4,
900                                 NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK,
901                                 CALLBACK_RETURN_HANDLE, &ctx->rule.handle);
902
903                 nftnl_rule_free(rule);
904         }
905
906         if (src_ip) {
907                 err = build_rule_src_ip(src_ip, mark, &rule);
908                 if (err < 0)
909                         goto out;
910
911                 ctx->rule.chain = CONNMAN_CHAIN_ROUTE_OUTPUT;
912                 err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4,
913                                 NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK,
914                                 CALLBACK_RETURN_HANDLE, &ctx->rule.handle);
915
916                 nftnl_rule_free(rule);
917         }
918 out:
919         mnl_socket_close(nl);
920         return err;
921 }
922
923 int __connman_firewall_disable_marking(struct firewall_context *ctx)
924 {
925         int err;
926
927         DBG("");
928
929         err = rule_delete(&ctx->rule);
930         return err;
931 }
932
933 static struct nftnl_table *build_table(const char *name, uint16_t family)
934 {
935         struct nftnl_table *table;
936
937         table = nftnl_table_alloc();
938         if (!table)
939                 return NULL;
940
941         nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, family);
942         nftnl_table_set_str(table, NFTNL_TABLE_NAME, name);
943
944         return table;
945 }
946
947
948 static struct nftnl_chain *build_chain(const char *name, const char *table,
949                                 const char *type, int hooknum, int prio)
950 {
951         struct nftnl_chain *chain;
952
953         chain = nftnl_chain_alloc();
954         if (!chain)
955                 return NULL;
956
957         nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, table);
958         nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, name);
959
960         if (type)
961                 nftnl_chain_set_str(chain, NFTNL_CHAIN_TYPE, type);
962
963         if (hooknum >= 0)
964                 nftnl_chain_set_u32(chain, NFTNL_CHAIN_HOOKNUM, hooknum);
965
966         if (prio >= 0)
967                 nftnl_chain_set_u32(chain, NFTNL_CHAIN_PRIO, prio);
968
969         return chain;
970 }
971
972 static int create_table_and_chains(struct nftables_info *nft_info)
973 {
974         struct mnl_socket *nl;
975         struct nftnl_table *table;
976         struct nftnl_chain *chain;
977         int err;
978
979
980         DBG("");
981
982         err = socket_open_and_bind(&nl);
983         if (err < 0)
984                 return err;
985
986         /*
987          * Add table
988          * http://wiki.nftables.org/wiki-nftables/index.php/Configuring_tables
989          */
990
991         /*
992          * # nft add table connman
993          */
994         table = build_table(CONNMAN_TABLE, NFPROTO_IPV4);
995         if (!table) {
996                 err = -ENOMEM;
997                 goto out;
998         }
999
1000         err = table_cmd(nl, table, NFT_MSG_NEWTABLE, NFPROTO_IPV4,
1001                         NLM_F_CREATE|NLM_F_ACK);
1002         if (err < 0)
1003                 goto out;
1004
1005         /*
1006          * Add basic chains
1007          * http://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains
1008          */
1009
1010         /*
1011          * # nft add chain connman nat-prerouting               \
1012          *      { type nat hook prerouting priority 0 ; }
1013          */
1014         chain = build_chain(CONNMAN_CHAIN_NAT_PRE, CONNMAN_TABLE,
1015                                 "nat", NF_INET_PRE_ROUTING, 0);
1016         if (!chain) {
1017                 err = -ENOMEM;
1018                 goto out;
1019         }
1020
1021         err = chain_cmd(nl, chain, NFT_MSG_NEWCHAIN,
1022                         NFPROTO_IPV4, NLM_F_CREATE | NLM_F_ACK,
1023                         CALLBACK_RETURN_NONE, NULL);
1024         if (err < 0)
1025                 goto out;
1026
1027         /*
1028          * # nft add chain connman nat-postrouting              \
1029          *      { type nat hook postrouting priority 0 ; }
1030          */
1031         chain = build_chain(CONNMAN_CHAIN_NAT_POST, CONNMAN_TABLE,
1032                                 "nat", NF_INET_POST_ROUTING, 0);
1033         if (!chain) {
1034                 err = -ENOMEM;
1035                 goto out;
1036         }
1037
1038         err = chain_cmd(nl, chain, NFT_MSG_NEWCHAIN,
1039                         NFPROTO_IPV4, NLM_F_CREATE | NLM_F_ACK,
1040                         CALLBACK_RETURN_NONE, NULL);
1041         if (err < 0)
1042                 goto out;
1043
1044         /*
1045          * # nft add chain connman route-output         \
1046          *      { type route hook output priority 0 ; }
1047          */
1048         chain = build_chain(CONNMAN_CHAIN_ROUTE_OUTPUT, CONNMAN_TABLE,
1049                                 "route", NF_INET_LOCAL_OUT, 0);
1050         if (!chain) {
1051                 err = -ENOMEM;
1052                 goto out;
1053         }
1054
1055         err = chain_cmd(nl, chain, NFT_MSG_NEWCHAIN,
1056                         NFPROTO_IPV4, NLM_F_CREATE | NLM_F_ACK,
1057                         CALLBACK_RETURN_NONE, NULL);
1058         if (err < 0)
1059                 goto out;
1060
1061 out:
1062         if (err)
1063                 connman_warn("Failed to create basic chains: %s",
1064                                 strerror(-err));
1065         mnl_socket_close(nl);
1066         return err;
1067 }
1068
1069 static int cleanup_table_and_chains(void)
1070 {
1071         struct nftnl_table *table;
1072         struct mnl_socket *nl;
1073         int err;
1074
1075         DBG("");
1076
1077         err = socket_open_and_bind(&nl);
1078         if (err < 0)
1079                 return -ENOMEM;
1080
1081         /*
1082          * Cleanup everythying in one go. There is little point in
1083          * step-by-step removal of rules and chains if you can get it
1084          * as simple as this.
1085          */
1086         /*
1087          * # nft delete table connman
1088          */
1089         table = build_table(CONNMAN_TABLE, NFPROTO_IPV4);
1090         err = table_cmd(nl, table, NFT_MSG_DELTABLE, NFPROTO_IPV4, NLM_F_ACK);
1091
1092         mnl_socket_close(nl);
1093         return err;
1094 }
1095
1096 int __connman_firewall_init(void)
1097 {
1098         int err;
1099
1100         DBG("");
1101
1102         if (getenv("CONNMAN_NFTABLES_DEBUG"))
1103                 debug_enabled = true;
1104
1105         /*
1106          * EAFNOSUPPORT is return whenever the nf_tables_ipv4 hasn't been
1107          * loaded yet. ENOENT is return in case the table is missing.
1108          */
1109         err = cleanup_table_and_chains();
1110         if (err < 0 && (err != EAFNOSUPPORT && err != -ENOENT)) {
1111                 connman_warn("initializing nftable failed with '%s' %d. Check if kernel module nf_tables_ipv4 is missing\n",
1112                         strerror(-err), err);
1113                 return err;
1114         }
1115
1116         nft_info = g_new0(struct nftables_info, 1);
1117         err = create_table_and_chains(nft_info);
1118         if (err) {
1119                 g_free(nft_info);
1120                 nft_info = NULL;
1121         }
1122
1123         return err;
1124 }
1125
1126 void __connman_firewall_cleanup(void)
1127 {
1128         int err;
1129
1130         DBG("");
1131
1132         err = cleanup_table_and_chains();
1133         if (err < 0)
1134                 connman_warn("cleanup table and chains failed with '%s' %d\n",
1135                         strerror(-err), err);
1136
1137         g_free(nft_info);
1138         nft_info = NULL;
1139 }