- Detect an existing ipset without timeout support and migrate it in place
(temporary set + copy members + ipset swap + destroy) without unblocking.
- IPv6: "<set>6" (family inet6) + ip6tables match-set rule; ban/unban pick
the set by address family. IPv4 keeps working if IPv6 is unavailable.
- Remove unwrap()/panics: MartilloMaldito::new/with_config and every public
method return Result<_, MartilloError>; ignore multiport/range rules when
parsing secured ports; handle an empty DOCKER-USER chain.
- Ipset::save_all / save-ipsets for ipset-persistent (/etc/iptables/ipsets).
- structopt -> clap 4, thiserror 1 -> 2. CLI exits 1 on errors.
- Kernel tests skip without CAP_NET_ADMIN; run them with `unshare -rn`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New ipset.rs module wrapping ipset binary (hash:ip backend)
- ban_ip() and unban_ip() use ipset.add/del instead of per-IP iptables rules
- get_banned_ips() parses ipset list -json
- Optional timeout via BAN_DURATION_SECS (default 3600s = 1h)
- Auto-installs single iptables rule: -m set --match-set banned src -j DROP
- New CLI subcommands: ban-ip, unban-ip (with --timeout)
- 13 unit tests for ipset module + integration test script using netns
- Update Woodpecker CI to install ipset