Add support for netifd-generated rules
[project/firewall3.git] / ubus.c
1 /*
2  * firewall3 - 3rd OpenWrt UCI firewall implementation
3  *
4  *   Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include "ubus.h"
20
21 static struct blob_attr *interfaces = NULL;
22
23
24 static void dump_cb(struct ubus_request *req, int type, struct blob_attr *msg)
25 {
26         struct blob_attr *cur;
27         unsigned rem = blob_len(msg);
28         __blob_for_each_attr(cur, blob_data(msg), rem)
29                 if (!strcmp(blobmsg_name(cur), "interface"))
30                         interfaces = blob_memdup(cur);
31 }
32
33 bool
34 fw3_ubus_connect(void)
35 {
36         bool status = false;
37         uint32_t id;
38         struct ubus_context *ctx = ubus_connect(NULL);
39
40         if (!ctx)
41                 goto out;
42
43         if (ubus_lookup_id(ctx, "network.interface", &id))
44                 goto out;
45
46         if (ubus_invoke(ctx, id, "dump", NULL, dump_cb, NULL, 500))
47                 goto out;
48
49         status = true;
50
51 out:
52         if (ctx)
53                 ubus_free(ctx);
54         return status;
55 }
56
57 void
58 fw3_ubus_disconnect(void)
59 {
60         free(interfaces);
61         interfaces = NULL;
62 }
63
64 static struct fw3_address *
65 parse_subnet(enum fw3_family family, struct blob_attr *dict, int rem)
66 {
67         struct blob_attr *cur;
68         struct fw3_address *addr;
69
70         addr = malloc(sizeof(*addr));
71
72         if (!addr)
73                 return NULL;
74
75         memset(addr, 0, sizeof(*addr));
76
77         addr->set = true;
78         addr->family = family;
79
80         __blob_for_each_attr(cur, dict, rem)
81         {
82                 if (!strcmp(blobmsg_name(cur), "address"))
83                         inet_pton(family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
84                                   blobmsg_data(cur), &addr->address.v6);
85
86                 else if (!strcmp(blobmsg_name(cur), "mask"))
87                         addr->mask = be32_to_cpu(*(uint32_t *)blobmsg_data(cur));
88         }
89
90         return addr;
91 }
92
93 static void
94 parse_subnets(struct list_head *head, enum fw3_family family,
95               struct blob_attr *list, int rem)
96 {
97         struct blob_attr *cur;
98         struct fw3_address *addr;
99
100         __blob_for_each_attr(cur, list, rem)
101         {
102                 addr = parse_subnet(family, blobmsg_data(cur), blobmsg_data_len(cur));
103
104                 if (addr)
105                         list_add_tail(&addr->list, head);
106         }
107 }
108
109 static void *
110 invoke_common(const char *net, bool device)
111 {
112         struct fw3_device *dev = NULL;
113         struct list_head *addr = NULL;
114         struct blob_attr *c, *cur;
115         unsigned r, rem;
116         char *data;
117         bool matched;
118
119         if (!net || !interfaces)
120                 return NULL;
121
122         if (device)
123                 dev = malloc(sizeof(*dev));
124         else
125                 addr = malloc(sizeof(*addr));
126
127         if ((device && !dev) || (!device && !addr))
128                 goto fail;
129
130         if (device)
131                 memset(dev, 0, sizeof(*dev));
132         else
133                 INIT_LIST_HEAD(addr);
134
135         blobmsg_for_each_attr(c, interfaces, r) {
136                 matched = false;
137                 blobmsg_for_each_attr(cur, c, rem)
138                         if (!strcmp(blobmsg_name(cur), "interface"))
139                                 matched = !strcmp(blobmsg_get_string(cur), net);
140
141                 if (!matched)
142                         continue;
143
144                 blobmsg_for_each_attr(cur, c, rem) {
145                         data = blobmsg_data(cur);
146
147                         if (dev && !strcmp(blobmsg_name(cur), "device") && !dev->name[0])
148                                 snprintf(dev->name, sizeof(dev->name), "%s", data);
149                         else if (dev && !strcmp(blobmsg_name(cur), "l3_device"))
150                                 snprintf(dev->name, sizeof(dev->name), "%s", data);
151                         else if (!dev && !strcmp(blobmsg_name(cur), "ipv4-address"))
152                                 parse_subnets(addr, FW3_FAMILY_V4,
153                                               blobmsg_data(cur), blobmsg_data_len(cur));
154                         else if (!dev && (!strcmp(blobmsg_name(cur), "ipv6-address") ||
155                                           !strcmp(blobmsg_name(cur), "ipv6-prefix-assignment")))
156                                 parse_subnets(addr, FW3_FAMILY_V6,
157                                               blobmsg_data(cur), blobmsg_data_len(cur));
158                 }
159
160                 if (dev)
161                         dev->set = !!dev->name[0];
162
163                 break;
164         }
165
166         if (device && dev->set)
167                 return dev;
168         else if (!device && !list_empty(addr))
169                 return addr;
170
171 fail:
172         free(dev);
173         free(addr);
174
175         return NULL;
176 }
177
178 struct fw3_device *
179 fw3_ubus_device(const char *net)
180 {
181         return invoke_common(net, true);
182 }
183
184 struct list_head *
185 fw3_ubus_address(const char *net)
186 {
187         return invoke_common(net, false);
188 }
189
190 void
191 fw3_ubus_zone_devices(struct fw3_zone *zone)
192 {
193         struct blob_attr *c, *cur, *dcur;
194         unsigned r, rem, drem;
195         const char *name;
196         bool matches;
197
198         blobmsg_for_each_attr(c, interfaces, r) {
199                 name = NULL;
200                 matches = false;
201
202                 blobmsg_for_each_attr(cur, c, rem) {
203                         if (!strcmp(blobmsg_name(cur), "interface"))
204                                 name = blobmsg_get_string(cur);
205                         else if (!strcmp(blobmsg_name(cur), "data"))
206                                 blobmsg_for_each_attr(dcur, cur, drem)
207                                         if (!strcmp(blobmsg_name(dcur), "zone"))
208                                                 matches = !strcmp(blobmsg_get_string(dcur), zone->name);
209                 }
210
211                 if (name && matches)
212                         fw3_parse_device(&zone->networks, name, true);
213         }
214 }
215
216 void
217 fw3_ubus_rules(struct blob_buf *b)
218 {
219         blob_buf_init(b, 0);
220
221         struct blob_attr *c, *cur, *dcur, *rule, *ropt;
222         unsigned r, rem, drem, rrem, orem;
223
224         blobmsg_for_each_attr(c, interfaces, r) {
225                 const char *l3_device = NULL;
226                 struct blob_attr *data = NULL;
227
228                 blobmsg_for_each_attr(cur, c, rem) {
229                         if (!strcmp(blobmsg_name(cur), "l3_device"))
230                                 l3_device = blobmsg_get_string(cur);
231                         else if (!strcmp(blobmsg_name(cur), "data"))
232                                 data = cur;
233                 }
234
235                 if (!data || !l3_device)
236                         continue;
237
238                 blobmsg_for_each_attr(dcur, data, drem) {
239                         if (strcmp(blobmsg_name(dcur), "firewall"))
240                                 continue;
241
242                         blobmsg_for_each_attr(rule, dcur, rrem) {
243                                 void *k = blobmsg_open_table(b, "");
244
245                                 blobmsg_for_each_attr(ropt, rule, orem)
246                                         if (strcmp(blobmsg_name(ropt), "device"))
247                                                 blobmsg_add_blob(b, ropt);
248
249                                 blobmsg_add_string(b, "device", l3_device);
250                                 blobmsg_close_table(b, k);
251                         }
252                 }
253         }
254 }