+static void
+write_zone_uci(struct uci_context *ctx, struct fw3_zone *z,
+ struct uci_package *dest)
+{
+ struct fw3_device *dev;
+ struct fw3_address *sub;
+ enum fw3_family fam = FW3_FAMILY_ANY;
+
+ char *p, buf[34];
+
+ struct uci_ptr ptr = { .p = dest };
+
+ if (!z->enabled)
+ return;
+
+ if (fw3_no_table(z->flags[0]) && !fw3_no_table(z->flags[1]))
+ fam = FW3_FAMILY_V6;
+ else if (!fw3_no_table(z->flags[0]) && fw3_no_table(z->flags[1]))
+ fam = FW3_FAMILY_V4;
+ else if (fw3_no_table(z->flags[0]) && fw3_no_table(z->flags[1]))
+ return;
+
+ uci_add_section(ctx, dest, "zone", &ptr.s);
+
+ ptr.o = NULL;
+ ptr.option = "name";
+ ptr.value = z->name;
+ uci_set(ctx, &ptr);
+
+ ptr.o = NULL;
+ ptr.option = "input";
+ ptr.value = fw3_flag_names[z->policy_input];
+ uci_set(ctx, &ptr);
+
+ ptr.o = NULL;
+ ptr.option = "output";
+ ptr.value = fw3_flag_names[z->policy_output];
+ uci_set(ctx, &ptr);
+
+ ptr.o = NULL;
+ ptr.option = "forward";
+ ptr.value = fw3_flag_names[z->policy_forward];
+ uci_set(ctx, &ptr);
+
+ ptr.o = NULL;
+ ptr.option = "masq";
+ ptr.value = z->masq ? "1" : "0";
+ uci_set(ctx, &ptr);
+
+ ptr.o = NULL;
+ ptr.option = "conntrack";
+ ptr.value = z->conntrack ? "1" : "0";
+ uci_set(ctx, &ptr);
+
+ ptr.o = NULL;
+ ptr.option = "mtu_fix";
+ ptr.value = z->mtu_fix ? "1" : "0";
+ uci_set(ctx, &ptr);
+
+ ptr.o = NULL;
+ ptr.option = "custom_chains";
+ ptr.value = z->custom_chains ? "1" : "0";
+ uci_set(ctx, &ptr);
+
+ if (fam != FW3_FAMILY_ANY)
+ {
+ ptr.o = NULL;
+ ptr.option = "family";
+ ptr.value = fw3_flag_names[fam];
+ uci_set(ctx, &ptr);
+ }
+
+ ptr.o = NULL;
+ ptr.option = "device";
+
+ fw3_foreach(dev, &z->devices)
+ {
+ if (!dev)
+ continue;
+
+ p = buf;
+
+ if (dev->invert)
+ p += sprintf(p, "!");
+
+ if (*dev->network)
+ p += sprintf(p, "%s@%s", dev->name, dev->network);
+ else
+ p += sprintf(p, "%s", dev->name);
+
+ ptr.value = buf;
+ uci_add_list(ctx, &ptr);
+ }
+
+ ptr.o = NULL;
+ ptr.option = "subnet";
+
+ fw3_foreach(sub, &z->subnets)
+ {
+ if (!sub)
+ continue;
+
+ ptr.value = fw3_address_to_string(sub, true);
+ uci_add_list(ctx, &ptr);
+ }
+
+ sprintf(buf, "0x%x", z->flags[0]);
+ ptr.o = NULL;
+ ptr.option = "__flags_v4";
+ ptr.value = buf;
+ uci_set(ctx, &ptr);
+
+ sprintf(buf, "0x%x", z->flags[1]);
+ ptr.o = NULL;
+ ptr.option = "__flags_v6";
+ ptr.value = buf;
+ uci_set(ctx, &ptr);
+}
+
+static void
+write_ipset_uci(struct uci_context *ctx, struct fw3_ipset *s,
+ struct uci_package *dest)
+{
+ struct fw3_ipset_datatype *type;
+
+ char buf[sizeof("65535-65535\0")];
+
+ struct uci_ptr ptr = { .p = dest };
+
+ if (!s->enabled || s->external)
+ return;
+
+ uci_add_section(ctx, dest, "ipset", &ptr.s);
+
+ ptr.o = NULL;
+ ptr.option = "name";
+ ptr.value = s->name;
+ uci_set(ctx, &ptr);
+
+ ptr.o = NULL;
+ ptr.option = "storage";
+ ptr.value = fw3_ipset_method_names[s->method];
+ uci_set(ctx, &ptr);
+
+ list_for_each_entry(type, &s->datatypes, list)
+ {
+ sprintf(buf, "%s_%s", type->dest ? "dst" : "src",
+ fw3_ipset_type_names[type->type]);
+
+ ptr.o = NULL;
+ ptr.option = "match";
+ ptr.value = buf;
+ uci_add_list(ctx, &ptr);
+ }
+
+ if (s->iprange.set)
+ {
+ ptr.o = NULL;
+ ptr.option = "iprange";
+ ptr.value = fw3_address_to_string(&s->iprange, false);
+ uci_set(ctx, &ptr);
+ }
+
+ if (s->portrange.set)
+ {
+ sprintf(buf, "%u-%u", s->portrange.port_min, s->portrange.port_max);
+ ptr.o = NULL;
+ ptr.option = "portrange";
+ ptr.value = buf;
+ uci_set(ctx, &ptr);
+ }
+}
+
+void
+fw3_write_statefile(void *state)