c7f29c617b6cf9512e74399c0d1c0da0f5c816f2
[project/firewall3.git] / snats.c
1 /*
2  * firewall3 - 3rd OpenWrt UCI firewall implementation
3  *
4  *   Copyright (C) 2014 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 "snats.h"
20
21
22 const struct fw3_option fw3_snat_opts[] = {
23         FW3_OPT("enabled",             bool,      snat,     enabled),
24
25         FW3_OPT("name",                string,    snat,     name),
26         FW3_OPT("family",              family,    snat,     family),
27
28         FW3_OPT("src",                 device,    snat,     src),
29
30         FW3_OPT("ipset",               setmatch,  snat,     ipset),
31
32         FW3_LIST("proto",              protocol,  snat,     proto),
33
34         FW3_OPT("src_ip",              network,   snat,     ip_src),
35         FW3_OPT("src_port",            port,      snat,     port_src),
36
37         FW3_OPT("snat_ip",             network,   snat,     ip_snat),
38         FW3_OPT("snat_port",           port,      snat,     port_snat),
39
40         FW3_OPT("dest_ip",             network,   snat,     ip_dest),
41         FW3_OPT("dest_port",           port,      snat,     port_dest),
42
43         FW3_OPT("extra",               string,    snat,     extra),
44
45         FW3_OPT("limit",               limit,     snat,     limit),
46         FW3_OPT("limit_burst",         int,       snat,     limit.burst),
47
48         FW3_OPT("connlimit_ports",     bool,      snat,     connlimit_ports),
49
50         FW3_OPT("utc_time",            bool,      snat,     time.utc),
51         FW3_OPT("start_date",          date,      snat,     time.datestart),
52         FW3_OPT("stop_date",           date,      snat,     time.datestop),
53         FW3_OPT("start_time",          time,      snat,     time.timestart),
54         FW3_OPT("stop_time",           time,      snat,     time.timestop),
55         FW3_OPT("weekdays",            weekdays,  snat,     time.weekdays),
56         FW3_OPT("monthdays",           monthdays, snat,     time.monthdays),
57
58         FW3_OPT("mark",                mark,      snat,     mark),
59
60         FW3_OPT("target",              target,    snat,     target),
61
62         { }
63 };
64
65
66 static bool
67 check_families(struct uci_element *e, struct fw3_snat *r)
68 {
69         if (r->family == FW3_FAMILY_ANY)
70                 return true;
71
72         if (r->_src && r->_src->family && r->_src->family != r->family)
73         {
74                 warn_elem(e, "refers to source zone with different family");
75                 return false;
76         }
77
78         if (r->ipset.ptr && r->ipset.ptr->family &&
79             r->ipset.ptr->family != r->family)
80         {
81                 warn_elem(e, "refers to ipset with different family");
82                 return false;
83         }
84
85         if (r->ip_src.family && r->ip_src.family != r->family)
86         {
87                 warn_elem(e, "uses source ip with different family");
88                 return false;
89         }
90
91         if (r->ip_dest.family && r->ip_dest.family != r->family)
92         {
93                 warn_elem(e, "uses destination ip with different family");
94                 return false;
95         }
96
97         if (r->ip_snat.family && r->ip_snat.family != r->family)
98         {
99                 warn_elem(e, "uses snat ip with different family");
100                 return false;
101         }
102
103         return true;
104 }
105
106 void
107 fw3_load_snats(struct fw3_state *state, struct uci_package *p)
108 {
109         struct uci_section *s;
110         struct uci_element *e;
111         struct fw3_snat *snat;
112
113         INIT_LIST_HEAD(&state->snats);
114
115         uci_foreach_element(&p->sections, e)
116         {
117                 s = uci_to_section(e);
118
119                 if (strcmp(s->type, "nat"))
120                         continue;
121
122                 snat = malloc(sizeof(*snat));
123
124                 if (!snat)
125                         continue;
126
127                 memset(snat, 0, sizeof(*snat));
128
129                 INIT_LIST_HEAD(&snat->proto);
130
131                 snat->enabled = true;
132
133                 if (!fw3_parse_options(snat, fw3_snat_opts, s))
134                 {
135                         warn_elem(e, "skipped due to invalid options");
136                         fw3_free_snat(snat);
137                         continue;
138                 }
139
140                 if (!snat->enabled)
141                 {
142                         fw3_free_snat(snat);
143                         continue;
144                 }
145
146                 if (snat->src.invert)
147                 {
148                         warn_elem(e, "must not have an inverted source");
149                         fw3_free_snat(snat);
150                         continue;
151                 }
152                 else if (snat->src.set && !snat->src.any &&
153                          !(snat->_src = fw3_lookup_zone(state, snat->src.name)))
154                 {
155                         warn_elem(e, "refers to not existing zone '%s'", snat->src.name);
156                         fw3_free_snat(snat);
157                         continue;
158                 }
159                 else if (snat->ipset.set && state->disable_ipsets)
160                 {
161                         warn_elem(e, "skipped due to disabled ipset support");
162                         fw3_free_snat(snat);
163                         continue;
164                 }
165                 else if (snat->ipset.set &&
166                          !(snat->ipset.ptr = fw3_lookup_ipset(state, snat->ipset.name)))
167                 {
168                         warn_elem(e, "refers to unknown ipset '%s'", snat->ipset.name);
169                         fw3_free_snat(snat);
170                         continue;
171                 }
172
173                 if (!check_families(e, snat))
174                 {
175                         fw3_free_snat(snat);
176                         continue;
177                 }
178
179                 if (snat->target == FW3_FLAG_UNSPEC)
180                 {
181                         warn_elem(e, "has no target specified, defaulting to MASQUERADE");
182                         snat->target = FW3_FLAG_MASQUERADE;
183                 }
184                 else if (snat->target != FW3_FLAG_ACCEPT && snat->target != FW3_FLAG_SNAT &&
185                                 snat->target != FW3_FLAG_MASQUERADE)
186                 {
187                         warn_elem(e, "has invalid target specified, defaulting to MASQUERADE");
188                         snat->target = FW3_FLAG_MASQUERADE;
189                 }
190
191                 if (snat->target == FW3_FLAG_SNAT &&
192                     !snat->ip_snat.set && !snat->port_snat.set)
193                 {
194                         warn_elem(e, "needs either 'snat_ip' or 'snat_port' for SNAT");
195                         fw3_free_snat(snat);
196                         continue;
197                 }
198                 else if (snat->target != FW3_FLAG_SNAT && snat->ip_snat.set)
199                 {
200                         warn_elem(e, "must not use 'snat_ip' for non-SNAT");
201                         fw3_free_snat(snat);
202                         continue;
203                 }
204                 else if (snat->target != FW3_FLAG_SNAT && snat->port_snat.set)
205                 {
206                         warn_elem(e, "must not use 'snat_port' for non-SNAT");
207                         fw3_free_snat(snat);
208                         continue;
209                 }
210
211                 if (list_empty(&snat->proto))
212                 {
213                         warn_elem(e, "does not specify a protocol, assuming all");
214                         fw3_parse_protocol(&snat->proto, "all", true);
215                 }
216
217                 if (snat->_src)
218                 {
219                         set(snat->_src->flags, FW3_FAMILY_V4, FW3_FLAG_SNAT);
220                         snat->_src->conntrack = true;
221                 }
222
223                 list_add_tail(&snat->list, &state->snats);
224         }
225 }
226
227 static void
228 append_chain(struct fw3_ipt_rule *r, struct fw3_snat *snat)
229 {
230         if (snat->_src)
231                 fw3_ipt_rule_append(r, "zone_%s_postrouting", snat->src.name);
232         else
233                 fw3_ipt_rule_append(r, "delegate_postrouting");
234 }
235
236 static void
237 set_target(struct fw3_ipt_rule *r, struct fw3_snat *snat,
238            struct fw3_protocol *proto)
239 {
240         char buf[sizeof("255.255.255.255:65535-65535\0")];
241
242         if (snat->target == FW3_FLAG_SNAT)
243         {
244                 buf[0] = '\0';
245
246                 if (snat->ip_snat.set)
247                 {
248                         inet_ntop(AF_INET, &snat->ip_snat.address.v4, buf, sizeof(buf));
249                 }
250
251                 if (snat->port_snat.set && proto && !proto->any &&
252                     (proto->protocol == 6 || proto->protocol == 17 || proto->protocol == 1))
253                 {
254                         if (snat->port_snat.port_min == snat->port_snat.port_max)
255                                 sprintf(buf + strlen(buf), ":%u", snat->port_snat.port_min);
256                         else
257                                 sprintf(buf + strlen(buf), ":%u-%u",
258                                                 snat->port_snat.port_min, snat->port_snat.port_max);
259
260                         if (snat->connlimit_ports) {
261                                 char portcntbuf[6];
262                                 snprintf(portcntbuf, sizeof(portcntbuf), "%u",
263                                                 1 + snat->port_snat.port_max - snat->port_snat.port_min);
264
265                                 fw3_ipt_rule_addarg(r, false, "-m", "connlimit");
266                                 fw3_ipt_rule_addarg(r, false, "--connlimit-daddr", NULL);
267                                 fw3_ipt_rule_addarg(r, false, "--connlimit-upto", portcntbuf);
268                         }
269                 }
270
271                 fw3_ipt_rule_target(r, "SNAT");
272                 fw3_ipt_rule_addarg(r, false, "--to-source", buf);
273         }
274         else if (snat->target == FW3_FLAG_ACCEPT)
275         {
276                 fw3_ipt_rule_target(r, "ACCEPT");
277         }
278         else
279         {
280                 fw3_ipt_rule_target(r, "MASQUERADE");
281         }
282 }
283
284 static void
285 set_comment(struct fw3_ipt_rule *r, const char *name, int num)
286 {
287         if (name)
288                 fw3_ipt_rule_comment(r, name);
289         else
290                 fw3_ipt_rule_comment(r, "@nat[%u]", num);
291 }
292
293 static void
294 print_snat(struct fw3_ipt_handle *h, struct fw3_state *state,
295            struct fw3_snat *snat, int num, struct fw3_protocol *proto)
296 {
297         struct fw3_ipt_rule *r;
298         struct fw3_address *src, *dst;
299         struct fw3_port *spt, *dpt;
300
301         switch (h->table)
302         {
303         case FW3_TABLE_NAT:
304                 src = &snat->ip_src;
305                 dst = &snat->ip_dest;
306                 spt = &snat->port_src;
307                 dpt = &snat->port_dest;
308
309                 r = fw3_ipt_rule_create(h, proto, NULL, NULL, src, dst);
310                 fw3_ipt_rule_sport_dport(r, spt, dpt);
311                 fw3_ipt_rule_ipset(r, &snat->ipset);
312                 fw3_ipt_rule_limit(r, &snat->limit);
313                 fw3_ipt_rule_time(r, &snat->time);
314                 fw3_ipt_rule_mark(r, &snat->mark);
315                 set_target(r, snat, proto);
316                 fw3_ipt_rule_extra(r, snat->extra);
317                 set_comment(r, snat->name, num);
318                 append_chain(r, snat);
319                 break;
320
321         default:
322                 break;
323         }
324 }
325
326 static void
327 expand_snat(struct fw3_ipt_handle *handle, struct fw3_state *state,
328                 struct fw3_snat *snat, int num)
329 {
330         struct fw3_protocol *proto;
331
332         if (snat->name)
333                 info("   * NAT '%s'", snat->name);
334         else
335                 info("   * NAT #%u", num);
336
337         if (!fw3_is_family(snat->_src, handle->family))
338         {
339                 info("     ! Skipping due to different family of zone");
340                 return;
341         }
342
343         if (!fw3_is_family(&snat->ip_src, handle->family) ||
344             !fw3_is_family(&snat->ip_dest, handle->family) ||
345                 !fw3_is_family(&snat->ip_snat, handle->family))
346         {
347                 if (!snat->ip_src.resolved ||
348                     !snat->ip_dest.resolved ||
349                     !snat->ip_snat.resolved)
350                         info("     ! Skipping due to different family of ip address");
351
352                 return;
353         }
354
355         if (snat->ipset.ptr)
356         {
357                 if (!fw3_is_family(snat->ipset.ptr, handle->family))
358                 {
359                         info("     ! Skipping due to different family in ipset");
360                         return;
361                 }
362
363                 if (!fw3_check_ipset(snat->ipset.ptr))
364                 {
365                         info("     ! Skipping due to missing ipset '%s'",
366                              snat->ipset.ptr->external ?
367                                         snat->ipset.ptr->external : snat->ipset.ptr->name);
368                         return;
369                 }
370
371                 set(snat->ipset.ptr->flags, handle->family, handle->family);
372         }
373
374         fw3_foreach(proto, &snat->proto)
375                 print_snat(handle, state, snat, num, proto);
376 }
377
378 void
379 fw3_print_snats(struct fw3_ipt_handle *handle, struct fw3_state *state)
380 {
381         int num = 0;
382         struct fw3_snat *snat;
383
384         if (handle->family == FW3_FAMILY_V6)
385                 return;
386
387         if (handle->table != FW3_TABLE_NAT)
388                 return;
389
390         list_for_each_entry(snat, &state->snats, list)
391                 expand_snat(handle, state, snat, num++);
392 }