fix for f12, gcc4.4
authorBarış Metin <Talip-Baris.Metin@sophia.inria.fr>
Fri, 15 Jan 2010 09:58:10 +0000 (09:58 +0000)
committerBarış Metin <Talip-Baris.Metin@sophia.inria.fr>
Fri, 15 Jan 2010 09:58:10 +0000 (09:58 +0000)
374 files changed:
.cvsignore [new file with mode: 0644]
COMMIT_NOTES [new file with mode: 0644]
COPYING [new file with mode: 0644]
INCOMPATIBILITIES [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile [new file with mode: 0644]
Rules.make [new file with mode: 0644]
extensions/.CLUSTERIP-test [new file with mode: 0755]
extensions/.IPV4OPTSSTRIP-test [new file with mode: 0755]
extensions/.NFLOG-test [new file with mode: 0755]
extensions/.NFLOG-test6 [new file with mode: 0755]
extensions/.REJECT-test6 [new file with mode: 0755]
extensions/.TARPIT-test [new file with mode: 0755]
extensions/.ah-test6 [new file with mode: 0755]
extensions/.condition-test [new file with mode: 0755]
extensions/.condition-test6 [new file with mode: 0755]
extensions/.connbytes-test [new file with mode: 0755]
extensions/.cvsignore [new file with mode: 0644]
extensions/.dccp-test [new file with mode: 0755]
extensions/.esp-test6 [new file with mode: 0755]
extensions/.frag-test6 [new file with mode: 0755]
extensions/.hashlimit-test6 [new file with mode: 0755]
extensions/.ipv4options-test [new file with mode: 0755]
extensions/.ipv6header-test6 [new file with mode: 0755]
extensions/.mh-test6 [new file with mode: 0755]
extensions/.opts-test6 [new file with mode: 0755]
extensions/.quota-test [new file with mode: 0755]
extensions/.recent-test [new file with mode: 0755]
extensions/.rt-test6 [new file with mode: 0755]
extensions/.sctp-test6 [new file with mode: 0755]
extensions/.set-test [new file with mode: 0755]
extensions/.statistic-test [new file with mode: 0755]
extensions/.string-test [new file with mode: 0755]
extensions/.u32-test [new file with mode: 0755]
extensions/Makefile [new file with mode: 0644]
extensions/libip6t_CONNMARK.c [new file with mode: 0644]
extensions/libip6t_CONNSECMARK.c [new file with mode: 0644]
extensions/libip6t_CONNSECMARK.man [new file with mode: 0644]
extensions/libip6t_HL.c [new file with mode: 0644]
extensions/libip6t_HL.man [new file with mode: 0644]
extensions/libip6t_LOG.c [new file with mode: 0644]
extensions/libip6t_LOG.man [new file with mode: 0644]
extensions/libip6t_MARK.c [new file with mode: 0644]
extensions/libip6t_MARK.man [new file with mode: 0644]
extensions/libip6t_NFLOG.c [new file with mode: 0644]
extensions/libip6t_NFQUEUE.c [new file with mode: 0644]
extensions/libip6t_NFQUEUE.man [new file with mode: 0644]
extensions/libip6t_REJECT.c [new file with mode: 0644]
extensions/libip6t_REJECT.man [new file with mode: 0644]
extensions/libip6t_SECMARK.c [new file with mode: 0644]
extensions/libip6t_SECMARK.man [new file with mode: 0644]
extensions/libip6t_TCPMSS.c [new file with mode: 0644]
extensions/libip6t_TCPMSS.man [new file with mode: 0644]
extensions/libip6t_ah.c [new file with mode: 0644]
extensions/libip6t_ah.man [new file with mode: 0644]
extensions/libip6t_condition.c [new file with mode: 0644]
extensions/libip6t_condition.man [new file with mode: 0644]
extensions/libip6t_connmark.c [new file with mode: 0644]
extensions/libip6t_dst.c [new file with mode: 0644]
extensions/libip6t_dst.man [new file with mode: 0644]
extensions/libip6t_esp.c [new file with mode: 0644]
extensions/libip6t_esp.man [new file with mode: 0644]
extensions/libip6t_eui64.c [new file with mode: 0644]
extensions/libip6t_eui64.man [new file with mode: 0644]
extensions/libip6t_frag.c [new file with mode: 0644]
extensions/libip6t_frag.man [new file with mode: 0644]
extensions/libip6t_hashlimit.c [new file with mode: 0644]
extensions/libip6t_hbh.c [new file with mode: 0644]
extensions/libip6t_hbh.man [new file with mode: 0644]
extensions/libip6t_hl.c [new file with mode: 0644]
extensions/libip6t_hl.man [new file with mode: 0644]
extensions/libip6t_icmp6.c [new file with mode: 0644]
extensions/libip6t_icmp6.man [new file with mode: 0644]
extensions/libip6t_ipv6header.c [new file with mode: 0644]
extensions/libip6t_ipv6header.man [new file with mode: 0644]
extensions/libip6t_length.c [new file with mode: 0644]
extensions/libip6t_length.man [new file with mode: 0644]
extensions/libip6t_limit.c [new file with mode: 0644]
extensions/libip6t_limit.man [new file with mode: 0644]
extensions/libip6t_mac.c [new file with mode: 0644]
extensions/libip6t_mac.man [new file with mode: 0644]
extensions/libip6t_mark.c [new file with mode: 0644]
extensions/libip6t_mark.man [new file with mode: 0644]
extensions/libip6t_mh.c [new file with mode: 0644]
extensions/libip6t_mh.man [new file with mode: 0644]
extensions/libip6t_multiport.c [new file with mode: 0644]
extensions/libip6t_multiport.man [new file with mode: 0644]
extensions/libip6t_owner.c [new file with mode: 0644]
extensions/libip6t_owner.man [new file with mode: 0644]
extensions/libip6t_physdev.c [new file with mode: 0644]
extensions/libip6t_physdev.man [new file with mode: 0644]
extensions/libip6t_policy.c [new file with mode: 0644]
extensions/libip6t_policy.man [new file with mode: 0644]
extensions/libip6t_rt.c [new file with mode: 0644]
extensions/libip6t_rt.man [new file with mode: 0644]
extensions/libip6t_sctp.c [new file with mode: 0644]
extensions/libip6t_standard.c [new file with mode: 0644]
extensions/libip6t_state.c [new file with mode: 0644]
extensions/libip6t_tcp.c [new file with mode: 0644]
extensions/libip6t_tcp.man [new file with mode: 0644]
extensions/libip6t_udp.c [new file with mode: 0644]
extensions/libip6t_udp.man [new file with mode: 0644]
extensions/libipt_CLASSIFY.c [new file with mode: 0644]
extensions/libipt_CLASSIFY.man [new file with mode: 0644]
extensions/libipt_CLUSTERIP.c [new file with mode: 0644]
extensions/libipt_CLUSTERIP.man [new file with mode: 0644]
extensions/libipt_CONNMARK.c [new file with mode: 0644]
extensions/libipt_CONNMARK.man [new file with mode: 0644]
extensions/libipt_CONNSECMARK.c [new file with mode: 0644]
extensions/libipt_CONNSECMARK.man [new file with mode: 0644]
extensions/libipt_DNAT.c [new file with mode: 0644]
extensions/libipt_DNAT.man [new file with mode: 0644]
extensions/libipt_DSCP.c [new file with mode: 0644]
extensions/libipt_DSCP.man [new file with mode: 0644]
extensions/libipt_ECN.c [new file with mode: 0644]
extensions/libipt_ECN.man [new file with mode: 0644]
extensions/libipt_IPV4OPTSSTRIP.c [new file with mode: 0644]
extensions/libipt_IPV4OPTSSTRIP.man [new file with mode: 0644]
extensions/libipt_LOG.c [new file with mode: 0644]
extensions/libipt_LOG.man [new file with mode: 0644]
extensions/libipt_MARK.c [new file with mode: 0644]
extensions/libipt_MARK.man [new file with mode: 0644]
extensions/libipt_MASQUERADE.c [new file with mode: 0644]
extensions/libipt_MASQUERADE.man [new file with mode: 0644]
extensions/libipt_MIRROR.c [new file with mode: 0644]
extensions/libipt_MIRROR.man [new file with mode: 0644]
extensions/libipt_NETMAP.c [new file with mode: 0644]
extensions/libipt_NETMAP.man [new file with mode: 0644]
extensions/libipt_NFLOG.c [new file with mode: 0644]
extensions/libipt_NFQUEUE.c [new file with mode: 0644]
extensions/libipt_NFQUEUE.man [new file with mode: 0644]
extensions/libipt_NOTRACK.c [new file with mode: 0644]
extensions/libipt_NOTRACK.man [new file with mode: 0644]
extensions/libipt_REDIRECT.c [new file with mode: 0644]
extensions/libipt_REDIRECT.man [new file with mode: 0644]
extensions/libipt_REJECT.c [new file with mode: 0644]
extensions/libipt_REJECT.man [new file with mode: 0644]
extensions/libipt_SAME.c [new file with mode: 0644]
extensions/libipt_SAME.man [new file with mode: 0644]
extensions/libipt_SECMARK.c [new file with mode: 0644]
extensions/libipt_SECMARK.man [new file with mode: 0644]
extensions/libipt_SET.c [new file with mode: 0644]
extensions/libipt_SET.man [new file with mode: 0644]
extensions/libipt_SNAT.c [new file with mode: 0644]
extensions/libipt_SNAT.man [new file with mode: 0644]
extensions/libipt_TARPIT.c [new file with mode: 0644]
extensions/libipt_TARPIT.man [new file with mode: 0644]
extensions/libipt_TCPMSS.c [new file with mode: 0644]
extensions/libipt_TCPMSS.man [new file with mode: 0644]
extensions/libipt_TOS.c [new file with mode: 0644]
extensions/libipt_TOS.man [new file with mode: 0644]
extensions/libipt_TTL.c [new file with mode: 0644]
extensions/libipt_TTL.man [new file with mode: 0644]
extensions/libipt_ULOG.c [new file with mode: 0644]
extensions/libipt_ULOG.man [new file with mode: 0644]
extensions/libipt_addrtype.c [new file with mode: 0644]
extensions/libipt_addrtype.man [new file with mode: 0644]
extensions/libipt_ah.c [new file with mode: 0644]
extensions/libipt_ah.man [new file with mode: 0644]
extensions/libipt_comment.c [new file with mode: 0644]
extensions/libipt_comment.man [new file with mode: 0644]
extensions/libipt_condition.c [new file with mode: 0644]
extensions/libipt_condition.man [new file with mode: 0644]
extensions/libipt_connbytes.c [new file with mode: 0644]
extensions/libipt_connbytes.man [new file with mode: 0644]
extensions/libipt_connmark.c [new file with mode: 0644]
extensions/libipt_connmark.man [new file with mode: 0644]
extensions/libipt_connrate.c [new file with mode: 0644]
extensions/libipt_connrate.man [new file with mode: 0644]
extensions/libipt_conntrack.c [new file with mode: 0644]
extensions/libipt_conntrack.man [new file with mode: 0644]
extensions/libipt_dccp.c [new file with mode: 0644]
extensions/libipt_dccp.man [new file with mode: 0644]
extensions/libipt_dscp.c [new file with mode: 0644]
extensions/libipt_dscp.man [new file with mode: 0644]
extensions/libipt_dscp_helper.c [new file with mode: 0644]
extensions/libipt_ecn.c [new file with mode: 0644]
extensions/libipt_ecn.man [new file with mode: 0644]
extensions/libipt_esp.c [new file with mode: 0644]
extensions/libipt_esp.man [new file with mode: 0644]
extensions/libipt_hashlimit.c [new file with mode: 0644]
extensions/libipt_hashlimit.man [new file with mode: 0644]
extensions/libipt_helper.c [new file with mode: 0644]
extensions/libipt_helper.man [new file with mode: 0644]
extensions/libipt_icmp.c [new file with mode: 0644]
extensions/libipt_icmp.man [new file with mode: 0644]
extensions/libipt_iprange.c [new file with mode: 0644]
extensions/libipt_iprange.man [new file with mode: 0644]
extensions/libipt_ipv4options.c [new file with mode: 0644]
extensions/libipt_ipv4options.man [new file with mode: 0644]
extensions/libipt_length.c [new file with mode: 0644]
extensions/libipt_length.man [new file with mode: 0644]
extensions/libipt_limit.c [new file with mode: 0644]
extensions/libipt_limit.man [new file with mode: 0644]
extensions/libipt_mac.c [new file with mode: 0644]
extensions/libipt_mac.man [new file with mode: 0644]
extensions/libipt_mark.c [new file with mode: 0644]
extensions/libipt_mark.man [new file with mode: 0644]
extensions/libipt_multiport.c [new file with mode: 0644]
extensions/libipt_multiport.man [new file with mode: 0644]
extensions/libipt_owner.c [new file with mode: 0644]
extensions/libipt_owner.man [new file with mode: 0644]
extensions/libipt_physdev.c [new file with mode: 0644]
extensions/libipt_physdev.man [new file with mode: 0644]
extensions/libipt_pkttype.c [new file with mode: 0644]
extensions/libipt_pkttype.man [new file with mode: 0644]
extensions/libipt_policy.c [new file with mode: 0644]
extensions/libipt_policy.man [new file with mode: 0644]
extensions/libipt_pool.c.orig [new file with mode: 0644]
extensions/libipt_quota.c [new file with mode: 0644]
extensions/libipt_quota.man [new file with mode: 0644]
extensions/libipt_realm.c [new file with mode: 0644]
extensions/libipt_realm.man [new file with mode: 0644]
extensions/libipt_recent.c [new file with mode: 0644]
extensions/libipt_recent.man [new file with mode: 0644]
extensions/libipt_sctp.c [new file with mode: 0644]
extensions/libipt_sctp.man [new file with mode: 0644]
extensions/libipt_set.c [new file with mode: 0644]
extensions/libipt_set.h [new file with mode: 0644]
extensions/libipt_set.man [new file with mode: 0644]
extensions/libipt_standard.c [new file with mode: 0644]
extensions/libipt_state.c [new file with mode: 0644]
extensions/libipt_state.man [new file with mode: 0644]
extensions/libipt_statistic.c [new file with mode: 0644]
extensions/libipt_string.c [new file with mode: 0644]
extensions/libipt_string.man [new file with mode: 0644]
extensions/libipt_tcp.c [new file with mode: 0644]
extensions/libipt_tcp.man [new file with mode: 0644]
extensions/libipt_tcpmss.c [new file with mode: 0644]
extensions/libipt_tcpmss.man [new file with mode: 0644]
extensions/libipt_tos.c [new file with mode: 0644]
extensions/libipt_tos.man [new file with mode: 0644]
extensions/libipt_ttl.c [new file with mode: 0644]
extensions/libipt_ttl.man [new file with mode: 0644]
extensions/libipt_u32.c [new file with mode: 0644]
extensions/libipt_u32.man [new file with mode: 0644]
extensions/libipt_udp.c [new file with mode: 0644]
extensions/libipt_udp.man [new file with mode: 0644]
extensions/libipt_unclean.c [new file with mode: 0644]
extensions/libipt_unclean.man [new file with mode: 0644]
include/ip6tables.h [new file with mode: 0644]
include/iptables.h [new file with mode: 0644]
include/iptables_common.h [new file with mode: 0644]
include/libipq/ip_queue_64.h [new file with mode: 0644]
include/libipq/libipq.h [new file with mode: 0644]
include/libiptc/ipt_kernel_headers.h [new file with mode: 0644]
include/libiptc/libip6tc.h [new file with mode: 0644]
include/libiptc/libiptc.h [new file with mode: 0644]
include/libipulog/libipulog.h [new file with mode: 0644]
include/linux/netfilter/nf_conntrack_common.h [new file with mode: 0644]
include/linux/netfilter/nf_conntrack_tuple.h [new file with mode: 0644]
include/linux/netfilter/nf_conntrack_tuple_common.h [new file with mode: 0644]
include/linux/netfilter/nf_nat.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_CLASSIFY.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_CLUSTERIP.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_CONNMARK.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_DSCP.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_ECN.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_FTOS.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_MARK.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_NFQUEUE.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_SAME.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_TCPMSS.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_TTL.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_ULOG.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_addrtype.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_ah.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_comment.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_connlimit.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_connmark.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_conntrack.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_dscp.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_dstlimit.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_ecn.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_esp.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_hashlimit.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_helper.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_iprange.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_length.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_limit.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_mark.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_multiport.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_physdev.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_pkttype.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_policy.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_realm.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_rpc.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_sctp.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_tcpmss.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_ttl.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_HL.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_MARK.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_REJECT.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_TCPMSS.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_ah.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_esp.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_frag.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_hl.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_length.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_limit.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_mark.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_multiport.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_owner.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_physdev.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_policy.h [new file with mode: 0644]
ip6tables-restore.8 [new file with mode: 0644]
ip6tables-restore.c [new file with mode: 0644]
ip6tables-save.8 [new file with mode: 0644]
ip6tables-save.c [new file with mode: 0644]
ip6tables-standalone.c [new file with mode: 0644]
ip6tables.8 [new file with mode: 0644]
ip6tables.8.in [new file with mode: 0644]
ip6tables.c [new file with mode: 0644]
ipset/ChangeLog [new file with mode: 0644]
ipset/ChangeLog.ippool [new file with mode: 0644]
ipset/Makefile [new file with mode: 0644]
ipset/TODO [new file with mode: 0644]
ipset/ipset.8 [new file with mode: 0644]
ipset/ipset.c [new file with mode: 0644]
ipset/ipset.h [new file with mode: 0644]
ipset/ipset_iphash.c [new file with mode: 0644]
ipset/ipset_ipmap.c [new file with mode: 0644]
ipset/ipset_ipporthash.c [new file with mode: 0644]
ipset/ipset_iptree.c [new file with mode: 0644]
ipset/ipset_iptreemap.c [new file with mode: 0644]
ipset/ipset_macipmap.c [new file with mode: 0644]
ipset/ipset_nethash.c [new file with mode: 0644]
ipset/ipset_portmap.c [new file with mode: 0644]
ipset/libipt_set.h [new file with mode: 0644]
iptables-config [new file with mode: 0644]
iptables-multi.c [new file with mode: 0644]
iptables-restore.8 [new file with mode: 0644]
iptables-restore.c [new file with mode: 0644]
iptables-restore.c.orig [new file with mode: 0644]
iptables-save.8 [new file with mode: 0644]
iptables-save.c [new file with mode: 0644]
iptables-standalone.c [new file with mode: 0644]
iptables-xml.c [new file with mode: 0644]
iptables.8 [new file with mode: 0644]
iptables.8.in [new file with mode: 0644]
iptables.c [new file with mode: 0644]
iptables.init [new file with mode: 0755]
iptables.spec [new file with mode: 0644]
iptables.xslt [new file with mode: 0644]
libipq/Makefile [new file with mode: 0644]
libipq/ipq_create_handle.3 [new file with mode: 0644]
libipq/ipq_destroy_handle.3 [new file with mode: 0644]
libipq/ipq_errstr.3 [new file with mode: 0644]
libipq/ipq_get_msgerr.3 [new file with mode: 0644]
libipq/ipq_get_packet.3 [new file with mode: 0644]
libipq/ipq_message_type.3 [new file with mode: 0644]
libipq/ipq_perror.3 [new file with mode: 0644]
libipq/ipq_read.3 [new file with mode: 0644]
libipq/ipq_set_mode.3 [new file with mode: 0644]
libipq/ipq_set_verdict.3 [new file with mode: 0644]
libipq/libipq.3 [new file with mode: 0644]
libipq/libipq.c [new file with mode: 0644]
libiptc/.cvsignore [new file with mode: 0644]
libiptc/Makefile [new file with mode: 0644]
libiptc/libip4tc.c [new file with mode: 0644]
libiptc/libip6tc.c [new file with mode: 0644]
libiptc/libip6tc.c.orig [new file with mode: 0644]
libiptc/libiptc.c [new file with mode: 0644]
libiptc/linux_list.h [new file with mode: 0644]
libiptc/linux_stddef.h [new file with mode: 0644]
libiptc2/foo.diff [new file with mode: 0644]
libiptc2/libip4tc.c [new file with mode: 0644]
libiptc2/libip6tc.c [new file with mode: 0644]
libiptc2/libiptc.c [new file with mode: 0644]
libiptc2/libiptc.cvs.c [new file with mode: 0644]
libiptc2/libiptc2.c [new file with mode: 0644]
libiptc2/linux_list.h [new file with mode: 0644]
libiptc2/linux_listhelp.h [new file with mode: 0644]
planetlab-config [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..7bdbcc3
--- /dev/null
@@ -0,0 +1,8 @@
+.makefirst
+ip6tables
+ip6tables-restore
+ip6tables-save
+iptables
+iptables-restore
+iptables-save
+*.d
diff --git a/COMMIT_NOTES b/COMMIT_NOTES
new file mode 100644 (file)
index 0000000..5b6e6f7
--- /dev/null
@@ -0,0 +1,24 @@
+A quick list of rules for committing stuff into netfilter svn:
+
+- Always include the Name of the Author/Contributor in the SVN comment
+  like 'fix for foo (Au Thor)'
+
+- make sure that you have set the executable bits on an 'extensions/.*-test'
+  script before adding/committing it to SVN
+
+- If the commit fixes a bugzilla bug, please include '(Closes: #bugnr)' in
+  the commit message
+
+- Make sure you don't commit to svn while a feature freeze is announced
+
+- For new extensions, there are two possible cases:
+       1) header files are just in patch-o-matic patch, you need an
+          'extensions/.*-test' script to have a conditional build
+       2) header files are in patch-o-matic patch, and copied to
+          'netfilter/include/linux/netfilter_xxx'.  This way the extension
+          can be built _unconditionally_, and thus be included in
+          'extensions/Makefile'.  Make sure to keep the headers in sync!
+
+  Usually '1)' is used, but in case something is expected to show up in the
+  kernel soon, we should already make userspace support unconditionally.
+
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/INCOMPATIBILITIES b/INCOMPATIBILITIES
new file mode 100644 (file)
index 0000000..ddb2408
--- /dev/null
@@ -0,0 +1,14 @@
+INCOMPATIBILITIES:
+
+- The REJECT target has an '--reject-with admin-prohib' option which used
+  with kernels that do not support it, will result in a plain DROP instead
+  of REJECT.  Use with caution.
+  Kernels that do support it:
+       2.4 - since 2.4.22-pre9
+       2.6 - all
+
+- There are some issues related to upgrading from 1.2.x to 1.3.x on a system
+  with dynamic ruleset changes during runtime. (Please see 
+  https://bugzilla.netfilter.org/bugzilla/show_bug.cgi?id=334).
+  After upgrading from 1.2 to 1.3, it suggest go do an iptables-save, then
+  iptables-restore to ensure your dynamic rule changes continue to work.
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..5e840c6
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,47 @@
+FOLLOW THESE STEPS:
+
+0) There may be some outstanding bugfixes or tweaks which are not yet
+   in the official kernel.  Those are now (as of iptables-1.2.7) kept
+   in a seperate package, called patch-o-matic.  It is available from
+   ftp://ftp.netfilter.org/pub/patch-o-matic/
+
+1) Next, make the package.
+       % make KERNEL_DIR=<<where-you-built-your-kernel>>
+
+2) Finally, you need to to install the shared libraries, and the binary:
+       # make install KERNEL_DIR=<<where-you-built-your-kernel>>
+
+If you are a developer, you can install the headers, development libraries
+and associated development man pages, with:
+       # make install-devel
+
+That's it!
+================================================================
+PROBLEMS YOU MAY ENCOUNTER:
+
+1) This package requires a 2.4.4 kernel, or above.
+
+2) If you get the kernel directory wrong, you may see a message like:
+       Please try `make KERNEL_DIR=path-to-correct-kernel'
+
+3) If you want to specify alternate directories for installation
+(instead of /usr/local/ bin lib man), do this:
+
+       % make BINDIR=/usr/bin LIBDIR=/usr/lib MANDIR=/usr/man
+       # make BINDIR=/usr/bin LIBDIR=/usr/lib MANDIR=/usr/man install
+
+4) If you want to build a statically linked version of the iptables binary,
+   without the need for loading the plugins at runtime (e.g. for an embedded
+   device or router-on-a-disk), please use
+
+       % make NO_SHARED_LIBS=1
+
+5) If you want to build a single BusyBox style multipurpose binary instead of
+   the individual iptables, iptables-save and iptables-restore binaries, then
+   please use
+
+       % make DO_MULTI=1
+
+NOTE: make sure you build with at least the correct LIBDIR=
+specification, otherwise iptables(8) won't know where to find the
+dynamic objects.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..f668f36
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,278 @@
+# uncomment this to get a fully statically linked version
+# NO_SHARED_LIBS = 1
+
+# uncomment this to disable IPv6 support
+# DO_IPV6 = 0
+
+######################################################################
+# YOU SHOULD NOT NEED TO TOUCH ANYTHING BELOW THIS LINE
+######################################################################
+
+# Standard part of Makefile for topdir.
+TOPLEVEL_INCLUDED=YES
+
+ifndef KERNEL_DIR
+KERNEL_DIR="/lib/modules/$(shell uname -r)/build"
+endif
+IPTABLES_VERSION:=1.3.8
+OLD_IPTABLES_VERSION:=1.3.7
+
+PREFIX:=/usr/local
+LIBDIR:=$(PREFIX)/lib
+BINDIR:=$(PREFIX)/sbin
+MANDIR:=$(PREFIX)/man
+INCDIR:=$(PREFIX)/include
+
+# directory for new iptables releases
+RELEASE_DIR:=/tmp
+
+# Need libc6 for this.  FIXME: Should covert to autoconf.
+ifeq ($(shell [ -f /usr/include/netinet/ip6.h ] && echo YES), YES)
+DO_IPV6:=1
+endif
+
+# Enable linking to libselinux via enviornment 'DO_SELINUX=1'
+ifndef DO_SELINUX
+DO_SELINUX=0
+endif
+
+COPT_FLAGS:=-O2
+CFLAGS:=$(COPT_FLAGS) -Wall -Wunused -I$(KERNEL_DIR)/include -D__KERNGLUE__ -Iinclude/ -DIPTABLES_VERSION=\"$(IPTABLES_VERSION)\" #-g -DDEBUG #-pg # -DIPTC_DEBUG
+
+ifdef NO_SHARED_LIBS
+CFLAGS += -DNO_SHARED_LIBS=1
+endif
+
+EXTRAS+=iptables iptables.o iptables.8
+EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/iptables $(DESTDIR)$(MANDIR)/man8/iptables.8
+
+# No longer experimental.
+ifneq ($(DO_MULTI), 1)
+EXTRAS+=iptables-save iptables-restore iptables-xml
+endif
+EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/iptables-save $(DESTDIR)$(BINDIR)/iptables-restore $(DESTDIR)$(BINDIR)/iptables-xml $(DESTDIR)$(MANDIR)/man8/iptables-restore.8 $(DESTDIR)$(MANDIR)/man8/iptables-save.8
+
+ifeq ($(DO_IPV6), 1)
+EXTRAS+=ip6tables ip6tables.o ip6tables.8
+EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/ip6tables $(DESTDIR)$(MANDIR)/man8/ip6tables.8
+EXTRAS_EXP+=ip6tables-save ip6tables-restore
+EXTRA_INSTALLS_EXP+=$(DESTDIR)$(BINDIR)/ip6tables-save $(DESTDIR)$(BINDIR)/ip6tables-restore # $(DESTDIR)$(MANDIR)/man8/iptables-restore.8 $(DESTDIR)$(MANDIR)/man8/iptables-save.8 $(DESTDIR)$(MANDIR)/man8/ip6tables-save.8 $(DESTDIR)$(MANDIR)/man8/ip6tables-restore.8
+endif
+
+# Sparc64 hack
+ifeq ($(shell uname -m),sparc64)
+       POINTERTEST:=1
+       32bituser := $(shell echo -e "\#include <stdio.h>\n\#if !defined(__sparcv9) && !defined(__arch64__) && !defined(_LP64)\nuserspace_is_32bit\n\#endif" | $(CC) $(CFLAGS) -E - | grep userspace_is_32bit)
+       ifdef 32bituser
+               # The kernel is 64-bit, even though userspace is 32.
+               CFLAGS+=-DIPT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32
+       else
+               EXT_LDFLAGS+=-Wl,-m,elf64_sparc
+       endif
+endif
+
+# Alpha only has 64bit userspace and fails the test below
+ifeq ($(shell uname -m), alpha)
+       POINTERTEST:=1
+endif
+
+# Generic test if arch wasn't found above
+ifneq ($(POINTERTEST),1)
+       # Try to determine if kernel is 64bit and we are compiling for 32bit
+       ifeq ($(shell [ -d $(KERNEL_DIR)/include/asm ] && echo YES), YES)
+               64bitkernel := $(shell echo -e "\#include <asm/types.h>\n\#if BITS_PER_LONG == 64\nkernel_is_64bits\n\#endif" | $(CC) $(CFLAGS) -D__KERNEL__ -E - | grep kernel_is_64bits)
+               ifdef 64bitkernel
+                       32bituser := $(shell echo -e "\#include <stdio.h>\n\#if !defined(__arch64__) && !defined(_LP64)\nuserspace_is_32bit\n\#endif" | $(CC) $(CFLAGS) -E - | grep userspace_is_32bit)
+                       ifdef 32bituser
+                               CFLAGS+=-DIPT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32
+                       endif
+               endif
+       else
+               CFLAGS+=-D_UNKNOWN_KERNEL_POINTER_SIZE
+       endif
+endif
+
+ifndef IPT_LIBDIR
+IPT_LIBDIR:=$(LIBDIR)/iptables
+endif
+
+ifndef NO_SHARED_LIBS
+DEPFILES = $(SHARED_LIBS:%.so=%.d)
+DEPFILES += $(SHARED_SE_LIBS:%.so=%.d)
+SH_CFLAGS:=$(CFLAGS) -fPIC
+STATIC_LIBS  =
+STATIC6_LIBS =
+LDFLAGS      = -rdynamic
+LDLIBS       = -ldl
+ifeq ($(DO_SELINUX), 1)
+LDLIBS       += -lselinux
+endif
+else
+DEPFILES = $(EXT_OBJS:%.o=%.d)
+STATIC_LIBS  = extensions/libext.a
+STATIC6_LIBS = extensions/libext6.a
+LDFLAGS      = -static
+LDLIBS      =
+ifeq ($(DO_SELINUX), 1)
+LDLIBS       += -lselinux
+endif
+endif
+
+.PHONY: default
+default: print-extensions all
+
+.PHONY: print-extensions
+print-extensions:
+       @[ -n "$(OPTIONALS)" ] && echo Extensions found: $(OPTIONALS)
+
+iptables.o: iptables.c
+       $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" -c -o $@ $<
+
+ifeq ($(DO_MULTI), 1)
+iptables: iptables-multi.c iptables-save.c iptables-restore.c iptables-xml.c iptables-standalone.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIPTABLES_MULTI -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+else
+iptables: iptables-standalone.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+endif
+
+$(DESTDIR)$(BINDIR)/iptables: iptables
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+iptables-save: iptables-save.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+ifeq ($(DO_MULTI), 1)
+$(DESTDIR)$(BINDIR)/iptables-save: iptables
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       ln -sf $< $@
+else
+$(DESTDIR)$(BINDIR)/iptables-save: iptables-save
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+endif
+
+iptables-restore: iptables-restore.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+ifeq ($(DO_MULTI), 1)
+$(DESTDIR)$(BINDIR)/iptables-restore: iptables
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       ln -sf $< $@
+else
+$(DESTDIR)$(BINDIR)/iptables-restore: iptables-restore
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+endif
+
+iptables-xml: iptables-xml.c #iptables.o # $(STATIC_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+ifeq ($(DO_MULTI), 1)
+$(DESTDIR)$(BINDIR)/iptables-xml: iptables
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       ln -sf $< $@
+else
+$(DESTDIR)$(BINDIR)/iptables-xml: iptables-xml
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+endif
+
+ip6tables.o: ip6tables.c
+       $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" -c -o $@ $<
+
+ip6tables: ip6tables-standalone.c ip6tables.o $(STATIC6_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/ip6tables: ip6tables
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+ip6tables-save: ip6tables-save.c ip6tables.o $(STATIC6_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/ip6tables-save: ip6tables-save
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+ip6tables-restore: ip6tables-restore.c ip6tables.o $(STATIC6_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/ip6tables-restore: ip6tables-restore
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+$(DESTDIR)$(MANDIR)/man8/%.8: %.8
+       @[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8
+       cp $< $@
+
+EXTRA_DEPENDS+=iptables-standalone.d iptables.d
+
+iptables-standalone.d iptables.d: %.d: %.c
+       @-$(CC) -M -MG $(CFLAGS) $< | sed -e 's@^.*\.o:@$*.d $*.o:@' > $@
+
+iptables.8: iptables.8.in extensions/libipt_matches.man extensions/libipt_targets.man
+       sed -e '/@MATCH@/ r extensions/libipt_matches.man' -e '/@TARGET@/ r extensions/libipt_targets.man' iptables.8.in >iptables.8
+
+ip6tables.8: ip6tables.8.in extensions/libip6t_matches.man extensions/libip6t_targets.man
+       sed -e '/@MATCH@/ r extensions/libip6t_matches.man' -e '/@TARGET@/ r extensions/libip6t_targets.man' ip6tables.8.in >ip6tables.8
+
+# Development Targets
+.PHONY: install-devel-man3
+install-devel-man3: $(DEVEL_MAN3)
+       @[ -d $(DESTDIR)$(MANDIR)/man3 ] || mkdir -p $(DESTDIR)$(MANDIR)/man3
+       @cp -v $(DEVEL_MAN3) $(DESTDIR)$(MANDIR)/man3
+
+.PHONY: install-devel-headers
+install-devel-headers: $(DEVEL_HEADERS)
+       @[ -d $(DESTDIR)$(INCDIR) ] || mkdir -p $(DESTDIR)$(INCDIR)
+       @cp -v $(DEVEL_HEADERS) $(DESTDIR)$(INCDIR)
+
+.PHONY: install-devel-libs
+install-devel-libs: $(DEVEL_LIBS)
+       @[ -d $(DESTDIR)$(LIBDIR) ] || mkdir -p $(DESTDIR)$(LIBDIR)
+       @cp -v $(DEVEL_LIBS) $(DESTDIR)$(LIBDIR)
+
+.PHONY: install-devel
+install-devel: all install-devel-man3 install-devel-headers install-devel-libs
+
+.PHONY: distclean
+distclean: clean
+       @rm -f TAGS `find . -name '*~' -o -name '.*~'` `find . -name '*.rej'` `find . -name '*.d'` .makefirst
+
+# Rusty's distro magic.
+.PHONY: distrib
+distrib: check distclean delrelease $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2 diff md5sums # nowhitespace
+
+# Makefile must not define:
+# -g -pg -DIPTC_DEBUG
+.PHONY: check
+check:
+       @if echo $(CFLAGS) | egrep -e '(^|[[:space:]])(-g|-pg|-DIPTC_DEBUG)([[:space:]]|$)' >/dev/null; then echo Remove debugging flags; exit 1; else exit 0; fi
+
+.PHONY: nowhitespace
+nowhitespace:
+       @if grep -n '[  ]$$' `find . -name 'Makefile' -o -name '*.[ch]'`; then exit 1; else exit 0; fi
+
+.PHONY: delrelease
+delrelease:
+       rm -f $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2
+
+$(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2:
+       cd .. && ln -sf iptables iptables-$(IPTABLES_VERSION) && tar cvf - --exclude .svn iptables-$(IPTABLES_VERSION)/. | bzip2 -9 > $@ && rm iptables-$(IPTABLES_VERSION)
+
+.PHONY: diff
+diff: $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2
+       @mkdir /tmp/diffdir
+       @cd /tmp/diffdir && tar -x --bzip2 -f $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2
+       @set -e; cd /tmp/diffdir; tar -x --bzip2 -f $(RELEASE_DIR)/iptables-$(OLD_IPTABLES_VERSION).tar.bz2; echo Creating patch-iptables-$(OLD_IPTABLES_VERSION)-$(IPTABLES_VERSION).bz2; diff -urN iptables-$(OLD_IPTABLES_VERSION) iptables-$(IPTABLES_VERSION) | bzip2 -9 > $(RELEASE_DIR)/patch-iptables-$(OLD_IPTABLES_VERSION)-$(IPTABLES_VERSION).bz2
+       @rm -rf /tmp/diffdir
+
+.PHONY: md5sums
+md5sums:
+       cd $(RELEASE_DIR)/ && md5sum patch-iptables-*-$(IPTABLES_VERSION).bz2 iptables-$(IPTABLES_VERSION).tar.bz2
+
+# $(wildcard) fails wierdly with make v.3.78.1.
+include $(shell echo */Makefile)
+include Rules.make
diff --git a/Rules.make b/Rules.make
new file mode 100644 (file)
index 0000000..17ea017
--- /dev/null
@@ -0,0 +1,58 @@
+#! /usr/bin/make
+
+all: $(SHARED_LIBS) $(SHARED_SE_LIBS) $(EXTRAS)
+
+experimental: $(EXTRAS_EXP)
+
+# Have to handle extensions which no longer exist.
+clean: $(EXTRA_CLEANS)
+       rm -f $(SHARED_LIBS) $(SHARED_SE_LIBS) $(EXTRAS) $(EXTRAS_EXP) $(SHARED_LIBS:%.so=%_sh.o) $(SHARED_SE_LIBS:%.so=%_sh.o)
+       rm -f extensions/initext.c extensions/initext6.c
+       @find . -name '*.[ao]' -o -name '*.so' | xargs rm -f
+
+install: all $(EXTRA_INSTALLS)
+       @if [ -f /usr/local/bin/iptables -a "$(BINDIR)" = "/usr/local/sbin" ];\
+       then echo 'Erasing iptables from old location (now /usr/local/sbin).';\
+       rm -f /usr/local/bin/iptables;\
+       fi
+
+install-experimental: $(EXTRA_INSTALLS_EXP)
+
+TAGS:
+       @rm -f $@
+       find . -name '*.[ch]' | xargs etags -a
+
+dep: $(DEPFILES) $(EXTRA_DEPENDS)
+       @echo Dependencies will be generated on next make.
+       rm -f $(DEPFILES) $(EXTRA_DEPENDS) .makefirst
+
+$(SHARED_LIBS:%.so=%.d): %.d: %.c
+       @-$(CC) -M -MG $(CFLAGS) $< | \
+           sed -e 's@^.*\.o:@$*.d $*_sh.o:@' > $@
+
+$(SHARED_LIBS): %.so : %_sh.o
+       $(CC) -shared $(EXT_LDFLAGS) -o $@ $<
+
+$(SHARED_SE_LIBS:%.so=%.d): %.d: %.c
+       @-$(CC) -M -MG $(CFLAGS) $< | \
+               sed -e 's@^.*\.o:@$*.d $*_sh.o:@' > $@
+
+$(SHARED_SE_LIBS): %.so : %_sh.o
+       $(LD) -shared $(EXT_LDFLAGS) -o $@ $< $(LDLIBS)
+
+%_sh.o : %.c
+       $(CC) $(SH_CFLAGS) -o $@ -c $<
+
+.makefirst:
+       @echo Making dependencies: please wait...
+       @touch .makefirst
+
+# This is useful for when dependencies completely screwed
+%.h::
+       @echo "Unable to resolve dependency on $@. Try 'make clean'."
+       @-rm -f $(DEPFILES) $(EXTRA_DEPENDS) .makefirst
+       @[ -d $(KERNEL_DIR)/include/linux/netfilter_ipv4 ] || echo -e '\n\n    Please try `make KERNEL_DIR=path-to-correct-kernel'\'.'\n\n'
+       @exit 1
+
+-include $(DEPFILES) $(EXTRA_DEPENDS)
+-include .makefirst
diff --git a/extensions/.CLUSTERIP-test b/extensions/.CLUSTERIP-test
new file mode 100755 (executable)
index 0000000..6d0017a
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_CLUSTERIP.c ] && echo CLUSTERIP
diff --git a/extensions/.IPV4OPTSSTRIP-test b/extensions/.IPV4OPTSSTRIP-test
new file mode 100755 (executable)
index 0000000..cfd84ee
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if IPV4OPTSSTRIP patch is applied.
+[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c ] && echo IPV4OPTSSTRIP
diff --git a/extensions/.NFLOG-test b/extensions/.NFLOG-test
new file mode 100755 (executable)
index 0000000..25f0dee
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter/xt_NFLOG.h ] && echo NFLOG
diff --git a/extensions/.NFLOG-test6 b/extensions/.NFLOG-test6
new file mode 100755 (executable)
index 0000000..25f0dee
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter/xt_NFLOG.h ] && echo NFLOG
diff --git a/extensions/.REJECT-test6 b/extensions/.REJECT-test6
new file mode 100755 (executable)
index 0000000..1f09694
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+FILE=$KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_REJECT.h
+# True if REJECT is applied.
+[ -f $FILE ] && grep IP6T_ICMP6_NO_ROUTE 2>&1 >/dev/null $FILE && echo REJECT
diff --git a/extensions/.TARPIT-test b/extensions/.TARPIT-test
new file mode 100755 (executable)
index 0000000..150600a
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_TARPIT.c ] && echo TARPIT
diff --git a/extensions/.ah-test6 b/extensions/.ah-test6
new file mode 100755 (executable)
index 0000000..1812c56
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/net/ipv6/netfilter/ip6t_ah.c -a -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_ah.h ] && echo ah
diff --git a/extensions/.condition-test b/extensions/.condition-test
new file mode 100755 (executable)
index 0000000..20f3bc7
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if condition is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_condition.h ] && echo condition
diff --git a/extensions/.condition-test6 b/extensions/.condition-test6
new file mode 100755 (executable)
index 0000000..f4af61f
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if condition6 is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_condition.h ] && echo condition
diff --git a/extensions/.connbytes-test b/extensions/.connbytes-test
new file mode 100755 (executable)
index 0000000..61355d0
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_connbytes.h ] && echo connbytes
diff --git a/extensions/.cvsignore b/extensions/.cvsignore
new file mode 100644 (file)
index 0000000..a438335
--- /dev/null
@@ -0,0 +1 @@
+*.d
diff --git a/extensions/.dccp-test b/extensions/.dccp-test
new file mode 100755 (executable)
index 0000000..5b67527
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if dccp is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_dccp.h ] && echo dccp
diff --git a/extensions/.esp-test6 b/extensions/.esp-test6
new file mode 100755 (executable)
index 0000000..7ded945
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_esp.h ] && echo esp
diff --git a/extensions/.frag-test6 b/extensions/.frag-test6
new file mode 100755 (executable)
index 0000000..ff3650d
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/net/ipv6/netfilter/ip6t_frag.c -a -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_frag.h ] && echo frag
diff --git a/extensions/.hashlimit-test6 b/extensions/.hashlimit-test6
new file mode 100755 (executable)
index 0000000..9a2a465
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter/xt_hashlimit.h ] && echo hashlimit
+
diff --git a/extensions/.ipv4options-test b/extensions/.ipv4options-test
new file mode 100755 (executable)
index 0000000..134ab09
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if ipv4options is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_ipv4options.h ] && echo ipv4options
diff --git a/extensions/.ipv6header-test6 b/extensions/.ipv6header-test6
new file mode 100755 (executable)
index 0000000..47f6f06
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/net/ipv6/netfilter/ip6t_ipv6header.c -a -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_ipv6header.h ] && echo ipv6header
diff --git a/extensions/.mh-test6 b/extensions/.mh-test6
new file mode 100755 (executable)
index 0000000..1142096
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_mh.h ] && echo mh
diff --git a/extensions/.opts-test6 b/extensions/.opts-test6
new file mode 100755 (executable)
index 0000000..1ed2013
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/net/ipv6/netfilter/ip6t_hbh.c -a -f $KERNEL_DIR/net/ipv6/netfilter/ip6t_dst.c -a -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_opts.h ] && echo hbh dst
diff --git a/extensions/.quota-test b/extensions/.quota-test
new file mode 100755 (executable)
index 0000000..b21058c
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter/xt_quota.h ] && echo quota
+
diff --git a/extensions/.recent-test b/extensions/.recent-test
new file mode 100755 (executable)
index 0000000..2a47fc9
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if recent match patch is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_recent.h ] && echo recent
diff --git a/extensions/.rt-test6 b/extensions/.rt-test6
new file mode 100755 (executable)
index 0000000..e8d5855
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/net/ipv6/netfilter/ip6t_rt.c -a -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_rt.h ] && echo rt
diff --git a/extensions/.sctp-test6 b/extensions/.sctp-test6
new file mode 100755 (executable)
index 0000000..3cfc7b8
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter/xt_sctp.h ] && echo sctp
+
diff --git a/extensions/.set-test b/extensions/.set-test
new file mode 100755 (executable)
index 0000000..700a73c
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ip_set.h ] && echo set SET
diff --git a/extensions/.statistic-test b/extensions/.statistic-test
new file mode 100755 (executable)
index 0000000..843cb41
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/net/netfilter/xt_statistic.c -a -f $KERNEL_DIR/include/linux/netfilter/xt_statistic.h ] && echo statistic
diff --git a/extensions/.string-test b/extensions/.string-test
new file mode 100755 (executable)
index 0000000..609f1c2
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_string.h ] && echo string
diff --git a/extensions/.u32-test b/extensions/.u32-test
new file mode 100755 (executable)
index 0000000..77d8a00
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if u32 is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_u32.h ] && echo u32
diff --git a/extensions/Makefile b/extensions/Makefile
new file mode 100644 (file)
index 0000000..64ca7a8
--- /dev/null
@@ -0,0 +1,180 @@
+#! /usr/bin/make
+
+# WARNING:
+# only add extensions here that are either present in the kernel, or whose
+# header files are present in the include/linux directory of this iptables
+# package (HW)
+#
+PF_EXT_SLIB:=ah addrtype comment connmark conntrack dscp ecn esp hashlimit helper icmp iprange length limit mac mark multiport owner physdev pkttype policy realm sctp standard state tcp tcpmss tos ttl udp unclean CLASSIFY CONNMARK DNAT DSCP ECN LOG MARK MASQUERADE MIRROR NETMAP NFQUEUE NOTRACK REDIRECT REJECT SAME SNAT TCPMSS TOS TTL ULOG
+PF6_EXT_SLIB:=connmark eui64 hl icmp6 length limit mac mark multiport owner physdev policy standard state tcp udp CONNMARK HL LOG NFQUEUE MARK TCPMSS
+
+ifeq ($(DO_SELINUX), 1)
+PF_EXT_SE_SLIB:=SECMARK CONNSECMARK
+PF6_EXT_SE_SLIB:=SECMARK CONNSECMARK
+endif
+
+# Optionals
+PF_EXT_SLIB_OPTS:=$(foreach T,$(wildcard extensions/.*-test),$(shell KERNEL_DIR=$(KERNEL_DIR) $(T)))
+PF6_EXT_SLIB_OPTS:=$(foreach T,$(wildcard extensions/.*-test6),$(shell KERNEL_DIR=$(KERNEL_DIR) $(T)))
+
+PF_EXT_ALL_SLIB:=$(patsubst extensions/libipt_%.c, %, $(wildcard extensions/libipt_*.c))
+PF6_EXT_ALL_SLIB:=$(patsubst extensions/libip6t_%.c, %, $(wildcard extensions/libip6t_*.c))
+
+PF_EXT_MAN_ALL_MATCHES:=$(foreach T,$(PF_EXT_ALL_SLIB),$(shell test -f extensions/libipt_$(T).man && grep -q register_match extensions/libipt_$(T).c  && echo $(T)))
+PF_EXT_MAN_ALL_TARGETS:=$(foreach T,$(PF_EXT_ALL_SLIB),$(shell test -f extensions/libipt_$(T).man && grep -q register_target extensions/libipt_$(T).c && echo $(T)))
+PF6_EXT_MAN_ALL_MATCHES:=$(foreach T,$(PF6_EXT_ALL_SLIB),$(shell test -f extensions/libip6t_$(T).man && grep -q register_match6 extensions/libip6t_$(T).c  && echo $(T)))
+PF6_EXT_MAN_ALL_TARGETS:=$(foreach T,$(PF6_EXT_ALL_SLIB),$(shell test -f extensions/libip6t_$(T).man && grep -q register_target6 extensions/libip6t_$(T).c && echo $(T)))
+
+PF_EXT_MAN_MATCHES:=$(filter $(PF_EXT_ALL_SLIB), $(PF_EXT_MAN_ALL_MATCHES))
+PF_EXT_MAN_TARGETS:=$(filter $(PF_EXT_ALL_SLIB), $(PF_EXT_MAN_ALL_TARGETS))
+PF_EXT_MAN_EXTRA_MATCHES:=$(filter-out $(PF_EXT_MAN_MATCHES), $(PF_EXT_MAN_ALL_MATCHES))
+PF_EXT_MAN_EXTRA_TARGETS:=$(filter-out $(PF_EXT_MAN_TARGETS), $(PF_EXT_MAN_ALL_TARGETS))
+PF6_EXT_MAN_MATCHES:=$(filter $(PF6_EXT_ALL_SLIB), $(PF6_EXT_MAN_ALL_MATCHES))
+PF6_EXT_MAN_TARGETS:=$(filter $(PF6_EXT_ALL_SLIB), $(PF6_EXT_MAN_ALL_TARGETS))
+PF6_EXT_MAN_EXTRA_MATCHES:=$(filter-out $(PF6_EXT_MAN_MATCHES), $(PF6_EXT_MAN_ALL_MATCHES))
+PF6_EXT_MAN_EXTRA_TARGETS:=$(filter-out $(PF6_EXT_MAN_TARGETS), $(PF6_EXT_MAN_ALL_TARGETS))
+
+
+allman:
+       @echo ALL_SLIB: $(PF_EXT_ALL_SLIB)
+       @echo ALL_MATCH: $(PF_EXT_MAN_ALL_MATCHES)
+       @echo ALL_TARGET: $(PF_EXT_MAN_ALL_TARGETS)
+
+PF_EXT_SLIB+=$(PF_EXT_SLIB_OPTS)
+PF6_EXT_SLIB+=$(PF6_EXT_SLIB_OPTS)
+
+OPTIONALS+=$(patsubst %,IPv4:%,$(PF_EXT_SLIB_OPTS))
+OPTIONALS+=$(patsubst %,IPv6:%,$(PF6_EXT_SLIB_OPTS))
+
+ifndef NO_SHARED_LIBS
+SHARED_LIBS+=$(foreach T,$(PF_EXT_SLIB),extensions/libipt_$(T).so)
+SHARED_SE_LIBS+=$(foreach T,$(PF_EXT_SE_SLIB),extensions/libipt_$(T).so)
+EXTRA_INSTALLS+=$(foreach T, $(PF_EXT_SLIB), $(DESTDIR)$(LIBDIR)/iptables/libipt_$(T).so)
+EXTRA_INSTALLS+=$(foreach T, $(PF_EXT_SE_SLIB), $(DESTDIR)$(LIBDIR)/iptables/libipt_$(T).so)
+
+ifeq ($(DO_IPV6), 1)
+SHARED_LIBS+=$(foreach T,$(PF6_EXT_SLIB),extensions/libip6t_$(T).so)
+SHARED_SE_LIBS+=$(foreach T,$(PF6_EXT_SE_SLIB),extensions/libip6t_$(T).so)
+EXTRA_INSTALLS+=$(foreach T, $(PF6_EXT_SLIB), $(DESTDIR)$(LIBDIR)/iptables/libip6t_$(T).so)
+EXTRA_INSTALLS+=$(foreach T, $(PF6_EXT_SE_SLIB), $(DESTDIR)$(LIBDIR)/iptables/libip6t_$(T).so)
+endif
+else   # NO_SHARED_LIBS
+EXT_OBJS+=$(foreach T,$(PF_EXT_SLIB),extensions/libipt_$(T).o)
+EXT_OBJS+=$(foreach T,$(PF_EXT_SE_SLIB),extensions/libipt_$(T).o)
+EXT_FUNC+=$(foreach T,$(PF_EXT_SLIB),ipt_$(T))
+EXT_FUNC+=$(foreach T,$(PF_EXT_SE_SLIB),ipt_$(T))
+EXT_OBJS+= extensions/initext.o
+ifeq ($(DO_IPV6), 1)
+EXT6_OBJS+=$(foreach T,$(PF6_EXT_SLIB),extensions/libip6t_$(T).o)
+EXT6_OBJS+=$(foreach T,$(PF6_EXT_SE_SLIB),extensions/libip6t_$(T).o)
+EXT6_FUNC+=$(foreach T,$(PF6_EXT_SLIB),ip6t_$(T))
+EXT6_FUNC+=$(foreach T,$(PF6_EXT_SE_SLIB),ip6t_$(T))
+EXT6_OBJS+= extensions/initext6.o
+endif  # DO_IPV6
+endif  # NO_SHARED_LIBS
+
+ifndef TOPLEVEL_INCLUDED
+local:
+       cd .. && $(MAKE) $(SHARED_LIBS) $(SHARED_SE_LIBS)
+endif
+
+ifdef NO_SHARED_LIBS
+extensions/libext.a: $(EXT_OBJS)
+       rm -f $@; ar crv $@ $(EXT_OBJS)
+
+extensions/libext6.a: $(EXT6_OBJS)
+       rm -f $@; ar crv $@ $(EXT6_OBJS)
+
+extensions/initext.o: extensions/initext.c
+extensions/initext6.o: extensions/initext6.c
+
+extensions/initext.c: extensions/Makefile
+       echo "" > $@
+       for i in $(EXT_FUNC); do \
+               echo "extern void $${i}_init(void);" >> $@; \
+       done
+       echo "void init_extensions(void) {" >> $@
+       for i in $(EXT_FUNC); do \
+               echo "  $${i}_init();" >> $@; \
+       done
+       echo "}" >> $@
+
+extensions/initext6.c: extensions/Makefile
+       echo "" > $@
+       for i in $(EXT6_FUNC); do \
+               echo "extern void $${i}_init(void);" >> $@; \
+       done
+       echo "void init_extensions(void) {" >> $@
+       for i in $(EXT6_FUNC); do \
+               echo "  $${i}_init();" >> $@; \
+       done
+       echo "}" >> $@
+
+extensions/lib%.o: extensions/lib%.c
+       $(CC) $(CFLAGS) -D_INIT=$*_init -c -o $@ $<
+
+endif
+EXTRAS += extensions/libipt_targets.man
+extensions/libipt_targets.man: $(patsubst %,extensions/libipt_%.man,$(PF_EXT_MAN_ALL_TARGETS))
+       @for ext in $(PF_EXT_MAN_TARGETS); do \
+           echo ".SS $$ext" ;\
+           cat extensions/libipt_$$ext.man ;\
+       done >extensions/libipt_targets.man
+       @if [ -n "$(PF_EXT_MAN_EXTRA_TARGETS)" ]; then \
+           extra=$(PF_EXT_MAN_EXTRA_TARGETS) ;\
+           for ext in $${extra:-""}; do \
+               echo ".SS $$ext (not supported, see Patch-O-Matic)" ;\
+               cat extensions/libipt_$$ext.man ;\
+           done ;\
+               fi >>extensions/libipt_targets.man
+
+EXTRAS += extensions/libipt_matches.man
+extensions/libipt_matches.man: $(patsubst %,extensions/libipt_%.man,$(PF_EXT_MAN_ALL_MATCHES))
+       @for ext in $(PF_EXT_MAN_MATCHES); do \
+           echo ".SS $$ext" ;\
+           cat extensions/libipt_$$ext.man ;\
+       done >extensions/libipt_matches.man
+       @if [ -n "$(PF_EXT_MAN_EXTRA_MATCHES)" ]; then \
+           extra=$(PF_EXT_MAN_EXTRA_MATCHES) ;\
+           for ext in $${extra:-""}; do \
+               echo ".SS $$ext (not supported, see Patch-O-Matic)" ;\
+               cat extensions/libipt_$$ext.man ;\
+           done ;\
+               fi >>extensions/libipt_matches.man
+
+EXTRAS += extensions/libip6t_targets.man
+extensions/libip6t_targets.man: $(patsubst %, extensions/libip6t_%.man, $(PF6_EXT_MAN_ALL_TARGETS))
+       @for ext in $(PF6_EXT_MAN_TARGETS); do \
+           echo ".SS $$ext" ;\
+           cat extensions/libip6t_$$ext.man ;\
+       done >extensions/libip6t_targets.man
+       @if [ -n "$(PF6_EXT_MAN_EXTRA_TARGETS)" ]; then \
+           extra=$(PF6_EXT_MAN_EXTRA_TARGETS) ;\
+           for ext in $${extra:-""}; do \
+               echo ".SS $$ext (not supported, see Patch-O-Matic)" ;\
+               cat extensions/libip6t_$$ext.man ;\
+           done ;\
+               fi >>extensions/libip6t_targets.man
+
+EXTRAS += extensions/libip6t_matches.man
+extensions/libip6t_matches.man: $(patsubst %, extensions/libip6t_%.man, $(PF6_EXT_MAN_ALL_MATCHES))
+       @for ext in $(PF6_EXT_MAN_MATCHES); do \
+           echo ".SS $$ext" ;\
+           cat extensions/libip6t_$$ext.man ;\
+       done >extensions/libip6t_matches.man
+       @if [ -n "$(PF6_EXT_MAN_EXTRA_MATCHES)" ]; then \
+           extra=$(PF6_EXT_MAN_EXTRA_MATCHES) ;\
+           for ext in $${extra:-""}; do \
+               echo ".SS $$ext (not supported, see Patch-O-Matic)" ;\
+               cat extensions/libip6t_$$ext.man ;\
+           done ;\
+               fi >>extensions/libip6t_matches.man
+
+$(DESTDIR)$(LIBDIR)/iptables/libipt_%.so: extensions/libipt_%.so
+       @[ -d $(DESTDIR)$(LIBDIR)/iptables ] || mkdir -p $(DESTDIR)$(LIBDIR)/iptables
+       cp $< $@
+
+$(DESTDIR)$(LIBDIR)/iptables/libip6t_%.so: extensions/libip6t_%.so
+       @[ -d $(DESTDIR)$(LIBDIR)/iptables ] || mkdir -p $(DESTDIR)$(LIBDIR)/iptables
+       cp $< $@
diff --git a/extensions/libip6t_CONNMARK.c b/extensions/libip6t_CONNMARK.c
new file mode 100644 (file)
index 0000000..9506f26
--- /dev/null
@@ -0,0 +1,220 @@
+/* Shared library add-on to iptables to add CONNMARK target support.
+ *
+ * (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * Version 1.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include "../include/linux/netfilter_ipv4/ipt_CONNMARK.h"
+
+#if 0
+struct markinfo {
+       struct ipt_entry_target t;
+       struct ipt_connmark_target_info mark;
+};
+#endif
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"CONNMARK target v%s options:\n"
+"  --set-mark value[/mask]       Set conntrack mark value\n"
+"  --save-mark [--mask mask]     Save the packet nfmark in the connection\n"
+"  --restore-mark [--mask mask]  Restore saved nfmark value\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "set-mark", 1, 0, '1' },
+       { "save-mark", 0, 0, '2' },
+       { "restore-mark", 0, 0, '3' },
+       { "mask", 1, 0, '4' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ip6t_entry_target *t, unsigned int *nfcache)
+{
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      struct ip6t_entry_target **target)
+{
+       struct ipt_connmark_target_info *markinfo
+               = (struct ipt_connmark_target_info *)(*target)->data;
+
+       markinfo->mask = 0xffffffffUL;
+
+       switch (c) {
+               char *end;
+       case '1':
+               markinfo->mode = IPT_CONNMARK_SET;
+
+               markinfo->mark = strtoul(optarg, &end, 0);
+               if (*end == '/' && end[1] != '\0')
+                   markinfo->mask = strtoul(end+1, &end, 0);
+
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "CONNMARK target: Can't specify --set-mark twice");
+               *flags = 1;
+               break;
+       case '2':
+               markinfo->mode = IPT_CONNMARK_SAVE;
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "CONNMARK target: Can't specify --save-mark twice");
+               *flags = 1;
+               break;
+       case '3':
+               markinfo->mode = IPT_CONNMARK_RESTORE;
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "CONNMARK target: Can't specify --restore-mark twice");
+               *flags = 1;
+               break;
+       case '4':
+               if (!*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "CONNMARK target: Can't specify --mask without a operation");
+               markinfo->mask = strtoul(optarg, &end, 0);
+
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad MASK value `%s'", optarg);
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "CONNMARK target: No operation specified");
+}
+
+static void
+print_mark(unsigned long mark)
+{
+       printf("0x%lx", mark);
+}
+
+static void
+print_mask(const char *text, unsigned long mask)
+{
+       if (mask != 0xffffffffUL)
+               printf("%s0x%lx", text, mask);
+}
+
+
+/* Prints out the target info. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_target *target,
+      int numeric)
+{
+       const struct ipt_connmark_target_info *markinfo =
+               (const struct ipt_connmark_target_info *)target->data;
+       switch (markinfo->mode) {
+       case IPT_CONNMARK_SET:
+           printf("CONNMARK set ");
+           print_mark(markinfo->mark);
+           print_mask("/", markinfo->mask);
+           printf(" ");
+           break;
+       case IPT_CONNMARK_SAVE:
+           printf("CONNMARK save ");
+           print_mask("mask ", markinfo->mask);
+           printf(" ");
+           break;
+       case IPT_CONNMARK_RESTORE:
+           printf("CONNMARK restore ");
+           print_mask("mask ", markinfo->mask);
+           break;
+       default:
+           printf("ERROR: UNKNOWN CONNMARK MODE ");
+           break;
+       }
+}
+
+/* Saves the target into in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_target *target)
+{
+       const struct ipt_connmark_target_info *markinfo =
+               (const struct ipt_connmark_target_info *)target->data;
+
+       switch (markinfo->mode) {
+       case IPT_CONNMARK_SET:
+           printf("--set-mark ");
+           print_mark(markinfo->mark);
+           print_mask("/", markinfo->mask);
+           printf(" ");
+           break;
+       case IPT_CONNMARK_SAVE:
+           printf("--save-mark ");
+           print_mask("--mask ", markinfo->mask);
+           break;
+       case IPT_CONNMARK_RESTORE:
+           printf("--restore-mark ");
+           print_mask("--mask ", markinfo->mask);
+           break;
+       default:
+           printf("ERROR: UNKNOWN CONNMARK MODE ");
+           break;
+       }
+}
+
+static struct ip6tables_target connmark_target = {
+    .name          = "CONNMARK",
+    .version       = IPTABLES_VERSION,
+    .size          = IP6T_ALIGN(sizeof(struct ipt_connmark_target_info)),
+    .userspacesize = IP6T_ALIGN(sizeof(struct ipt_connmark_target_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_target6(&connmark_target);
+}
diff --git a/extensions/libip6t_CONNSECMARK.c b/extensions/libip6t_CONNSECMARK.c
new file mode 100644 (file)
index 0000000..b11ed07
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Shared library add-on to ip6tables to add CONNSECMARK target support.
+ *
+ * Based on the MARK and CONNMARK targets.
+ *
+ * Copyright (C) 2006 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter/xt_CONNSECMARK.h>
+
+#define PFX "CONNSECMARK target: "
+
+static void help(void)
+{
+       printf(
+"CONNSECMARK target v%s options:\n"
+"  --save                   Copy security mark from packet to conntrack\n"
+"  --restore                Copy security mark from connection to packet\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "save", 0, 0, '1' },
+       { "restore", 0, 0, '2' },
+       { 0 }
+};
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+                 const struct ip6t_entry *entry, struct ip6t_entry_target **target)
+{
+       struct xt_connsecmark_target_info *info =
+               (struct xt_connsecmark_target_info*)(*target)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & CONNSECMARK_SAVE)
+                       exit_error(PARAMETER_PROBLEM, PFX
+                                  "Can't specify --save twice");
+               info->mode = CONNSECMARK_SAVE;
+               *flags |= CONNSECMARK_SAVE;
+               break;
+
+       case '2':
+               if (*flags & CONNSECMARK_RESTORE)
+                       exit_error(PARAMETER_PROBLEM, PFX
+                                  "Can't specify --restore twice");
+               info->mode = CONNSECMARK_RESTORE;
+               *flags |= CONNSECMARK_RESTORE;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, PFX "parameter required");
+
+       if (flags == (CONNSECMARK_SAVE|CONNSECMARK_RESTORE))
+               exit_error(PARAMETER_PROBLEM, PFX "only one flag of --save "
+                          "or --restore is allowed");
+}
+
+static void print_connsecmark(struct xt_connsecmark_target_info *info)
+{
+       switch (info->mode) {
+       case CONNSECMARK_SAVE:
+               printf("save ");
+               break;
+               
+       case CONNSECMARK_RESTORE:
+               printf("restore ");
+               break;
+               
+       default:
+               exit_error(OTHER_PROBLEM, PFX "invalid mode %hhu\n", info->mode);
+       }
+}
+
+static void print(const struct ip6t_ip6 *ip,
+                 const struct ip6t_entry_target *target, int numeric)
+{
+       struct xt_connsecmark_target_info *info =
+               (struct xt_connsecmark_target_info*)(target)->data;
+
+       printf("CONNSECMARK ");
+       print_connsecmark(info);
+}
+
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_target *target)
+{
+       struct xt_connsecmark_target_info *info =
+               (struct xt_connsecmark_target_info*)target->data;
+
+       printf("--");
+       print_connsecmark(info);
+}
+
+static struct ip6tables_target connsecmark = {
+       .name           = "CONNSECMARK",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct xt_connsecmark_target_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct xt_connsecmark_target_info)),
+       .parse          = &parse,
+       .help           = &help,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target6(&connsecmark);
+}
diff --git a/extensions/libip6t_CONNSECMARK.man b/extensions/libip6t_CONNSECMARK.man
new file mode 100644 (file)
index 0000000..b94353a
--- /dev/null
@@ -0,0 +1,15 @@
+This module copies security markings from packets to connections
+(if unlabeled), and from connections back to packets (also only
+if unlabeled).  Typically used in conjunction with SECMARK, it is
+only valid in the
+.B mangle
+table.
+.TP
+.B --save
+If the packet has a security marking, copy it to the connection
+if the connection is not marked.
+.TP
+.B --restore
+If the packet does not have a security marking, and the connection
+does, copy the security marking from the connection to the packet.
+
diff --git a/extensions/libip6t_HL.c b/extensions/libip6t_HL.c
new file mode 100644 (file)
index 0000000..2062828
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * IPv6 Hop Limit Target module
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's ttl target
+ * This program is distributed under the terms of GNU GPL
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_HL.h>
+
+#define IP6T_HL_USED   1
+
+static void init(struct ip6t_entry_target *t, unsigned int *nfcache) 
+{
+}
+
+static void help(void) 
+{
+       printf(
+"HL target v%s options\n"
+"  --hl-set value              Set HL to <value 0-255>\n"
+"  --hl-dec value              Decrement HL by <value 1-255>\n"
+"  --hl-inc value              Increment HL by <value 1-255>\n"
+, IPTABLES_VERSION);
+}
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+               const struct ip6t_entry *entry,
+               struct ip6t_entry_target **target)
+{
+       struct ip6t_HL_info *info = (struct ip6t_HL_info *) (*target)->data;
+       unsigned int value;
+
+       if (*flags & IP6T_HL_USED) {
+               exit_error(PARAMETER_PROBLEM, 
+                               "Can't specify HL option twice");
+       }
+
+       if (!optarg) 
+               exit_error(PARAMETER_PROBLEM, 
+                               "HL: You must specify a value");
+
+       if (check_inverse(optarg, &invert, NULL, 0))
+               exit_error(PARAMETER_PROBLEM,
+                               "HL: unexpected `!'");
+       
+       if (string_to_number(optarg, 0, 255, &value) == -1)     
+               exit_error(PARAMETER_PROBLEM,   
+                          "HL: Expected value between 0 and 255");
+
+       switch (c) {
+
+               case '1':
+                       info->mode = IP6T_HL_SET;
+                       break;
+
+               case '2':
+                       if (value == 0) {
+                               exit_error(PARAMETER_PROBLEM,
+                                       "HL: decreasing by 0?");
+                       }
+
+                       info->mode = IP6T_HL_DEC;
+                       break;
+
+               case '3':
+                       if (value == 0) {
+                               exit_error(PARAMETER_PROBLEM,
+                                       "HL: increasing by 0?");
+                       }
+
+                       info->mode = IP6T_HL_INC;
+                       break;
+
+               default:
+                       return 0;
+
+       }
+       
+       info->hop_limit = value;
+       *flags |= IP6T_HL_USED;
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!(flags & IP6T_HL_USED))
+               exit_error(PARAMETER_PROBLEM,
+                               "HL: You must specify an action");
+}
+
+static void save(const struct ip6t_ip6 *ip,
+               const struct ip6t_entry_target *target)
+{
+       const struct ip6t_HL_info *info = 
+               (struct ip6t_HL_info *) target->data;
+
+       switch (info->mode) {
+               case IP6T_HL_SET:
+                       printf("--hl-set ");
+                       break;
+               case IP6T_HL_DEC:
+                       printf("--hl-dec ");
+                       break;
+
+               case IP6T_HL_INC:
+                       printf("--hl-inc ");
+                       break;
+       }
+       printf("%u ", info->hop_limit);
+}
+
+static void print(const struct ip6t_ip6 *ip,
+               const struct ip6t_entry_target *target, int numeric)
+{
+       const struct ip6t_HL_info *info =
+               (struct ip6t_HL_info *) target->data;
+
+       printf("HL ");
+       switch (info->mode) {
+               case IP6T_HL_SET:
+                       printf("set to ");
+                       break;
+               case IP6T_HL_DEC:
+                       printf("decrement by ");
+                       break;
+               case IP6T_HL_INC:
+                       printf("increment by ");
+                       break;
+       }
+       printf("%u ", info->hop_limit);
+}
+
+static struct option opts[] = {
+       { "hl-set", 1, 0, '1' },
+       { "hl-dec", 1, 0, '2' },
+       { "hl-inc", 1, 0, '3' },
+       { 0 }
+};
+
+static
+struct ip6tables_target HL = { NULL, 
+       .name           = "HL",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_HL_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_HL_info)),
+       .help           = &help, 
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts 
+};
+
+void _init(void)
+{
+       register_target6(&HL);
+}
diff --git a/extensions/libip6t_HL.man b/extensions/libip6t_HL.man
new file mode 100644 (file)
index 0000000..bf46881
--- /dev/null
@@ -0,0 +1,17 @@
+This is used to modify the Hop Limit field in IPv6 header. The Hop Limit field
+is similar to what is known as TTL value in IPv4.  Setting or incrementing the
+Hop Limit field can potentially be very dangerous, so it should be avoided at
+any cost. This target is only valid in
+.B mangle
+table.
+.TP
+.B Don't ever set or increment the value on packets that leave your local network!
+.TP
+.BI "--hl-set " "value"
+Set the Hop Limit to `value'.
+.TP
+.BI "--hl-dec " "value"
+Decrement the Hop Limit `value' times.
+.TP
+.BI "--hl-inc " "value"
+Increment the Hop Limit `value' times.
diff --git a/extensions/libip6t_LOG.c b/extensions/libip6t_LOG.c
new file mode 100644 (file)
index 0000000..5043b44
--- /dev/null
@@ -0,0 +1,290 @@
+/* Shared library add-on to iptables to add LOG support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_LOG.h>
+
+#ifndef IP6T_LOG_UID   /* Old kernel */
+#define IP6T_LOG_UID   0x08
+#undef  IP6T_LOG_MASK
+#define IP6T_LOG_MASK  0x0f
+#endif
+
+#define LOG_DEFAULT_LEVEL LOG_WARNING
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"LOG v%s options:\n"
+" --log-level level            Level of logging (numeric or see syslog.conf)\n"
+" --log-prefix prefix          Prefix log messages with this prefix.\n\n"
+" --log-tcp-sequence           Log TCP sequence numbers.\n\n"
+" --log-tcp-options            Log TCP options.\n\n"
+" --log-ip-options             Log IP options.\n\n"
+" --log-uid                    Log UID owning the local socket.\n\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "log-level",        .has_arg = 1, .flag = 0, .val = '!' },
+       { .name = "log-prefix",       .has_arg = 1, .flag = 0, .val = '#' },
+       { .name = "log-tcp-sequence", .has_arg = 0, .flag = 0, .val = '1' },
+       { .name = "log-tcp-options",  .has_arg = 0, .flag = 0, .val = '2' },
+       { .name = "log-ip-options",   .has_arg = 0, .flag = 0, .val = '3' },
+       { .name = "log-uid",          .has_arg = 0, .flag = 0, .val = '4' },
+       { .name = 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ip6t_entry_target *t, unsigned int *nfcache)
+{
+       struct ip6t_log_info *loginfo = (struct ip6t_log_info *)t->data;
+
+       loginfo->level = LOG_DEFAULT_LEVEL;
+
+}
+
+struct ip6t_log_names {
+       const char *name;
+       unsigned int level;
+};
+
+static struct ip6t_log_names ip6t_log_names[]
+= { { .name = "alert",   .level = LOG_ALERT },
+    { .name = "crit",    .level = LOG_CRIT },
+    { .name = "debug",   .level = LOG_DEBUG },
+    { .name = "emerg",   .level = LOG_EMERG },
+    { .name = "error",   .level = LOG_ERR },           /* DEPRECATED */
+    { .name = "info",    .level = LOG_INFO },
+    { .name = "notice",  .level = LOG_NOTICE },
+    { .name = "panic",   .level = LOG_EMERG },         /* DEPRECATED */
+    { .name = "warning", .level = LOG_WARNING }
+};
+
+static u_int8_t
+parse_level(const char *level)
+{
+       unsigned int lev = -1;
+       unsigned int set = 0;
+
+       if (string_to_number(level, 0, 7, &lev) == -1) {
+               unsigned int i = 0;
+
+               for (i = 0;
+                    i < sizeof(ip6t_log_names) / sizeof(struct ip6t_log_names);
+                    i++) {
+                       if (strncasecmp(level, ip6t_log_names[i].name,
+                                       strlen(level)) == 0) {
+                               if (set++)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "log-level `%s' ambiguous",
+                                                  level);
+                               lev = ip6t_log_names[i].level;
+                       }
+               }
+
+               if (!set)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "log-level `%s' unknown", level);
+       }
+
+       return (u_int8_t)lev;
+}
+
+#define IP6T_LOG_OPT_LEVEL 0x01
+#define IP6T_LOG_OPT_PREFIX 0x02
+#define IP6T_LOG_OPT_TCPSEQ 0x04
+#define IP6T_LOG_OPT_TCPOPT 0x08
+#define IP6T_LOG_OPT_IPOPT 0x10
+#define IP6T_LOG_OPT_UID 0x20
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      struct ip6t_entry_target **target)
+{
+       struct ip6t_log_info *loginfo = (struct ip6t_log_info *)(*target)->data;
+
+       switch (c) {
+       case '!':
+               if (*flags & IP6T_LOG_OPT_LEVEL)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-level twice");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --log-level");
+
+               loginfo->level = parse_level(optarg);
+               *flags |= IP6T_LOG_OPT_LEVEL;
+               break;
+
+       case '#':
+               if (*flags & IP6T_LOG_OPT_PREFIX)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-prefix twice");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --log-prefix");
+
+               if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Maximum prefix length %u for --log-prefix",
+                                  (unsigned int)sizeof(loginfo->prefix) - 1);
+
+               if (strlen(optarg) == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "No prefix specified for --log-prefix");
+
+               if (strlen(optarg) != strlen(strtok(optarg, "\n")))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Newlines not allowed in --log-prefix");
+
+               strcpy(loginfo->prefix, optarg);
+               *flags |= IP6T_LOG_OPT_PREFIX;
+               break;
+
+       case '1':
+               if (*flags & IP6T_LOG_OPT_TCPSEQ)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-tcp-sequence "
+                                  "twice");
+
+               loginfo->logflags |= IP6T_LOG_TCPSEQ;
+               *flags |= IP6T_LOG_OPT_TCPSEQ;
+               break;
+
+       case '2':
+               if (*flags & IP6T_LOG_OPT_TCPOPT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-tcp-options twice");
+
+               loginfo->logflags |= IP6T_LOG_TCPOPT;
+               *flags |= IP6T_LOG_OPT_TCPOPT;
+               break;
+
+       case '3':
+               if (*flags & IP6T_LOG_OPT_IPOPT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-ip-options twice");
+
+               loginfo->logflags |= IP6T_LOG_IPOPT;
+               *flags |= IP6T_LOG_OPT_IPOPT;
+               break;
+
+       case '4':
+               if (*flags & IP6T_LOG_OPT_UID)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-uid twice");
+
+               loginfo->logflags |= IP6T_LOG_UID;
+               *flags |= IP6T_LOG_OPT_UID;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_target *target,
+      int numeric)
+{
+       const struct ip6t_log_info *loginfo
+               = (const struct ip6t_log_info *)target->data;
+       unsigned int i = 0;
+
+       printf("LOG ");
+       if (numeric)
+               printf("flags %u level %u ",
+                      loginfo->logflags, loginfo->level);
+       else {
+               for (i = 0;
+                    i < sizeof(ip6t_log_names) / sizeof(struct ip6t_log_names);
+                    i++) {
+                       if (loginfo->level == ip6t_log_names[i].level) {
+                               printf("level %s ", ip6t_log_names[i].name);
+                               break;
+                       }
+               }
+               if (i == sizeof(ip6t_log_names) / sizeof(struct ip6t_log_names))
+                       printf("UNKNOWN level %u ", loginfo->level);
+               if (loginfo->logflags & IP6T_LOG_TCPSEQ)
+                       printf("tcp-sequence ");
+               if (loginfo->logflags & IP6T_LOG_TCPOPT)
+                       printf("tcp-options ");
+               if (loginfo->logflags & IP6T_LOG_IPOPT)
+                       printf("ip-options ");
+               if (loginfo->logflags & IP6T_LOG_UID)
+                       printf("uid ");
+               if (loginfo->logflags & ~(IP6T_LOG_MASK))
+                       printf("unknown-flags ");
+       }
+
+       if (strcmp(loginfo->prefix, "") != 0)
+               printf("prefix `%s' ", loginfo->prefix);
+}
+
+/* Saves the union ip6t_targinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_target *target)
+{
+       const struct ip6t_log_info *loginfo
+               = (const struct ip6t_log_info *)target->data;
+
+       if (strcmp(loginfo->prefix, "") != 0)
+               printf("--log-prefix \"%s\" ", loginfo->prefix);
+
+       if (loginfo->level != LOG_DEFAULT_LEVEL)
+               printf("--log-level %d ", loginfo->level);
+
+       if (loginfo->logflags & IP6T_LOG_TCPSEQ)
+               printf("--log-tcp-sequence ");
+       if (loginfo->logflags & IP6T_LOG_TCPOPT)
+               printf("--log-tcp-options ");
+       if (loginfo->logflags & IP6T_LOG_IPOPT)
+               printf("--log-ip-options ");
+       if (loginfo->logflags & IP6T_LOG_UID)
+               printf("--log-uid ");
+}
+
+static
+struct ip6tables_target log
+= {
+    .name          = "LOG",
+    .version       = IPTABLES_VERSION,
+    .size          = IP6T_ALIGN(sizeof(struct ip6t_log_info)),
+    .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_log_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_target6(&log);
+}
diff --git a/extensions/libip6t_LOG.man b/extensions/libip6t_LOG.man
new file mode 100644 (file)
index 0000000..9d51fd4
--- /dev/null
@@ -0,0 +1,31 @@
+Turn on kernel logging of matching packets.  When this option is set
+for a rule, the Linux kernel will print some information on all
+matching packets (like most IPv6 IPv6-header fields) via the kernel log
+(where it can be read with
+.I dmesg
+or 
+.IR syslogd (8)).
+This is a "non-terminating target", i.e. rule traversal continues at
+the next rule.  So if you want to LOG the packets you refuse, use two
+separate rules with the same matching criteria, first using target LOG
+then DROP (or REJECT).
+.TP
+.BI "--log-level " "level"
+Level of logging (numeric or see \fIsyslog.conf\fP(5)).
+.TP
+.BI "--log-prefix " "prefix"
+Prefix log messages with the specified prefix; up to 29 letters long,
+and useful for distinguishing messages in the logs.
+.TP
+.B --log-tcp-sequence
+Log TCP sequence numbers. This is a security risk if the log is
+readable by users.
+.TP
+.B --log-tcp-options
+Log options from the TCP packet header.
+.TP
+.B --log-ip-options
+Log options from the IPv6 packet header.
+.TP
+.B --log-uid
+Log the userid of the process which generated the packet.
diff --git a/extensions/libip6t_MARK.c b/extensions/libip6t_MARK.c
new file mode 100644 (file)
index 0000000..a7f1a9d
--- /dev/null
@@ -0,0 +1,131 @@
+/* Shared library add-on to iptables to add MARK target support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv6/ip6t_MARK.h"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"MARK target v%s options:\n"
+"  --set-mark value                   Set nfmark value\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "set-mark", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ip6t_entry_target *t, unsigned int *nfcache)
+{
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      struct ip6t_entry_target **target)
+{
+       struct ip6t_mark_target_info *markinfo
+               = (struct ip6t_mark_target_info *)(*target)->data;
+
+       switch (c) {
+       case '1':
+#ifdef KERNEL_64_USERSPACE_32
+               if (string_to_number_ll(optarg, 0, 0, 
+                                    &markinfo->mark))
+#else
+               if (string_to_number_l(optarg, 0, 0, 
+                                    &markinfo->mark))
+#endif
+                       exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "MARK target: Can't specify --set-mark twice");
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK target: Parameter --set-mark is required");
+}
+
+#ifdef KERNEL_64_USERSPACE_32
+static void
+print_mark(unsigned long long mark)
+{
+       printf("0x%llx ", mark);
+}
+#else
+static void
+print_mark(unsigned long mark)
+{
+       printf("0x%lx ", mark);
+}
+#endif
+
+/* Prints out the targinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_target *target,
+      int numeric)
+{
+       const struct ip6t_mark_target_info *markinfo =
+               (const struct ip6t_mark_target_info *)target->data;
+
+       printf("MARK set ");
+       print_mark(markinfo->mark);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_target *target)
+{
+       const struct ip6t_mark_target_info *markinfo =
+               (const struct ip6t_mark_target_info *)target->data;
+
+       printf("--set-mark ");
+       print_mark(markinfo->mark);
+}
+
+static
+struct ip6tables_target mark = {
+       .name          = "MARK",
+       .version       = IPTABLES_VERSION,
+       .size          = IP6T_ALIGN(sizeof(struct ip6t_mark_target_info)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_mark_target_info)),
+       .help          = &help,
+       .init          = &init,
+       .parse         = &parse,
+       .final_check   = &final_check,
+       .print         = &print,
+       .save          = &save,
+       .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_target6(&mark);
+}
diff --git a/extensions/libip6t_MARK.man b/extensions/libip6t_MARK.man
new file mode 100644 (file)
index 0000000..1f3260c
--- /dev/null
@@ -0,0 +1,6 @@
+This is used to set the netfilter mark value associated with the
+packet.  It is only valid in the
+.B mangle
+table.
+.TP
+.BI "--set-mark " "mark"
diff --git a/extensions/libip6t_NFLOG.c b/extensions/libip6t_NFLOG.c
new file mode 100644 (file)
index 0000000..2c0cd3d
--- /dev/null
@@ -0,0 +1,161 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <ip6tables.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter/xt_NFLOG.h>
+
+enum {
+       NFLOG_GROUP     = 0x1,
+       NFLOG_PREFIX    = 0x2,
+       NFLOG_RANGE     = 0x4,
+       NFLOG_THRESHOLD = 0x8,
+};
+
+static struct option opts[] = {
+       { "nflog-group",     1, 0, NFLOG_GROUP },
+       { "nflog-prefix",    1, 0, NFLOG_PREFIX },
+       { "nflog-range",     1, 0, NFLOG_RANGE },
+       { "nflog-threshold", 1, 0, NFLOG_THRESHOLD },
+};
+
+static void help(void)
+{
+       printf("NFLOG v%s options:\n"
+              " --nflog-group NUM              NETLINK group used for logging\n"
+              " --nflog-range NUM              Number of byte to copy\n"
+              " --nflog-threshold NUM          Message threshold of in-kernel queue\n"
+              " --nflog-prefix STRING          Prefix string for log messages\n\n",
+              IPTABLES_VERSION);
+}
+
+static void init(struct ip6t_entry_target *t, unsigned int *nfcache)
+{
+       struct xt_nflog_info *info = (struct xt_nflog_info *)t->data;
+
+       info->group     = 0;
+       info->threshold = XT_NFLOG_DEFAULT_THRESHOLD;
+}
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+                const struct ip6t_entry *entry,
+                struct xt_entry_target **target)
+{
+       struct xt_nflog_info *info = (struct xt_nflog_info *)(*target)->data;
+       int n;
+
+       switch (c) {
+       case NFLOG_GROUP:
+               if (*flags & NFLOG_GROUP)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --nflog-group twice");
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --nflog-group");
+
+               n = atoi(optarg);
+               if (n < 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--nflog-group can not be negative");
+               info->group = n;
+               break;
+       case NFLOG_PREFIX:
+               if (*flags & NFLOG_PREFIX)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --nflog-prefix twice");
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --nflog-prefix");
+
+               n = strlen(optarg);
+               if (n == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "No prefix specified for --nflog-prefix");
+               if (n >= sizeof(info->prefix))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--nflog-prefix too long, max %Zu characters",
+                                  sizeof(info->prefix) - 1);
+               if (n != strlen(strtok(optarg, "\n")))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Newlines are not allowed in --nflog-prefix");
+               strcpy(info->prefix, optarg);
+               break;
+       case NFLOG_RANGE:
+               if (*flags & NFLOG_RANGE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --nflog-range twice");
+               n = atoi(optarg);
+               if (n < 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid --nflog-range, must be >= 0");
+               info->len = n;
+               break;
+       case NFLOG_THRESHOLD:
+               if (*flags & NFLOG_THRESHOLD)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --nflog-threshold twice");
+               n = atoi(optarg);
+               if (n < 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid --nflog-threshold, must be >= 1");
+               info->threshold = n;
+               break;
+       default:
+               return 0;
+       }
+       *flags |= c;
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       return;
+}
+
+static void nflog_print(const struct xt_nflog_info *info, char *prefix)
+{
+       if (info->prefix[0] != '\0')
+               printf("%snflog-prefix \"%s\" ", prefix, info->prefix);
+       if (info->group)
+               printf("%snflog-group %u ", prefix, info->group);
+       if (info->len)
+               printf("%snflog-range %u ", prefix, info->len);
+       if (info->threshold != XT_NFLOG_DEFAULT_THRESHOLD)
+               printf("%snflog-threshold %u ", prefix, info->threshold);
+}
+
+static void print(const struct ip6t_ip6 *ip, const struct xt_entry_target *target,
+                 int numeric)
+{
+       const struct xt_nflog_info *info = (struct xt_nflog_info *)target->data;
+
+       nflog_print(info, "");
+}
+
+static void save(const struct ip6t_ip6 *ip, const struct xt_entry_target *target)
+{
+       const struct xt_nflog_info *info = (struct xt_nflog_info *)target->data;
+
+       nflog_print(info, "--");
+}
+
+static struct ip6tables_target nflog = {
+       .name           = "NFLOG",
+       .version        = IPTABLES_VERSION,
+       .size           = XT_ALIGN(sizeof(struct xt_nflog_info)),
+       .userspacesize  = XT_ALIGN(sizeof(struct xt_nflog_info)),
+       .help           = help,
+       .init           = init,
+       .parse          = parse,
+       .final_check    = final_check,
+       .print          = print,
+       .save           = save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_target6(&nflog);
+}
diff --git a/extensions/libip6t_NFQUEUE.c b/extensions/libip6t_NFQUEUE.c
new file mode 100644 (file)
index 0000000..e1964af
--- /dev/null
@@ -0,0 +1,114 @@
+/* Shared library add-on to ip666666tables for NFQ
+ *
+ * (C) 2005 by Harald Welte <laforge@netfilter.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv4/ipt_NFQUEUE.h>
+
+static void init(struct ip6t_entry_target *t, unsigned int *nfcache) 
+{
+}
+
+static void help(void) 
+{
+       printf(
+"NFQUEUE target options\n"
+"  --queue-num value           Send packet to QUEUE number <value>.\n"
+"                              Valid queue numbers are 0-65535\n"
+);
+}
+
+static struct option opts[] = {
+       { "queue-num", 1, 0, 'F' },
+       { 0 }
+};
+
+static void
+parse_num(const char *s, struct ipt_NFQ_info *tinfo)
+{
+       unsigned int num;
+       
+       if (string_to_number(s, 0, 65535, &num) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid queue number `%s'\n", s);
+
+       tinfo->queuenum = num & 0xffff;
+       return;
+}
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      struct ip6t_entry_target **target)
+{
+       struct ipt_NFQ_info *tinfo
+               = (struct ipt_NFQ_info *)(*target)->data;
+
+       switch (c) {
+       case 'F':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM, "NFQUEUE target: "
+                                  "Only use --queue-num ONCE!");
+               parse_num(optarg, tinfo);
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_target *target,
+      int numeric)
+{
+       const struct ipt_NFQ_info *tinfo =
+               (const struct ipt_NFQ_info *)target->data;
+       printf("NFQUEUE num %u", tinfo->queuenum);
+}
+
+/* Saves the union ip6t_targinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_target *target)
+{
+       const struct ipt_NFQ_info *tinfo =
+               (const struct ipt_NFQ_info *)target->data;
+
+       printf("--queue-num %u ", tinfo->queuenum);
+}
+
+static struct ip6tables_target nfqueue = { 
+       .next           = NULL,
+       .name           = "NFQUEUE",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ipt_NFQ_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ipt_NFQ_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target6(&nfqueue);
+}
diff --git a/extensions/libip6t_NFQUEUE.man b/extensions/libip6t_NFQUEUE.man
new file mode 100644 (file)
index 0000000..c4e9d11
--- /dev/null
@@ -0,0 +1,12 @@
+This target is an extension of the QUEUE target. As opposed to QUEUE, it allows
+you to put a packet into any specific queue, identified by its 16-bit queue
+number.  
+.TP
+.BR "--queue-num " "\fIvalue"
+This specifies the QUEUE number to use. Valud queue numbers are 0 to 65535. The default value is 0.
+.TP
+It can only be used with Kernel versions 2.6.14 or later, since it requires
+the
+.B
+nfnetlink_queue
+kernel support.
diff --git a/extensions/libip6t_REJECT.c b/extensions/libip6t_REJECT.c
new file mode 100644 (file)
index 0000000..879716b
--- /dev/null
@@ -0,0 +1,170 @@
+/* Shared library add-on to iptables to add customized REJECT support.
+ *
+ * (C) 2000 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ * 
+ * ported to IPv6 by Harald Welte <laforge@gnumonks.org>
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_REJECT.h>
+
+struct reject_names {
+       const char *name;
+       const char *alias;
+       enum ip6t_reject_with with;
+       const char *desc;
+};
+
+static const struct reject_names reject_table[] = {
+       {"icmp6-no-route", "no-route",
+               IP6T_ICMP6_NO_ROUTE, "ICMPv6 no route"},
+       {"icmp6-adm-prohibited", "adm-prohibited",
+               IP6T_ICMP6_ADM_PROHIBITED, "ICMPv6 administratively prohibited"},
+#if 0
+       {"icmp6-not-neighbor", "not-neighbor"},
+               IP6T_ICMP6_NOT_NEIGHBOR, "ICMPv6 not a neighbor"},
+#endif
+       {"icmp6-addr-unreachable", "addr-unreach",
+               IP6T_ICMP6_ADDR_UNREACH, "ICMPv6 address unreachable"},
+       {"icmp6-port-unreachable", "port-unreach",
+               IP6T_ICMP6_PORT_UNREACH, "ICMPv6 port unreachable"},
+       {"tcp-reset", "tcp-reset",
+               IP6T_TCP_RESET, "TCP RST packet"}
+};
+
+static void
+print_reject_types()
+{
+       unsigned int i;
+
+       printf("Valid reject types:\n");
+
+       for (i = 0; i < sizeof(reject_table)/sizeof(struct reject_names); i++) {
+               printf("    %-25s\t%s\n", reject_table[i].name, reject_table[i].desc);
+               printf("    %-25s\talias\n", reject_table[i].alias);
+       }
+       printf("\n");
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"REJECT options:\n"
+"--reject-with type              drop input packet and send back\n"
+"                                a reply packet according to type:\n");
+
+       print_reject_types();
+}
+
+static struct option opts[] = {
+       { "reject-with", 1, 0, '1' },
+       { 0 }
+};
+
+/* Allocate and initialize the target. */
+static void
+init(struct ip6t_entry_target *t, unsigned int *nfcache)
+{
+       struct ip6t_reject_info *reject = (struct ip6t_reject_info *)t->data;
+
+       /* default */
+       reject->with = IP6T_ICMP6_PORT_UNREACH;
+
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      struct ip6t_entry_target **target)
+{
+       struct ip6t_reject_info *reject = 
+               (struct ip6t_reject_info *)(*target)->data;
+       unsigned int limit = sizeof(reject_table)/sizeof(struct reject_names);
+       unsigned int i;
+
+       switch(c) {
+       case '1':
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --reject-with");
+               for (i = 0; i < limit; i++) {
+                       if ((strncasecmp(reject_table[i].name, optarg, strlen(optarg)) == 0)
+                           || (strncasecmp(reject_table[i].alias, optarg, strlen(optarg)) == 0)) {
+                               reject->with = reject_table[i].with;
+                               return 1;
+                       }
+               }
+               exit_error(PARAMETER_PROBLEM, "unknown reject type `%s'",optarg);
+       default:
+               /* Fall through */
+               break;
+       }
+       return 0;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out ipt_reject_info. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_target *target,
+      int numeric)
+{
+       const struct ip6t_reject_info *reject
+               = (const struct ip6t_reject_info *)target->data;
+       unsigned int i;
+
+       for (i = 0; i < sizeof(reject_table)/sizeof(struct reject_names); i++) {
+               if (reject_table[i].with == reject->with)
+                       break;
+       }
+       printf("reject-with %s ", reject_table[i].name);
+}
+
+/* Saves ipt_reject in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, 
+                const struct ip6t_entry_target *target)
+{
+       const struct ip6t_reject_info *reject
+               = (const struct ip6t_reject_info *)target->data;
+       unsigned int i;
+
+       for (i = 0; i < sizeof(reject_table)/sizeof(struct reject_names); i++)
+               if (reject_table[i].with == reject->with)
+                       break;
+
+       printf("--reject-with %s ", reject_table[i].name);
+}
+
+struct ip6tables_target reject = {
+       .name = "REJECT",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_reject_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_reject_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_target6(&reject);
+}
diff --git a/extensions/libip6t_REJECT.man b/extensions/libip6t_REJECT.man
new file mode 100644 (file)
index 0000000..909d826
--- /dev/null
@@ -0,0 +1,36 @@
+This is used to send back an error packet in response to the matched
+packet: otherwise it is equivalent to 
+.B DROP
+so it is a terminating TARGET, ending rule traversal.
+This target is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains.  The following option controls the nature of the error packet
+returned:
+.TP
+.BI "--reject-with " "type"
+The type given can be
+.nf
+.B " icmp6-no-route"
+.B " no-route"
+.B " icmp6-adm-prohibited"
+.B " adm-prohibited"
+.B " icmp6-addr-unreachable"
+.B " addr-unreach"
+.B " icmp6-port-unreachable"
+.B " port-unreach"
+.fi
+which return the appropriate ICMPv6 error message (\fBport-unreach\fP is
+the default). Finally, the option
+.B tcp-reset
+can be used on rules which only match the TCP protocol: this causes a
+TCP RST packet to be sent back.  This is mainly useful for blocking 
+.I ident
+(113/tcp) probes which frequently occur when sending mail to broken mail
+hosts (which won't accept your mail otherwise).
+.B tcp-reset
+can only be used with kernel versions 2.6.14 or latter.
+
diff --git a/extensions/libip6t_SECMARK.c b/extensions/libip6t_SECMARK.c
new file mode 100644 (file)
index 0000000..8fbae05
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Shared library add-on to iptables to add SECMARK target support.
+ *
+ * Based on the MARK target.
+ *
+ * IPv6 version.
+ *
+ * Copyright (C) 2006 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter/xt_SECMARK.h>
+
+#define PFX "SECMARK target: "
+
+static void help(void)
+{
+       printf(
+"SECMARK target v%s options:\n"
+"  --selctx value                     Set the SELinux security context\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "selctx", 1, 0, '1' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void init(struct ip6t_entry_target *t, unsigned int *nfcache)
+{ }
+
+/*
+ * Function which parses command options; returns true if it
+ * ate an option.
+ */
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+                 const struct ip6t_entry *entry, struct ip6t_entry_target **target)
+{
+       struct xt_secmark_target_info *info =
+               (struct xt_secmark_target_info*)(*target)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & SECMARK_MODE_SEL)
+                       exit_error(PARAMETER_PROBLEM, PFX
+                                  "Can't specify --selctx twice");
+               info->mode = SECMARK_MODE_SEL;
+
+               if (strlen(optarg) > SECMARK_SELCTX_MAX-1)
+                       exit_error(PARAMETER_PROBLEM, PFX
+                                  "Maximum length %u exceeded by --selctx"
+                                  " parameter (%zu)",
+                                  SECMARK_SELCTX_MAX-1, strlen(optarg));
+
+               strcpy(info->u.sel.selctx, optarg);
+               *flags |= SECMARK_MODE_SEL;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, PFX "parameter required");
+}
+
+static void print_secmark(struct xt_secmark_target_info *info)
+{
+       switch (info->mode) {
+       case SECMARK_MODE_SEL:
+               printf("selctx %s ", info->u.sel.selctx);\
+               break;
+       
+       default:
+               exit_error(OTHER_PROBLEM, PFX "invalid mode %hhu\n", info->mode);
+       }
+}
+
+static void print(const struct ip6t_ip6 *ip,
+                 const struct ip6t_entry_target *target, int numeric)
+{
+       struct xt_secmark_target_info *info =
+               (struct xt_secmark_target_info*)(target)->data;
+
+       printf("SECMARK ");
+       print_secmark(info);
+}
+
+/* Saves the target info in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_target *target)
+{
+       struct xt_secmark_target_info *info =
+               (struct xt_secmark_target_info*)target->data;
+
+       printf("--");
+       print_secmark(info);
+}
+
+static struct ip6tables_target secmark = {
+       .name           = "SECMARK",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct xt_secmark_target_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct xt_secmark_target_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target6(&secmark);
+}
diff --git a/extensions/libip6t_SECMARK.man b/extensions/libip6t_SECMARK.man
new file mode 100644 (file)
index 0000000..f892de9
--- /dev/null
@@ -0,0 +1,7 @@
+This is used to set the security mark value associated with the
+packet for use by security subsystems such as SELinux.  It is only
+valid in the
+.B mangle
+table.
+.TP
+.BI "--selctx " "security_context"
diff --git a/extensions/libip6t_TCPMSS.c b/extensions/libip6t_TCPMSS.c
new file mode 100644 (file)
index 0000000..7fcccd5
--- /dev/null
@@ -0,0 +1,134 @@
+/* Shared library add-on to iptables to add TCPMSS target support.
+ *
+ * Copyright (c) 2000 Marc Boucher
+*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_TCPMSS.h>
+
+struct mssinfo {
+       struct ip6t_entry_target t;
+       struct ip6t_tcpmss_info mss;
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"TCPMSS target v%s mutually-exclusive options:\n"
+"  --set-mss value               explicitly set MSS option to specified value\n"
+"  --clamp-mss-to-pmtu           automatically clamp MSS value to (path_MTU - 60)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "set-mss", 1, 0, '1' },
+       { "clamp-mss-to-pmtu", 0, 0, '2' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ip6t_entry_target *t, unsigned int *nfcache)
+{
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      struct ip6t_entry_target **target)
+{
+       struct ip6t_tcpmss_info *mssinfo
+               = (struct ip6t_tcpmss_info *)(*target)->data;
+
+       switch (c) {
+               unsigned int mssval;
+
+       case '1':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "TCPMSS target: Only one option may be specified");
+               if (string_to_number(optarg, 0, 65535 - 60, &mssval) == -1)
+                       exit_error(PARAMETER_PROBLEM, "Bad TCPMSS value `%s'", optarg);
+
+               mssinfo->mss = mssval;
+               *flags = 1;
+               break;
+
+       case '2':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "TCPMSS target: Only one option may be specified");
+               mssinfo->mss = IP6T_TCPMSS_CLAMP_PMTU;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "TCPMSS target: At least one parameter is required");
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ip6t_ip6 *ip6,
+      const struct ip6t_entry_target *target,
+      int numeric)
+{
+       const struct ip6t_tcpmss_info *mssinfo =
+               (const struct ip6t_tcpmss_info *)target->data;
+       if(mssinfo->mss == IP6T_TCPMSS_CLAMP_PMTU)
+               printf("TCPMSS clamp to PMTU ");
+       else
+               printf("TCPMSS set %u ", mssinfo->mss);
+}
+
+/* Saves the union ip6t_targinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_target *target)
+{
+       const struct ip6t_tcpmss_info *mssinfo =
+               (const struct ip6t_tcpmss_info *)target->data;
+
+       if(mssinfo->mss == IP6T_TCPMSS_CLAMP_PMTU)
+               printf("--clamp-mss-to-pmtu ");
+       else
+               printf("--set-mss %u ", mssinfo->mss);
+}
+
+static struct ip6tables_target mss = {
+       .next           = NULL,
+       .name           = "TCPMSS",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_tcpmss_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_tcpmss_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target6(&mss);
+}
diff --git a/extensions/libip6t_TCPMSS.man b/extensions/libip6t_TCPMSS.man
new file mode 100644 (file)
index 0000000..b4c357e
--- /dev/null
@@ -0,0 +1,42 @@
+This target allows to alter the MSS value of TCP SYN packets, to control
+the maximum size for that connection (usually limiting it to your
+outgoing interface's MTU minus 60).  Of course, it can only be used
+in conjunction with
+.BR "-p tcp" .
+It is only valid in the
+.BR mangle
+table.
+.br
+This target is used to overcome criminally braindead ISPs or servers
+which block ICMPv6 Packet Too Big packets or are unable to send them.
+The symptoms of this problem are that everything works fine from your 
+Linux firewall/router, but machines behind it can never exchange large
+packets:
+.PD 0
+.RS 0.1i
+.TP 0.3i
+1)
+Web browsers connect, then hang with no data received.
+.TP
+2)
+Small mail works fine, but large emails hang.
+.TP
+3)
+ssh works fine, but scp hangs after initial handshaking.
+.RE
+.PD
+Workaround: activate this option and add a rule to your firewall
+configuration like:
+.nf
+ ip6tables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \\
+             -j TCPMSS --clamp-mss-to-pmtu
+.fi
+.TP
+.BI "--set-mss " "value"
+Explicitly set MSS option to specified value.
+.TP
+.B "--clamp-mss-to-pmtu"
+Automatically clamp MSS value to (path_MTU - 60).
+.TP
+These options are mutually exclusive.
+
diff --git a/extensions/libip6t_ah.c b/extensions/libip6t_ah.c
new file mode 100644 (file)
index 0000000..794e02e
--- /dev/null
@@ -0,0 +1,227 @@
+/* Shared library add-on to ip6tables to add AH support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_ah.h>
+                                        
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"AH v%s options:\n"
+" --ahspi [!] spi[:spi]         match spi (range)\n"
+" --ahlen [!] length            total length of this header\n"
+" --ahres                       check the reserved filed, too\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "ahspi", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "ahlen", .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "ahres", .has_arg = 0, .flag = 0, .val = '3' },
+       { .name = 0 }
+};
+
+static u_int32_t
+parse_ah_spi(const char *spistr, const char *typestr)
+{
+       unsigned long int spi;
+       char* ep;
+
+       spi = strtoul(spistr, &ep, 0);
+
+       if ( spistr == ep )
+               exit_error(PARAMETER_PROBLEM,
+                          "AH no valid digits in %s `%s'", typestr, spistr);
+
+       if ( spi == ULONG_MAX  && errno == ERANGE )
+               exit_error(PARAMETER_PROBLEM,
+                          "%s `%s' specified too big: would overflow",
+                          typestr, spistr);
+
+       if ( *spistr != '\0'  && *ep != '\0' )
+               exit_error(PARAMETER_PROBLEM,
+                          "AH error parsing %s `%s'", typestr, spistr);
+
+       return (u_int32_t) spi;
+}
+
+static void
+parse_ah_spis(const char *spistring, u_int32_t *spis)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(spistring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               spis[0] = spis[1] = parse_ah_spi(buffer, "spi");
+       else {
+               *cp = '\0';
+               cp++;
+
+               spis[0] = buffer[0] ? parse_ah_spi(buffer, "spi") : 0;
+               spis[1] = cp[0] ? parse_ah_spi(cp, "spi") : 0xFFFFFFFF;
+       }
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_ah *ahinfo = (struct ip6t_ah *)m->data;
+
+       ahinfo->spis[1] = 0xFFFFFFFF;
+       ahinfo->hdrlen = 0;
+       ahinfo->hdrres = 0;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_ah *ahinfo = (struct ip6t_ah *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IP6T_AH_SPI)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--ahspi' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_ah_spis(argv[optind-1], ahinfo->spis);
+               if (invert)
+                       ahinfo->invflags |= IP6T_AH_INV_SPI;
+               *flags |= IP6T_AH_SPI;
+               break;
+       case '2':
+               if (*flags & IP6T_AH_LEN)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--ahlen' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               ahinfo->hdrlen = parse_ah_spi(argv[optind-1], "length");
+               if (invert)
+                       ahinfo->invflags |= IP6T_AH_INV_LEN;
+               *flags |= IP6T_AH_LEN;
+               break;
+       case '3':
+               if (*flags & IP6T_AH_RES)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--ahres' allowed");
+               ahinfo->hdrres = 1;
+               *flags |= IP6T_AH_RES;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_spis(const char *name, u_int32_t min, u_int32_t max,
+           int invert)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFFFFFF || invert) {
+               if (min == max)
+                       printf("%s:%s%u ", name, inv, min);
+               else
+                       printf("%ss:%s%u:%u ", name, inv, min, max);
+       }
+}
+
+static void
+print_len(const char *name, u_int32_t len, int invert)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (len != 0 || invert)
+               printf("%s:%s%u ", name, inv, len);
+}
+
+/* Prints out the union ip6t_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_ah *ah = (struct ip6t_ah *)match->data;
+
+       printf("ah ");
+       print_spis("spi", ah->spis[0], ah->spis[1],
+                   ah->invflags & IP6T_AH_INV_SPI);
+       print_len("length", ah->hdrlen, 
+                   ah->invflags & IP6T_AH_INV_LEN);
+
+       if (ah->hdrres)
+               printf("reserved ");
+
+       if (ah->invflags & ~IP6T_AH_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      ah->invflags & ~IP6T_AH_INV_MASK);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_ah *ahinfo = (struct ip6t_ah *)match->data;
+
+       if (!(ahinfo->spis[0] == 0
+           && ahinfo->spis[1] == 0xFFFFFFFF)) {
+               printf("--ahspi %s", 
+                       (ahinfo->invflags & IP6T_AH_INV_SPI) ? "! " : "");
+               if (ahinfo->spis[0]
+                   != ahinfo->spis[1])
+                       printf("%u:%u ",
+                              ahinfo->spis[0],
+                              ahinfo->spis[1]);
+               else
+                       printf("%u ",
+                              ahinfo->spis[0]);
+       }
+
+       if (ahinfo->hdrlen != 0 || (ahinfo->invflags & IP6T_AH_INV_LEN) ) {
+               printf("--ahlen %s%u ", 
+                       (ahinfo->invflags & IP6T_AH_INV_LEN) ? "! " : "", 
+                       ahinfo->hdrlen);
+       }
+
+       if (ahinfo->hdrres != 0 )
+               printf("--ahres ");
+}
+
+static
+struct ip6tables_match ah = {
+       .name          = "ah",
+       .version       = IPTABLES_VERSION,
+       .size          = IP6T_ALIGN(sizeof(struct ip6t_ah)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_ah)),
+       .help          = &help,
+       .init          = &init,
+       .parse         = &parse,
+       .final_check   = &final_check,
+       .print         = &print,
+       .save          = &save,
+       .extra_opts    = opts
+};
+
+void
+_init(void)
+{
+       register_match6(&ah);
+}
diff --git a/extensions/libip6t_ah.man b/extensions/libip6t_ah.man
new file mode 100644 (file)
index 0000000..09d00fd
--- /dev/null
@@ -0,0 +1,10 @@
+This module matches the parameters in Authentication header of IPsec packets.
+.TP
+.BR "--ahspi " "[!] \fIspi\fP[:\fIspi\fP]"
+Matches SPI.
+.TP
+.BR "--ahlen " "[!] \fIlength"
+Total length of this header in octets.
+.TP
+.BI "--ahres"
+Matches if the reserved field is filled with zero.
diff --git a/extensions/libip6t_condition.c b/extensions/libip6t_condition.c
new file mode 100644 (file)
index 0000000..0e94c39
--- /dev/null
@@ -0,0 +1,106 @@
+/* Shared library add-on to ip6tables for condition match */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <ip6tables.h>
+
+#include<linux/netfilter_ipv6/ip6_tables.h>
+#include<linux/netfilter_ipv6/ip6t_condition.h>
+
+
+static void
+help(void)
+{
+       printf("condition match v%s options:\n"
+              "--condition [!] filename       "
+              "Match on boolean value stored in /proc file\n",
+              IPTABLES_VERSION);
+}
+
+
+static struct option opts[] = {
+       { .name = "condition", .has_arg = 1, .flag = 0, .val = 'X' },
+       { .name = 0 }
+};
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry, unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct condition6_info *info =
+           (struct condition6_info *) (*match)->data;
+
+       if (c == 'X') {
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify multiple conditions");
+
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (strlen(argv[optind - 1]) < CONDITION6_NAME_LEN)
+                       strcpy(info->name, argv[optind - 1]);
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "File name too long");
+
+               info->invert = invert;
+               *flags = 1;
+               return 1;
+       }
+
+       return 0;
+}
+
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "Condition match: must specify --condition");
+}
+
+
+static void
+print(const struct ip6t_ip6 *ip,
+                 const struct ip6t_entry_match *match, int numeric)
+{
+       const struct condition6_info *info =
+           (const struct condition6_info *) match->data;
+
+       printf("condition %s%s ", (info->invert) ? "!" : "", info->name);
+}
+
+
+static void
+save(const struct ip6t_ip6 *ip,
+                const struct ip6t_entry_match *match)
+{
+       const struct condition6_info *info =
+           (const struct condition6_info *) match->data;
+
+       printf("--condition %s\"%s\" ", (info->invert) ? "! " : "", info->name);
+}
+
+
+static struct ip6tables_match condition = {
+       .name = "condition",
+       .version = IPTABLES_VERSION,
+       .size = IP6T_ALIGN(sizeof(struct condition6_info)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct condition6_info)),
+       .help = &help,
+       .parse = &parse,
+       .final_check = &final_check,
+       .print = &print,
+       .save = &save,
+       .extra_opts = opts
+};
+
+
+void
+_init(void)
+{
+       register_match6(&condition);
+}
diff --git a/extensions/libip6t_condition.man b/extensions/libip6t_condition.man
new file mode 100644 (file)
index 0000000..e0bba75
--- /dev/null
@@ -0,0 +1,4 @@
+This matches if a specific /proc filename is '0' or '1'.
+.TP
+.BR "--condition " "[!] \fIfilename"
+Match on boolean value stored in /proc/net/ip6t_condition/filename file
diff --git a/extensions/libip6t_connmark.c b/extensions/libip6t_connmark.c
new file mode 100644 (file)
index 0000000..419da30
--- /dev/null
@@ -0,0 +1,151 @@
+/* Shared library add-on to iptables to add connmark matching support.
+ *
+ * (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * Version 1.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <ip6tables.h>
+#include "../include/linux/netfilter_ipv4/ipt_connmark.h"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"CONNMARK match v%s options:\n"
+"[!] --mark value[/mask]         Match nfmark value with optional mask\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "mark", 1, 0, '1' },
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       /* Can't cache this. */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ipt_connmark_info *markinfo = (struct ipt_connmark_info *)(*match)->data;
+
+       switch (c) {
+               char *end;
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               markinfo->mark = strtoul(optarg, &end, 0);
+               markinfo->mask = 0xffffffffUL;
+               
+               if (*end == '/')
+                       markinfo->mask = strtoul(end+1, &end, 0);
+
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
+               if (invert)
+                       markinfo->invert = 1;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+print_mark(unsigned long mark, unsigned long mask, int numeric)
+{
+       if(mask != 0xffffffffUL)
+               printf("0x%lx/0x%lx ", mark, mask);
+       else
+               printf("0x%lx ", mark);
+}
+
+/* Final check; must have specified --mark. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK match: You must specify `--mark'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       struct ipt_connmark_info *info = (struct ipt_connmark_info *)match->data;
+
+       printf("CONNMARK match ");
+       if (info->invert)
+               printf("!");
+       print_mark(info->mark, info->mask, numeric);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       struct ipt_connmark_info *info = (struct ipt_connmark_info *)match->data;
+
+       if (info->invert)
+               printf("! ");
+
+       printf("--mark ");
+       print_mark(info->mark, info->mask, 0);
+}
+
+static struct ip6tables_match connmark_match = {
+    .name          = "connmark",
+    .version       = IPTABLES_VERSION,
+    .size          = IP6T_ALIGN(sizeof(struct ipt_connmark_info)),
+    .userspacesize = IP6T_ALIGN(sizeof(struct ipt_connmark_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_match6(&connmark_match);
+}
diff --git a/extensions/libip6t_dst.c b/extensions/libip6t_dst.c
new file mode 100644 (file)
index 0000000..19ca23c
--- /dev/null
@@ -0,0 +1,269 @@
+/* Shared library add-on to ip6tables to add Hop-by-Hop and Dst headers support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_opts.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#ifdef HOPBYHOP
+#define UNAME "HBH"
+#define LNAME "hbh"
+#else
+#define UNAME "DST"
+#define LNAME "dst"
+#endif
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+UNAME " v%s options:\n"
+" --" LNAME "-len [!] length           total length of this header\n"
+" --" LNAME "-opts TYPE[:LEN][,TYPE[:LEN]...] \n"
+"                               Options and its length (list, max: %d)\n", 
+IPTABLES_VERSION, IP6T_OPTS_OPTSNR);
+}
+
+static struct option opts[] = {
+       { .name = LNAME "-len",        .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = LNAME "-opts",       .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = LNAME "-not-strict", .has_arg = 1, .flag = 0, .val = '3' },
+       { .name = 0 }
+};
+
+static u_int32_t
+parse_opts_num(const char *idstr, const char *typestr)
+{
+       unsigned long int id;
+       char* ep;
+
+       id = strtoul(idstr, &ep, 0);
+
+       if ( idstr == ep ) {
+               exit_error(PARAMETER_PROBLEM,
+                          UNAME " no valid digits in %s `%s'", typestr, idstr);
+       }
+       if ( id == ULONG_MAX  && errno == ERANGE ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "%s `%s' specified too big: would overflow",
+                          typestr, idstr);
+       }
+       if ( *idstr != '\0'  && *ep != '\0' ) {
+               exit_error(PARAMETER_PROBLEM,
+                          UNAME " error parsing %s `%s'", typestr, idstr);
+       }
+       return (u_int32_t) id;
+}
+
+static int
+parse_options(const char *optsstr, u_int16_t *opts)
+{
+        char *buffer, *cp, *next, *range;
+        unsigned int i;
+       
+       buffer = strdup(optsstr);
+        if (!buffer)
+               exit_error(OTHER_PROBLEM, "strdup failed");
+                       
+        for (cp = buffer, i = 0; cp && i < IP6T_OPTS_OPTSNR; cp = next, i++)
+        {
+                next = strchr(cp, ',');
+
+                if (next)
+                       *next++='\0';
+
+                range = strchr(cp, ':');
+
+                if (range) {
+                        if (i == IP6T_OPTS_OPTSNR-1)
+                                exit_error(PARAMETER_PROBLEM,
+                                           "too many ports specified");
+                        *range++ = '\0';
+                }
+
+                opts[i] = (u_int16_t)((parse_opts_num(cp,"opt") & 0x000000FF)<<8); 
+                if (range) {
+                       if (opts[i] == 0)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "PAD0 hasn't got length");
+                        opts[i] |= (u_int16_t)(parse_opts_num(range,"length") &
+                                       0x000000FF);
+                } else
+                        opts[i] |= (0x00FF);
+
+#ifdef DEBUG
+               printf("opts str: %s %s\n", cp, range);
+               printf("opts opt: %04X\n", opts[i]);
+#endif
+       }
+
+        if (cp)
+               exit_error(PARAMETER_PROBLEM, "too many addresses specified");
+
+       free(buffer);
+
+#ifdef DEBUG
+       printf("addr nr: %d\n", i);
+#endif
+
+       return i;
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_opts *optinfo = (struct ip6t_opts *)m->data;
+
+       optinfo->hdrlen = 0;
+       optinfo->flags = 0;
+       optinfo->invflags = 0;
+       optinfo->optsnr = 0;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_opts *optinfo = (struct ip6t_opts *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IP6T_OPTS_LEN)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--" LNAME "-len' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               optinfo->hdrlen = parse_opts_num(argv[optind-1], "length");
+               if (invert)
+                       optinfo->invflags |= IP6T_OPTS_INV_LEN;
+               optinfo->flags |= IP6T_OPTS_LEN;
+               *flags |= IP6T_OPTS_LEN;
+               break;
+       case '2':
+               if (*flags & IP6T_OPTS_OPTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--" LNAME "-opts' allowed");
+                check_inverse(optarg, &invert, &optind, 0);
+                if (invert)
+                        exit_error(PARAMETER_PROBLEM,
+                               " '!' not allowed with `--" LNAME "-opts'");
+               optinfo->optsnr = parse_options(argv[optind-1], optinfo->opts);
+               optinfo->flags |= IP6T_OPTS_OPTS;
+               *flags |= IP6T_OPTS_OPTS;
+               break;
+       case '3':
+               if (*flags & IP6T_OPTS_NSTRICT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--" LNAME "-not-strict' allowed");
+               if ( !(*flags & IP6T_OPTS_OPTS) )
+                       exit_error(PARAMETER_PROBLEM,
+                                  "`--" LNAME "-opts ...' required before `--"
+                                  LNAME "-not-strict'");
+               optinfo->flags |= IP6T_OPTS_NSTRICT;
+               *flags |= IP6T_OPTS_NSTRICT;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_options(int optsnr, u_int16_t *optsp)
+{
+       unsigned int i;
+
+       for(i = 0; i < optsnr; i++) {
+               printf("%d", (optsp[i] & 0xFF00) >> 8);
+
+               if ((optsp[i] & 0x00FF) != 0x00FF)
+                       printf(":%d", (optsp[i] & 0x00FF));
+
+               printf("%c", (i != optsnr - 1) ? ',' : ' ');
+       }
+}
+
+/* Prints out the union ip6t_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
+
+       printf(LNAME " ");
+       if (optinfo->flags & IP6T_OPTS_LEN)
+               printf("length:%s%u ",
+                       optinfo->invflags & IP6T_OPTS_INV_LEN ? "!" : "",
+                       optinfo->hdrlen);
+
+       if (optinfo->flags & IP6T_OPTS_OPTS)
+               printf("opts ");
+
+       print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
+
+       if (optinfo->flags & IP6T_OPTS_NSTRICT)
+               printf("not-strict ");
+
+       if (optinfo->invflags & ~IP6T_OPTS_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      optinfo->invflags & ~IP6T_OPTS_INV_MASK);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
+
+       if (optinfo->flags & IP6T_OPTS_LEN) {
+               printf("--" LNAME "-len %s%u ", 
+                       (optinfo->invflags & IP6T_OPTS_INV_LEN) ? "! " : "", 
+                       optinfo->hdrlen);
+       }
+
+       if (optinfo->flags & IP6T_OPTS_OPTS)
+               printf("--" LNAME "-opts ");
+
+       print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
+
+       if (optinfo->flags & IP6T_OPTS_NSTRICT)
+               printf("--" LNAME "-not-strict ");
+}
+
+static
+struct ip6tables_match optstruct = {
+       .name          = LNAME,
+       .version       = IPTABLES_VERSION,
+       .size          = IP6T_ALIGN(sizeof(struct ip6t_opts)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_opts)),
+       .help          = &help,
+       .init          = &init,
+       .parse         = &parse,
+       .final_check   = &final_check,
+       .print         = &print,
+       .save          = &save,
+       .extra_opts    = opts
+};
+
+void
+_init(void)
+{
+       register_match6(&optstruct);
+}
diff --git a/extensions/libip6t_dst.man b/extensions/libip6t_dst.man
new file mode 100644 (file)
index 0000000..f42d822
--- /dev/null
@@ -0,0 +1,7 @@
+This module matches the parameters in Destination Options header
+.TP
+.BR "--dst-len " "[!] \fIlength"
+Total length of this header in octets.
+.TP
+.BR "--dst-opts " "\fItype\fP[:\fIlength\fP][,\fItype\fP[:\fIlength\fP]...]"
+numeric type of option and the length of the option data in octets.
diff --git a/extensions/libip6t_esp.c b/extensions/libip6t_esp.c
new file mode 100644 (file)
index 0000000..886e09b
--- /dev/null
@@ -0,0 +1,185 @@
+/* Shared library add-on to ip6tables to add ESP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_esp.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"ESP v%s options:\n"
+" --espspi [!] spi[:spi]        match spi (range)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "espspi", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = 0 }
+};
+
+static u_int32_t
+parse_esp_spi(const char *spistr)
+{
+       unsigned long int spi;
+       char* ep;
+
+       spi = strtoul(spistr, &ep, 0);
+
+       if ( spistr == ep ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "ESP no valid digits in spi `%s'", spistr);
+       }
+       if ( spi == ULONG_MAX  && errno == ERANGE ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "spi `%s' specified too big: would overflow", spistr);
+       }       
+       if ( *spistr != '\0'  && *ep != '\0' ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "ESP error parsing spi `%s'", spistr);
+       }
+       return (u_int32_t) spi;
+}
+
+static void
+parse_esp_spis(const char *spistring, u_int32_t *spis)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(spistring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               spis[0] = spis[1] = parse_esp_spi(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               spis[0] = buffer[0] ? parse_esp_spi(buffer) : 0;
+               spis[1] = cp[0] ? parse_esp_spi(cp) : 0xFFFFFFFF;
+               if (spis[0] > spis[1])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid ESP spi range: %s", spistring);
+       }
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_esp *espinfo = (struct ip6t_esp *)m->data;
+
+       espinfo->spis[1] = 0xFFFFFFFF;
+}
+
+#define ESP_SPI 0x01
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_esp *espinfo = (struct ip6t_esp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & ESP_SPI)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--espspi' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_esp_spis(argv[optind-1], espinfo->spis);
+               if (invert)
+                       espinfo->invflags |= IP6T_ESP_INV_SPI;
+               *flags |= ESP_SPI;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_spis(const char *name, u_int32_t min, u_int32_t max,
+           int invert)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFFFFFF || invert) {
+               if (min == max)
+                       printf("%s:%s%u ", name, inv, min);
+               else
+                       printf("%ss:%s%u:%u ", name, inv, min, max);
+       }
+}
+
+/* Prints out the union ip6t_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_esp *esp = (struct ip6t_esp *)match->data;
+
+       printf("esp ");
+       print_spis("spi", esp->spis[0], esp->spis[1],
+                   esp->invflags & IP6T_ESP_INV_SPI);
+       if (esp->invflags & ~IP6T_ESP_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      esp->invflags & ~IP6T_ESP_INV_MASK);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_esp *espinfo = (struct ip6t_esp *)match->data;
+
+       if (!(espinfo->spis[0] == 0
+           && espinfo->spis[1] == 0xFFFFFFFF)) {
+               printf("--espspi %s", 
+                       (espinfo->invflags & IP6T_ESP_INV_SPI) ? "! " : "");
+               if (espinfo->spis[0]
+                   != espinfo->spis[1])
+                       printf("%u:%u ",
+                              espinfo->spis[0],
+                              espinfo->spis[1]);
+               else
+                       printf("%u ",
+                              espinfo->spis[0]);
+       }
+
+}
+
+static
+struct ip6tables_match esp = {
+       .name          = "esp",
+       .version       = IPTABLES_VERSION,
+       .size          = IP6T_ALIGN(sizeof(struct ip6t_esp)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_esp)),
+       .help          = &help,
+       .init          = &init,
+       .parse         = &parse,
+       .final_check   = &final_check,
+       .print         = &print,
+       .save          = &save,
+       .extra_opts    = opts
+};
+
+void
+_init(void)
+{
+       register_match6(&esp);
+}
diff --git a/extensions/libip6t_esp.man b/extensions/libip6t_esp.man
new file mode 100644 (file)
index 0000000..7898e02
--- /dev/null
@@ -0,0 +1,3 @@
+This module matches the SPIs in ESP header of IPsec packets.
+.TP
+.BR "--espspi " "[!] \fIspi\fP[:\fIspi\fP]"
diff --git a/extensions/libip6t_eui64.c b/extensions/libip6t_eui64.c
new file mode 100644 (file)
index 0000000..c74b04d
--- /dev/null
@@ -0,0 +1,76 @@
+/* Shared library add-on to ip6tables to add EUI64 address checking support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+#include <ip6tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"eui64 v%s options:\n"
+" This module hasn't got any option\n"
+" This module checks for EUI64 IPv6 addresses\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       {0}
+};
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       return 0;
+}
+
+/* Final check */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       printf("eui64 ");
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+
+}
+
+static struct ip6tables_match eui64 = {
+       .name           = "eui64",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(int)),
+       .userspacesize  = IP6T_ALIGN(sizeof(int)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_match6(&eui64);
+}
diff --git a/extensions/libip6t_eui64.man b/extensions/libip6t_eui64.man
new file mode 100644 (file)
index 0000000..cd80b98
--- /dev/null
@@ -0,0 +1,10 @@
+This module matches the EUI-64 part of a stateless autoconfigured IPv6 address.
+It compares the EUI-64 derived from the source MAC address in Ethernet frame
+with the lower 64 bits of the IPv6 source address. But "Universal/Local"
+bit is not compared. This module doesn't match other link layer frame, and
+is only valid in the
+.BR PREROUTING ,
+.BR INPUT
+and
+.BR FORWARD
+chains.
diff --git a/extensions/libip6t_frag.c b/extensions/libip6t_frag.c
new file mode 100644 (file)
index 0000000..51a14fa
--- /dev/null
@@ -0,0 +1,272 @@
+/* Shared library add-on to ip6tables to add Fragmentation header support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_frag.h>
+                                        
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"FRAG v%s options:\n"
+" --fragid [!] id[:id]          match the id (range)\n"
+" --fraglen [!] length          total length of this header\n"
+" --fragres                     check the reserved filed, too\n"
+" --fragfirst                   matches on the first fragment\n"
+" [--fragmore|--fraglast]       there are more fragments or this\n"
+"                               is the last one\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "fragid",    .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "fraglen",   .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "fragres",   .has_arg = 0, .flag = 0, .val = '3' },
+       { .name = "fragfirst", .has_arg = 0, .flag = 0, .val = '4' },
+       { .name = "fragmore",  .has_arg = 0, .flag = 0, .val = '5' },
+       { .name = "fraglast",  .has_arg = 0, .flag = 0, .val = '6' },
+       { .name = 0 }
+};
+
+static u_int32_t
+parse_frag_id(const char *idstr, const char *typestr)
+{
+       unsigned long int id;
+       char* ep;
+
+       id = strtoul(idstr, &ep, 0);
+
+       if ( idstr == ep ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "FRAG no valid digits in %s `%s'", typestr, idstr);
+       }
+       if ( id == ULONG_MAX  && errno == ERANGE ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "%s `%s' specified too big: would overflow",
+                          typestr, idstr);
+       }       
+       if ( *idstr != '\0'  && *ep != '\0' ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "FRAG error parsing %s `%s'", typestr, idstr);
+       }
+       return (u_int32_t) id;
+}
+
+static void
+parse_frag_ids(const char *idstring, u_int32_t *ids)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(idstring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               ids[0] = ids[1] = parse_frag_id(buffer,"id");
+       else {
+               *cp = '\0';
+               cp++;
+
+               ids[0] = buffer[0] ? parse_frag_id(buffer,"id") : 0;
+               ids[1] = cp[0] ? parse_frag_id(cp,"id") : 0xFFFFFFFF;
+       }
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_frag *fraginfo = (struct ip6t_frag *)m->data;
+
+       fraginfo->ids[0] = 0x0L;
+       fraginfo->ids[1] = 0xFFFFFFFF;
+       fraginfo->hdrlen = 0;
+       fraginfo->flags = 0;
+       fraginfo->invflags = 0;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_frag *fraginfo = (struct ip6t_frag *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IP6T_FRAG_IDS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--fragid' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_frag_ids(argv[optind-1], fraginfo->ids);
+               if (invert)
+                       fraginfo->invflags |= IP6T_FRAG_INV_IDS;
+               fraginfo->flags |= IP6T_FRAG_IDS;
+               *flags |= IP6T_FRAG_IDS;
+               break;
+       case '2':
+               if (*flags & IP6T_FRAG_LEN)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--fraglen' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               fraginfo->hdrlen = parse_frag_id(argv[optind-1], "length");
+               if (invert)
+                       fraginfo->invflags |= IP6T_FRAG_INV_LEN;
+               fraginfo->flags |= IP6T_FRAG_LEN;
+               *flags |= IP6T_FRAG_LEN;
+               break;
+       case '3':
+               if (*flags & IP6T_FRAG_RES)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--fragres' allowed");
+               fraginfo->flags |= IP6T_FRAG_RES;
+               *flags |= IP6T_FRAG_RES;
+               break;
+       case '4':
+               if (*flags & IP6T_FRAG_FST)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--fragfirst' allowed");
+               fraginfo->flags |= IP6T_FRAG_FST;
+               *flags |= IP6T_FRAG_FST;
+               break;
+       case '5':
+               if (*flags & (IP6T_FRAG_MF|IP6T_FRAG_NMF)) 
+                       exit_error(PARAMETER_PROBLEM,
+                          "Only one `--fragmore' or `--fraglast' allowed");
+               fraginfo->flags |= IP6T_FRAG_MF;
+               *flags |= IP6T_FRAG_MF;
+               break;
+       case '6':
+               if (*flags & (IP6T_FRAG_MF|IP6T_FRAG_NMF)) 
+                       exit_error(PARAMETER_PROBLEM,
+                          "Only one `--fragmore' or `--fraglast' allowed");
+               fraginfo->flags |= IP6T_FRAG_NMF;
+               *flags |= IP6T_FRAG_NMF;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_ids(const char *name, u_int32_t min, u_int32_t max,
+           int invert)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFFFFFF || invert) {
+               printf("%s", name);
+               if (min == max)
+                       printf(":%s%u ", inv, min);
+               else
+                       printf("s:%s%u:%u ", inv, min, max);
+       }
+}
+
+/* Prints out the union ip6t_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_frag *frag = (struct ip6t_frag *)match->data;
+
+       printf("frag ");
+       print_ids("id", frag->ids[0], frag->ids[1],
+                   frag->invflags & IP6T_FRAG_INV_IDS);
+
+       if (frag->flags & IP6T_FRAG_LEN) {
+               printf("length:%s%u ",
+                       frag->invflags & IP6T_FRAG_INV_LEN ? "!" : "",
+                       frag->hdrlen);
+       }
+
+       if (frag->flags & IP6T_FRAG_RES)
+               printf("reserved ");
+
+       if (frag->flags & IP6T_FRAG_FST)
+               printf("first ");
+
+       if (frag->flags & IP6T_FRAG_MF)
+               printf("more ");
+
+       if (frag->flags & IP6T_FRAG_NMF)
+               printf("last ");
+
+       if (frag->invflags & ~IP6T_FRAG_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      frag->invflags & ~IP6T_FRAG_INV_MASK);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_frag *fraginfo = (struct ip6t_frag *)match->data;
+
+       if (!(fraginfo->ids[0] == 0
+           && fraginfo->ids[1] == 0xFFFFFFFF)) {
+               printf("--fragid %s", 
+                       (fraginfo->invflags & IP6T_FRAG_INV_IDS) ? "! " : "");
+               if (fraginfo->ids[0]
+                   != fraginfo->ids[1])
+                       printf("%u:%u ",
+                              fraginfo->ids[0],
+                              fraginfo->ids[1]);
+               else
+                       printf("%u ",
+                              fraginfo->ids[0]);
+       }
+
+       if (fraginfo->flags & IP6T_FRAG_LEN) {
+               printf("--fraglen %s%u ", 
+                       (fraginfo->invflags & IP6T_FRAG_INV_LEN) ? "! " : "", 
+                       fraginfo->hdrlen);
+       }
+
+       if (fraginfo->flags & IP6T_FRAG_RES)
+               printf("--fragres ");
+
+       if (fraginfo->flags & IP6T_FRAG_FST)
+               printf("--fragfirst ");
+
+       if (fraginfo->flags & IP6T_FRAG_MF)
+               printf("--fragmore ");
+
+       if (fraginfo->flags & IP6T_FRAG_NMF)
+               printf("--fraglast ");
+}
+
+static
+struct ip6tables_match frag = {
+       .name          = "frag",
+       .version       = IPTABLES_VERSION,
+       .size          = IP6T_ALIGN(sizeof(struct ip6t_frag)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_frag)),
+       .help          = &help,
+       .init          = &init,
+       .parse         = &parse,
+       .final_check   = &final_check,
+       .print         = &print,
+       .save          = &save,
+       .extra_opts    = opts
+};
+
+void
+_init(void)
+{
+       register_match6(&frag);
+}
diff --git a/extensions/libip6t_frag.man b/extensions/libip6t_frag.man
new file mode 100644 (file)
index 0000000..5ac13a4
--- /dev/null
@@ -0,0 +1,20 @@
+This module matches the parameters in Fragment header.
+.TP
+.BR "--fragid " "[!] \fIid\fP[:\fIid\fP]"
+Matches the given Identification or range of it.
+.TP
+.BR "--fraglen " "[!] \fIlength\fP"
+This option cannot be used with kernel version 2.6.10 or later. The length of
+Fragment header is static and this option doesn't make sense.
+.TP
+.BR "--fragres "
+Matches if the reserved fields are filled with zero.
+.TP
+.BR "--fragfirst "
+Matches on the first fragment.
+.TP
+.BR "[--fragmore]"
+Matches if there are more fragments.
+.TP
+.BR "[--fraglast]"
+Matches if this is the last fragement.
diff --git a/extensions/libip6t_hashlimit.c b/extensions/libip6t_hashlimit.c
new file mode 100644 (file)
index 0000000..70d2ff3
--- /dev/null
@@ -0,0 +1,369 @@
+/* ip6tables match extension for limiting packets per destination
+ *
+ * (C) 2003-2004 by Harald Welte <laforge@netfilter.org>
+ *
+ * Development of this code was funded by Astaro AG, http://www.astaro.com/
+ *
+ * Based on ipt_limit.c by
+ * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
+ * Hervé Eychenne    <rv@wallfire.org>
+ * 
+ * Error corections by nmalykh@bilim.com (22.01.2005)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <stddef.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter/xt_hashlimit.h>
+
+#define XT_HASHLIMIT_BURST     5
+
+/* miliseconds */
+#define XT_HASHLIMIT_GCINTERVAL        1000
+#define XT_HASHLIMIT_EXPIRE    10000
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"hashlimit v%s options:\n"
+"--hashlimit <avg>             max average match rate\n"
+"                                [Packets per second unless followed by \n"
+"                                /sec /minute /hour /day postfixes]\n"
+"--hashlimit-mode <mode>               mode is a comma-separated list of\n"
+"                                      dstip,srcip,dstport,srcport\n"
+"--hashlimit-name <name>               name for /proc/net/ipt_hashlimit/\n"
+"[--hashlimit-burst <num>]     number to match in a burst, default %u\n"
+"[--hashlimit-htable-size <num>]       number of hashtable buckets\n"
+"[--hashlimit-htable-max <num>]        number of hashtable entries\n"
+"[--hashlimit-htable-gcinterval]       interval between garbage collection runs\n"
+"[--hashlimit-htable-expire]   after which time are idle entries expired?\n"
+"\n", IPTABLES_VERSION, XT_HASHLIMIT_BURST);
+}
+
+static struct option opts[] = {
+       { "hashlimit", 1, 0, '%' },
+       { "hashlimit-burst", 1, 0, '$' },
+       { "hashlimit-htable-size", 1, 0, '&' },
+       { "hashlimit-htable-max", 1, 0, '*' },
+       { "hashlimit-htable-gcinterval", 1, 0, '(' },
+       { "hashlimit-htable-expire", 1, 0, ')' },
+       { "hashlimit-mode", 1, 0, '_' },
+       { "hashlimit-name", 1, 0, '"' },
+       { 0 }
+};
+
+static
+int parse_rate(const char *rate, u_int32_t *val)
+{
+       const char *delim;
+       u_int32_t r;
+       u_int32_t mult = 1;  /* Seconds by default. */
+
+       delim = strchr(rate, '/');
+       if (delim) {
+               if (strlen(delim+1) == 0)
+                       return 0;
+
+               if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
+                       mult = 1;
+               else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
+                       mult = 60;
+               else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
+                       mult = 60*60;
+               else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
+                       mult = 24*60*60;
+               else
+                       return 0;
+       }
+       r = atoi(rate);
+       if (!r)
+               return 0;
+
+       /* This would get mapped to infinite (1/day is minimum they
+           can specify, so we're ok at that end). */
+       if (r / mult > XT_HASHLIMIT_SCALE)
+               exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
+
+       *val = XT_HASHLIMIT_SCALE * mult / r;
+       return 1;
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct xt_hashlimit_info *r = (struct xt_hashlimit_info *)m->data;
+
+       r->cfg.burst = XT_HASHLIMIT_BURST;
+       r->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
+       r->cfg.expire = XT_HASHLIMIT_EXPIRE;
+
+}
+
+
+/* Parse a 'mode' parameter into the required bitmask */
+static int parse_mode(struct xt_hashlimit_info *r, char *optarg)
+{
+       char *tok;
+       char *arg = strdup(optarg);
+
+       if (!arg)
+               return -1;
+
+       r->cfg.mode = 0;
+
+       for (tok = strtok(arg, ",|");
+            tok;
+            tok = strtok(NULL, ",|")) {
+               if (!strcmp(tok, "dstip"))
+                       r->cfg.mode |= XT_HASHLIMIT_HASH_DIP;
+               else if (!strcmp(tok, "srcip"))
+                       r->cfg.mode |= XT_HASHLIMIT_HASH_SIP;
+               else if (!strcmp(tok, "srcport"))
+                       r->cfg.mode |= XT_HASHLIMIT_HASH_SPT;
+               else if (!strcmp(tok, "dstport"))
+                       r->cfg.mode |= XT_HASHLIMIT_HASH_DPT;
+               else {
+                       free(arg);
+                       return -1;
+               }
+       }
+       free(arg);
+       return 0;
+}
+
+#define PARAM_LIMIT            0x00000001
+#define PARAM_BURST            0x00000002
+#define PARAM_MODE             0x00000004
+#define PARAM_NAME             0x00000008
+#define PARAM_SIZE             0x00000010
+#define PARAM_MAX              0x00000020
+#define PARAM_GCINTERVAL       0x00000040
+#define PARAM_EXPIRE           0x00000080
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct xt_hashlimit_info *r = 
+                       (struct xt_hashlimit_info *)(*match)->data;
+       unsigned int num;
+
+       switch(c) {
+       case '%':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (!parse_rate(optarg, &r->cfg.avg))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "bad rate `%s'", optarg);
+               *flags |= PARAM_LIMIT;
+               break;
+
+       case '$':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 10000, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "bad --hashlimit-burst `%s'", optarg);
+               r->cfg.burst = num;
+               *flags |= PARAM_BURST;
+               break;
+       case '&':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --hashlimit-htable-size: `%s'", optarg);
+               r->cfg.size = num;
+               *flags |= PARAM_SIZE;
+               break;
+       case '*':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --hashlimit-htable-max: `%s'", optarg);
+               r->cfg.max = num;
+               *flags |= PARAM_MAX;
+               break;
+       case '(':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --hashlimit-htable-gcinterval: `%s'", 
+                               optarg);
+               /* FIXME: not HZ dependent!! */
+               r->cfg.gc_interval = num;
+               *flags |= PARAM_GCINTERVAL;
+               break;
+       case ')':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --hashlimit-htable-expire: `%s'", optarg);
+               /* FIXME: not HZ dependent */
+               r->cfg.expire = num;
+               *flags |= PARAM_EXPIRE;
+               break;
+       case '_':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (parse_mode(r, optarg) < 0)
+                       exit_error(PARAMETER_PROBLEM, 
+                                  "bad --hashlimit-mode: `%s'\n", optarg);
+               *flags |= PARAM_MODE;
+               break;
+       case '"':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (strlen(optarg) == 0)
+                       exit_error(PARAMETER_PROBLEM, "Zero-length name?");
+               strncpy(r->name, optarg, sizeof(r->name));
+               *flags |= PARAM_NAME;
+               break;
+       default:
+               return 0;
+       }
+
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "hashlimit does not support invert");
+
+       return 1;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+       if (!(flags & PARAM_LIMIT))
+               exit_error(PARAMETER_PROBLEM,
+                               "You have to specify --hashlimit");
+       if (!(flags & PARAM_MODE))
+               exit_error(PARAMETER_PROBLEM,
+                               "You have to specify --hashlimit-mode");
+       if (!(flags & PARAM_NAME))
+               exit_error(PARAMETER_PROBLEM,
+                               "You have to specify --hashlimit-name");
+}
+
+static struct rates
+{
+       const char *name;
+       u_int32_t mult;
+} rates[] = { { "day", XT_HASHLIMIT_SCALE*24*60*60 },
+             { "hour", XT_HASHLIMIT_SCALE*60*60 },
+             { "min", XT_HASHLIMIT_SCALE*60 },
+             { "sec", XT_HASHLIMIT_SCALE } };
+
+static void print_rate(u_int32_t period)
+{
+       unsigned int i;
+
+       for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
+               if (period > rates[i].mult
+            || rates[i].mult/period < rates[i].mult%period)
+                       break;
+       }
+
+       printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
+}
+
+static void print_mode(const struct xt_hashlimit_info *r, char separator)
+{
+       int prevmode = 0;
+
+       if (r->cfg.mode & XT_HASHLIMIT_HASH_SIP) {
+               if (prevmode)
+                       putchar(separator);
+               fputs("srcip", stdout);
+               prevmode = 1;
+       }
+       if (r->cfg.mode & XT_HASHLIMIT_HASH_SPT) {
+               if (prevmode)
+                       putchar(separator);
+               fputs("srcport", stdout);
+               prevmode = 1;
+       }
+       if (r->cfg.mode & XT_HASHLIMIT_HASH_DIP) {
+               if (prevmode)
+                       putchar(separator);
+               fputs("dstip", stdout);
+               prevmode = 1;
+       }
+       if (r->cfg.mode & XT_HASHLIMIT_HASH_DPT) {
+               if (prevmode)
+                       putchar(separator);
+               fputs("dstport", stdout);
+       }
+       putchar(' ');
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       struct xt_hashlimit_info *r = 
+               (struct xt_hashlimit_info *)match->data;
+       fputs("limit: avg ", stdout); print_rate(r->cfg.avg);
+       printf("burst %u ", r->cfg.burst);
+       fputs("mode ", stdout);
+       print_mode(r, '-');
+       if (r->cfg.size)
+               printf("htable-size %u ", r->cfg.size);
+       if (r->cfg.max)
+               printf("htable-max %u ", r->cfg.max);
+       if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
+               printf("htable-gcinterval %u ", r->cfg.gc_interval);
+       if (r->cfg.expire != XT_HASHLIMIT_EXPIRE)
+               printf("htable-expire %u ", r->cfg.expire);
+}
+
+/* FIXME: Make minimalist: only print rate if not default --RR */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       struct xt_hashlimit_info *r = 
+               (struct xt_hashlimit_info *)match->data;
+
+       fputs("--hashlimit ", stdout); print_rate(r->cfg.avg);
+       if (r->cfg.burst != XT_HASHLIMIT_BURST)
+               printf("--hashlimit-burst %u ", r->cfg.burst);
+
+       fputs("--hashlimit-mode ", stdout);
+       print_mode(r, ',');
+       
+       printf("--hashlimit-name %s ", r->name);
+
+       if (r->cfg.size)
+               printf("--hashlimit-htable-size %u ", r->cfg.size);
+       if (r->cfg.max)
+               printf("--hashlimit-htable-max %u ", r->cfg.max);
+       if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
+               printf("--hashlimit-htable-gcinterval %u", r->cfg.gc_interval);
+       if (r->cfg.expire != XT_HASHLIMIT_EXPIRE)
+               printf("--hashlimit-htable-expire %u ", r->cfg.expire);
+}
+
+static struct ip6tables_match hashlimit = { NULL,
+       .name           = "hashlimit",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct xt_hashlimit_info)),
+       .userspacesize  = offsetof(struct xt_hashlimit_info, hinfo),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match6(&hashlimit);
+}
diff --git a/extensions/libip6t_hbh.c b/extensions/libip6t_hbh.c
new file mode 100644 (file)
index 0000000..bdcbf9b
--- /dev/null
@@ -0,0 +1,262 @@
+/* Shared library add-on to ip6tables to add Hop-by-Hop and Dst headers support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ip6tables.h>
+/*#include <linux/in6.h>*/
+#include <linux/netfilter_ipv6/ip6t_opts.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+                                        
+#define DEBUG          0
+#define HOPBYHOP       1
+#define UNAME          (HOPBYHOP ? "HBH" : "DST")
+#define LNAME          (HOPBYHOP ? "hbh" : "dst")
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"%s v%s options:\n"
+" --%s-len [!] length           total length of this header\n"
+" --%s-opts TYPE[:LEN][,TYPE[:LEN]...] \n"
+"                               Options and its length (list, max: %d)\n", 
+UNAME , IPTABLES_VERSION, LNAME, LNAME, IP6T_OPTS_OPTSNR);
+}
+
+#if HOPBYHOP
+static struct option opts[] = {
+       { "hbh-len", 1, 0, '1' },
+       { "hbh-opts", 1, 0, '2' },
+       { "hbh-not-strict", 1, 0, '3' },
+       {0}
+};
+#else
+static struct option opts[] = {
+       { "dst-len", 1, 0, '1' },
+       { "dst-opts", 1, 0, '2' },
+       { "dst-not-strict", 1, 0, '3' },
+       {0}
+};
+#endif
+
+static u_int32_t
+parse_opts_num(const char *idstr, const char *typestr)
+{
+       unsigned long int id;
+       char* ep;
+
+       id =  strtoul(idstr,&ep,0) ;
+
+       if ( idstr == ep ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "%s no valid digits in %s `%s'", UNAME, typestr, idstr);
+       }
+       if ( id == ULONG_MAX  && errno == ERANGE ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "%s `%s' specified too big: would overflow",
+                          typestr, idstr);
+       }       
+       if ( *idstr != '\0'  && *ep != '\0' ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "%s error parsing %s `%s'", UNAME, typestr, idstr);
+       }
+       return (u_int32_t) id;
+}
+
+static int
+parse_options(const char *optsstr, u_int16_t *opts)
+{
+        char *buffer, *cp, *next, *range;
+        unsigned int i;
+       
+       buffer = strdup(optsstr);
+        if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
+                       
+        for (cp=buffer, i=0; cp && i<IP6T_OPTS_OPTSNR; cp=next,i++)
+        {
+                next=strchr(cp, ',');
+                if (next) *next++='\0';
+                range = strchr(cp, ':');
+                if (range) {
+                        if (i == IP6T_OPTS_OPTSNR-1)
+                                exit_error(PARAMETER_PROBLEM,
+                                           "too many ports specified");
+                        *range++ = '\0';
+                }
+                opts[i] = (u_int16_t)((parse_opts_num(cp,"opt") & 0x000000FF)<<8); 
+                if (range) {
+                       if (opts[i] == 0)
+                               exit_error(PARAMETER_PROBLEM, "PAD0 hasn't got length");
+                        opts[i] |= (u_int16_t)(parse_opts_num(range,"length") &
+                                       0x000000FF);
+                } else {
+                        opts[i] |= (0x00FF);
+               }
+
+#if DEBUG
+               printf("opts str: %s %s\n", cp, range);
+               printf("opts opt: %04X\n", opts[i]);
+#endif
+       }
+        if (cp) exit_error(PARAMETER_PROBLEM, "too many addresses specified");
+
+       free(buffer);
+
+#if DEBUG
+       printf("addr nr: %d\n", i);
+#endif
+
+       return i;
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_opts *optinfo = (struct ip6t_opts *)m->data;
+
+       optinfo->hdrlen = 0;
+       optinfo->flags = 0;
+       optinfo->invflags = 0;
+       optinfo->optsnr = 0;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_opts *optinfo = (struct ip6t_opts *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IP6T_OPTS_LEN)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--%s-len' allowed", LNAME);
+               check_inverse(optarg, &invert, &optind, 0);
+               optinfo->hdrlen = parse_opts_num(argv[optind-1], "length");
+               if (invert)
+                       optinfo->invflags |= IP6T_OPTS_INV_LEN;
+               optinfo->flags |= IP6T_OPTS_LEN;
+               *flags |= IP6T_OPTS_LEN;
+               break;
+       case '2':
+               if (*flags & IP6T_OPTS_OPTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--%s-opts' allowed", LNAME);
+                check_inverse(optarg, &invert, &optind, 0);
+                if (invert)
+                        exit_error(PARAMETER_PROBLEM,
+                               " '!' not allowed with `--%s-opts'", LNAME);
+               optinfo->optsnr = parse_options(argv[optind-1], optinfo->opts);
+               optinfo->flags |= IP6T_OPTS_OPTS;
+               *flags |= IP6T_OPTS_OPTS;
+               break;
+       case '3':
+               if (*flags & IP6T_OPTS_NSTRICT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--%s-not-strict' allowed", LNAME);
+               if ( !(*flags & IP6T_OPTS_OPTS) )
+                       exit_error(PARAMETER_PROBLEM,
+                                  "`--%s-opts ...' required before `--%s-not-strict'", LNAME, LNAME);
+               optinfo->flags |= IP6T_OPTS_NSTRICT;
+               *flags |= IP6T_OPTS_NSTRICT;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_options(int optsnr, u_int16_t *optsp)
+{
+       unsigned int i;
+
+       for(i=0; i<optsnr; i++){
+               printf("%d", (optsp[i] & 0xFF00)>>8);
+               if ((optsp[i] & 0x00FF) != 0x00FF){
+                       printf(":%d", (optsp[i] & 0x00FF));
+               } 
+               printf("%c", (i!=optsnr-1)?',':' ');
+       }
+}
+
+/* Prints out the union ip6t_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
+
+       printf("%s ", LNAME);
+       if (optinfo->flags & IP6T_OPTS_LEN) {
+               printf("length");
+               printf(":%s", optinfo->invflags & IP6T_OPTS_INV_LEN ? "!" : "");
+               printf("%u", optinfo->hdrlen);
+               printf(" ");
+       }
+       if (optinfo->flags & IP6T_OPTS_OPTS) printf("opts ");
+       print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
+       if (optinfo->flags & IP6T_OPTS_NSTRICT) printf("not-strict ");
+       if (optinfo->invflags & ~IP6T_OPTS_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      optinfo->invflags & ~IP6T_OPTS_INV_MASK);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
+
+       if (optinfo->flags & IP6T_OPTS_LEN) {
+               printf("--%s-len %s%u ", LNAME, 
+                       (optinfo->invflags & IP6T_OPTS_INV_LEN) ? "! " : "", 
+                       optinfo->hdrlen);
+       }
+
+       if (optinfo->flags & IP6T_OPTS_OPTS) printf("--%s-opts ", LNAME);
+       print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
+       if (optinfo->flags & IP6T_OPTS_NSTRICT) printf("--%s-not-strict ", LNAME);
+
+}
+
+static struct ip6tables_match optstruct = {
+#if HOPBYHOP
+       .name           = "hbh",
+#else
+       .name           = "dst",
+#endif
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_opts)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_opts)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void
+_init(void)
+{
+       register_match6(&optstruct);
+}
diff --git a/extensions/libip6t_hbh.man b/extensions/libip6t_hbh.man
new file mode 100644 (file)
index 0000000..938e1f3
--- /dev/null
@@ -0,0 +1,7 @@
+This module matches the parameters in Hop-by-Hop Options header
+.TP
+.BR "--hbh-len " "[!] \fIlength\fP"
+Total length of this header in octets.
+.TP
+.BR "--hbh-opts " "\fItype\fP[:\fIlength\fP][,\fItype\fP[:\fIlength\fP]...]"
+numeric type of option and the length of the option data in octets.
diff --git a/extensions/libip6t_hl.c b/extensions/libip6t_hl.c
new file mode 100644 (file)
index 0000000..208da33
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * IPv6 Hop Limit matching module
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's ttl match
+ * This program is released under the terms of GNU GPL
+ * Cleanups by Stephane Ouellette <ouellettes@videotron.ca>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <ip6tables.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_hl.h>
+
+static void help(void) 
+{
+       printf(
+"HL match v%s options:\n"
+"  --hl-eq [!] value   Match hop limit value\n"
+"  --hl-lt value       Match HL < value\n"
+"  --hl-gt value       Match HL > value\n"
+, IPTABLES_VERSION);
+}
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+               const struct ip6t_entry *entry, unsigned int *nfcache,
+               struct ip6t_entry_match **match)
+{
+       struct ip6t_hl_info *info = (struct ip6t_hl_info *) (*match)->data;
+       u_int8_t value;
+
+       check_inverse(optarg, &invert, &optind, 0);
+       value = atoi(argv[optind-1]);
+
+       if (*flags) 
+               exit_error(PARAMETER_PROBLEM, 
+                               "Can't specify HL option twice");
+
+       if (!optarg)
+               exit_error(PARAMETER_PROBLEM,
+                               "hl: You must specify a value");
+       switch (c) {
+               case '2':
+                       if (invert)
+                               info->mode = IP6T_HL_NE;
+                       else
+                               info->mode = IP6T_HL_EQ;
+
+                       /* is 0 allowed? */
+                       info->hop_limit = value;
+                       *flags = 1;
+
+                       break;
+               case '3':
+                       if (invert) 
+                               exit_error(PARAMETER_PROBLEM,
+                                               "hl: unexpected `!'");
+
+                       info->mode = IP6T_HL_LT;
+                       info->hop_limit = value;
+                       *flags = 1;
+
+                       break;
+               case '4':
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                               "hl: unexpected `!'");
+
+                       info->mode = IP6T_HL_GT;
+                       info->hop_limit = value;
+                       *flags = 1;
+
+                       break;
+               default:
+                       return 0;
+       }
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!flags) 
+               exit_error(PARAMETER_PROBLEM,
+                       "HL match: You must specify one of "
+                       "`--hl-eq', `--hl-lt', `--hl-gt'");
+}
+
+static void print(const struct ip6t_ip6 *ip, 
+               const struct ip6t_entry_match *match,
+               int numeric)
+{
+       static const char *op[] = {
+               [IP6T_HL_EQ] = "==",
+               [IP6T_HL_NE] = "!=",
+               [IP6T_HL_LT] = "<",
+               [IP6T_HL_GT] = ">" };
+
+       const struct ip6t_hl_info *info = 
+               (struct ip6t_hl_info *) match->data;
+
+       printf("HL match HL %s %u ", op[info->mode], info->hop_limit);
+}
+
+static void save(const struct ip6t_ip6 *ip, 
+               const struct ip6t_entry_match *match)
+{
+       static const char *op[] = {
+               [IP6T_HL_EQ] = "eq",
+               [IP6T_HL_NE] = "eq !",
+               [IP6T_HL_LT] = "lt",
+               [IP6T_HL_GT] = "gt" };
+
+       const struct ip6t_hl_info *info =
+               (struct ip6t_hl_info *) match->data;
+
+       printf("--hl-%s %u ", op[info->mode], info->hop_limit);
+}
+
+static struct option opts[] = {
+       { .name = "hl",    .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "hl-eq", .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "hl-lt", .has_arg = 1, .flag = 0, .val = '3' },
+       { .name = "hl-gt", .has_arg = 1, .flag = 0, .val = '4' },
+       { 0 }
+};
+
+static
+struct ip6tables_match hl = {
+       .name          = "hl",
+       .version       = IPTABLES_VERSION,
+       .size          = IP6T_ALIGN(sizeof(struct ip6t_hl_info)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_hl_info)),
+       .help          = &help,
+       .parse         = &parse,
+       .final_check   = &final_check,
+       .print         = &print,
+       .save          = &save,
+       .extra_opts    = opts
+};
+
+
+void _init(void) 
+{
+       register_match6(&hl);
+}
diff --git a/extensions/libip6t_hl.man b/extensions/libip6t_hl.man
new file mode 100644 (file)
index 0000000..d33e431
--- /dev/null
@@ -0,0 +1,10 @@
+This module matches the Hop Limit field in the IPv6 header.
+.TP
+.BR "--hl-eq " "[!] \fIvalue\fP"
+Matches if Hop Limit equals \fIvalue\fP.
+.TP
+.BI "--hl-lt " "value"
+Matches if Hop Limit is less than \fIvalue\fP.
+.TP
+.BI "--hl-gt " "value"
+Matches if Hop Limit is greater than \fIvalue\fP.
diff --git a/extensions/libip6t_icmp6.c b/extensions/libip6t_icmp6.c
new file mode 100644 (file)
index 0000000..6940d0e
--- /dev/null
@@ -0,0 +1,278 @@
+/* Shared library add-on to iptables to add ICMP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+struct icmpv6_names {
+       const char *name;
+       u_int8_t type;
+       u_int8_t code_min, code_max;
+};
+
+static const struct icmpv6_names icmpv6_codes[] = {
+       { "destination-unreachable", 1, 0, 0xFF },
+       {   "no-route", 1, 0, 0 },
+       {   "communication-prohibited", 1, 1, 1 },
+       {   "address-unreachable", 1, 3, 3 },
+       {   "port-unreachable", 1, 4, 4 },
+
+       { "packet-too-big", 2, 0, 0xFF },
+
+       { "time-exceeded", 3, 0, 0xFF },
+       /* Alias */ { "ttl-exceeded", 3, 0, 0xFF },
+       {   "ttl-zero-during-transit", 3, 0, 0 },
+       {   "ttl-zero-during-reassembly", 3, 1, 1 },
+
+       { "parameter-problem", 4, 0, 0xFF },
+       {   "bad-header", 4, 0, 0 },
+       {   "unknown-header-type", 4, 1, 1 },
+       {   "unknown-option", 4, 2, 2 },
+
+       { "echo-request", 128, 0, 0xFF },
+       /* Alias */ { "ping", 128, 0, 0xFF },
+
+       { "echo-reply", 129, 0, 0xFF },
+       /* Alias */ { "pong", 129, 0, 0xFF },
+
+       { "router-solicitation", 133, 0, 0xFF },
+
+       { "router-advertisement", 134, 0, 0xFF },
+
+       { "neighbour-solicitation", 135, 0, 0xFF },
+       /* Alias */ { "neighbor-solicitation", 135, 0, 0xFF },
+
+       { "neighbour-advertisement", 136, 0, 0xFF },
+       /* Alias */ { "neighbor-advertisement", 136, 0, 0xFF },
+
+       { "redirect", 137, 0, 0xFF },
+
+};
+
+static void
+print_icmpv6types()
+{
+       unsigned int i;
+       printf("Valid ICMPv6 Types:");
+
+       for (i = 0; i < sizeof(icmpv6_codes)/sizeof(struct icmpv6_names); i++) {
+               if (i && icmpv6_codes[i].type == icmpv6_codes[i-1].type) {
+                       if (icmpv6_codes[i].code_min == icmpv6_codes[i-1].code_min
+                           && (icmpv6_codes[i].code_max
+                               == icmpv6_codes[i-1].code_max))
+                               printf(" (%s)", icmpv6_codes[i].name);
+                       else
+                               printf("\n   %s", icmpv6_codes[i].name);
+               }
+               else
+                       printf("\n%s", icmpv6_codes[i].name);
+       }
+       printf("\n");
+}
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"ICMPv6 v%s options:\n"
+" --icmpv6-type [!] typename   match icmpv6 type\n"
+"                              (or numeric type or type/code)\n"
+"\n", IPTABLES_VERSION);
+       print_icmpv6types();
+}
+
+static struct option opts[] = {
+       { "icmpv6-type", 1, 0, '1' },
+       {0}
+};
+
+static void
+parse_icmpv6(const char *icmpv6type, u_int8_t *type, u_int8_t code[])
+{
+       unsigned int limit = sizeof(icmpv6_codes)/sizeof(struct icmpv6_names);
+       unsigned int match = limit;
+       unsigned int i;
+
+       for (i = 0; i < limit; i++) {
+               if (strncasecmp(icmpv6_codes[i].name, icmpv6type, strlen(icmpv6type))
+                   == 0) {
+                       if (match != limit)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Ambiguous ICMPv6 type `%s':"
+                                          " `%s' or `%s'?",
+                                          icmpv6type,
+                                          icmpv6_codes[match].name,
+                                          icmpv6_codes[i].name);
+                       match = i;
+               }
+       }
+
+       if (match != limit) {
+               *type = icmpv6_codes[match].type;
+               code[0] = icmpv6_codes[match].code_min;
+               code[1] = icmpv6_codes[match].code_max;
+       } else {
+               char *slash;
+               char buffer[strlen(icmpv6type) + 1];
+               unsigned int number;
+
+               strcpy(buffer, icmpv6type);
+               slash = strchr(buffer, '/');
+
+               if (slash)
+                       *slash = '\0';
+
+               if (string_to_number(buffer, 0, 255, &number) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid ICMPv6 type `%s'\n", buffer);
+               *type = number;
+               if (slash) {
+                       if (string_to_number(slash+1, 0, 255, &number) == -1)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Invalid ICMPv6 code `%s'\n",
+                                          slash+1);
+                       code[0] = code[1] = number;
+               } else {
+                       code[0] = 0;
+                       code[1] = 0xFF;
+               }
+       }
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_icmp *icmpv6info = (struct ip6t_icmp *)m->data;
+
+       icmpv6info->code[1] = 0xFF;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_icmp *icmpv6info = (struct ip6t_icmp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags == 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "icmpv6 match: only use --icmpv6-type once!");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_icmpv6(argv[optind-1], &icmpv6info->type, 
+                            icmpv6info->code);
+               if (invert)
+                       icmpv6info->invflags |= IP6T_ICMP_INV;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void print_icmpv6type(u_int8_t type,
+                          u_int8_t code_min, u_int8_t code_max,
+                          int invert,
+                          int numeric)
+{
+       if (!numeric) {
+               unsigned int i;
+
+               for (i = 0;
+                    i < sizeof(icmpv6_codes)/sizeof(struct icmpv6_names);
+                    i++) {
+                       if (icmpv6_codes[i].type == type
+                           && icmpv6_codes[i].code_min == code_min
+                           && icmpv6_codes[i].code_max == code_max)
+                               break;
+               }
+
+               if (i != sizeof(icmpv6_codes)/sizeof(struct icmpv6_names)) {
+                       printf("%s%s ",
+                              invert ? "!" : "",
+                              icmpv6_codes[i].name);
+                       return;
+               }
+       }
+
+       if (invert)
+               printf("!");
+
+       printf("type %u", type);
+       if (code_min == 0 && code_max == 0xFF)
+               printf(" ");
+       else if (code_min == code_max)
+               printf(" code %u ", code_min);
+       else
+               printf(" codes %u-%u ", code_min, code_max);
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       const struct ip6t_icmp *icmpv6 = (struct ip6t_icmp *)match->data;
+
+       printf("ipv6-icmp ");
+       print_icmpv6type(icmpv6->type, icmpv6->code[0], icmpv6->code[1],
+                      icmpv6->invflags & IP6T_ICMP_INV,
+                      numeric);
+
+       if (icmpv6->invflags & ~IP6T_ICMP_INV)
+               printf("Unknown invflags: 0x%X ",
+                      icmpv6->invflags & ~IP6T_ICMP_INV);
+}
+
+/* Saves the match in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_icmp *icmpv6 = (struct ip6t_icmp *)match->data;
+
+       if (icmpv6->invflags & IP6T_ICMP_INV)
+               printf("! ");
+
+       printf("--icmpv6-type %u", icmpv6->type);
+       if (icmpv6->code[0] != 0 || icmpv6->code[1] != 0xFF)
+               printf("/%u", icmpv6->code[0]);
+       printf(" ");
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "icmpv6 match: You must specify `--icmpv6-type'");
+}
+
+static struct ip6tables_match icmpv6 = {
+       .name           = "icmp6",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_icmp)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_icmp)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_match6(&icmpv6);
+}
diff --git a/extensions/libip6t_icmp6.man b/extensions/libip6t_icmp6.man
new file mode 100644 (file)
index 0000000..c755fbf
--- /dev/null
@@ -0,0 +1,14 @@
+This extension can be used if `--protocol ipv6-icmp' or `--protocol icmpv6' is
+specified. It provides the following option:
+.TP
+.BR "--icmpv6-type " "[!] \fItype\fP[/\fIcode\fP]|\fItypename\fP"
+This allows specification of the ICMPv6 type, which can be a numeric
+ICMPv6
+.IR type ,
+.IR type
+and
+.IR code ,
+or one of the ICMPv6 type names shown by the command
+.nf
+ ip6tables -p ipv6-icmp -h
+.fi
diff --git a/extensions/libip6t_ipv6header.c b/extensions/libip6t_ipv6header.c
new file mode 100644 (file)
index 0000000..a260e6e
--- /dev/null
@@ -0,0 +1,316 @@
+/* ipv6header match - matches IPv6 packets based
+on whether they contain certain headers */
+
+/* Original idea: Brad Chapman 
+ * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */
+
+#include <getopt.h>
+#include <ip6tables.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/types.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_ipv6header.h>
+
+/* This maybe required 
+#include <linux/in.h>
+#include <linux/in6.h>
+*/
+
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+ *    /etc/protocols */
+struct pprot {
+       char *name;
+       u_int8_t num;
+};
+
+struct numflag {
+       u_int8_t proto;
+       u_int8_t flag;
+};
+
+static const struct pprot chain_protos[] = {
+       { "hop-by-hop", IPPROTO_HOPOPTS },
+       { "protocol", IPPROTO_RAW },
+       { "hop", IPPROTO_HOPOPTS },
+       { "dst", IPPROTO_DSTOPTS },
+       { "route", IPPROTO_ROUTING },
+       { "frag", IPPROTO_FRAGMENT },
+       { "auth", IPPROTO_AH },
+       { "esp", IPPROTO_ESP },
+       { "none", IPPROTO_NONE },
+       { "prot", IPPROTO_RAW },
+       { "0", IPPROTO_HOPOPTS },
+       { "60", IPPROTO_DSTOPTS },
+       { "43", IPPROTO_ROUTING },
+       { "44", IPPROTO_FRAGMENT },
+       { "51", IPPROTO_AH },
+       { "50", IPPROTO_ESP },
+       { "59", IPPROTO_NONE },
+       { "255", IPPROTO_RAW },
+       /* { "all", 0 }, */
+};
+
+static const struct numflag chain_flags[] = {
+       { IPPROTO_HOPOPTS, MASK_HOPOPTS },
+       { IPPROTO_DSTOPTS, MASK_DSTOPTS },
+       { IPPROTO_ROUTING, MASK_ROUTING },
+       { IPPROTO_FRAGMENT, MASK_FRAGMENT },
+       { IPPROTO_AH, MASK_AH },
+       { IPPROTO_ESP, MASK_ESP },
+       { IPPROTO_NONE, MASK_NONE },
+       { IPPROTO_RAW, MASK_PROTO },
+};
+
+static char *
+proto_to_name(u_int8_t proto, int nolookup)
+{
+        unsigned int i;
+
+        if (proto && !nolookup) {
+                struct protoent *pent = getprotobynumber(proto);
+                if (pent)
+                        return pent->p_name;
+        }
+
+        for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+                if (chain_protos[i].num == proto)
+                        return chain_protos[i].name;
+
+        return NULL;
+}
+
+static u_int16_t
+name_to_proto(const char *s)
+{
+        unsigned int proto=0;
+        struct protoent *pent;
+
+        if ((pent = getprotobyname(s)))
+               proto = pent->p_proto;
+        else {
+               unsigned int i;
+               for (i = 0;
+                       i < sizeof(chain_protos)/sizeof(struct pprot);
+                       i++) {
+                       if (strcmp(s, chain_protos[i].name) == 0) {
+                               proto = chain_protos[i].num;
+                               break;
+                       }
+               }
+
+               if (i == sizeof(chain_protos)/sizeof(struct pprot))
+                       exit_error(PARAMETER_PROBLEM,
+                               "unknown header `%s' specified",
+                               s);
+        }
+
+        return (u_int16_t)proto;
+}
+
+static unsigned int 
+add_proto_to_mask(int proto){
+       unsigned int i=0, flag=0;
+
+       for (i = 0;
+               i < sizeof(chain_flags)/sizeof(struct numflag);
+               i++) {
+                       if (proto == chain_flags[i].proto){
+                               flag = chain_flags[i].flag;
+                               break;
+                       }
+       }
+
+       if (i == sizeof(chain_flags)/sizeof(struct numflag))
+               exit_error(PARAMETER_PROBLEM,
+               "unknown header `%d' specified",
+               proto);
+       
+       return flag;
+}      
+
+static void
+help(void)
+{
+       printf(
+"ipv6header v%s match options:\n"
+"--header [!] headers     Type of header to match, by name\n"
+"                         names: hop,dst,route,frag,auth,esp,none,proto\n"
+"                    long names: hop-by-hop,ipv6-opts,ipv6-route,\n"
+"                                ipv6-frag,ah,esp,ipv6-nonxt,protocol\n"
+"                       numbers: 0,60,43,44,51,50,59\n"
+"--soft                    The header CONTAINS the specified extensions\n",
+       IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "header", 1, 0, '1' },
+       { "soft", 0, 0, '2' },
+       { 0 }
+};
+
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_ipv6header_info *info = (struct ip6t_ipv6header_info *)m->data;
+
+       info->matchflags = 0x00;
+       info->invflags = 0x00;
+       info->modeflag = 0x00;
+}
+
+static unsigned int
+parse_header(const char *flags) {
+        unsigned int ret = 0;
+        char *ptr;
+        char *buffer;
+
+        buffer = strdup(flags);
+
+        for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) 
+               ret |= add_proto_to_mask(name_to_proto(ptr));
+                
+        free(buffer);
+        return ret;
+}
+
+#define IPV6_HDR_HEADER        0x01
+#define IPV6_HDR_SOFT  0x02
+
+/* Parses command options; returns 0 if it ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_ipv6header_info *info = (struct ip6t_ipv6header_info *)(*match)->data;
+
+       switch (c) {
+               case '1' : 
+                       /* Parse the provided header names */
+                       if (*flags & IPV6_HDR_HEADER)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "Only one `--header' allowed");
+
+                       check_inverse(optarg, &invert, &optind, 0);
+
+                       if (! (info->matchflags = parse_header(argv[optind-1])) )
+                               exit_error(PARAMETER_PROBLEM, "ip6t_ipv6header: cannot parse header names");
+
+                       if (invert) 
+                               info->invflags |= 0xFF;
+                       *flags |= IPV6_HDR_HEADER;
+                       break;
+               case '2' : 
+                       /* Soft-mode requested? */
+                       if (*flags & IPV6_HDR_SOFT)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "Only one `--soft' allowed");
+
+                       info->modeflag |= 0xFF;
+                       *flags |= IPV6_HDR_SOFT;
+                       break;
+               default:
+                       return 0;
+       }
+
+       return 1;
+}
+
+/* Checks the flags variable */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags) exit_error(PARAMETER_PROBLEM, "ip6t_ipv6header: no options specified");
+}
+
+static void
+print_header(u_int8_t flags){
+        int have_flag = 0;
+
+        while (flags) {
+                unsigned int i;
+
+                for (i = 0; (flags & chain_flags[i].flag) == 0; i++);
+
+                if (have_flag)
+                        printf(",");
+
+                printf("%s", proto_to_name(chain_flags[i].proto,0));
+                have_flag = 1;
+
+                flags &= ~chain_flags[i].flag;
+        }
+
+        if (!have_flag)
+                printf("NONE");
+}
+
+/* Prints out the match */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data;
+       printf("ipv6header ");
+
+        if (info->matchflags || info->invflags) {
+                printf("flags:%s", info->invflags ? "!" : "");
+                if (numeric)
+                        printf("0x%02X ", info->matchflags);
+                else {
+                        print_header(info->matchflags);
+                        printf(" ");
+                }
+        }
+
+       if (info->modeflag)
+               printf("soft ");
+
+       return;
+}
+
+/* Saves the match */
+static void
+save(const struct ip6t_ip6 *ip,
+     const struct ip6t_entry_match *match)
+{
+
+       const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data;
+
+       printf("--header ");
+       printf("%s", info->invflags ? "!" : "");
+       print_header(info->matchflags);
+       printf(" ");
+       if (info->modeflag)
+               printf("--soft ");
+
+       return;
+}
+
+static
+struct ip6tables_match ipv6header = {
+       .name           = "ipv6header",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_match6(&ipv6header);
+}
diff --git a/extensions/libip6t_ipv6header.man b/extensions/libip6t_ipv6header.man
new file mode 100644 (file)
index 0000000..fe3fe98
--- /dev/null
@@ -0,0 +1,29 @@
+This module matches IPv6 extension headers and/or upper layer header.
+.TP
+.BR "--header " "[!] \fIheader\fP[,\fIheader\fP...]"
+Matches the packet which EXACTLY includes all specified headers. The headers
+encapsulated with ESP header are out of scope.
+.IR header
+can be
+.IR hop | hop-by-hop
+(Hop-by-Hop Options header),
+.IR dst
+(Destination Options header),
+.IR route
+(Routing header),
+.IR frag
+(Fragment header),
+.IR auth
+(Authentication header),
+.IR esp
+(Encapsulating Security Payload header),
+.IR none
+(No Next header) which matches 59 in the 'Next Header field' of IPv6 header or any IPv6 extension headers, or
+.IR proto
+which matches any upper layer protocol header. A protocol name from /etc/protocols and numeric value also allowed. The number 255 is equivalent to
+.IR proto .
+.TP
+.BR "[--soft]"
+Matches if the packet includes all specified headers with
+.BR --header ,
+AT LEAST.
diff --git a/extensions/libip6t_length.c b/extensions/libip6t_length.c
new file mode 100644 (file)
index 0000000..9f7ba16
--- /dev/null
@@ -0,0 +1,152 @@
+/* Shared library add-on to ip6tables to add packet length matching support. */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_length.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"length v%s options:\n"
+"[!] --length length[:length]    Match packet length against value or range\n"
+"                                of values (inclusive)\n",
+IPTABLES_VERSION);
+
+}
+  
+static struct option opts[] = {
+       { "length", 1, 0, '1' },
+       {0}
+};
+
+static u_int16_t
+parse_length(const char *s)
+{
+
+       unsigned int len;
+       
+       if (string_to_number(s, 0, 0xFFFF, &len) == -1)
+               exit_error(PARAMETER_PROBLEM, "length invalid: `%s'\n", s);
+       else
+               return (u_int16_t )len;
+}
+
+/* If a single value is provided, min and max are both set to the value */
+static void
+parse_lengths(const char *s, struct ip6t_length_info *info)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(s);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               info->min = info->max = parse_length(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               info->min = buffer[0] ? parse_length(buffer) : 0;
+               info->max = cp[0] ? parse_length(cp) : 0xFFFF;
+       }
+       free(buffer);
+       
+       if (info->min > info->max)
+               exit_error(PARAMETER_PROBLEM,
+                          "length min. range value `%u' greater than max. "
+                          "range value `%u'", info->min, info->max);
+       
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_length_info *info = (struct ip6t_length_info *)(*match)->data;
+
+       switch (c) {
+               case '1':
+                       if (*flags)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "length: `--length' may only be "
+                                          "specified once");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       parse_lengths(argv[optind-1], info);
+                       if (invert)
+                               info->invert = 1;
+                       *flags = 1;
+                       break;
+                       
+               default:
+                       return 0;
+       }
+       return 1;
+}
+
+/* Final check; must have specified --length. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "length: You must specify `--length'");
+}
+
+/* Common match printing code. */
+static void
+print_length(struct ip6t_length_info *info)
+{
+       if (info->invert)
+               printf("! ");
+       
+       if (info->max == info->min)
+               printf("%u ", info->min);
+       else
+               printf("%u:%u ", info->min, info->max);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       printf("length ");
+       print_length((struct ip6t_length_info *)match->data);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       printf("--length ");
+       print_length((struct ip6t_length_info *)match->data);
+}
+
+struct ip6tables_match length = {
+       .name           = "length",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_length_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_length_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_match6(&length);
+}
diff --git a/extensions/libip6t_length.man b/extensions/libip6t_length.man
new file mode 100644 (file)
index 0000000..d781a04
--- /dev/null
@@ -0,0 +1,4 @@
+This module matches the length of the IPv6 payload in octets, or range of it.
+IPv6 header itself isn't counted.
+.TP
+.BR "--length " "[!] \fIlength\fP[:\fIlength\fP]"
diff --git a/extensions/libip6t_limit.c b/extensions/libip6t_limit.c
new file mode 100644 (file)
index 0000000..6c88ee1
--- /dev/null
@@ -0,0 +1,195 @@
+/* Shared library add-on to iptables to add limit support.
+ *
+ * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
+ * Hervé Eychenne    <rv@wallfire.org>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <stddef.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv6/ip6t_limit.h"
+
+#define IP6T_LIMIT_AVG "3/hour"
+#define IP6T_LIMIT_BURST       5
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"limit v%s options:\n"
+"--limit avg                   max average match rate: default "IP6T_LIMIT_AVG"\n"
+"                                [Packets per second unless followed by \n"
+"                                /sec /minute /hour /day postfixes]\n"
+"--limit-burst number          number to match in a burst, default %u\n"
+"\n", IPTABLES_VERSION, IP6T_LIMIT_BURST);
+}
+
+static struct option opts[] = {
+       { "limit", 1, 0, '%' },
+       { "limit-burst", 1, 0, '$' },
+       { 0 }
+};
+
+static
+int parse_rate(const char *rate, u_int32_t *val)
+{
+       const char *delim;
+       u_int32_t r;
+       u_int32_t mult = 1;  /* Seconds by default. */
+
+       delim = strchr(rate, '/');
+       if (delim) {
+               if (strlen(delim+1) == 0)
+                       return 0;
+
+               if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
+                       mult = 1;
+               else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
+                       mult = 60;
+               else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
+                       mult = 60*60;
+               else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
+                       mult = 24*60*60;
+               else
+                       return 0;
+       }
+       r = atoi(rate);
+       if (!r)
+               return 0;
+
+       /* This would get mapped to infinite (1/day is minimum they
+           can specify, so we're ok at that end). */
+       if (r / mult > IP6T_LIMIT_SCALE)
+               exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
+
+       *val = IP6T_LIMIT_SCALE * mult / r;
+       return 1;
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)m->data;
+
+       parse_rate(IP6T_LIMIT_AVG, &r->avg);
+       r->burst = IP6T_LIMIT_BURST;
+
+}
+
+/* FIXME: handle overflow:
+       if (r->avg*r->burst/r->burst != r->avg)
+               exit_error(PARAMETER_PROBLEM,
+                          "Sorry: burst too large for that avg rate.\n");
+*/
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)(*match)->data;
+       unsigned int num;
+
+       switch(c) {
+       case '%':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (!parse_rate(optarg, &r->avg))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "bad rate `%s'", optarg);
+               break;
+
+       case '$':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 10000, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "bad --limit-burst `%s'", optarg);
+               r->burst = num;
+               break;
+
+       default:
+               return 0;
+       }
+
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "limit does not support invert");
+
+       return 1;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+}
+
+static struct rates
+{
+       const char *name;
+       u_int32_t mult;
+} rates[] = { { "day", IP6T_LIMIT_SCALE*24*60*60 },
+             { "hour", IP6T_LIMIT_SCALE*60*60 },
+             { "min", IP6T_LIMIT_SCALE*60 },
+             { "sec", IP6T_LIMIT_SCALE } };
+
+static void print_rate(u_int32_t period)
+{
+       unsigned int i;
+
+       for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
+               if (period > rates[i].mult
+                   || rates[i].mult % period != 0)
+                       break;
+       }
+
+       printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)match->data;
+       printf("limit: avg "); print_rate(r->avg);
+       printf("burst %u ", r->burst);
+}
+
+/* FIXME: Make minimalist: only print rate if not default --RR */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)match->data;
+
+       printf("--limit "); print_rate(r->avg);
+       if (r->burst != IP6T_LIMIT_BURST)
+               printf("--limit-burst %u ", r->burst);
+}
+
+static struct ip6tables_match limit = {
+       .name           = "limit",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_rateinfo)),
+       .userspacesize  = offsetof(struct ip6t_rateinfo, prev),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_match6(&limit);
+}
diff --git a/extensions/libip6t_limit.man b/extensions/libip6t_limit.man
new file mode 100644 (file)
index 0000000..84b63d4
--- /dev/null
@@ -0,0 +1,15 @@
+This module matches at a limited rate using a token bucket filter.
+A rule using this extension will match until this limit is reached
+(unless the `!' flag is used).  It can be used in combination with the
+.B LOG
+target to give limited logging, for example.
+.TP
+.BI "--limit " "rate"
+Maximum average matching rate: specified as a number, with an optional
+`/second', `/minute', `/hour', or `/day' suffix; the default is
+3/hour.
+.TP
+.BI "--limit-burst " "number"
+Maximum initial number of packets to match: this number gets
+recharged by one every time the limit specified above is not reached,
+up to this number; the default is 5.
diff --git a/extensions/libip6t_mac.c b/extensions/libip6t_mac.c
new file mode 100644 (file)
index 0000000..e47f21f
--- /dev/null
@@ -0,0 +1,139 @@
+/* Shared library add-on to iptables to add MAC address support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_mac.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"MAC v%s options:\n"
+" --mac-source [!] XX:XX:XX:XX:XX:XX\n"
+"                              Match source MAC address\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "mac-source", 1, 0, '1' },
+       {0}
+};
+
+static void
+parse_mac(const char *mac, struct ip6t_mac_info *info)
+{
+       unsigned int i = 0;
+
+       if (strlen(mac) != ETH_ALEN*3-1)
+               exit_error(PARAMETER_PROBLEM, "Bad mac address `%s'", mac);
+
+       for (i = 0; i < ETH_ALEN; i++) {
+               long number;
+               char *end;
+
+               number = strtol(mac + i*3, &end, 16);
+
+               if (end == mac + i*3 + 2
+                   && number >= 0
+                   && number <= 255)
+                       info->srcaddr[i] = number;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Bad mac address `%s'", mac);
+       }
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_mac_info *macinfo = (struct ip6t_mac_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_mac(argv[optind-1], macinfo);
+               if (invert)
+                       macinfo->invert = 1;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void print_mac(unsigned char macaddress[ETH_ALEN])
+{
+       unsigned int i;
+
+       printf("%02X", macaddress[0]);
+       for (i = 1; i < ETH_ALEN; i++)
+               printf(":%02X", macaddress[i]);
+       printf(" ");
+}
+
+/* Final check; must have specified --mac. */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "You must specify `--mac-source'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       printf("MAC ");
+
+       if (((struct ip6t_mac_info *)match->data)->invert)
+               printf("! ");
+
+       print_mac(((struct ip6t_mac_info *)match->data)->srcaddr);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       if (((struct ip6t_mac_info *)match->data)->invert)
+               printf("! ");
+
+       printf("--mac-source ");
+       print_mac(((struct ip6t_mac_info *)match->data)->srcaddr);
+}
+
+static struct ip6tables_match mac = {
+       .name           = "mac",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_mac_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_mac_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_match6(&mac);
+}
diff --git a/extensions/libip6t_mac.man b/extensions/libip6t_mac.man
new file mode 100644 (file)
index 0000000..5321ca1
--- /dev/null
@@ -0,0 +1,10 @@
+.TP
+.BR "--mac-source " "[!] \fIaddress\fP"
+Match source MAC address.  It must be of the form XX:XX:XX:XX:XX:XX.
+Note that this only makes sense for packets coming from an Ethernet device
+and entering the
+.BR PREROUTING ,
+.B FORWARD
+or
+.B INPUT
+chains.
diff --git a/extensions/libip6t_mark.c b/extensions/libip6t_mark.c
new file mode 100644 (file)
index 0000000..b831cfe
--- /dev/null
@@ -0,0 +1,142 @@
+/* Shared library add-on to ip6tables to add NFMARK matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <ip6tables.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv6/ip6t_mark.h"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"MARK match v%s options:\n"
+"[!] --mark value[/mask]         Match nfmark value with optional mask\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "mark", 1, 0, '1' },
+       {0}
+};
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_mark_info *markinfo = (struct ip6t_mark_info *)(*match)->data;
+
+       switch (c) {
+               char *end;
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+#ifdef KERNEL_64_USERSPACE_32
+               markinfo->mark = strtoull(optarg, &end, 0);
+               if (*end == '/') {
+                       markinfo->mask = strtoull(end+1, &end, 0);
+               } else
+                       markinfo->mask = 0xffffffffffffffffULL;
+#else
+               markinfo->mark = strtoul(optarg, &end, 0);
+               if (*end == '/') {
+                       markinfo->mask = strtoul(end+1, &end, 0);
+               } else
+                       markinfo->mask = 0xffffffff;
+#endif
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
+               if (invert)
+                       markinfo->invert = 1;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+#ifdef KERNEL_64_USERSPACE_32
+static void
+print_mark(unsigned long long mark, unsigned long long mask, int numeric)
+{
+       if(mask != 0xffffffffffffffffULL)
+               printf("0x%llx/0x%llx ", mark, mask);
+       else
+               printf("0x%llx ", mark);
+}
+#else
+static void
+print_mark(unsigned long mark, unsigned long mask, int numeric)
+{
+       if(mask != 0xffffffff)
+               printf("0x%lx/0x%lx ", mark, mask);
+       else
+               printf("0x%lx ", mark);
+}
+#endif
+
+/* Final check; must have specified --mark. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK match: You must specify `--mark'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       struct ip6t_mark_info *info = (struct ip6t_mark_info *)match->data;
+
+       printf("MARK match ");
+
+       if (info->invert)
+               printf("!");
+       
+       print_mark(info->mark, info->mask, numeric);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       struct ip6t_mark_info *info = (struct ip6t_mark_info *)match->data;
+
+       if (info->invert)
+               printf("! ");
+       
+       printf("--mark ");
+       print_mark(info->mark, info->mask, 0);
+}
+
+static struct ip6tables_match mark = {
+       .name           = "mark",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_mark_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_mark_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_match6(&mark);
+}
diff --git a/extensions/libip6t_mark.man b/extensions/libip6t_mark.man
new file mode 100644 (file)
index 0000000..a2a1395
--- /dev/null
@@ -0,0 +1,9 @@
+This module matches the netfilter mark field associated with a packet
+(which can be set using the
+.B MARK
+target below).
+.TP
+.BR "--mark " "\fIvalue\fP[/\fImask\fP]"
+Matches packets with the given unsigned mark value (if a \fImask\fP is
+specified, this is logically ANDed with the \fImask\fP before the
+comparison).
diff --git a/extensions/libip6t_mh.c b/extensions/libip6t_mh.c
new file mode 100644 (file)
index 0000000..2475b4d
--- /dev/null
@@ -0,0 +1,252 @@
+/* Shared library add-on to ip6tables to add mobility header support. */
+/*
+ * Copyright (C)2006 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Author:
+ *     Masahide NAKAMURA @USAGI <masahide.nakamura.cz@hitachi.com>
+ *
+ * Based on libip6t_{icmpv6,udp}.c
+ */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_mh.h>
+
+struct mh_name {
+       const char *name;
+       u_int8_t type;
+};
+
+static const struct mh_name mh_names[] = {
+       { "binding-refresh-request", 0, },
+       /* Alias */ { "brr", 0, },
+       { "home-test-init", 1, },
+       /* Alias */ { "hoti", 1, },
+       { "careof-test-init", 2, },
+       /* Alias */ { "coti", 2, },
+       { "home-test", 3, },
+       /* Alias */ { "hot", 3, },
+       { "careof-test", 4, },
+       /* Alias */ { "cot", 4, },
+       { "binding-update", 5, },
+       /* Alias */ { "bu", 5, },
+       { "binding-acknowledgement", 6, },
+       /* Alias */ { "ba", 6, },
+       { "binding-error", 7, },
+       /* Alias */ { "be", 7, },
+};
+
+static void print_types_all(void)
+{
+       unsigned int i;
+       printf("Valid MH types:");
+
+       for (i = 0; i < sizeof(mh_names)/sizeof(struct mh_name); i++) {
+               if (i && mh_names[i].type == mh_names[i-1].type)
+                       printf(" (%s)", mh_names[i].name);
+               else
+                       printf("\n%s", mh_names[i].name);
+       }
+       printf("\n");
+}
+
+static void help(void)
+{
+       printf(
+"MH v%s options:\n"
+" --mh-type [!] type[:type]    match mh type\n",
+IPTABLES_VERSION);
+       print_types_all();
+}
+
+static void init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_mh *mhinfo = (struct ip6t_mh *)m->data;
+
+       mhinfo->types[1] = 0xFF;
+}
+
+static unsigned int name_to_type(const char *name)
+{
+       int namelen = strlen(name);
+       unsigned int limit = sizeof(mh_names)/sizeof(struct mh_name);
+       unsigned int match = limit;
+       unsigned int i;
+
+       for (i = 0; i < limit; i++) {
+               if (strncasecmp(mh_names[i].name, name, namelen) == 0) {
+                       int len = strlen(mh_names[i].name);
+                       if (match == limit || len == namelen)
+                               match = i;
+               }
+       }
+
+       if (match != limit) {
+               return mh_names[match].type;
+       } else {
+               unsigned int number;
+
+               if (string_to_number(name, 0, 255, &number) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid MH type `%s'\n", name);
+               return number;
+       }
+}
+
+static void parse_mh_types(const char *mhtype, u_int8_t *types)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(mhtype);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               types[0] = types[1] = name_to_type(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               types[0] = buffer[0] ? name_to_type(buffer) : 0;
+               types[1] = cp[0] ? name_to_type(cp) : 0xFF;
+
+               if (types[0] > types[1])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid MH type range (min > max)");
+       }
+       free(buffer);
+}
+
+#define MH_TYPES 0x01
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+                const struct ip6t_entry *entry,
+                unsigned int *nfcache,
+                struct ip6t_entry_match **match)
+{
+       struct ip6t_mh *mhinfo = (struct ip6t_mh *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & MH_TYPES)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--mh-type' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_mh_types(argv[optind-1], mhinfo->types);
+               if (invert)
+                       mhinfo->invflags |= IP6T_MH_INV_TYPE;
+               *flags |= MH_TYPES;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+static const char *type_to_name(u_int8_t type)
+{
+       unsigned int i;
+
+       for (i = 0; i < sizeof(mh_names)/sizeof(struct mh_name); i++) {
+               if (mh_names[i].type == type)
+                       return mh_names[i].name;
+       }
+
+       return NULL;
+}
+
+static void print_type(u_int8_t type, int numeric)
+{
+       const char *name;
+       if (numeric || !(name = type_to_name(type)))
+               printf("%u", type);
+       else
+               printf("%s", name);
+}
+
+static void print_types(u_int8_t min, u_int8_t max, int invert, int numeric)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFF || invert) {
+               if (min == max) {
+                       printf("%s", inv);
+                       print_type(min, numeric);
+               } else {
+                       printf("%s", inv);
+                       print_type(min, numeric);
+                       printf(":");
+                       print_type(max, numeric);
+               }
+               printf(" ");
+       }
+}
+
+static void print(const struct ip6t_ip6 *ip,
+                 const struct ip6t_entry_match *match,
+                 int numeric)
+{
+       const struct ip6t_mh *mhinfo = (struct ip6t_mh *)match->data;
+
+       printf("mh ");
+       print_types(mhinfo->types[0], mhinfo->types[1],
+                   mhinfo->invflags & IP6T_MH_INV_TYPE,
+                   numeric);
+       if (mhinfo->invflags & ~IP6T_MH_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      mhinfo->invflags & ~IP6T_MH_INV_MASK);
+}
+
+static void save(const struct ip6t_ip6 *ip,
+                const struct ip6t_entry_match *match)
+{
+       const struct ip6t_mh *mhinfo = (struct ip6t_mh *)match->data;
+
+       if (mhinfo->types[0] == 0 && mhinfo->types[1] == 0xFF)
+               return;
+
+       if (mhinfo->invflags & IP6T_MH_INV_TYPE)
+               printf("! ");
+
+       if (mhinfo->types[0] != mhinfo->types[1])
+               printf("--mh-type %u:%u ", mhinfo->types[0], mhinfo->types[1]);
+       else
+               printf("--mh-type %u ", mhinfo->types[0]);
+}
+
+static struct option opts[] = {
+       { "mh-type", 1, 0, '1' },
+       {0}
+};
+
+static struct ip6tables_match mh = {
+       .name           = "mh",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_mh)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_mh)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_match6(&mh);
+}
diff --git a/extensions/libip6t_mh.man b/extensions/libip6t_mh.man
new file mode 100644 (file)
index 0000000..14f1c64
--- /dev/null
@@ -0,0 +1,12 @@
+This extension is loaded if `--protocol ipv6-mh' or `--protocol mh' is
+specified. It provides the following option:
+.TP
+.BR "--mh-type " "[!] \fItype\fP[:\fItype\fP]"
+This allows specification of the Mobility Header(MH) type, which can be
+a numeric MH
+.IR type ,
+.IR type
+or one of the MH type names shown by the command
+.nf
+ ip6tables -p ipv6-mh -h
+.fi
diff --git a/extensions/libip6t_multiport.c b/extensions/libip6t_multiport.c
new file mode 100644 (file)
index 0000000..166abce
--- /dev/null
@@ -0,0 +1,458 @@
+/* Shared library add-on to iptables to add multiple TCP port support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+/* To ensure that iptables compiles with an old kernel */
+#include "../include/linux/netfilter_ipv6/ip6t_multiport.h"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"multiport v%s options:\n"
+" --source-ports port[,port,port...]\n"
+" --sports ...\n"
+"                              match source port(s)\n"
+" --destination-ports port[,port,port...]\n"
+" --dports ...\n"
+"                              match destination port(s)\n"
+" --ports port[,port,port]\n"
+"                              match both source and destination port(s)\n"
+" NOTE: this kernel does not support port ranges in multiport.\n",
+IPTABLES_VERSION);
+}
+
+static void
+help_v1(void)
+{
+       printf(
+"multiport v%s options:\n"
+" --source-ports [!] port[,port:port,port...]\n"
+" --sports ...\n"
+"                              match source port(s)\n"
+" --destination-ports [!] port[,port:port,port...]\n"
+" --dports ...\n"
+"                              match destination port(s)\n"
+" --ports [!] port[,port:port,port]\n"
+"                              match both source and destination port(s)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "source-ports", 1, 0, '1' },
+       { "sports", 1, 0, '1' }, /* synonym */
+       { "destination-ports", 1, 0, '2' },
+       { "dports", 1, 0, '2' }, /* synonym */
+       { "ports", 1, 0, '3' },
+       {0}
+};
+
+static char *
+proto_to_name(u_int8_t proto)
+{
+       switch (proto) {
+       case IPPROTO_TCP:
+               return "tcp";
+       case IPPROTO_UDP:
+               return "udp";
+       case IPPROTO_SCTP:
+               return "sctp";
+       case IPPROTO_DCCP:
+               return "dccp";
+       default:
+               return NULL;
+       }
+}
+
+static unsigned int
+parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto)
+{
+       char *buffer, *cp, *next;
+       unsigned int i;
+
+       buffer = strdup(portstring);
+       if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
+
+       for (cp=buffer, i=0; cp && i<IP6T_MULTI_PORTS; cp=next,i++)
+       {
+               next=strchr(cp, ',');
+               if (next) *next++='\0';
+               ports[i] = parse_port(cp, proto);
+       }
+       if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
+       free(buffer);
+       return i;
+}
+
+static void
+parse_multi_ports_v1(const char *portstring, 
+                    struct ip6t_multiport_v1 *multiinfo,
+                    const char *proto)
+{
+       char *buffer, *cp, *next, *range;
+       unsigned int i;
+       u_int16_t m;
+
+       buffer = strdup(portstring);
+       if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
+
+       for (i=0; i<IP6T_MULTI_PORTS; i++)
+               multiinfo->pflags[i] = 0;
+       for (cp=buffer, i=0; cp && i<IP6T_MULTI_PORTS; cp=next, i++) {
+               next=strchr(cp, ',');
+               if (next) *next++='\0';
+               range = strchr(cp, ':');
+               if (range) {
+                       if (i == IP6T_MULTI_PORTS-1)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "too many ports specified");
+                       *range++ = '\0';
+               }
+               multiinfo->ports[i] = parse_port(cp, proto);
+               if (range) {
+                       multiinfo->pflags[i] = 1;
+                       multiinfo->ports[++i] = parse_port(range, proto);
+                       if (multiinfo->ports[i-1] >= multiinfo->ports[i])
+                               exit_error(PARAMETER_PROBLEM,
+                                          "invalid portrange specified");
+                       m <<= 1;
+               }
+       }
+       multiinfo->count = i;
+       if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+}
+
+static const char *
+check_proto(const struct ip6t_entry *entry)
+{
+       char *proto;
+
+       if ((proto = proto_to_name(entry->ipv6.proto)) != NULL)
+               return proto;
+       else if (!entry->ipv6.proto)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport needs `-p tcp', `-p udp', `-p sctp' or `-p dccp'");
+       else
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport only works with TCP, UDP, SCTP and DCCP");
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       const char *proto;
+       struct ip6t_multiport *multiinfo
+               = (struct ip6t_multiport *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               multiinfo->count = parse_multi_ports(argv[optind-1],
+                                                    multiinfo->ports, proto);
+               multiinfo->flags = IP6T_MULTIPORT_SOURCE;
+               break;
+
+       case '2':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               multiinfo->count = parse_multi_ports(argv[optind-1],
+                                                    multiinfo->ports, proto);
+               multiinfo->flags = IP6T_MULTIPORT_DESTINATION;
+               break;
+
+       case '3':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               multiinfo->count = parse_multi_ports(argv[optind-1],
+                                                    multiinfo->ports, proto);
+               multiinfo->flags = IP6T_MULTIPORT_EITHER;
+               break;
+
+       default:
+               return 0;
+       }
+
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport does not support invert");
+
+       if (*flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport can only have one option");
+       *flags = 1;
+       return 1;
+}
+
+static int
+parse_v1(int c, char **argv, int invert, unsigned int *flags,
+        const struct ip6t_entry *entry,
+        unsigned int *nfcache,
+        struct ip6t_entry_match **match)
+{
+       const char *proto;
+       struct ip6t_multiport_v1 *multiinfo
+               = (struct ip6t_multiport_v1 *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
+               multiinfo->flags = IP6T_MULTIPORT_SOURCE;
+               break;
+
+       case '2':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
+               multiinfo->flags = IP6T_MULTIPORT_DESTINATION;
+               break;
+
+       case '3':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
+               multiinfo->flags = IP6T_MULTIPORT_EITHER;
+               break;
+
+       default:
+               return 0;
+       }
+
+       if (invert)
+               multiinfo->invert = 1;
+
+       if (*flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport can only have one option");
+       *flags = 1;
+       return 1;
+}
+
+/* Final check; must specify something. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, "multiport expection an option");
+}
+
+static char *
+port_to_service(int port, u_int8_t proto)
+{
+       struct servent *service;
+
+       if ((service = getservbyport(htons(port), proto_to_name(proto))))
+               return service->s_name;
+
+       return NULL;
+}
+
+static void
+print_port(u_int16_t port, u_int8_t protocol, int numeric)
+{
+       char *service;
+
+       if (numeric || (service = port_to_service(port, protocol)) == NULL)
+               printf("%u", port);
+       else
+               printf("%s", service);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       const struct ip6t_multiport *multiinfo
+               = (const struct ip6t_multiport *)match->data;
+       unsigned int i;
+
+       printf("multiport ");
+
+       switch (multiinfo->flags) {
+       case IP6T_MULTIPORT_SOURCE:
+               printf("sports ");
+               break;
+
+       case IP6T_MULTIPORT_DESTINATION:
+               printf("dports ");
+               break;
+
+       case IP6T_MULTIPORT_EITHER:
+               printf("ports ");
+               break;
+
+       default:
+               printf("ERROR ");
+               break;
+       }
+
+       for (i=0; i < multiinfo->count; i++) {
+               printf("%s", i ? "," : "");
+               print_port(multiinfo->ports[i], ip->proto, numeric);
+       }
+       printf(" ");
+}
+
+static void
+print_v1(const struct ip6t_ip6 *ip,
+        const struct ip6t_entry_match *match,
+        int numeric)
+{
+       const struct ip6t_multiport_v1 *multiinfo
+               = (const struct ip6t_multiport_v1 *)match->data;
+       unsigned int i;
+
+       printf("multiport ");
+
+       switch (multiinfo->flags) {
+       case IP6T_MULTIPORT_SOURCE:
+               printf("sports ");
+               break;
+
+       case IP6T_MULTIPORT_DESTINATION:
+               printf("dports ");
+               break;
+
+       case IP6T_MULTIPORT_EITHER:
+               printf("ports ");
+               break;
+
+       default:
+               printf("ERROR ");
+               break;
+       }
+
+       if (multiinfo->invert)
+               printf("! ");
+
+       for (i=0; i < multiinfo->count; i++) {
+               printf("%s", i ? "," : "");
+               print_port(multiinfo->ports[i], ip->proto, numeric);
+               if (multiinfo->pflags[i]) {
+                       printf(":");
+                       print_port(multiinfo->ports[++i], ip->proto, numeric);
+               }
+       }
+       printf(" ");
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_multiport *multiinfo
+               = (const struct ip6t_multiport *)match->data;
+       unsigned int i;
+
+       switch (multiinfo->flags) {
+       case IP6T_MULTIPORT_SOURCE:
+               printf("--sports ");
+               break;
+
+       case IP6T_MULTIPORT_DESTINATION:
+               printf("--dports ");
+               break;
+
+       case IP6T_MULTIPORT_EITHER:
+               printf("--ports ");
+               break;
+       }
+
+       for (i=0; i < multiinfo->count; i++) {
+               printf("%s", i ? "," : "");
+               print_port(multiinfo->ports[i], ip->proto, 1);
+       }
+       printf(" ");
+}
+
+static void save_v1(const struct ip6t_ip6 *ip, 
+                   const struct ip6t_entry_match *match)
+{
+       const struct ip6t_multiport_v1 *multiinfo
+               = (const struct ip6t_multiport_v1 *)match->data;
+       unsigned int i;
+
+       switch (multiinfo->flags) {
+       case IP6T_MULTIPORT_SOURCE:
+               printf("--sports ");
+               break;
+
+       case IP6T_MULTIPORT_DESTINATION:
+               printf("--dports ");
+               break;
+
+       case IP6T_MULTIPORT_EITHER:
+               printf("--ports ");
+               break;
+       }
+
+       if (multiinfo->invert)
+               printf("! ");
+
+       for (i=0; i < multiinfo->count; i++) {
+               printf("%s", i ? "," : "");
+               print_port(multiinfo->ports[i], ip->proto, 1);
+               if (multiinfo->pflags[i]) {
+                       printf(":");
+                       print_port(multiinfo->ports[++i], ip->proto, 1);
+               }
+       }
+       printf(" ");
+}
+
+static struct ip6tables_match multiport = {
+       .name           = "multiport",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_multiport)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_multiport)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+static struct ip6tables_match multiport_v1 = { 
+       .next           = NULL,
+       .name           = "multiport",
+       .revision       = 1,
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_multiport_v1)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_multiport_v1)),
+       .help           = &help_v1,
+       .init           = &init,
+       .parse          = &parse_v1,
+       .final_check    = &final_check,
+       .print          = &print_v1,
+       .save           = &save_v1,
+       .extra_opts     = opts
+};
+
+void
+_init(void)
+{
+       register_match6(&multiport);
+       register_match6(&multiport_v1);
+}
diff --git a/extensions/libip6t_multiport.man b/extensions/libip6t_multiport.man
new file mode 100644 (file)
index 0000000..6f75a6e
--- /dev/null
@@ -0,0 +1,20 @@
+This module matches a set of source or destination ports.  Up to 15
+ports can be specified.  It can only be used in conjunction
+with
+.B "-p tcp"
+or
+.BR "-p udp" .
+.TP
+.BR "--source-ports " "\fI[!] port\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the source port is one of the given ports.  The flag
+.B --sports
+is a convenient alias for this option.
+.TP
+.BR "--destination-ports " "\fI[!] port\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the destination port is one of the given ports.  The flag
+.B --dports
+is a convenient alias for this option.
+.TP
+.BR "--ports " "\fI[!] port\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the both the source and destination ports are equal to each
+other and to one of the given ports.
diff --git a/extensions/libip6t_owner.c b/extensions/libip6t_owner.c
new file mode 100644 (file)
index 0000000..99b5c13
--- /dev/null
@@ -0,0 +1,248 @@
+/* Shared library add-on to iptables to add OWNER matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_owner.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+#ifdef IP6T_OWNER_COMM
+       printf(
+"OWNER match v%s options:\n"
+"[!] --uid-owner userid     Match local uid\n"
+"[!] --gid-owner groupid    Match local gid\n"
+"[!] --pid-owner processid  Match local pid\n"
+"[!] --sid-owner sessionid  Match local sid\n"
+"[!] --cmd-owner name       Match local command name\n"
+"\n",
+IPTABLES_VERSION);
+#else
+       printf(
+"OWNER match v%s options:\n"
+"[!] --uid-owner userid     Match local uid\n"
+"[!] --gid-owner groupid    Match local gid\n"
+"[!] --pid-owner processid  Match local pid\n"
+"[!] --sid-owner sessionid  Match local sid\n"
+"\n",
+IPTABLES_VERSION);
+#endif /* IP6T_OWNER_COMM */
+}
+
+static struct option opts[] = {
+       { "uid-owner", 1, 0, '1' },
+       { "gid-owner", 1, 0, '2' },
+       { "pid-owner", 1, 0, '3' },
+       { "sid-owner", 1, 0, '4' },
+#ifdef IP6T_OWNER_COMM
+       { "cmd-owner", 1, 0, '5' },
+#endif
+       {0}
+};
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_owner_info *ownerinfo = (struct ip6t_owner_info *)(*match)->data;
+
+       switch (c) {
+               char *end;
+               struct passwd *pwd;
+               struct group *grp;
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if ((pwd = getpwnam(optarg)))
+                       ownerinfo->uid = pwd->pw_uid;
+               else {
+                       ownerinfo->uid = strtoul(optarg, &end, 0);
+                       if (*end != '\0' || end == optarg)
+                               exit_error(PARAMETER_PROBLEM, "Bad OWNER UID value `%s'", optarg);
+               }
+               if (invert)
+                       ownerinfo->invert |= IP6T_OWNER_UID;
+               ownerinfo->match |= IP6T_OWNER_UID;
+               *flags = 1;
+               break;
+
+       case '2':
+               check_inverse(optarg, &invert, &optind, 0);
+               if ((grp = getgrnam(optarg)))
+                       ownerinfo->gid = grp->gr_gid;
+               else {
+                       ownerinfo->gid = strtoul(optarg, &end, 0);
+                       if (*end != '\0' || end == optarg)
+                               exit_error(PARAMETER_PROBLEM, "Bad OWNER GID value `%s'", optarg);
+               }
+               if (invert)
+                       ownerinfo->invert |= IP6T_OWNER_GID;
+               ownerinfo->match |= IP6T_OWNER_GID;
+               *flags = 1;
+               break;
+
+       case '3':
+               check_inverse(optarg, &invert, &optind, 0);
+               ownerinfo->pid = strtoul(optarg, &end, 0);
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad OWNER PID value `%s'", optarg);
+               if (invert)
+                       ownerinfo->invert |= IP6T_OWNER_PID;
+               ownerinfo->match |= IP6T_OWNER_PID;
+               *flags = 1;
+               break;
+
+       case '4':
+               check_inverse(optarg, &invert, &optind, 0);
+               ownerinfo->sid = strtoul(optarg, &end, 0);
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad OWNER SID value `%s'", optarg);
+               if (invert)
+                       ownerinfo->invert |= IP6T_OWNER_SID;
+               ownerinfo->match |= IP6T_OWNER_SID;
+               *flags = 1;
+               break;
+
+#ifdef IP6T_OWNER_COMM
+       case '5':
+               check_inverse(optarg, &invert, &optind, 0);
+               if(strlen(optarg) > sizeof(ownerinfo->comm))
+                       exit_error(PARAMETER_PROBLEM, "OWNER CMD `%s' too long, max %d characters", optarg, sizeof(ownerinfo->comm));
+               
+               strncpy(ownerinfo->comm, optarg, sizeof(ownerinfo->comm));
+               ownerinfo->comm[sizeof(ownerinfo->comm)-1] = '\0';
+
+               if (invert)
+                       ownerinfo->invert |= IP6T_OWNER_COMM;
+               ownerinfo->match |= IP6T_OWNER_COMM;
+               *flags = 1;
+               break;
+#endif
+               
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+print_item(struct ip6t_owner_info *info, u_int8_t flag, int numeric, char *label)
+{
+       if(info->match & flag) {
+
+               if (info->invert & flag)
+                       printf("! ");
+
+               printf(label);
+
+               switch(info->match & flag) {
+               case IP6T_OWNER_UID:
+                       if(!numeric) {
+                               struct passwd *pwd = getpwuid(info->uid);
+
+                               if(pwd && pwd->pw_name) {
+                                       printf("%s ", pwd->pw_name);
+                                       break;
+                               }
+                               /* FALLTHROUGH */
+                       }
+                       printf("%u ", info->uid);
+                       break;
+               case IP6T_OWNER_GID:
+                       if(!numeric) {
+                               struct group *grp = getgrgid(info->gid);
+
+                               if(grp && grp->gr_name) {
+                                       printf("%s ", grp->gr_name);
+                                       break;
+                               }
+                               /* FALLTHROUGH */
+                       }
+                       printf("%u ", info->gid);
+                       break;
+               case IP6T_OWNER_PID:
+                       printf("%u ", info->pid);
+                       break;
+               case IP6T_OWNER_SID:
+                       printf("%u ", info->sid);
+                       break;
+#ifdef IP6T_OWNER_COMM
+               case IP6T_OWNER_COMM:
+                       printf("%.*s ", (int)sizeof(info->comm), info->comm);
+                       break;
+#endif
+               default:
+                       break;
+               }
+       }
+}
+
+/* Final check; must have specified --own. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "OWNER match: You must specify one or more options");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       struct ip6t_owner_info *info = (struct ip6t_owner_info *)match->data;
+
+       print_item(info, IP6T_OWNER_UID, numeric, "OWNER UID match ");
+       print_item(info, IP6T_OWNER_GID, numeric, "OWNER GID match ");
+       print_item(info, IP6T_OWNER_PID, numeric, "OWNER PID match ");
+       print_item(info, IP6T_OWNER_SID, numeric, "OWNER SID match ");
+#ifdef IP6T_OWNER_COMM
+       print_item(info, IP6T_OWNER_COMM, numeric, "OWNER CMD match ");
+#endif
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       struct ip6t_owner_info *info = (struct ip6t_owner_info *)match->data;
+
+       print_item(info, IP6T_OWNER_UID, 0, "--uid-owner ");
+       print_item(info, IP6T_OWNER_GID, 0, "--gid-owner ");
+       print_item(info, IP6T_OWNER_PID, 0, "--pid-owner ");
+       print_item(info, IP6T_OWNER_SID, 0, "--sid-owner ");
+#ifdef IP6T_OWNER_COMM
+       print_item(info, IP6T_OWNER_COMM, 0, "--cmd-owner ");
+#endif
+}
+
+static struct ip6tables_match owner = {
+       .name           = "owner",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_owner_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_owner_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_match6(&owner);
+}
diff --git a/extensions/libip6t_owner.man b/extensions/libip6t_owner.man
new file mode 100644 (file)
index 0000000..edd72b1
--- /dev/null
@@ -0,0 +1,23 @@
+This module attempts to match various characteristics of the packet
+creator, for locally-generated packets.  It is only valid in the
+.B OUTPUT
+chain, and even this some packets (such as ICMPv6 ping responses) may
+have no owner, and hence never match.  This is regarded as experimental.
+.TP
+.BI "--uid-owner " "userid"
+Matches if the packet was created by a process with the given
+effective user id.
+.TP
+.BI "--gid-owner " "groupid"
+Matches if the packet was created by a process with the given
+effective group id.
+.TP
+.BI "--pid-owner " "processid"
+Matches if the packet was created by a process with the given
+process id.
+.TP
+.BI "--sid-owner " "sessionid"
+Matches if the packet was created by a process in the given session
+group.
+.TP
+.B NOTE: pid, sid and command matching are broken on SMP
diff --git a/extensions/libip6t_physdev.c b/extensions/libip6t_physdev.c
new file mode 100644 (file)
index 0000000..e7fa22e
--- /dev/null
@@ -0,0 +1,192 @@
+/* Shared library add-on to iptables to add bridge port matching support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_physdev.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+static void
+help(void)
+{
+       printf(
+"physdev v%s options:\n"
+" --physdev-in [!] input name[+]               bridge port name ([+] for wildcard)\n"
+" --physdev-out [!] output name[+]     bridge port name ([+] for wildcard)\n"
+" [!] --physdev-is-in                  arrived on a bridge device\n"
+" [!] --physdev-is-out                 will leave on a bridge device\n"
+" [!] --physdev-is-bridged             it's a bridged packet\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "physdev-in", 1, 0, '1' },
+       { "physdev-out", 1, 0, '2' },
+       { "physdev-is-in", 0, 0, '3' },
+       { "physdev-is-out", 0, 0, '4' },
+       { "physdev-is-bridged", 0, 0, '5' },
+       {0}
+};
+
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+}
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_physdev_info *info =
+               (struct ip6t_physdev_info*)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IP6T_PHYSDEV_OP_IN)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_interface(argv[optind-1], info->physindev,
+                               (unsigned char *)info->in_mask);
+               if (invert)
+                       info->invert |= IP6T_PHYSDEV_OP_IN;
+               info->bitmask |= IP6T_PHYSDEV_OP_IN;
+               *flags |= IP6T_PHYSDEV_OP_IN;
+               break;
+
+       case '2':
+               if (*flags & IP6T_PHYSDEV_OP_OUT)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_interface(argv[optind-1], info->physoutdev,
+                               (unsigned char *)info->out_mask);
+               if (invert)
+                       info->invert |= IP6T_PHYSDEV_OP_OUT;
+               info->bitmask |= IP6T_PHYSDEV_OP_OUT;
+               *flags |= IP6T_PHYSDEV_OP_OUT;
+               break;
+
+       case '3':
+               if (*flags & IP6T_PHYSDEV_OP_ISIN)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               info->bitmask |= IP6T_PHYSDEV_OP_ISIN;
+               if (invert)
+                       info->invert |= IP6T_PHYSDEV_OP_ISIN;
+               *flags |= IP6T_PHYSDEV_OP_ISIN;
+               break;
+
+       case '4':
+               if (*flags & IP6T_PHYSDEV_OP_ISOUT)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               info->bitmask |= IP6T_PHYSDEV_OP_ISOUT;
+               if (invert)
+                       info->invert |= IP6T_PHYSDEV_OP_ISOUT;
+               *flags |= IP6T_PHYSDEV_OP_ISOUT;
+               break;
+
+       case '5':
+               if (*flags & IP6T_PHYSDEV_OP_BRIDGED)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               if (invert)
+                       info->invert |= IP6T_PHYSDEV_OP_BRIDGED;
+               *flags |= IP6T_PHYSDEV_OP_BRIDGED;
+               info->bitmask |= IP6T_PHYSDEV_OP_BRIDGED;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+multiple_use:
+       exit_error(PARAMETER_PROBLEM,
+          "multiple use of the same physdev option is not allowed");
+
+}
+
+static void final_check(unsigned int flags)
+{
+       if (flags == 0)
+               exit_error(PARAMETER_PROBLEM, "PHYSDEV: no physdev option specified");
+}
+
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       struct ip6t_physdev_info *info =
+               (struct ip6t_physdev_info*)match->data;
+
+       printf("PHYSDEV match");
+       if (info->bitmask & IP6T_PHYSDEV_OP_ISIN)
+               printf("%s --physdev-is-in",
+                      info->invert & IP6T_PHYSDEV_OP_ISIN ? " !":"");
+       if (info->bitmask & IP6T_PHYSDEV_OP_IN)
+               printf("%s --physdev-in %s",
+               (info->invert & IP6T_PHYSDEV_OP_IN) ? " !":"", info->physindev);
+
+       if (info->bitmask & IP6T_PHYSDEV_OP_ISOUT)
+               printf("%s --physdev-is-out",
+                      info->invert & IP6T_PHYSDEV_OP_ISOUT ? " !":"");
+       if (info->bitmask & IP6T_PHYSDEV_OP_OUT)
+               printf("%s --physdev-out %s",
+               (info->invert & IP6T_PHYSDEV_OP_OUT) ? " !":"", info->physoutdev);
+       if (info->bitmask & IP6T_PHYSDEV_OP_BRIDGED)
+               printf("%s --physdev-is-bridged",
+                      info->invert & IP6T_PHYSDEV_OP_BRIDGED ? " !":"");
+       printf(" ");
+}
+
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       struct ip6t_physdev_info *info =
+               (struct ip6t_physdev_info*)match->data;
+
+       if (info->bitmask & IP6T_PHYSDEV_OP_ISIN)
+               printf("%s --physdev-is-in",
+                      info->invert & IP6T_PHYSDEV_OP_ISIN ? " !":"");
+       if (info->bitmask & IP6T_PHYSDEV_OP_IN)
+               printf("%s --physdev-in %s",
+               (info->invert & IP6T_PHYSDEV_OP_IN) ? " !":"", info->physindev);
+
+       if (info->bitmask & IP6T_PHYSDEV_OP_ISOUT)
+               printf("%s --physdev-is-out",
+                      info->invert & IP6T_PHYSDEV_OP_ISOUT ? " !":"");
+       if (info->bitmask & IP6T_PHYSDEV_OP_OUT)
+               printf("%s --physdev-out %s",
+               (info->invert & IP6T_PHYSDEV_OP_OUT) ? " !":"", info->physoutdev);
+       if (info->bitmask & IP6T_PHYSDEV_OP_BRIDGED)
+               printf("%s --physdev-is-bridged",
+                      info->invert & IP6T_PHYSDEV_OP_BRIDGED ? " !":"");
+       printf(" ");
+}
+
+static struct ip6tables_match physdev = {
+       .name           = "physdev",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_physdev_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_physdev_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_match6(&physdev);
+}
diff --git a/extensions/libip6t_physdev.man b/extensions/libip6t_physdev.man
new file mode 100644 (file)
index 0000000..1e635fc
--- /dev/null
@@ -0,0 +1,42 @@
+This module matches on the bridge port input and output devices enslaved
+to a bridge device. This module is a part of the infrastructure that enables
+a transparent bridging IP firewall and is only useful for kernel versions
+above version 2.5.44.
+.TP
+.BR --physdev-in " [!] \fIname\fP"
+Name of a bridge port via which a packet is received (only for
+packets entering the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains). If the interface name ends in a "+", then any
+interface which begins with this name will match. If the packet didn't arrive
+through a bridge device, this packet won't match this option, unless '!' is used.
+.TP
+.BR --physdev-out " [!] \fIname\fP"
+Name of a bridge port via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD ,
+.B OUTPUT
+and
+.B POSTROUTING
+chains).  If the interface name ends in a "+", then any
+interface which begins with this name will match. Note that in the
+.BR nat " and " mangle
+.B OUTPUT
+chains one cannot match on the bridge output port, however one can in the
+.B "filter OUTPUT"
+chain. If the packet won't leave by a bridge device or it is yet unknown what
+the output device will be, then the packet won't match this option, unless
+'!' is used.
+.TP
+.RB "[!] " --physdev-is-in
+Matches if the packet has entered through a bridge interface.
+.TP
+.RB "[!] " --physdev-is-out
+Matches if the packet will leave through a bridge interface.
+.TP
+.RB "[!] " --physdev-is-bridged
+Matches if the packet is being bridged and therefore is not being routed.
+This is only useful in the FORWARD and POSTROUTING chains.
diff --git a/extensions/libip6t_policy.c b/extensions/libip6t_policy.c
new file mode 100644 (file)
index 0000000..2f4453e
--- /dev/null
@@ -0,0 +1,478 @@
+/* Shared library add-on to iptables to add policy support. */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ip6tables.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include "../include/linux/netfilter_ipv6/ip6t_policy.h"
+
+/*
+ * HACK: global pointer to current matchinfo for making
+ * final checks and adjustments in final_check.
+ */
+static struct ip6t_policy_info *policy_info;
+
+static void help(void)
+{
+       printf(
+"policy v%s options:\n"
+"  --dir in|out                        match policy applied during decapsulation/\n"
+"                              policy to be applied during encapsulation\n"
+"  --pol none|ipsec            match policy\n"
+"  --strict                    match entire policy instead of single element\n"
+"                              at any position\n"
+"[!] --reqid reqid             match reqid\n"
+"[!] --spi spi                 match SPI\n"
+"[!] --proto proto             match protocol (ah/esp/ipcomp)\n"
+"[!] --mode mode               match mode (transport/tunnel)\n"
+"[!] --tunnel-src addr/masklen match tunnel source\n"
+"[!] --tunnel-dst addr/masklen match tunnel destination\n"
+"  --next                      begin next element in policy\n",
+       IPTABLES_VERSION);
+}
+
+static struct option opts[] =
+{
+       {
+               .name           = "dir",
+               .has_arg        = 1,
+               .val            = '1',
+       },
+       {
+               .name           = "pol",
+               .has_arg        = 1,
+               .val            = '2',
+       },
+       {
+               .name           = "strict",
+               .val            = '3'
+       },
+       {
+               .name           = "reqid",
+               .has_arg        = 1,
+               .val            = '4',
+       },
+       {
+               .name           = "spi",
+               .has_arg        = 1,
+               .val            = '5'
+       },
+       {
+               .name           = "tunnel-src",
+               .has_arg        = 1,
+               .val            = '6'
+       },
+       {
+               .name           = "tunnel-dst",
+               .has_arg        = 1,
+               .val            = '7'
+       },
+       {
+               .name           = "proto",
+               .has_arg        = 1,
+               .val            = '8'
+       },
+       {
+               .name           = "mode",
+               .has_arg        = 1,
+               .val            = '9'
+       },
+       {
+               .name           = "next",
+               .val            = 'a'
+       },
+       { }
+};
+
+/* FIXME - Duplicated code from ip6tables.c */
+/* Duplicated to stop too many changes in other files .... */
+static void
+in6addrcpy(struct in6_addr *dst, struct in6_addr *src)
+{
+        memcpy(dst, src, sizeof(struct in6_addr));
+        /* dst->s6_addr = src->s6_addr; */
+}
+
+static char *
+addr_to_numeric(const struct in6_addr *addrp)
+{
+        /* 0000:0000:0000:0000:0000:000.000.000.000
+        * 0000:0000:0000:0000:0000:0000:0000:0000 */
+        static char buf[50+1];
+        return (char *)inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
+}
+
+static char *
+mask_to_numeric(const struct in6_addr *addrp)
+{
+        static char buf[50+2];
+        int l = ipv6_prefix_length(addrp);
+        if (l == -1) {
+               strcpy(buf, "/");
+               strcat(buf, addr_to_numeric(addrp));
+               return buf;
+       }
+       sprintf(buf, "/%d", l);
+       return buf;
+}
+
+/* These should be in include/ip6tables.h... */
+extern u_int16_t parse_protocol(const char *s);
+extern void parse_hostnetworkmask(const char *name, struct in6_addr **addrpp,
+               struct in6_addr *maskp, unsigned int *naddrs);
+
+/* End duplicated code from ip6tables.c */
+
+static void init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       *nfcache |= NFC_UNKNOWN;
+}
+
+static int parse_direction(char *s)
+{
+       if (strcmp(s, "in") == 0)
+               return IP6T_POLICY_MATCH_IN;
+       if (strcmp(s, "out") == 0)
+               return IP6T_POLICY_MATCH_OUT;
+       exit_error(PARAMETER_PROBLEM, "policy_match: invalid dir `%s'", s);
+}
+
+static int parse_policy(char *s)
+{
+       if (strcmp(s, "none") == 0)
+               return IP6T_POLICY_MATCH_NONE;
+       if (strcmp(s, "ipsec") == 0)
+               return 0;
+       exit_error(PARAMETER_PROBLEM, "policy match: invalid policy `%s'", s);
+}
+
+static int parse_mode(char *s)
+{
+       if (strcmp(s, "transport") == 0)
+               return IP6T_POLICY_MODE_TRANSPORT;
+       if (strcmp(s, "tunnel") == 0)
+               return IP6T_POLICY_MODE_TUNNEL;
+       exit_error(PARAMETER_PROBLEM, "policy match: invalid mode `%s'", s);
+}
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+                 const struct ip6t_entry *entry,
+                 unsigned int *nfcache,
+                 struct ip6t_entry_match **match)
+{
+       struct ip6t_policy_info *info = (void *)(*match)->data;
+       struct ip6t_policy_elem *e = &info->pol[info->len];
+       struct in6_addr *addr = NULL, mask;
+       unsigned int naddr = 0;
+       int mode;
+
+       check_inverse(optarg, &invert, &optind, 0);
+
+       switch (c) {
+       case '1':
+               if (info->flags & (IP6T_POLICY_MATCH_IN|IP6T_POLICY_MATCH_OUT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --dir option");
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: can't invert --dir option");
+
+               info->flags |= parse_direction(argv[optind-1]);
+               break;
+       case '2':
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: can't invert --policy option");
+
+               info->flags |= parse_policy(argv[optind-1]);
+               break;
+       case '3':
+               if (info->flags & IP6T_POLICY_MATCH_STRICT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --strict option");
+
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: can't invert --strict option");
+
+               info->flags |= IP6T_POLICY_MATCH_STRICT;
+               break;
+       case '4':
+               if (e->match.reqid)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --reqid option");
+
+               e->match.reqid = 1;
+               e->invert.reqid = invert;
+               e->reqid = strtol(argv[optind-1], NULL, 10);
+               break;
+       case '5':
+               if (e->match.spi)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --spi option");
+
+               e->match.spi = 1;
+               e->invert.spi = invert;
+               e->spi = strtol(argv[optind-1], NULL, 0x10);
+               break;
+       case '6':
+               if (e->match.saddr)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --tunnel-src option");
+
+               parse_hostnetworkmask(argv[optind-1], &addr, &mask, &naddr);
+               if (naddr > 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: name resolves to multiple IPs");
+
+               e->match.saddr = 1;
+               e->invert.saddr = invert;
+               in6addrcpy(&e->saddr.a6, addr);
+               in6addrcpy(&e->smask.a6, &mask);
+                break;
+       case '7':
+               if (e->match.daddr)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --tunnel-dst option");
+
+               parse_hostnetworkmask(argv[optind-1], &addr, &mask, &naddr);
+               if (naddr > 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: name resolves to multiple IPs");
+
+               e->match.daddr = 1;
+               e->invert.daddr = invert;
+               in6addrcpy(&e->daddr.a6, addr);
+               in6addrcpy(&e->dmask.a6, &mask);
+               break;
+       case '8':
+               if (e->match.proto)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --proto option");
+
+               e->proto = parse_protocol(argv[optind-1]);
+               if (e->proto != IPPROTO_AH && e->proto != IPPROTO_ESP &&
+                   e->proto != IPPROTO_COMP)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: protocol must ah/esp/ipcomp");
+               e->match.proto = 1;
+               e->invert.proto = invert;
+               break;
+       case '9':
+               if (e->match.mode)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --mode option");
+
+               mode = parse_mode(argv[optind-1]);
+               e->match.mode = 1;
+               e->invert.mode = invert;
+               e->mode = mode;
+               break;
+       case 'a':
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: can't invert --next option");
+
+               if (++info->len == IP6T_POLICY_MAX_ELEM)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: maximum policy depth reached");
+               break;
+       default:
+               return 0;
+       }
+
+       policy_info = info;
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       struct ip6t_policy_info *info = policy_info;
+       struct ip6t_policy_elem *e;
+       int i;
+
+       if (info == NULL)
+               exit_error(PARAMETER_PROBLEM,
+                          "policy match: no parameters given");
+
+       if (!(info->flags & (IP6T_POLICY_MATCH_IN|IP6T_POLICY_MATCH_OUT)))
+               exit_error(PARAMETER_PROBLEM,
+                          "policy match: neither --in nor --out specified");
+
+       if (info->flags & IP6T_POLICY_MATCH_NONE) {
+               if (info->flags & IP6T_POLICY_MATCH_STRICT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: policy none but --strict given");
+
+               if (info->len != 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: policy none but policy given");
+       } else
+               info->len++;    /* increase len by 1, no --next after last element */
+
+       if (!(info->flags & IP6T_POLICY_MATCH_STRICT) && info->len > 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "policy match: multiple elements but no --strict");
+
+       for (i = 0; i < info->len; i++) {
+               e = &info->pol[i];
+
+                if (info->flags & IP6T_POLICY_MATCH_STRICT &&
+                   !(e->match.reqid || e->match.spi || e->match.saddr ||
+                      e->match.daddr || e->match.proto || e->match.mode))
+                        exit_error(PARAMETER_PROBLEM,
+                                   "policy match: empty policy element");
+
+               if ((e->match.saddr || e->match.daddr)
+                   && ((e->mode == IP6T_POLICY_MODE_TUNNEL && e->invert.mode) ||
+                       (e->mode == IP6T_POLICY_MODE_TRANSPORT && !e->invert.mode)))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: --tunnel-src/--tunnel-dst "
+                                  "is only valid in tunnel mode");
+       }
+}
+
+static void print_mode(char *prefix, u_int8_t mode, int numeric)
+{
+       printf("%smode ", prefix);
+
+       switch (mode) {
+       case IP6T_POLICY_MODE_TRANSPORT:
+               printf("transport ");
+               break;
+       case IP6T_POLICY_MODE_TUNNEL:
+               printf("tunnel ");
+               break;
+       default:
+               printf("??? ");
+               break;
+       }
+}
+
+static void print_proto(char *prefix, u_int8_t proto, int numeric)
+{
+       struct protoent *p = NULL;
+
+       printf("%sproto ", prefix);
+       if (!numeric)
+               p = getprotobynumber(proto);
+       if (p != NULL)
+               printf("%s ", p->p_name);
+       else
+               printf("%u ", proto);
+}
+
+#define PRINT_INVERT(x)                \
+do {                           \
+       if (x)                  \
+               printf("! ");   \
+} while(0)
+
+static void print_entry(char *prefix, const struct ip6t_policy_elem *e,
+                        int numeric)
+{
+       if (e->match.reqid) {
+               PRINT_INVERT(e->invert.reqid);
+               printf("%sreqid %u ", prefix, e->reqid);
+       }
+       if (e->match.spi) {
+               PRINT_INVERT(e->invert.spi);
+               printf("%sspi 0x%x ", prefix, e->spi);
+       }
+       if (e->match.proto) {
+               PRINT_INVERT(e->invert.proto);
+               print_proto(prefix, e->proto, numeric);
+       }
+       if (e->match.mode) {
+               PRINT_INVERT(e->invert.mode);
+               print_mode(prefix, e->mode, numeric);
+       }
+       if (e->match.daddr) {
+               PRINT_INVERT(e->invert.daddr);
+               printf("%stunnel-dst %s%s ", prefix,
+                      addr_to_numeric((struct in6_addr *)&e->daddr),
+                      mask_to_numeric((struct in6_addr *)&e->dmask));
+       }
+       if (e->match.saddr) {
+               PRINT_INVERT(e->invert.saddr);
+               printf("%stunnel-src %s%s ", prefix,
+                      addr_to_numeric((struct in6_addr *)&e->saddr),
+                      mask_to_numeric((struct in6_addr *)&e->smask));
+       }
+}
+
+static void print_flags(char *prefix, const struct ip6t_policy_info *info)
+{
+       if (info->flags & IP6T_POLICY_MATCH_IN)
+               printf("%sdir in ", prefix);
+       else
+               printf("%sdir out ", prefix);
+
+       if (info->flags & IP6T_POLICY_MATCH_NONE)
+               printf("%spol none ", prefix);
+       else
+               printf("%spol ipsec ", prefix);
+
+       if (info->flags & IP6T_POLICY_MATCH_STRICT)
+               printf("%sstrict ", prefix);
+}
+
+static void print(const struct ip6t_ip6 *ip,
+                  const struct ip6t_entry_match *match,
+                 int numeric)
+{
+       const struct ip6t_policy_info *info = (void *)match->data;
+       unsigned int i;
+
+       printf("policy match ");
+       print_flags("", info);
+       for (i = 0; i < info->len; i++) {
+               if (info->len > 1)
+                       printf("[%u] ", i);
+               print_entry("", &info->pol[i], numeric);
+       }
+
+       printf("\n");
+}
+
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_policy_info *info = (void *)match->data;
+       unsigned int i;
+
+       print_flags("--", info);
+       for (i = 0; i < info->len; i++) {
+               print_entry("--", &info->pol[i], 0);
+               if (i + 1 < info->len)
+                       printf("--next ");
+       }
+}
+
+struct ip6tables_match policy = {
+       .name           = "policy",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_policy_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_policy_info)),
+       .help           = help,
+       .init           = init,
+       .parse          = parse,
+       .final_check    = final_check,
+       .print          = print,
+       .save           = save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match6(&policy);
+}
diff --git a/extensions/libip6t_policy.man b/extensions/libip6t_policy.man
new file mode 100644 (file)
index 0000000..eed163e
--- /dev/null
@@ -0,0 +1,48 @@
+This modules matches the policy used by IPsec for handling a packet.
+.TP
+.BI "--dir " "in|out"
+Used to select whether to match the policy used for decapsulation or the
+policy that will be used for encapsulation.
+.B in
+is valid in the
+.B PREROUTING, INPUT and FORWARD
+chains,
+.B out
+is valid in the
+.B POSTROUTING, OUTPUT and FORWARD
+chains.
+.TP
+.BI "--pol " "none|ipsec"
+Matches if the packet is subject to IPsec processing.
+.TP
+.BI "--strict"
+Selects whether to match the exact policy or match if any rule of
+the policy matches the given policy.
+.TP
+.BI "--reqid " "id"
+Matches the reqid of the policy rule. The reqid can be specified with
+.B setkey(8)
+using
+.B unique:id
+as level.
+.TP
+.BI "--spi " "spi"
+Matches the SPI of the SA.
+.TP
+.BI "--proto " "ah|esp|ipcomp"
+Matches the encapsulation protocol.
+.TP
+.BI "--mode " "tunnel|transport"
+Matches the encapsulation mode.
+.TP
+.BI "--tunnel-src " "addr[/mask]"
+Matches the source end-point address of a tunnel mode SA.
+Only valid with --mode tunnel.
+.TP
+.BI "--tunnel-dst " "addr[/mask]"
+Matches the destination end-point address of a tunnel mode SA.
+Only valid with --mode tunnel.
+.TP
+.BI "--next"
+Start the next element in the policy specification. Can only be used with
+--strict
diff --git a/extensions/libip6t_rt.c b/extensions/libip6t_rt.c
new file mode 100644 (file)
index 0000000..251604b
--- /dev/null
@@ -0,0 +1,362 @@
+/* Shared library add-on to ip6tables to add Routing header support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ip6tables.h>
+/*#include <linux/in6.h>*/
+#include <linux/netfilter_ipv6/ip6t_rt.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+                                        
+/*#define DEBUG        1*/
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"RT v%s options:\n"
+" --rt-type [!] type            match the type\n"
+" --rt-segsleft [!] num[:num]   match the Segments Left field (range)\n"
+" --rt-len [!] length           total length of this header\n"
+" --rt-0-res                    check the reserved filed, too (type 0)\n"
+" --rt-0-addrs ADDR[,ADDR...]   Type=0 addresses (list, max: %d)\n"
+" --rt-0-not-strict             List of Type=0 addresses not a strict list\n",
+IPTABLES_VERSION, IP6T_RT_HOPS);
+}
+
+static struct option opts[] = {
+       { "rt-type", 1, 0, '1' },
+       { "rt-segsleft", 1, 0, '2' },
+       { "rt-len", 1, 0, '3' },
+       { "rt-0-res", 0, 0, '4' },
+       { "rt-0-addrs", 1, 0, '5' },
+       { "rt-0-not-strict", 0, 0, '6' },
+       {0}
+};
+
+static u_int32_t
+parse_rt_num(const char *idstr, const char *typestr)
+{
+       unsigned long int id;
+       char* ep;
+
+       id =  strtoul(idstr,&ep,0) ;
+
+       if ( idstr == ep ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "RT no valid digits in %s `%s'", typestr, idstr);
+       }
+       if ( id == ULONG_MAX  && errno == ERANGE ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "%s `%s' specified too big: would overflow",
+                          typestr, idstr);
+       }       
+       if ( *idstr != '\0'  && *ep != '\0' ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "RT error parsing %s `%s'", typestr, idstr);
+       }
+       return (u_int32_t) id;
+}
+
+static void
+parse_rt_segsleft(const char *idstring, u_int32_t *ids)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(idstring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               ids[0] = ids[1] = parse_rt_num(buffer,"segsleft");
+       else {
+               *cp = '\0';
+               cp++;
+
+               ids[0] = buffer[0] ? parse_rt_num(buffer,"segsleft") : 0;
+               ids[1] = cp[0] ? parse_rt_num(cp,"segsleft") : 0xFFFFFFFF;
+       }
+       free(buffer);
+}
+
+static char *
+addr_to_numeric(const struct in6_addr *addrp)
+{
+       static char buf[50+1];
+       return (char *)inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
+}
+
+static struct in6_addr *
+numeric_to_addr(const char *num)
+{
+       static struct in6_addr ap;
+       int err;
+
+       if ((err=inet_pton(AF_INET6, num, &ap)) == 1)
+               return &ap;
+#ifdef DEBUG
+       fprintf(stderr, "\nnumeric2addr: %d\n", err);
+#endif
+        exit_error(PARAMETER_PROBLEM, "bad address: %s", num);
+
+       return (struct in6_addr *)NULL;
+}
+
+
+static int
+parse_addresses(const char *addrstr, struct in6_addr *addrp)
+{
+        char *buffer, *cp, *next;
+        unsigned int i;
+       
+       buffer = strdup(addrstr);
+        if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
+                       
+        for (cp=buffer, i=0; cp && i<IP6T_RT_HOPS; cp=next,i++)
+        {
+                next=strchr(cp, ',');
+                if (next) *next++='\0';
+               memcpy(&(addrp[i]), numeric_to_addr(cp), sizeof(struct in6_addr));
+#if DEBUG
+               printf("addr str: %s\n", cp);
+               printf("addr ip6: %s\n", addr_to_numeric((numeric_to_addr(cp))));
+               printf("addr [%d]: %s\n", i, addr_to_numeric(&(addrp[i])));
+#endif
+       }
+        if (cp) exit_error(PARAMETER_PROBLEM, "too many addresses specified");
+
+       free(buffer);
+
+#if DEBUG
+       printf("addr nr: %d\n", i);
+#endif
+
+       return i;
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_rt *rtinfo = (struct ip6t_rt *)m->data;
+
+       rtinfo->rt_type = 0x0L;
+       rtinfo->segsleft[0] = 0x0L;
+       rtinfo->segsleft[1] = 0xFFFFFFFF;
+       rtinfo->hdrlen = 0;
+       rtinfo->flags = 0;
+       rtinfo->invflags = 0;
+       rtinfo->addrnr = 0;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_rt *rtinfo = (struct ip6t_rt *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IP6T_RT_TYP)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--rt-type' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               rtinfo->rt_type = parse_rt_num(argv[optind-1], "type");
+               if (invert)
+                       rtinfo->invflags |= IP6T_RT_INV_TYP;
+               rtinfo->flags |= IP6T_RT_TYP;
+               *flags |= IP6T_RT_TYP;
+               break;
+       case '2':
+               if (*flags & IP6T_RT_SGS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--rt-segsleft' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_rt_segsleft(argv[optind-1], rtinfo->segsleft);
+               if (invert)
+                       rtinfo->invflags |= IP6T_RT_INV_SGS;
+               rtinfo->flags |= IP6T_RT_SGS;
+               *flags |= IP6T_RT_SGS;
+               break;
+       case '3':
+               if (*flags & IP6T_RT_LEN)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--rt-len' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               rtinfo->hdrlen = parse_rt_num(argv[optind-1], "length");
+               if (invert)
+                       rtinfo->invflags |= IP6T_RT_INV_LEN;
+               rtinfo->flags |= IP6T_RT_LEN;
+               *flags |= IP6T_RT_LEN;
+               break;
+       case '4':
+               if (*flags & IP6T_RT_RES)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--rt-0-res' allowed");
+               if ( !(*flags & IP6T_RT_TYP) || (rtinfo->rt_type != 0) || (rtinfo->invflags & IP6T_RT_INV_TYP) )
+                       exit_error(PARAMETER_PROBLEM,
+                                  "`--rt-type 0' required before `--rt-0-res'");
+               rtinfo->flags |= IP6T_RT_RES;
+               *flags |= IP6T_RT_RES;
+               break;
+       case '5':
+               if (*flags & IP6T_RT_FST)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--rt-0-addrs' allowed");
+               if ( !(*flags & IP6T_RT_TYP) || (rtinfo->rt_type != 0) || (rtinfo->invflags & IP6T_RT_INV_TYP) )
+                       exit_error(PARAMETER_PROBLEM,
+                                  "`--rt-type 0' required before `--rt-0-addrs'");
+               check_inverse(optarg, &invert, &optind, 0);
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  " '!' not allowed with `--rt-0-addrs'");
+               rtinfo->addrnr = parse_addresses(argv[optind-1], rtinfo->addrs);
+               rtinfo->flags |= IP6T_RT_FST;
+               *flags |= IP6T_RT_FST;
+               break;
+       case '6':
+               if (*flags & IP6T_RT_FST_NSTRICT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--rt-0-not-strict' allowed");
+               if ( !(*flags & IP6T_RT_FST) )
+                       exit_error(PARAMETER_PROBLEM,
+                                  "`--rt-0-addr ...' required before `--rt-0-not-strict'");
+               rtinfo->flags |= IP6T_RT_FST_NSTRICT;
+               *flags |= IP6T_RT_FST_NSTRICT;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_nums(const char *name, u_int32_t min, u_int32_t max,
+           int invert)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFFFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       printf("%u", min);
+               } else {
+                       printf("s:%s", inv);
+                       printf("%u",min);
+                       printf(":");
+                       printf("%u",max);
+               }
+               printf(" ");
+       }
+}
+
+static void
+print_addresses(int addrnr, struct in6_addr *addrp)
+{
+       unsigned int i;
+
+       for(i=0; i<addrnr; i++){
+               printf("%s%c", addr_to_numeric(&(addrp[i])), (i!=addrnr-1)?',':' ');
+       }
+}
+
+/* Prints out the union ip6t_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_rt *rtinfo = (struct ip6t_rt *)match->data;
+
+       printf("rt ");
+       if (rtinfo->flags & IP6T_RT_TYP)
+           printf("type:%s%d ", rtinfo->invflags & IP6T_RT_INV_TYP ? "!" : "",
+                   rtinfo->rt_type);
+       print_nums("segsleft", rtinfo->segsleft[0], rtinfo->segsleft[1],
+                   rtinfo->invflags & IP6T_RT_INV_SGS);
+       if (rtinfo->flags & IP6T_RT_LEN) {
+               printf("length");
+               printf(":%s", rtinfo->invflags & IP6T_RT_INV_LEN ? "!" : "");
+               printf("%u", rtinfo->hdrlen);
+               printf(" ");
+       }
+       if (rtinfo->flags & IP6T_RT_RES) printf("reserved ");
+       if (rtinfo->flags & IP6T_RT_FST) printf("0-addrs ");
+       print_addresses(rtinfo->addrnr, (struct in6_addr *)rtinfo->addrs);
+       if (rtinfo->flags & IP6T_RT_FST_NSTRICT) printf("0-not-strict ");
+       if (rtinfo->invflags & ~IP6T_RT_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      rtinfo->invflags & ~IP6T_RT_INV_MASK);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_rt *rtinfo = (struct ip6t_rt *)match->data;
+
+       if (rtinfo->flags & IP6T_RT_TYP) {
+               printf("--rt-type %s%u ", 
+                       (rtinfo->invflags & IP6T_RT_INV_TYP) ? "! " : "", 
+                       rtinfo->rt_type);
+       }
+
+       if (!(rtinfo->segsleft[0] == 0
+           && rtinfo->segsleft[1] == 0xFFFFFFFF)) {
+               printf("--rt-segsleft %s", 
+                       (rtinfo->invflags & IP6T_RT_INV_SGS) ? "! " : "");
+               if (rtinfo->segsleft[0]
+                   != rtinfo->segsleft[1])
+                       printf("%u:%u ",
+                              rtinfo->segsleft[0],
+                              rtinfo->segsleft[1]);
+               else
+                       printf("%u ",
+                              rtinfo->segsleft[0]);
+       }
+
+       if (rtinfo->flags & IP6T_RT_LEN) {
+               printf("--rt-len %s%u ", 
+                       (rtinfo->invflags & IP6T_RT_INV_LEN) ? "! " : "", 
+                       rtinfo->hdrlen);
+       }
+
+       if (rtinfo->flags & IP6T_RT_RES) printf("--rt-0-res ");
+       if (rtinfo->flags & IP6T_RT_FST) printf("--rt-0-addrs ");
+       print_addresses(rtinfo->addrnr, (struct in6_addr *)rtinfo->addrs);
+       if (rtinfo->flags & IP6T_RT_FST_NSTRICT) printf("--rt-0-not-strict ");
+
+}
+
+static struct ip6tables_match rt = {
+       .name           = "rt",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_rt)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_rt)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void
+_init(void)
+{
+       register_match6(&rt);
+}
diff --git a/extensions/libip6t_rt.man b/extensions/libip6t_rt.man
new file mode 100644 (file)
index 0000000..e56d5f4
--- /dev/null
@@ -0,0 +1,19 @@
+Match on IPv6 routing header
+.TP
+.BR "--rt-type" " [!] \fItype\fP"
+Match the type (numeric).
+.TP
+.BR "--rt-segsleft" " [!] \fInum\fP[:\fInum\fP]"
+Match the `segments left' field (range).
+.TP
+.BR "--rt-len" " [!] \fIlength\fP"
+Match the length of this header.
+.TP
+.BR "--rt-0-res"
+Match the reserved field, too (type=0)
+.TP
+.BR "--rt-0-addrs" " \fIADDR\fP[,\fIADDR\fP...]"
+Match type=0 addresses (list).
+.TP
+.BR "--rt-0-not-strict"
+List of type=0 addresses is not a strict list.
diff --git a/extensions/libip6t_sctp.c b/extensions/libip6t_sctp.c
new file mode 100644 (file)
index 0000000..aee7072
--- /dev/null
@@ -0,0 +1,550 @@
+/* Shared library add-on to iptables for SCTP matching
+ *
+ * (C) 2003 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_ecn.c borrowed heavily from libipt_dscp.c
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <ctype.h>
+
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+#include <linux/netfilter/xt_sctp.h>
+
+/* Some ZS!#@:$%*#$! has replaced the ELEMCOUNT macro in ipt_sctp.h with
+ * ARRAY_SIZE without noticing that this file is used from userserspace,
+ * and userspace doesn't have ARRAY_SIZE */
+
+#ifndef ELEMCOUNT
+#define ELEMCOUNT ARRAY_SIZE
+#endif
+
+#if 0
+#define DEBUGP(format, first...) printf(format, ##first)
+#define static
+#else
+#define DEBUGP(format, fist...) 
+#endif
+
+static void
+print_chunk(u_int32_t chunknum, int numeric);
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, 
+     unsigned int *nfcache)
+{
+       int i;
+       struct xt_sctp_info *einfo = (struct xt_sctp_info *)m->data;
+
+       memset(einfo, 0, sizeof(struct xt_sctp_info));
+
+       for (i = 0; i < XT_NUM_SCTP_FLAGS; i++) {
+               einfo->flag_info[i].chunktype = -1;
+       }
+}
+
+static void help(void)
+{
+       printf(
+"SCTP match v%s options\n"
+" --source-port [!] port[:port]                          match source port(s)\n"
+" --sport ...\n"
+" --destination-port [!] port[:port]                     match destination port(s)\n"
+" --dport ...\n" 
+" --chunk-types [!] (all|any|none) (chunktype[:flags])+        match if all, any or none of\n"
+"                                                      chunktypes are present\n"
+"chunktypes - DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK ALL NONE\n",
+       IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "source-port", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "sport", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "destination-port", .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "dport", .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "chunk-types", .has_arg = 1, .flag = 0, .val = '3' },
+       { .name = 0 }
+};
+
+static void
+parse_sctp_ports(const char *portstring, 
+                u_int16_t *ports)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(portstring);
+       DEBUGP("%s\n", portstring);
+       if ((cp = strchr(buffer, ':')) == NULL) {
+               ports[0] = ports[1] = parse_port(buffer, "sctp");
+       }
+       else {
+               *cp = '\0';
+               cp++;
+
+               ports[0] = buffer[0] ? parse_port(buffer, "sctp") : 0;
+               ports[1] = cp[0] ? parse_port(cp, "sctp") : 0xFFFF;
+
+               if (ports[0] > ports[1])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "invalid portrange (min > max)");
+       }
+       free(buffer);
+}
+
+struct sctp_chunk_names {
+       const char *name;
+       unsigned int chunk_type;
+       const char *valid_flags;
+};
+
+/*'ALL' and 'NONE' will be treated specially. */
+static struct sctp_chunk_names sctp_chunk_names[]
+= { { .name = "DATA",          .chunk_type = 0,   .valid_flags = "-----UBE"},
+    { .name = "INIT",          .chunk_type = 1,   .valid_flags = "--------"},
+    { .name = "INIT_ACK",      .chunk_type = 2,   .valid_flags = "--------"},
+    { .name = "SACK",          .chunk_type = 3,   .valid_flags = "--------"},
+    { .name = "HEARTBEAT",     .chunk_type = 4,   .valid_flags = "--------"},
+    { .name = "HEARTBEAT_ACK", .chunk_type = 5,   .valid_flags = "--------"},
+    { .name = "ABORT",         .chunk_type = 6,   .valid_flags = "-------T"},
+    { .name = "SHUTDOWN",      .chunk_type = 7,   .valid_flags = "--------"},
+    { .name = "SHUTDOWN_ACK",  .chunk_type = 8,   .valid_flags = "--------"},
+    { .name = "ERROR",         .chunk_type = 9,   .valid_flags = "--------"},
+    { .name = "COOKIE_ECHO",   .chunk_type = 10,  .valid_flags = "--------"},
+    { .name = "COOKIE_ACK",    .chunk_type = 11,  .valid_flags = "--------"},
+    { .name = "ECN_ECNE",      .chunk_type = 12,  .valid_flags = "--------"},
+    { .name = "ECN_CWR",       .chunk_type = 13,  .valid_flags = "--------"},
+    { .name = "SHUTDOWN_COMPLETE", .chunk_type = 14,  .valid_flags = "-------T"},
+    { .name = "ASCONF",                .chunk_type = 31,  .valid_flags = "--------"},
+    { .name = "ASCONF_ACK",    .chunk_type = 30,  .valid_flags = "--------"},
+};
+
+static void
+save_chunk_flag_info(struct xt_sctp_flag_info *flag_info,
+                    int *flag_count,
+                    int chunktype, 
+                    int bit, 
+                    int set)
+{
+       int i;
+
+       for (i = 0; i < *flag_count; i++) {
+               if (flag_info[i].chunktype == chunktype) {
+                       DEBUGP("Previous match found\n");
+                       flag_info[i].chunktype = chunktype;
+                       flag_info[i].flag_mask |= (1 << bit);
+                       if (set) {
+                               flag_info[i].flag |= (1 << bit);
+                       }
+
+                       return;
+               }
+       }
+       
+       if (*flag_count == XT_NUM_SCTP_FLAGS) {
+               exit_error (PARAMETER_PROBLEM,
+                       "Number of chunk types with flags exceeds currently allowed limit."
+                       "Increasing this limit involves changing XT_NUM_SCTP_FLAGS and"
+                       "recompiling both the kernel space and user space modules\n");
+       }
+
+       flag_info[*flag_count].chunktype = chunktype;
+       flag_info[*flag_count].flag_mask |= (1 << bit);
+       if (set) {
+               flag_info[*flag_count].flag |= (1 << bit);
+       }
+       (*flag_count)++;
+}
+
+static void
+parse_sctp_chunk(struct xt_sctp_info *einfo, 
+                const char *chunks)
+{
+       char *ptr;
+       char *buffer;
+       unsigned int i, j;
+       int found = 0;
+       char *chunk_flags;
+
+       buffer = strdup(chunks);
+       DEBUGP("Buffer: %s\n", buffer);
+
+       SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+
+       if (!strcasecmp(buffer, "ALL")) {
+               SCTP_CHUNKMAP_SET_ALL(einfo->chunkmap);
+               goto out;
+       }
+       
+       if (!strcasecmp(buffer, "NONE")) {
+               SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+               goto out;
+       }
+
+       for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
+               found = 0;
+               DEBUGP("Next Chunk type %s\n", ptr);
+               
+               if ((chunk_flags = strchr(ptr, ':')) != NULL) {
+                       *chunk_flags++ = 0;
+               }
+               
+               for (i = 0; i < ELEMCOUNT(sctp_chunk_names); i++) {
+                       if (strcasecmp(sctp_chunk_names[i].name, ptr) == 0) {
+                               DEBUGP("Chunk num %d\n", sctp_chunk_names[i].chunk_type);
+                               SCTP_CHUNKMAP_SET(einfo->chunkmap, 
+                                       sctp_chunk_names[i].chunk_type);
+                               found = 1;
+                               break;
+                       }
+               }
+               if (!found)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unknown sctp chunk `%s'", ptr);
+
+               if (chunk_flags) {
+                       DEBUGP("Chunk flags %s\n", chunk_flags);
+                       for (j = 0; j < strlen(chunk_flags); j++) {
+                               char *p;
+                               int bit;
+
+                               if ((p = strchr(sctp_chunk_names[i].valid_flags, 
+                                               toupper(chunk_flags[j]))) != NULL) {
+                                       bit = p - sctp_chunk_names[i].valid_flags;
+                                       bit = 7 - bit;
+
+                                       save_chunk_flag_info(einfo->flag_info, 
+                                               &(einfo->flag_count), i, bit, 
+                                               isupper(chunk_flags[j]));
+                               } else {
+                                       exit_error(PARAMETER_PROBLEM, 
+                                               "Invalid flags for chunk type %d\n", i);
+                               }
+                       }
+               }
+       }
+out:
+       free(buffer);
+}
+
+static void
+parse_sctp_chunks(struct xt_sctp_info *einfo,
+                 const char *match_type,
+                 const char *chunks)
+{
+       DEBUGP("Match type: %s Chunks: %s\n", match_type, chunks);
+       if (!strcasecmp(match_type, "ANY")) {
+               einfo->chunk_match_type = SCTP_CHUNK_MATCH_ANY;
+       } else  if (!strcasecmp(match_type, "ALL")) {
+               einfo->chunk_match_type = SCTP_CHUNK_MATCH_ALL;
+       } else  if (!strcasecmp(match_type, "ONLY")) {
+               einfo->chunk_match_type = SCTP_CHUNK_MATCH_ONLY;
+       } else {
+               exit_error (PARAMETER_PROBLEM, 
+                       "Match type has to be one of \"ALL\", \"ANY\" or \"ONLY\"");
+       }
+
+       SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+       parse_sctp_chunk(einfo, chunks);
+}
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct xt_sctp_info *einfo
+               = (struct xt_sctp_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & XT_SCTP_SRC_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--source-port' allowed");
+               einfo->flags |= XT_SCTP_SRC_PORTS;
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_sctp_ports(argv[optind-1], einfo->spts);
+               if (invert)
+                       einfo->invflags |= XT_SCTP_SRC_PORTS;
+               *flags |= XT_SCTP_SRC_PORTS;
+               break;
+
+       case '2':
+               if (*flags & XT_SCTP_DEST_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--destination-port' allowed");
+               einfo->flags |= XT_SCTP_DEST_PORTS;
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_sctp_ports(argv[optind-1], einfo->dpts);
+               if (invert)
+                       einfo->invflags |= XT_SCTP_DEST_PORTS;
+               *flags |= XT_SCTP_DEST_PORTS;
+               break;
+
+       case '3':
+               if (*flags & XT_SCTP_CHUNK_TYPES)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--chunk-types' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (!argv[optind] 
+                   || argv[optind][0] == '-' || argv[optind][0] == '!')
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--chunk-types requires two args");
+
+               einfo->flags |= XT_SCTP_CHUNK_TYPES;
+               parse_sctp_chunks(einfo, argv[optind-1], argv[optind]);
+               if (invert)
+                       einfo->invflags |= XT_SCTP_CHUNK_TYPES;
+               optind++;
+               *flags |= XT_SCTP_CHUNK_TYPES;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+static char *
+port_to_service(int port)
+{
+       struct servent *service;
+
+       if ((service = getservbyport(htons(port), "sctp")))
+               return service->s_name;
+
+       return NULL;
+}
+
+static void
+print_port(u_int16_t port, int numeric)
+{
+       char *service;
+
+       if (numeric || (service = port_to_service(port)) == NULL)
+               printf("%u", port);
+       else
+               printf("%s", service);
+}
+
+static void
+print_ports(const char *name, u_int16_t min, u_int16_t max,
+           int invert, int numeric)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       print_port(min, numeric);
+               } else {
+                       printf("s:%s", inv);
+                       print_port(min, numeric);
+                       printf(":");
+                       print_port(max, numeric);
+               }
+               printf(" ");
+       }
+}
+
+static void
+print_chunk_flags(u_int32_t chunknum, u_int8_t chunk_flags, u_int8_t chunk_flags_mask)
+{
+       int i;
+
+       DEBUGP("type: %d\tflags: %x\tflag mask: %x\n", chunknum, chunk_flags, 
+                       chunk_flags_mask);
+
+       if (chunk_flags_mask) {
+               printf(":");
+       }
+
+       for (i = 7; i >= 0; i--) {
+               if (chunk_flags_mask & (1 << i)) {
+                       if (chunk_flags & (1 << i)) {
+                               printf("%c", sctp_chunk_names[chunknum].valid_flags[7-i]);
+                       } else {
+                               printf("%c", tolower(sctp_chunk_names[chunknum].valid_flags[7-i]));
+                       }
+               }
+       }
+}
+
+static void
+print_chunk(u_int32_t chunknum, int numeric)
+{
+       if (numeric) {
+               printf("0x%04X", chunknum);
+       }
+       else {
+               int i;
+
+               for (i = 0; i < ELEMCOUNT(sctp_chunk_names); i++) {
+                       if (sctp_chunk_names[i].chunk_type == chunknum)
+                               printf("%s", sctp_chunk_names[chunknum].name);
+               }
+       }
+}
+
+static void
+print_chunks(u_int32_t chunk_match_type, 
+            const u_int32_t *chunkmap, 
+            const struct xt_sctp_flag_info *flag_info,
+            int flag_count,
+            int numeric)
+{
+       int i, j;
+       int flag;
+
+       switch (chunk_match_type) {
+               case SCTP_CHUNK_MATCH_ANY:      printf("any "); break;
+               case SCTP_CHUNK_MATCH_ALL:      printf("all "); break;
+               case SCTP_CHUNK_MATCH_ONLY:     printf("only "); break;
+               default:        printf("Never reach herer\n"); break;
+       }
+
+       if (SCTP_CHUNKMAP_IS_CLEAR(chunkmap)) {
+               printf("NONE ");
+               goto out;
+       }
+       
+       if (SCTP_CHUNKMAP_IS_ALL_SET(chunkmap)) {
+               printf("ALL ");
+               goto out;
+       }
+       
+       flag = 0;
+       for (i = 0; i < 256; i++) {
+               if (SCTP_CHUNKMAP_IS_SET(chunkmap, i)) {
+                       if (flag)
+                               printf(",");
+                       flag = 1;
+                       print_chunk(i, numeric);
+                       for (j = 0; j < flag_count; j++) {
+                               if (flag_info[j].chunktype == i) {
+                                       print_chunk_flags(i, flag_info[j].flag,
+                                               flag_info[j].flag_mask);
+                               }
+                       }
+               }
+       }
+
+       if (flag)
+               printf(" ");
+out:
+       return;
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       const struct xt_sctp_info *einfo =
+               (const struct xt_sctp_info *)match->data;
+
+       printf("sctp ");
+
+       if (einfo->flags & XT_SCTP_SRC_PORTS) {
+               print_ports("spt", einfo->spts[0], einfo->spts[1],
+                       einfo->invflags & XT_SCTP_SRC_PORTS,
+                       numeric);
+       }
+
+       if (einfo->flags & XT_SCTP_DEST_PORTS) {
+               print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
+                       einfo->invflags & XT_SCTP_DEST_PORTS,
+                       numeric);
+       }
+
+       if (einfo->flags & XT_SCTP_CHUNK_TYPES) {
+               /* FIXME: print_chunks() is used in save() where the printing of '!'
+               s taken care of, so we need to do that here as well */
+               if (einfo->invflags & XT_SCTP_CHUNK_TYPES) {
+                       printf("! ");
+               }
+               print_chunks(einfo->chunk_match_type, einfo->chunkmap,
+                       einfo->flag_info, einfo->flag_count, numeric);
+       }
+}
+
+/* Saves the union xt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, 
+     const struct ip6t_entry_match *match)
+{
+       const struct xt_sctp_info *einfo =
+               (const struct xt_sctp_info *)match->data;
+
+       if (einfo->flags & XT_SCTP_SRC_PORTS) {
+               if (einfo->invflags & XT_SCTP_SRC_PORTS)
+                       printf("! ");
+               if (einfo->spts[0] != einfo->spts[1])
+                       printf("--sport %u:%u ", 
+                              einfo->spts[0], einfo->spts[1]);
+               else
+                       printf("--sport %u ", einfo->spts[0]);
+       }
+
+       if (einfo->flags & XT_SCTP_DEST_PORTS) {
+               if (einfo->invflags & XT_SCTP_DEST_PORTS)
+                       printf("! ");
+               if (einfo->dpts[0] != einfo->dpts[1])
+                       printf("--dport %u:%u ",
+                              einfo->dpts[0], einfo->dpts[1]);
+               else
+                       printf("--dport %u ", einfo->dpts[0]);
+       }
+
+       if (einfo->flags & XT_SCTP_CHUNK_TYPES) {
+               if (einfo->invflags & XT_SCTP_CHUNK_TYPES)
+                       printf("! ");
+               printf("--chunk-types ");
+
+               print_chunks(einfo->chunk_match_type, einfo->chunkmap, 
+                       einfo->flag_info, einfo->flag_count, 0);
+       }
+}
+
+static
+struct ip6tables_match sctp
+= { .name          = "sctp",
+    .version       = IPTABLES_VERSION,
+    .size          = IP6T_ALIGN(sizeof(struct xt_sctp_info)),
+    .userspacesize = IP6T_ALIGN(sizeof(struct xt_sctp_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_match6(&sctp);
+}
+
diff --git a/extensions/libip6t_standard.c b/extensions/libip6t_standard.c
new file mode 100644 (file)
index 0000000..c48882f
--- /dev/null
@@ -0,0 +1,66 @@
+/* Shared library add-on to iptables for standard target support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <getopt.h>
+#include <ip6tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"Standard v%s options:\n"
+"(If target is DROP, ACCEPT, RETURN or nothing)\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       {0}
+};
+
+/* Initialize the target. */
+static void
+init(struct ip6t_entry_target *t, unsigned int *nfcache)
+{
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      struct ip6t_entry_target **target)
+{
+       return 0;
+}
+
+/* Final check; don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Saves the targinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip6, const struct ip6t_entry_target *target)
+{
+}
+
+static struct ip6tables_target standard = {
+       .name           = "standard",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(int)),
+       .userspacesize  = IP6T_ALIGN(sizeof(int)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_target6(&standard);
+}
diff --git a/extensions/libip6t_state.c b/extensions/libip6t_state.c
new file mode 100644 (file)
index 0000000..a4477ce
--- /dev/null
@@ -0,0 +1,163 @@
+/* Ugly hack to make state matching for ipv6 work before iptables-1.4.x is finished */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <linux/netfilter_ipv4/ipt_state.h>
+
+#ifndef IPT_STATE_UNTRACKED
+#define IPT_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 1))
+#endif
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"state v%s options:\n"
+" [!] --state [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED][,...]\n"
+"                              State(s) to match\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "state", 1, 0, '1' },
+       {0}
+};
+
+static int
+parse_state(const char *state, size_t strlen, struct ipt_state_info *sinfo)
+{
+       if (strncasecmp(state, "INVALID", strlen) == 0)
+               sinfo->statemask |= IPT_STATE_INVALID;
+       else if (strncasecmp(state, "NEW", strlen) == 0)
+               sinfo->statemask |= IPT_STATE_BIT(IP_CT_NEW);
+       else if (strncasecmp(state, "ESTABLISHED", strlen) == 0)
+               sinfo->statemask |= IPT_STATE_BIT(IP_CT_ESTABLISHED);
+       else if (strncasecmp(state, "RELATED", strlen) == 0)
+               sinfo->statemask |= IPT_STATE_BIT(IP_CT_RELATED);
+       else if (strncasecmp(state, "UNTRACKED", strlen) == 0)
+               sinfo->statemask |= IPT_STATE_UNTRACKED;
+       else
+               return 0;
+       return 1;
+}
+
+static void
+parse_states(const char *arg, struct ipt_state_info *sinfo)
+{
+       const char *comma;
+
+       while ((comma = strchr(arg, ',')) != NULL) {
+               if (comma == arg || !parse_state(arg, comma-arg, sinfo))
+                       exit_error(PARAMETER_PROBLEM, "Bad state `%s'", arg);
+               arg = comma+1;
+       }
+
+       if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
+               exit_error(PARAMETER_PROBLEM, "Bad state `%s'", arg);
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ipt_state_info *sinfo = (struct ipt_state_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               parse_states(argv[optind-1], sinfo);
+               if (invert)
+                       sinfo->statemask = ~sinfo->statemask;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; must have specified --state. */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, "You must specify `--state'");
+}
+
+static void print_state(unsigned int statemask)
+{
+       const char *sep = "";
+
+       if (statemask & IPT_STATE_INVALID) {
+               printf("%sINVALID", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_STATE_BIT(IP_CT_NEW)) {
+               printf("%sNEW", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_STATE_BIT(IP_CT_RELATED)) {
+               printf("%sRELATED", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_STATE_BIT(IP_CT_ESTABLISHED)) {
+               printf("%sESTABLISHED", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_STATE_UNTRACKED) {
+               printf("%sUNTRACKED", sep);
+               sep = ",";
+       }
+       printf(" ");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       struct ipt_state_info *sinfo = (struct ipt_state_info *)match->data;
+
+       printf("state ");
+       print_state(sinfo->statemask);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       struct ipt_state_info *sinfo = (struct ipt_state_info *)match->data;
+
+       printf("--state ");
+       print_state(sinfo->statemask);
+}
+
+static struct ip6tables_match state = { 
+       .next           = NULL,
+       .name           = "state",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ipt_state_info)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ipt_state_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match6(&state);
+}
diff --git a/extensions/libip6t_tcp.c b/extensions/libip6t_tcp.c
new file mode 100644 (file)
index 0000000..734387c
--- /dev/null
@@ -0,0 +1,416 @@
+/* Shared library add-on to iptables to add TCP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"TCP v%s options:\n"
+" --tcp-flags [!] mask comp    match when TCP flags & mask == comp\n"
+"                              (Flags: SYN ACK FIN RST URG PSH ALL NONE)\n"
+"[!] --syn                     match when only SYN flag set\n"
+"                              (equivalent to --tcp-flags SYN,RST,ACK SYN)\n"
+" --source-port [!] port[:port]\n"
+" --sport ...\n"
+"                              match source port(s)\n"
+" --destination-port [!] port[:port]\n"
+" --dport ...\n"
+"                              match destination port(s)\n"
+" --tcp-option [!] number       match if TCP option set\n\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "source-port", 1, 0, '1' },
+       { "sport", 1, 0, '1' }, /* synonym */
+       { "destination-port", 1, 0, '2' },
+       { "dport", 1, 0, '2' }, /* synonym */
+       { "syn", 0, 0, '3' },
+       { "tcp-flags", 1, 0, '4' },
+       { "tcp-option", 1, 0, '5' },
+       {0}
+};
+
+static void
+parse_tcp_ports(const char *portstring, u_int16_t *ports)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(portstring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               ports[0] = ports[1] = parse_port(buffer, "tcp");
+       else {
+               *cp = '\0';
+               cp++;
+
+               ports[0] = buffer[0] ? parse_port(buffer, "tcp") : 0;
+               ports[1] = cp[0] ? parse_port(cp, "tcp") : 0xFFFF;
+               
+               if (ports[0] > ports[1])
+                       exit_error(PARAMETER_PROBLEM, 
+                                  "invalid portrange (min > max)");
+       }
+       free(buffer);
+}
+
+struct tcp_flag_names {
+       const char *name;
+       unsigned int flag;
+};
+
+static struct tcp_flag_names tcp_flag_names[]
+= { { "FIN", 0x01 },
+    { "SYN", 0x02 },
+    { "RST", 0x04 },
+    { "PSH", 0x08 },
+    { "ACK", 0x10 },
+    { "URG", 0x20 },
+    { "ALL", 0x3F },
+    { "NONE", 0 },
+};
+
+static unsigned int
+parse_tcp_flag(const char *flags)
+{
+       unsigned int ret = 0;
+       char *ptr;
+       char *buffer;
+
+       buffer = strdup(flags);
+
+       for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
+               unsigned int i;
+               for (i = 0;
+                    i < sizeof(tcp_flag_names)/sizeof(struct tcp_flag_names);
+                    i++) {
+                       if (strcasecmp(tcp_flag_names[i].name, ptr) == 0) {
+                               ret |= tcp_flag_names[i].flag;
+                               break;
+                       }
+               }
+               if (i == sizeof(tcp_flag_names)/sizeof(struct tcp_flag_names))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unknown TCP flag `%s'", ptr);
+               }
+
+       free(buffer);
+       return ret;
+}
+
+static void
+parse_tcp_flags(struct ip6t_tcp *tcpinfo,
+               const char *mask,
+               const char *cmp,
+               int invert)
+{
+       tcpinfo->flg_mask = parse_tcp_flag(mask);
+       tcpinfo->flg_cmp = parse_tcp_flag(cmp);
+
+       if (invert)
+               tcpinfo->invflags |= IP6T_TCP_INV_FLAGS;
+}
+
+static void
+parse_tcp_option(const char *option, u_int8_t *result)
+{
+       unsigned int ret;
+
+       if (string_to_number(option, 1, 255, &ret) == -1)
+               exit_error(PARAMETER_PROBLEM, "Bad TCP option `%s'", option);
+
+       *result = (u_int8_t)ret;
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_tcp *tcpinfo = (struct ip6t_tcp *)m->data;
+
+       tcpinfo->spts[1] = tcpinfo->dpts[1] = 0xFFFF;
+}
+
+#define TCP_SRC_PORTS 0x01
+#define TCP_DST_PORTS 0x02
+#define TCP_FLAGS 0x04
+#define TCP_OPTION     0x08
+
+/* Function which parses command options; returns true if it
+   ate an option. */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_tcp *tcpinfo = (struct ip6t_tcp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & TCP_SRC_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--source-port' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_tcp_ports(argv[optind-1], tcpinfo->spts);
+               if (invert)
+                       tcpinfo->invflags |= IP6T_TCP_INV_SRCPT;
+               *flags |= TCP_SRC_PORTS;
+               break;
+
+       case '2':
+               if (*flags & TCP_DST_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--destination-port' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_tcp_ports(argv[optind-1], tcpinfo->dpts);
+               if (invert)
+                       tcpinfo->invflags |= IP6T_TCP_INV_DSTPT;
+               *flags |= TCP_DST_PORTS;
+               break;
+
+       case '3':
+               if (*flags & TCP_FLAGS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one of `--syn' or `--tcp-flags' "
+                                  " allowed");
+               parse_tcp_flags(tcpinfo, "SYN,RST,ACK", "SYN", invert);
+               *flags |= TCP_FLAGS;
+               break;
+
+       case '4':
+               if (*flags & TCP_FLAGS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one of `--syn' or `--tcp-flags' "
+                                  " allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (!argv[optind]
+                   || argv[optind][0] == '-' || argv[optind][0] == '!')
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--tcp-flags requires two args.");
+
+               parse_tcp_flags(tcpinfo, argv[optind-1], argv[optind], 
+                               invert);
+               optind++;
+               *flags |= TCP_FLAGS;
+               break;
+
+       case '5':
+               if (*flags & TCP_OPTION)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--tcp-option' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_tcp_option(argv[optind-1], &tcpinfo->option);
+               if (invert)
+                       tcpinfo->invflags |= IP6T_TCP_INV_OPTION;
+               *flags |= TCP_OPTION;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static char *
+port_to_service(int port)
+{
+       struct servent *service;
+
+       if ((service = getservbyport(htons(port), "tcp")))
+               return service->s_name;
+
+       return NULL;
+}
+
+static void
+print_port(u_int16_t port, int numeric)
+{
+       char *service;
+
+       if (numeric || (service = port_to_service(port)) == NULL)
+               printf("%u", port);
+       else
+               printf("%s", service);
+}
+
+static void
+print_ports(const char *name, u_int16_t min, u_int16_t max,
+           int invert, int numeric)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       print_port(min, numeric);
+               } else {
+                       printf("s:%s", inv);
+                       print_port(min, numeric);
+                       printf(":");
+                       print_port(max, numeric);
+               }
+               printf(" ");
+       }
+}
+
+static void
+print_option(u_int8_t option, int invert, int numeric)
+{
+       if (option || invert)
+               printf("option=%s%u ", invert ? "!" : "", option);
+}
+
+static void
+print_tcpf(u_int8_t flags)
+{
+       int have_flag = 0;
+
+       while (flags) {
+               unsigned int i;
+
+               for (i = 0; (flags & tcp_flag_names[i].flag) == 0; i++);
+
+               if (have_flag)
+                       printf(",");
+               printf("%s", tcp_flag_names[i].name);
+               have_flag = 1;
+
+               flags &= ~tcp_flag_names[i].flag;
+       }
+
+       if (!have_flag)
+               printf("NONE");
+}
+
+static void
+print_flags(u_int8_t mask, u_int8_t cmp, int invert, int numeric)
+{
+       if (mask || invert) {
+               printf("flags:%s", invert ? "!" : "");
+               if (numeric)
+                       printf("0x%02X/0x%02X ", mask, cmp);
+               else {
+                       print_tcpf(mask);
+                       printf("/");
+                       print_tcpf(cmp);
+                       printf(" ");
+               }
+       }
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_tcp *tcp = (struct ip6t_tcp *)match->data;
+
+       printf("tcp ");
+       print_ports("spt", tcp->spts[0], tcp->spts[1],
+                   tcp->invflags & IP6T_TCP_INV_SRCPT,
+                   numeric);
+       print_ports("dpt", tcp->dpts[0], tcp->dpts[1],
+                   tcp->invflags & IP6T_TCP_INV_DSTPT,
+                   numeric);
+       print_option(tcp->option,
+                    tcp->invflags & IP6T_TCP_INV_OPTION,
+                    numeric);
+       print_flags(tcp->flg_mask, tcp->flg_cmp,
+                   tcp->invflags & IP6T_TCP_INV_FLAGS,
+                   numeric);
+       if (tcp->invflags & ~IP6T_TCP_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      tcp->invflags & ~IP6T_TCP_INV_MASK);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_tcp *tcpinfo = (struct ip6t_tcp *)match->data;
+
+       if (tcpinfo->spts[0] != 0
+           || tcpinfo->spts[1] != 0xFFFF) {
+               if (tcpinfo->invflags & IP6T_TCP_INV_SRCPT)
+                       printf("! ");
+               if (tcpinfo->spts[0]
+                   != tcpinfo->spts[1])
+                       printf("--sport %u:%u ",
+                              tcpinfo->spts[0],
+                              tcpinfo->spts[1]);
+               else
+                       printf("--sport %u ",
+                              tcpinfo->spts[0]);
+       }
+
+       if (tcpinfo->dpts[0] != 0
+           || tcpinfo->dpts[1] != 0xFFFF) {
+               if (tcpinfo->invflags & IP6T_TCP_INV_DSTPT)
+                       printf("! ");
+               if (tcpinfo->dpts[0]
+                   != tcpinfo->dpts[1])
+                       printf("--dport %u:%u ",
+                              tcpinfo->dpts[0],
+                              tcpinfo->dpts[1]);
+               else
+                       printf("--dport %u ",
+                              tcpinfo->dpts[0]);
+       }
+
+       if (tcpinfo->option
+           || (tcpinfo->invflags & IP6T_TCP_INV_OPTION)) {
+               if (tcpinfo->invflags & IP6T_TCP_INV_OPTION)
+                       printf("! ");
+               printf("--tcp-option %u ", tcpinfo->option);
+       }
+
+       if (tcpinfo->flg_mask
+           || (tcpinfo->invflags & IP6T_TCP_INV_FLAGS)) {
+               if (tcpinfo->invflags & IP6T_TCP_INV_FLAGS)
+                       printf("! ");
+
+               printf("--tcp-flags ");
+               if (tcpinfo->flg_mask != 0xFF) {
+                       print_tcpf(tcpinfo->flg_mask);
+               }
+               printf(" ");
+               print_tcpf(tcpinfo->flg_cmp);
+               printf(" ");
+       }
+}
+
+static struct ip6tables_match tcp = {
+       .name           = "tcp",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_tcp)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_tcp)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void
+_init(void)
+{
+       register_match6(&tcp);
+}
diff --git a/extensions/libip6t_tcp.man b/extensions/libip6t_tcp.man
new file mode 100644 (file)
index 0000000..e94566c
--- /dev/null
@@ -0,0 +1,45 @@
+These extensions can be used if `--protocol tcp' is specified. It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification. This can either be a service
+name or a port number. An inclusive range can also be specified,
+using the format
+.IR port : port .
+If the first port is omitted, "0" is assumed; if the last is omitted,
+"65535" is assumed.
+If the second port greater then the first they will be swapped.
+The flag
+.B --sport
+is a convenient alias for this option.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification.  The flag
+.B --dport
+is a convenient alias for this option.
+.TP
+.BR "--tcp-flags " "[!] \fImask\fP \fIcomp\fP"
+Match when the TCP flags are as specified.  The first argument is the
+flags which we should examine, written as a comma-separated list, and
+the second argument is a comma-separated list of flags which must be
+set.  Flags are: 
+.BR "SYN ACK FIN RST URG PSH ALL NONE" .
+Hence the command
+.nf
+ ip6tables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST SYN
+.fi
+will only match packets with the SYN flag set, and the ACK, FIN and
+RST flags unset.
+.TP
+.B "[!] --syn"
+Only match TCP packets with the SYN bit set and the ACK and RST bits
+cleared.  Such packets are used to request TCP connection initiation;
+for example, blocking such packets coming in an interface will prevent
+incoming TCP connections, but outgoing TCP connections will be
+unaffected.
+It is equivalent to \fB--tcp-flags SYN,RST,ACK SYN\fP.
+If the "!" flag precedes the "--syn", the sense of the
+option is inverted.
+.TP
+.BR "--tcp-option " "[!] \fInumber\fP"
+Match if TCP option set.
diff --git a/extensions/libip6t_udp.c b/extensions/libip6t_udp.c
new file mode 100644 (file)
index 0000000..cd3c3d4
--- /dev/null
@@ -0,0 +1,228 @@
+/* Shared library add-on to iptables to add UDP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"UDP v%s options:\n"
+" --source-port [!] port[:port]\n"
+" --sport ...\n"
+"                              match source port(s)\n"
+" --destination-port [!] port[:port]\n"
+" --dport ...\n"
+"                              match destination port(s)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "source-port", 1, 0, '1' },
+       { "sport", 1, 0, '1' }, /* synonym */
+       { "destination-port", 1, 0, '2' },
+       { "dport", 1, 0, '2' }, /* synonym */
+       {0}
+};
+
+static void
+parse_udp_ports(const char *portstring, u_int16_t *ports)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(portstring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               ports[0] = ports[1] = parse_port(buffer, "udp");
+       else {
+               *cp = '\0';
+               cp++;
+
+               ports[0] = buffer[0] ? parse_port(buffer, "udp") : 0;
+               ports[1] = cp[0] ? parse_port(cp, "udp") : 0xFFFF;
+
+               if (ports[0] > ports[1])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "invalid portrange (min > max)");
+       }
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_udp *udpinfo = (struct ip6t_udp *)m->data;
+
+       udpinfo->spts[1] = udpinfo->dpts[1] = 0xFFFF;
+}
+
+#define UDP_SRC_PORTS 0x01
+#define UDP_DST_PORTS 0x02
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_udp *udpinfo = (struct ip6t_udp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & UDP_SRC_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--source-port' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_udp_ports(argv[optind-1], udpinfo->spts);
+               if (invert)
+                       udpinfo->invflags |= IP6T_UDP_INV_SRCPT;
+               *flags |= UDP_SRC_PORTS;
+               break;
+
+       case '2':
+               if (*flags & UDP_DST_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--destination-port' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_udp_ports(argv[optind-1], udpinfo->dpts);
+               if (invert)
+                       udpinfo->invflags |= IP6T_UDP_INV_DSTPT;
+               *flags |= UDP_DST_PORTS;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static char *
+port_to_service(int port)
+{
+       struct servent *service;
+
+       if ((service = getservbyport(htons(port), "udp")))
+               return service->s_name;
+
+       return NULL;
+}
+
+static void
+print_port(u_int16_t port, int numeric)
+{
+       char *service;
+
+       if (numeric || (service = port_to_service(port)) == NULL)
+               printf("%u", port);
+       else
+               printf("%s", service);
+}
+
+static void
+print_ports(const char *name, u_int16_t min, u_int16_t max,
+           int invert, int numeric)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       print_port(min, numeric);
+               } else {
+                       printf("s:%s", inv);
+                       print_port(min, numeric);
+                       printf(":");
+                       print_port(max, numeric);
+               }
+               printf(" ");
+       }
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_udp *udp = (struct ip6t_udp *)match->data;
+
+       printf("udp ");
+       print_ports("spt", udp->spts[0], udp->spts[1],
+                   udp->invflags & IP6T_UDP_INV_SRCPT,
+                   numeric);
+       print_ports("dpt", udp->dpts[0], udp->dpts[1],
+                   udp->invflags & IP6T_UDP_INV_DSTPT,
+                   numeric);
+       if (udp->invflags & ~IP6T_UDP_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      udp->invflags & ~IP6T_UDP_INV_MASK);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_udp *udpinfo = (struct ip6t_udp *)match->data;
+
+       if (udpinfo->spts[0] != 0
+           || udpinfo->spts[1] != 0xFFFF) {
+               if (udpinfo->invflags & IP6T_UDP_INV_SRCPT)
+                       printf("! ");
+               if (udpinfo->spts[0]
+                   != udpinfo->spts[1])
+                       printf("--sport %u:%u ",
+                              udpinfo->spts[0],
+                              udpinfo->spts[1]);
+               else
+                       printf("--sport %u ",
+                              udpinfo->spts[0]);
+       }
+
+       if (udpinfo->dpts[0] != 0
+           || udpinfo->dpts[1] != 0xFFFF) {
+               if (udpinfo->invflags & IP6T_UDP_INV_DSTPT)
+                       printf("! ");
+               if (udpinfo->dpts[0]
+                   != udpinfo->dpts[1])
+                       printf("--dport %u:%u ",
+                              udpinfo->dpts[0],
+                              udpinfo->dpts[1]);
+               else
+                       printf("--dport %u ",
+                              udpinfo->dpts[0]);
+       }
+}
+
+static struct ip6tables_match udp = {
+       .name           = "udp",
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_udp)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_udp)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts,
+};
+
+void
+_init(void)
+{
+       register_match6(&udp);
+}
diff --git a/extensions/libip6t_udp.man b/extensions/libip6t_udp.man
new file mode 100644 (file)
index 0000000..1d5e590
--- /dev/null
@@ -0,0 +1,14 @@
+These extensions can be used if `--protocol udp' is specified.  It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification.
+See the description of the
+.B --source-port
+option of the TCP extension for details.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification.
+See the description of the
+.B --destination-port
+option of the TCP extension for details.
diff --git a/extensions/libipt_CLASSIFY.c b/extensions/libipt_CLASSIFY.c
new file mode 100644 (file)
index 0000000..bcedaa4
--- /dev/null
@@ -0,0 +1,142 @@
+/* Shared library add-on to iptables to add CLASSIFY target support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_CLASSIFY.h>
+#include <linux/types.h>
+#include <linux/pkt_sched.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"CLASSIFY target v%s options:\n"
+"  --set-class [MAJOR:MINOR]    Set skb->priority value\n"
+"  --add-mark                   Add value of skb->mark to skb->priority (PlanetLab specific)\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "set-class", 1, 0, '1' },
+       { "add-mark", 0, 0, '2' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+int string_to_priority(const char *s, unsigned int *p)
+{
+       unsigned int i, j;
+
+       if (sscanf(s, "%x:%x", &i, &j) != 2)
+               return 1;
+       
+       *p = TC_H_MAKE(i<<16, j);
+       return 0;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_classify_target_info *clinfo
+               = (struct ipt_classify_target_info *)(*target)->data;
+
+       clinfo->add_mark = 0;
+
+       switch (c) {
+       case '1':
+               if (string_to_priority(optarg, &clinfo->priority))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Bad class value `%s'", optarg);
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "CLASSIFY: Can't specify --set-class twice");
+               *flags = 1;
+               break;
+
+       case '2':
+               clinfo->add_mark = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "CLASSIFY: Parameter --set-class is required");
+}
+
+static void
+print_class(unsigned int priority, int numeric)
+{
+       printf("%x:%x ", TC_H_MAJ(priority)>>16, TC_H_MIN(priority));
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_classify_target_info *clinfo =
+               (const struct ipt_classify_target_info *)target->data;
+       printf("CLASSIFY set ");
+       print_class(clinfo->priority, numeric);
+       if (clinfo->add_mark)
+              printf ("add-mark ");
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_classify_target_info *clinfo =
+               (const struct ipt_classify_target_info *)target->data;
+
+       printf("--set-class %.4x:%.4x ",
+              TC_H_MAJ(clinfo->priority)>>16, TC_H_MIN(clinfo->priority));
+
+       if (clinfo->add_mark)
+              printf("--add-mark ");
+}
+
+static struct iptables_target classify = { 
+       .next           = NULL,
+       .name           = "CLASSIFY",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_classify_target_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_classify_target_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&classify);
+}
diff --git a/extensions/libipt_CLASSIFY.man b/extensions/libipt_CLASSIFY.man
new file mode 100644 (file)
index 0000000..393c329
--- /dev/null
@@ -0,0 +1,4 @@
+This module allows you to set the skb->priority value (and thus classify the packet into a specific CBQ class).
+.TP
+.BI "--set-class " "MAJOR:MINOR"
+Set the major and minor class value.
diff --git a/extensions/libipt_CLUSTERIP.c b/extensions/libipt_CLUSTERIP.c
new file mode 100644 (file)
index 0000000..3268ac5
--- /dev/null
@@ -0,0 +1,268 @@
+/* Shared library add-on to iptables to add CLUSTERIP target support. 
+ * (C) 2003 by Harald Welte <laforge@gnumonks.org>
+ *
+ * Development of this code was funded by SuSE AG, http://www.suse.com/
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <stddef.h>
+
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include "../include/linux/netfilter_ipv4/ipt_CLUSTERIP.h"
+
+static void
+help(void)
+{
+       printf(
+"CLUSTERIP target v%s options:\n"
+"  --new                        Create a new ClusterIP\n"
+"  --hashmode <mode>            Specify hashing mode\n"
+"                                      sourceip\n"
+"                                      sourceip-sourceport\n"
+"                                      sourceip-sourceport-destport\n"
+"  --clustermac <mac>           Set clusterIP MAC address\n"
+"  --total-nodes <num>          Set number of total nodes in cluster\n"
+"  --local-node <num>           Set the local node number\n"
+"  --hash-init <num>            Set init value of the Jenkins hash\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+#define        PARAM_NEW       0x0001
+#define PARAM_HMODE    0x0002
+#define PARAM_MAC      0x0004
+#define PARAM_TOTALNODE        0x0008
+#define PARAM_LOCALNODE        0x0010
+#define PARAM_HASHINIT 0x0020
+
+static struct option opts[] = {
+       { "new", 0, 0, '1' },
+       { "hashmode", 1, 0, '2' },
+       { "clustermac", 1, 0, '3' },
+       { "total-nodes", 1, 0, '4' },
+       { "local-node", 1, 0, '5' },
+       { "hash-init", 1, 0, '6' },
+       { 0 }
+};
+
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+static void
+parse_mac(const char *mac, char *macbuf)
+{
+       unsigned int i = 0;
+
+       if (strlen(mac) != ETH_ALEN*3-1)
+               exit_error(PARAMETER_PROBLEM, "Bad mac address `%s'", mac);
+
+       for (i = 0; i < ETH_ALEN; i++) {
+               long number;
+               char *end;
+
+               number = strtol(mac + i*3, &end, 16);
+
+               if (end == mac + i*3 + 2
+                   && number >= 0
+                   && number <= 255)
+                       macbuf[i] = number;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Bad mac address `%s'", mac);
+       }
+}
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_clusterip_tgt_info *cipinfo
+               = (struct ipt_clusterip_tgt_info *)(*target)->data;
+
+       switch (c) {
+               unsigned int num;
+       case '1':
+               cipinfo->flags |= CLUSTERIP_FLAG_NEW;
+               if (*flags & PARAM_NEW)
+                       exit_error(PARAMETER_PROBLEM, "Can only specify `--new' once\n");
+               *flags |= PARAM_NEW;
+               break;
+       case '2':
+               if (!(*flags & PARAM_NEW))
+                       exit_error(PARAMETER_PROBLEM, "Can only specify hashmode combined with `--new'\n");
+               if (*flags & PARAM_HMODE)
+                       exit_error(PARAMETER_PROBLEM, "Can only specify hashmode once\n");
+               if (!strcmp(optarg, "sourceip"))
+                       cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP;
+               else if (!strcmp(optarg, "sourceip-sourceport"))
+                       cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP_SPT;
+               else if (!strcmp(optarg, "sourceip-sourceport-destport"))
+                       cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP_SPT_DPT;
+               else
+                       exit_error(PARAMETER_PROBLEM, "Unknown hashmode `%s'\n",
+                                  optarg);
+               *flags |= PARAM_HMODE;
+               break;
+       case '3':
+               if (!(*flags & PARAM_NEW))
+                       exit_error(PARAMETER_PROBLEM, "Can only specify MAC combined with `--new'\n");
+               if (*flags & PARAM_MAC)
+                       exit_error(PARAMETER_PROBLEM, "Can only specify MAC once\n");
+               parse_mac(optarg, (char *)cipinfo->clustermac);
+               if (!(cipinfo->clustermac[0] & 0x01))
+                       exit_error(PARAMETER_PROBLEM, "MAC has to be a multicast ethernet address\n");
+               *flags |= PARAM_MAC;
+               break;
+       case '4':
+               if (!(*flags & PARAM_NEW))
+                       exit_error(PARAMETER_PROBLEM, "Can only specify node number combined with `--new'\n");
+               if (*flags & PARAM_TOTALNODE)
+                       exit_error(PARAMETER_PROBLEM, "Can only specify total node number once\n");
+               if (string_to_number(optarg, 1, CLUSTERIP_MAX_NODES, &num) < 0)
+                       exit_error(PARAMETER_PROBLEM, "Unable to parse `%s'\n", optarg);
+               cipinfo->num_total_nodes = (u_int16_t)num;
+               *flags |= PARAM_TOTALNODE;
+               break;
+       case '5':
+               if (!(*flags & PARAM_NEW))
+                       exit_error(PARAMETER_PROBLEM, "Can only specify node number combined with `--new'\n");
+               if (*flags & PARAM_LOCALNODE)
+                       exit_error(PARAMETER_PROBLEM, "Can only specify local node number once\n");
+               if (string_to_number(optarg, 1, CLUSTERIP_MAX_NODES, &num) < 0)
+                       exit_error(PARAMETER_PROBLEM, "Unable to parse `%s'\n", optarg);
+               cipinfo->num_local_nodes = 1;
+               cipinfo->local_nodes[0] = (u_int16_t)num;
+               *flags |= PARAM_LOCALNODE;
+               break;
+       case '6':
+               if (!(*flags & PARAM_NEW))
+                       exit_error(PARAMETER_PROBLEM, "Can only specify hash init value combined with `--new'\n");
+               if (*flags & PARAM_HASHINIT)
+                       exit_error(PARAMETER_PROBLEM, "Can specify hash init value only once\n");
+               if (string_to_number(optarg, 0, UINT_MAX, &num) < 0)
+                       exit_error(PARAMETER_PROBLEM, "Unable to parse `%s'\n", optarg);
+               cipinfo->hash_initval = num;
+               *flags |= PARAM_HASHINIT;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (flags == 0)
+               return;
+
+       if ((flags & (PARAM_NEW|PARAM_HMODE|PARAM_MAC|PARAM_TOTALNODE|PARAM_LOCALNODE))
+               == (PARAM_NEW|PARAM_HMODE|PARAM_MAC|PARAM_TOTALNODE|PARAM_LOCALNODE))
+               return;
+
+       exit_error(PARAMETER_PROBLEM, "CLUSTERIP target: Invalid parameter combination\n");
+}
+
+static char *hashmode2str(enum clusterip_hashmode mode)
+{
+       char *retstr;
+       switch (mode) {
+               case CLUSTERIP_HASHMODE_SIP:
+                       retstr = "sourceip";
+                       break;
+               case CLUSTERIP_HASHMODE_SIP_SPT:
+                       retstr = "sourceip-sourceport";
+                       break;
+               case CLUSTERIP_HASHMODE_SIP_SPT_DPT:
+                       retstr = "sourceip-sourceport-destport";
+                       break;
+               default:
+                       retstr = "unknown-error";
+                       break;
+       }
+       return retstr;
+}
+
+static char *mac2str(const u_int8_t mac[ETH_ALEN])
+{
+       static char buf[ETH_ALEN*3];
+       sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
+               mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+       return buf;
+}
+                       
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_clusterip_tgt_info *cipinfo =
+               (const struct ipt_clusterip_tgt_info *)target->data;
+       
+       if (!cipinfo->flags & CLUSTERIP_FLAG_NEW) {
+               printf("CLUSTERIP");
+               return;
+       }
+
+       printf("CLUSTERIP hashmode=%s clustermac=%s total_nodes=%u local_node=%u hash_init=%u", 
+               hashmode2str(cipinfo->hash_mode),
+               mac2str(cipinfo->clustermac),
+               cipinfo->num_total_nodes,
+               cipinfo->local_nodes[0],
+               cipinfo->hash_initval);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_clusterip_tgt_info *cipinfo =
+               (const struct ipt_clusterip_tgt_info *)target->data;
+
+       /* if this is not a new entry, we don't need to save target
+        * parameters */
+       if (!cipinfo->flags & CLUSTERIP_FLAG_NEW)
+               return;
+
+       printf("--new --hashmode %s --clustermac %s --total-nodes %d --local-node %d --hash-init %u",
+              hashmode2str(cipinfo->hash_mode),
+              mac2str(cipinfo->clustermac),
+              cipinfo->num_total_nodes,
+              cipinfo->local_nodes[0],
+              cipinfo->hash_initval);
+}
+
+static struct iptables_target clusterip = { 
+       .next           = NULL,
+       .name           = "CLUSTERIP",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_clusterip_tgt_info)),
+       .userspacesize  = offsetof(struct ipt_clusterip_tgt_info, config),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&clusterip);
+}
diff --git a/extensions/libipt_CLUSTERIP.man b/extensions/libipt_CLUSTERIP.man
new file mode 100644 (file)
index 0000000..8e766f3
--- /dev/null
@@ -0,0 +1,24 @@
+This module allows you to configure a simple cluster of nodes that share
+a certain IP and MAC address without an explicit load balancer in front of
+them.  Connections are statically distributed between the nodes in this
+cluster.
+.TP
+.BI "--new "
+Create a new ClusterIP.  You always have to set this on the first rule
+for a given ClusterIP.
+.TP
+.BI "--hashmode " "mode"
+Specify the hashing mode.  Has to be one of
+.B sourceip, sourceip-sourceport, sourceip-sourceport-destport
+.TP
+.BI "--clustermac " "mac"
+Specify the ClusterIP MAC address.  Has to be a link-layer multicast address
+.TP
+.BI "--total-nodes " "num"
+Number of total nodes within this cluster.
+.TP
+.BI "--local-node " "num"
+Local node number within this cluster.
+.TP
+.BI "--hash-init " "rnd"
+Specify the random seed used for hash initialization.
diff --git a/extensions/libipt_CONNMARK.c b/extensions/libipt_CONNMARK.c
new file mode 100644 (file)
index 0000000..2e17b3f
--- /dev/null
@@ -0,0 +1,220 @@
+/* Shared library add-on to iptables to add CONNMARK target support.
+ *
+ * (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * Version 1.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include "../include/linux/netfilter_ipv4/ipt_CONNMARK.h"
+
+#if 0
+struct markinfo {
+       struct ipt_entry_target t;
+       struct ipt_connmark_target_info mark;
+};
+#endif
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"CONNMARK target v%s options:\n"
+"  --set-mark value[/mask]       Set conntrack mark value\n"
+"  --save-mark [--mask mask]     Save the packet nfmark in the connection\n"
+"  --restore-mark [--mask mask]  Restore saved nfmark value\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "set-mark", 1, 0, '1' },
+       { "save-mark", 0, 0, '2' },
+       { "restore-mark", 0, 0, '3' },
+       { "mask", 1, 0, '4' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_connmark_target_info *markinfo
+               = (struct ipt_connmark_target_info *)(*target)->data;
+
+       markinfo->mask = 0xffffffffUL;
+
+       switch (c) {
+               char *end;
+       case '1':
+               markinfo->mode = IPT_CONNMARK_SET;
+
+               markinfo->mark = strtoul(optarg, &end, 0);
+               if (*end == '/' && end[1] != '\0')
+                   markinfo->mask = strtoul(end+1, &end, 0);
+
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "CONNMARK target: Can't specify --set-mark twice");
+               *flags = 1;
+               break;
+       case '2':
+               markinfo->mode = IPT_CONNMARK_SAVE;
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "CONNMARK target: Can't specify --save-mark twice");
+               *flags = 1;
+               break;
+       case '3':
+               markinfo->mode = IPT_CONNMARK_RESTORE;
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "CONNMARK target: Can't specify --restore-mark twice");
+               *flags = 1;
+               break;
+       case '4':
+               if (!*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "CONNMARK target: Can't specify --mask without a operation");
+               markinfo->mask = strtoul(optarg, &end, 0);
+
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad MASK value `%s'", optarg);
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "CONNMARK target: No operation specified");
+}
+
+static void
+print_mark(unsigned long mark)
+{
+       printf("0x%lx", mark);
+}
+
+static void
+print_mask(const char *text, unsigned long mask)
+{
+       if (mask != 0xffffffffUL)
+               printf("%s0x%lx", text, mask);
+}
+
+
+/* Prints out the target info. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_connmark_target_info *markinfo =
+               (const struct ipt_connmark_target_info *)target->data;
+       switch (markinfo->mode) {
+       case IPT_CONNMARK_SET:
+           printf("CONNMARK set ");
+           print_mark(markinfo->mark);
+           print_mask("/", markinfo->mask);
+           printf(" ");
+           break;
+       case IPT_CONNMARK_SAVE:
+           printf("CONNMARK save ");
+           print_mask("mask ", markinfo->mask);
+           printf(" ");
+           break;
+       case IPT_CONNMARK_RESTORE:
+           printf("CONNMARK restore ");
+           print_mask("mask ", markinfo->mask);
+           break;
+       default:
+           printf("ERROR: UNKNOWN CONNMARK MODE ");
+           break;
+       }
+}
+
+/* Saves the target into in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_connmark_target_info *markinfo =
+               (const struct ipt_connmark_target_info *)target->data;
+
+       switch (markinfo->mode) {
+       case IPT_CONNMARK_SET:
+           printf("--set-mark ");
+           print_mark(markinfo->mark);
+           print_mask("/", markinfo->mask);
+           printf(" ");
+           break;
+       case IPT_CONNMARK_SAVE:
+           printf("--save-mark ");
+           print_mask("--mask ", markinfo->mask);
+           break;
+       case IPT_CONNMARK_RESTORE:
+           printf("--restore-mark ");
+           print_mask("--mask ", markinfo->mask);
+           break;
+       default:
+           printf("ERROR: UNKNOWN CONNMARK MODE ");
+           break;
+       }
+}
+
+static struct iptables_target connmark_target = {
+    .name          = "CONNMARK",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_connmark_target_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_connmark_target_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_target(&connmark_target);
+}
diff --git a/extensions/libipt_CONNMARK.man b/extensions/libipt_CONNMARK.man
new file mode 100644 (file)
index 0000000..8b4de5a
--- /dev/null
@@ -0,0 +1,15 @@
+This module sets the netfilter mark value associated with a connection
+.TP
+.B --set-mark mark[/mask]
+Set connection mark. If a mask is specified then only those bits set in the
+mask is modified.
+.TP
+.B --save-mark [--mask mask]
+Copy the netfilter packet mark value to the connection mark. If a mask
+is specified then only those bits are copied.
+.TP
+.B --restore-mark [--mask mask]
+Copy the connection mark value to the packet. If a mask is specified
+then only those bits are copied. This is only valid in the
+.B mangle
+table.
diff --git a/extensions/libipt_CONNSECMARK.c b/extensions/libipt_CONNSECMARK.c
new file mode 100644 (file)
index 0000000..237a41f
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Shared library add-on to iptables to add CONNSECMARK target support.
+ *
+ * Based on the MARK and CONNMARK targets.
+ *
+ * Copyright (C) 2006 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter/xt_CONNSECMARK.h>
+
+#define PFX "CONNSECMARK target: "
+
+static void help(void)
+{
+       printf(
+"CONNSECMARK target v%s options:\n"
+"  --save                   Copy security mark from packet to conntrack\n"
+"  --restore                Copy security mark from connection to packet\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "save", 0, 0, '1' },
+       { "restore", 0, 0, '2' },
+       { 0 }
+};
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+                 const struct ipt_entry *entry, struct ipt_entry_target **target)
+{
+       struct xt_connsecmark_target_info *info =
+               (struct xt_connsecmark_target_info*)(*target)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & CONNSECMARK_SAVE)
+                       exit_error(PARAMETER_PROBLEM, PFX
+                                  "Can't specify --save twice");
+               info->mode = CONNSECMARK_SAVE;
+               *flags |= CONNSECMARK_SAVE;
+               break;
+
+       case '2':
+               if (*flags & CONNSECMARK_RESTORE)
+                       exit_error(PARAMETER_PROBLEM, PFX
+                                  "Can't specify --restore twice");
+               info->mode = CONNSECMARK_RESTORE;
+               *flags |= CONNSECMARK_RESTORE;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, PFX "parameter required");
+
+       if (flags == (CONNSECMARK_SAVE|CONNSECMARK_RESTORE))
+               exit_error(PARAMETER_PROBLEM, PFX "only one flag of --save "
+                          "or --restore is allowed");
+}
+
+static void print_connsecmark(struct xt_connsecmark_target_info *info)
+{
+       switch (info->mode) {
+       case CONNSECMARK_SAVE:
+               printf("save ");
+               break;
+               
+       case CONNSECMARK_RESTORE:
+               printf("restore ");
+               break;
+               
+       default:
+               exit_error(OTHER_PROBLEM, PFX "invalid mode %hhu\n", info->mode);
+       }
+}
+
+static void print(const struct ipt_ip *ip,
+                 const struct ipt_entry_target *target, int numeric)
+{
+       struct xt_connsecmark_target_info *info =
+               (struct xt_connsecmark_target_info*)(target)->data;
+
+       printf("CONNSECMARK ");
+       print_connsecmark(info);
+}
+
+static void save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       struct xt_connsecmark_target_info *info =
+               (struct xt_connsecmark_target_info*)target->data;
+
+       printf("--");
+       print_connsecmark(info);
+}
+
+static struct iptables_target connsecmark = {
+       .next           = NULL,
+       .name           = "CONNSECMARK",
+       .version        = IPTABLES_VERSION,
+       .revision       = 0,
+       .size           = IPT_ALIGN(sizeof(struct xt_connsecmark_target_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct xt_connsecmark_target_info)),
+       .parse          = &parse,
+       .help           = &help,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&connsecmark);
+}
diff --git a/extensions/libipt_CONNSECMARK.man b/extensions/libipt_CONNSECMARK.man
new file mode 100644 (file)
index 0000000..b94353a
--- /dev/null
@@ -0,0 +1,15 @@
+This module copies security markings from packets to connections
+(if unlabeled), and from connections back to packets (also only
+if unlabeled).  Typically used in conjunction with SECMARK, it is
+only valid in the
+.B mangle
+table.
+.TP
+.B --save
+If the packet has a security marking, copy it to the connection
+if the connection is not marked.
+.TP
+.B --restore
+If the packet does not have a security marking, and the connection
+does, copy the security marking from the connection to the packet.
+
diff --git a/extensions/libipt_DNAT.c b/extensions/libipt_DNAT.c
new file mode 100644 (file)
index 0000000..b0def31
--- /dev/null
@@ -0,0 +1,267 @@
+/* Shared library add-on to iptables to add destination-NAT support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter/nf_nat.h>
+
+#define IPT_DNAT_OPT_DEST 0x1
+#define IPT_DNAT_OPT_RANDOM 0x2
+
+/* Dest NAT data consists of a multi-range, indicating where to map
+   to. */
+struct ipt_natinfo
+{
+       struct ipt_entry_target t;
+       struct ip_nat_multi_range mr;
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"DNAT v%s options:\n"
+" --to-destination <ipaddr>[-<ipaddr>][:port-port]\n"
+"                              Address to map destination to.\n"
+"[--random]\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "to-destination", 1, 0, '1' },
+       { "random", 0, 0, '2' },
+       { 0 }
+};
+
+static struct ipt_natinfo *
+append_range(struct ipt_natinfo *info, const struct ip_nat_range *range)
+{
+       unsigned int size;
+
+       /* One rangesize already in struct ipt_natinfo */
+       size = IPT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
+
+       info = realloc(info, size);
+       if (!info)
+               exit_error(OTHER_PROBLEM, "Out of memory\n");
+
+       info->t.u.target_size = size;
+       info->mr.range[info->mr.rangesize] = *range;
+       info->mr.rangesize++;
+
+       return info;
+}
+
+/* Ranges expected in network order. */
+static struct ipt_entry_target *
+parse_to(char *arg, int portok, struct ipt_natinfo *info)
+{
+       struct ip_nat_range range;
+       char *colon, *dash, *error;
+       struct in_addr *ip;
+
+       memset(&range, 0, sizeof(range));
+       colon = strchr(arg, ':');
+
+       if (colon) {
+               int port;
+
+               if (!portok)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need TCP or UDP with port specification");
+
+               range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+
+               port = atoi(colon+1);
+               if (port <= 0 || port > 65535)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Port `%s' not valid\n", colon+1);
+
+               error = strchr(colon+1, ':');
+               if (error)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid port:port syntax - use dash\n");
+
+               dash = strchr(colon, '-');
+               if (!dash) {
+                       range.min.tcp.port
+                               = range.max.tcp.port
+                               = htons(port);
+               } else {
+                       int maxport;
+
+                       maxport = atoi(dash + 1);
+                       if (maxport <= 0 || maxport > 65535)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Port `%s' not valid\n", dash+1);
+                       if (maxport < port)
+                               /* People are stupid. */
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Port range `%s' funky\n", colon+1);
+                       range.min.tcp.port = htons(port);
+                       range.max.tcp.port = htons(maxport);
+               }
+               /* Starts with a colon? No IP info...*/
+               if (colon == arg)
+                       return &(append_range(info, &range)->t);
+               *colon = '\0';
+       }
+
+       range.flags |= IP_NAT_RANGE_MAP_IPS;
+       dash = strchr(arg, '-');
+       if (colon && dash && dash > colon)
+               dash = NULL;
+
+       if (dash)
+               *dash = '\0';
+
+       ip = dotted_to_addr(arg);
+       if (!ip)
+               exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
+                          arg);
+       range.min_ip = ip->s_addr;
+       if (dash) {
+               ip = dotted_to_addr(dash+1);
+               if (!ip)
+                       exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
+                                  dash+1);
+               range.max_ip = ip->s_addr;
+       } else
+               range.max_ip = range.min_ip;
+
+       return &(append_range(info, &range)->t);
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_natinfo *info = (void *)*target;
+       int portok;
+
+       if (entry->ip.proto == IPPROTO_TCP
+           || entry->ip.proto == IPPROTO_UDP
+           || entry->ip.proto == IPPROTO_ICMP)
+               portok = 1;
+       else
+               portok = 0;
+
+       switch (c) {
+       case '1':
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --to-destination");
+
+               if (*flags) {
+                       if (!kernel_version)
+                               get_kernel_version();
+                       if (kernel_version > LINUX_VERSION(2, 6, 10))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Multiple --to-destination not supported");
+               }
+               *target = parse_to(optarg, portok, info);
+               /* WTF do we need this for?? */
+               if (*flags & IPT_DNAT_OPT_RANDOM)
+                       info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
+               *flags |= IPT_DNAT_OPT_DEST;
+               return 1;
+
+       case '2':
+               if (*flags & IPT_DNAT_OPT_DEST) {
+                       info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
+                       *flags |= IPT_DNAT_OPT_RANDOM;
+               } else
+                       *flags |= IPT_DNAT_OPT_RANDOM;
+       default:
+               return 0;
+       }
+}
+
+/* Final check; must have specfied --to-source. */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "You must specify --to-destination");
+}
+
+static void print_range(const struct ip_nat_range *r)
+{
+       if (r->flags & IP_NAT_RANGE_MAP_IPS) {
+               struct in_addr a;
+
+               a.s_addr = r->min_ip;
+               printf("%s", addr_to_dotted(&a));
+               if (r->max_ip != r->min_ip) {
+                       a.s_addr = r->max_ip;
+                       printf("-%s", addr_to_dotted(&a));
+               }
+       }
+       if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+               printf(":");
+               printf("%hu", ntohs(r->min.tcp.port));
+               if (r->max.tcp.port != r->min.tcp.port)
+                       printf("-%hu", ntohs(r->max.tcp.port));
+       }
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       struct ipt_natinfo *info = (void *)target;
+       unsigned int i = 0;
+
+       printf("to:");
+       for (i = 0; i < info->mr.rangesize; i++) {
+               print_range(&info->mr.range[i]);
+               printf(" ");
+               if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
+                       printf("random ");
+       }
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       struct ipt_natinfo *info = (void *)target;
+       unsigned int i = 0;
+
+       for (i = 0; i < info->mr.rangesize; i++) {
+               printf("--to-destination ");
+               print_range(&info->mr.range[i]);
+               printf(" ");
+               if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
+                       printf("--random ");
+       }
+}
+
+static struct iptables_target dnat = { 
+       .next           = NULL,
+       .name           = "DNAT",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&dnat);
+}
diff --git a/extensions/libipt_DNAT.man b/extensions/libipt_DNAT.man
new file mode 100644 (file)
index 0000000..f11f4e2
--- /dev/null
@@ -0,0 +1,36 @@
+This target is only valid in the
+.B nat
+table, in the
+.B PREROUTING
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains.  It specifies that the destination address of the packet
+should be modified (and all future packets in this connection will
+also be mangled), and rules should cease being examined.  It takes one
+type of option:
+.TP
+.BR "--to-destination " "[\fIipaddr\fP][-\fIipaddr\fP][:\fIport\fP-\fIport\fP]"
+which can specify a single new destination IP address, an inclusive
+range of IP addresses, and optionally, a port range (which is only
+valid if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" ).
+If no port range is specified, then the destination port will never be
+modified. If no IP address is specified then only the destination port
+will be modified.
+
+In Kernels up to 2.6.10 you can add several --to-destination options.  For
+those kernels, if you specify more than one destination address, either via an
+address range or multiple --to-destination options, a simple round-robin (one
+after another in cycle) load balancing takes place between these addresses.
+Later Kernels (>= 2.6.11-rc1) don't have the ability to NAT to multiple ranges
+anymore.
+.TP
+.BR "--random"
+If option
+.B "--random"
+is used then port mapping will be randomized (kernel >= 2.6.22).
+.RS
+.PP
diff --git a/extensions/libipt_DSCP.c b/extensions/libipt_DSCP.c
new file mode 100644 (file)
index 0000000..c50d902
--- /dev/null
@@ -0,0 +1,164 @@
+/* Shared library add-on to iptables for DSCP
+ *
+ * (C) 2000- 2002 by Matthew G. Marsh <mgm@paktronix.com>,
+ *                  Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_DSCP.c borrowed heavily from libipt_TOS.c
+ *
+ * --set-class added by Iain Barnes
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_DSCP.h>
+
+/* This is evil, but it's my code - HW*/
+#include "libipt_dscp_helper.c"
+
+
+static void init(struct ipt_entry_target *t, unsigned int *nfcache) 
+{
+}
+
+static void help(void) 
+{
+       printf(
+"DSCP target options\n"
+"  --set-dscp value            Set DSCP field in packet header to value\n"
+"                              This value can be in decimal (ex: 32)\n"
+"                              or in hex (ex: 0x20)\n"
+"  --set-dscp-class class      Set the DSCP field in packet header to the\n"
+"                              value represented by the DiffServ class value.\n"
+"                              This class may be EF,BE or any of the CSxx\n"
+"                              or AFxx classes.\n"
+"\n"
+"                              These two options are mutually exclusive !\n"
+);
+}
+
+static struct option opts[] = {
+       { "set-dscp", 1, 0, 'F' },
+       { "set-dscp-class", 1, 0, 'G' },
+       { 0 }
+};
+
+static void
+parse_dscp(const char *s, struct ipt_DSCP_info *dinfo)
+{
+       unsigned int dscp;
+       
+       if (string_to_number(s, 0, 255, &dscp) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid dscp `%s'\n", s);
+
+       if (dscp > IPT_DSCP_MAX)
+               exit_error(PARAMETER_PROBLEM,
+                          "DSCP `%d` out of range\n", dscp);
+
+       dinfo->dscp = (u_int8_t )dscp;
+       return;
+}
+
+
+static void
+parse_class(const char *s, struct ipt_DSCP_info *dinfo)
+{
+       unsigned int dscp = class_to_dscp(s);
+
+       /* Assign the value */
+       dinfo->dscp = (u_int8_t)dscp;
+}
+
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_DSCP_info *dinfo
+               = (struct ipt_DSCP_info *)(*target)->data;
+
+       switch (c) {
+       case 'F':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "DSCP target: Only use --set-dscp ONCE!");
+               parse_dscp(optarg, dinfo);
+               *flags = 1;
+               break;
+       case 'G':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "DSCP target: Only use --set-dscp-class ONCE!");
+               parse_class(optarg, dinfo);
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "DSCP target: Parameter --set-dscp is required");
+}
+
+static void
+print_dscp(u_int8_t dscp, int numeric)
+{
+       printf("0x%02x ", dscp);
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_DSCP_info *dinfo =
+               (const struct ipt_DSCP_info *)target->data;
+       printf("DSCP set ");
+       print_dscp(dinfo->dscp, numeric);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_DSCP_info *dinfo =
+               (const struct ipt_DSCP_info *)target->data;
+
+       printf("--set-dscp 0x%02x ", dinfo->dscp);
+}
+
+static struct iptables_target dscp = { 
+       .next           = NULL,
+       .name           = "DSCP",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_DSCP_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_DSCP_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&dscp);
+}
diff --git a/extensions/libipt_DSCP.man b/extensions/libipt_DSCP.man
new file mode 100644 (file)
index 0000000..e8e5cf5
--- /dev/null
@@ -0,0 +1,9 @@
+This target allows to alter the value of the DSCP bits within the TOS
+header of the IPv4 packet.  As this manipulates a packet, it can only
+be used in the mangle table.
+.TP
+.BI "--set-dscp " "value"
+Set the DSCP field to a numerical value (can be decimal or hex)
+.TP
+.BI "--set-dscp-class " "class"
+Set the DSCP field to a DiffServ class.
diff --git a/extensions/libipt_ECN.c b/extensions/libipt_ECN.c
new file mode 100644 (file)
index 0000000..7e8d0c4
--- /dev/null
@@ -0,0 +1,185 @@
+/* Shared library add-on to iptables for ECN, $Version$
+ *
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_ECN.c borrowed heavily from libipt_DSCP.c
+ *
+ * $Id: libipt_ECN.c 3507 2004-12-28 13:11:59Z /C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=rusty/emailAddress=rusty@netfilter.org $
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_ECN.h>
+
+static void init(struct ipt_entry_target *t, unsigned int *nfcache) 
+{
+}
+
+static void help(void) 
+{
+       printf(
+"ECN target v%s options\n"
+"  --ecn-tcp-remove            Remove all ECN bits from TCP header\n",
+               IPTABLES_VERSION);
+}
+
+#if 0
+"ECN target v%s EXPERIMENTAL options (use with extreme care!)\n"
+"  --ecn-ip-ect                        Set the IPv4 ECT codepoint (0 to 3)\n"
+"  --ecn-tcp-cwr               Set the IPv4 CWR bit (0 or 1)\n"
+"  --ecn-tcp-ece               Set the IPv4 ECE bit (0 or 1)\n",
+#endif
+
+
+static struct option opts[] = {
+       { "ecn-tcp-remove", 0, 0, 'F' },
+       { "ecn-tcp-cwr", 1, 0, 'G' },
+       { "ecn-tcp-ece", 1, 0, 'H' },
+       { "ecn-ip-ect", 1, 0, '9' },
+       { 0 }
+};
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       unsigned int result;
+       struct ipt_ECN_info *einfo
+               = (struct ipt_ECN_info *)(*target)->data;
+
+       switch (c) {
+       case 'F':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                               "ECN target: Only use --ecn-tcp-remove ONCE!");
+               einfo->operation = IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR;
+               einfo->proto.tcp.ece = 0;
+               einfo->proto.tcp.cwr = 0;
+               *flags = 1;
+               break;
+       case 'G':
+               if (*flags & IPT_ECN_OP_SET_CWR)
+                       exit_error(PARAMETER_PROBLEM,
+                               "ECN target: Only use --ecn-tcp-cwr ONCE!");
+               if (string_to_number(optarg, 0, 1, &result))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN target: Value out of range");
+               einfo->operation |= IPT_ECN_OP_SET_CWR;
+               einfo->proto.tcp.cwr = result;
+               *flags |= IPT_ECN_OP_SET_CWR;
+               break;
+       case 'H':
+               if (*flags & IPT_ECN_OP_SET_ECE)
+                       exit_error(PARAMETER_PROBLEM,
+                               "ECN target: Only use --ecn-tcp-ece ONCE!");
+               if (string_to_number(optarg, 0, 1, &result))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN target: Value out of range");
+               einfo->operation |= IPT_ECN_OP_SET_ECE;
+               einfo->proto.tcp.ece = result;
+               *flags |= IPT_ECN_OP_SET_ECE;
+               break;
+       case '9':
+               if (*flags & IPT_ECN_OP_SET_IP)
+                       exit_error(PARAMETER_PROBLEM,
+                               "ECN target: Only use --ecn-ip-ect ONCE!");
+               if (string_to_number(optarg, 0, 3, &result))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN target: Value out of range");
+               einfo->operation |= IPT_ECN_OP_SET_IP;
+               einfo->ip_ect = result;
+               *flags |= IPT_ECN_OP_SET_IP;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "ECN target: Parameter --ecn-tcp-remove is required");
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_ECN_info *einfo =
+               (const struct ipt_ECN_info *)target->data;
+
+       printf("ECN ");
+
+       if (einfo->operation == (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR)
+           && einfo->proto.tcp.ece == 0
+           && einfo->proto.tcp.cwr == 0)
+               printf("TCP remove ");
+       else {
+               if (einfo->operation & IPT_ECN_OP_SET_ECE)
+                       printf("ECE=%u ", einfo->proto.tcp.ece);
+
+               if (einfo->operation & IPT_ECN_OP_SET_CWR)
+                       printf("CWR=%u ", einfo->proto.tcp.cwr);
+
+               if (einfo->operation & IPT_ECN_OP_SET_IP)
+                       printf("ECT codepoint=%u ", einfo->ip_ect);
+       }
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_ECN_info *einfo =
+               (const struct ipt_ECN_info *)target->data;
+
+       if (einfo->operation == (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR)
+           && einfo->proto.tcp.ece == 0
+           && einfo->proto.tcp.cwr == 0)
+               printf("--ecn-tcp-remove ");
+       else {
+
+               if (einfo->operation & IPT_ECN_OP_SET_ECE)
+                       printf("--ecn-tcp-ece %d ", einfo->proto.tcp.ece);
+
+               if (einfo->operation & IPT_ECN_OP_SET_CWR)
+                       printf("--ecn-tcp-cwr %d ", einfo->proto.tcp.cwr);
+
+               if (einfo->operation & IPT_ECN_OP_SET_IP)
+                       printf("--ecn-ip-ect %d ", einfo->ip_ect);
+       }
+}
+
+static
+struct iptables_target ecn = { 
+       .next           = NULL,
+       .name           = "ECN",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_ECN_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_ECN_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&ecn);
+}
diff --git a/extensions/libipt_ECN.man b/extensions/libipt_ECN.man
new file mode 100644 (file)
index 0000000..3668490
--- /dev/null
@@ -0,0 +1,7 @@
+This target allows to selectively work around known ECN blackholes.
+It can only be used in the mangle table.
+.TP
+.BI "--ecn-tcp-remove"
+Remove all ECN bits from the TCP header.  Of course, it can only be used
+in conjunction with
+.BR "-p tcp" .
diff --git a/extensions/libipt_IPV4OPTSSTRIP.c b/extensions/libipt_IPV4OPTSSTRIP.c
new file mode 100644 (file)
index 0000000..d0305e6
--- /dev/null
@@ -0,0 +1,74 @@
+/* Shared library add-on to iptables for IPV4OPTSSTRIP
+ * This modules strip all the IP options.
+ *
+ * (C) 2001 by Fabrice MARIE <fabrice@netfilter.org>
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+static void help(void) 
+{
+       printf("IPV4OPTSSTRIP v%s target takes no option !! Make sure you use it in the mangle table.\n",
+              IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { 0 }
+};
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       return 0;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       /* nothing to print, we don't take option... */
+}
+
+/* Saves the stuff in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       /* nothing to print, we don't take option... */
+}
+
+static struct iptables_target IPV4OPTSSTRIP = { 
+       .next           = NULL,
+       .name           = "IPV4OPTSSTRIP",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(0),
+       .userspacesize  = IPT_ALIGN(0),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&IPV4OPTSSTRIP);
+}
diff --git a/extensions/libipt_IPV4OPTSSTRIP.man b/extensions/libipt_IPV4OPTSSTRIP.man
new file mode 100644 (file)
index 0000000..a17d8a2
--- /dev/null
@@ -0,0 +1,5 @@
+Strip all the IP options from a packet.
+
+The target doesn't take any option, and therefore is extremly easy to use :
+
+# iptables -t mangle -A PREROUTING -j IPV4OPTSSTRIP
diff --git a/extensions/libipt_LOG.c b/extensions/libipt_LOG.c
new file mode 100644 (file)
index 0000000..96cc701
--- /dev/null
@@ -0,0 +1,290 @@
+/* Shared library add-on to iptables to add LOG support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_LOG.h>
+
+#define LOG_DEFAULT_LEVEL LOG_WARNING
+
+#ifndef IPT_LOG_UID /* Old kernel */
+#define IPT_LOG_UID    0x08    /* Log UID owning local socket */
+#undef  IPT_LOG_MASK
+#define IPT_LOG_MASK   0x0f
+#endif
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"LOG v%s options:\n"
+" --log-level level            Level of logging (numeric or see syslog.conf)\n"
+" --log-prefix prefix          Prefix log messages with this prefix.\n\n"
+" --log-tcp-sequence           Log TCP sequence numbers.\n\n"
+" --log-tcp-options            Log TCP options.\n\n"
+" --log-ip-options             Log IP options.\n\n"
+" --log-uid                    Log UID owning the local socket.\n\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "log-level",        .has_arg = 1, .flag = 0, .val = '!' },
+       { .name = "log-prefix",       .has_arg = 1, .flag = 0, .val = '#' },
+       { .name = "log-tcp-sequence", .has_arg = 0, .flag = 0, .val = '1' },
+       { .name = "log-tcp-options",  .has_arg = 0, .flag = 0, .val = '2' },
+       { .name = "log-ip-options",   .has_arg = 0, .flag = 0, .val = '3' },
+       { .name = "log-uid",          .has_arg = 0, .flag = 0, .val = '4' },
+       { .name = 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+       struct ipt_log_info *loginfo = (struct ipt_log_info *)t->data;
+
+       loginfo->level = LOG_DEFAULT_LEVEL;
+
+}
+
+struct ipt_log_names {
+       const char *name;
+       unsigned int level;
+};
+
+static struct ipt_log_names ipt_log_names[]
+= { { .name = "alert",   .level = LOG_ALERT },
+    { .name = "crit",    .level = LOG_CRIT },
+    { .name = "debug",   .level = LOG_DEBUG },
+    { .name = "emerg",   .level = LOG_EMERG },
+    { .name = "error",   .level = LOG_ERR },           /* DEPRECATED */
+    { .name = "info",    .level = LOG_INFO },
+    { .name = "notice",  .level = LOG_NOTICE },
+    { .name = "panic",   .level = LOG_EMERG },         /* DEPRECATED */
+    { .name = "warning", .level = LOG_WARNING }
+};
+
+static u_int8_t
+parse_level(const char *level)
+{
+       unsigned int lev = -1;
+       unsigned int set = 0;
+
+       if (string_to_number(level, 0, 7, &lev) == -1) {
+               unsigned int i = 0;
+
+               for (i = 0;
+                    i < sizeof(ipt_log_names) / sizeof(struct ipt_log_names);
+                    i++) {
+                       if (strncasecmp(level, ipt_log_names[i].name,
+                                       strlen(level)) == 0) {
+                               if (set++)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "log-level `%s' ambiguous",
+                                                  level);
+                               lev = ipt_log_names[i].level;
+                       }
+               }
+
+               if (!set)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "log-level `%s' unknown", level);
+       }
+
+       return (u_int8_t)lev;
+}
+
+#define IPT_LOG_OPT_LEVEL 0x01
+#define IPT_LOG_OPT_PREFIX 0x02
+#define IPT_LOG_OPT_TCPSEQ 0x04
+#define IPT_LOG_OPT_TCPOPT 0x08
+#define IPT_LOG_OPT_IPOPT 0x10
+#define IPT_LOG_OPT_UID 0x20
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_log_info *loginfo = (struct ipt_log_info *)(*target)->data;
+
+       switch (c) {
+       case '!':
+               if (*flags & IPT_LOG_OPT_LEVEL)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-level twice");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --log-level");
+
+               loginfo->level = parse_level(optarg);
+               *flags |= IPT_LOG_OPT_LEVEL;
+               break;
+
+       case '#':
+               if (*flags & IPT_LOG_OPT_PREFIX)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-prefix twice");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --log-prefix");
+
+               if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Maximum prefix length %u for --log-prefix",
+                                  (unsigned int)sizeof(loginfo->prefix) - 1);
+
+               if (strlen(optarg) == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "No prefix specified for --log-prefix");
+
+               if (strlen(optarg) != strlen(strtok(optarg, "\n")))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Newlines not allowed in --log-prefix");
+
+               strcpy(loginfo->prefix, optarg);
+               *flags |= IPT_LOG_OPT_PREFIX;
+               break;
+
+       case '1':
+               if (*flags & IPT_LOG_OPT_TCPSEQ)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-tcp-sequence "
+                                  "twice");
+
+               loginfo->logflags |= IPT_LOG_TCPSEQ;
+               *flags |= IPT_LOG_OPT_TCPSEQ;
+               break;
+
+       case '2':
+               if (*flags & IPT_LOG_OPT_TCPOPT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-tcp-options twice");
+
+               loginfo->logflags |= IPT_LOG_TCPOPT;
+               *flags |= IPT_LOG_OPT_TCPOPT;
+               break;
+
+       case '3':
+               if (*flags & IPT_LOG_OPT_IPOPT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-ip-options twice");
+
+               loginfo->logflags |= IPT_LOG_IPOPT;
+               *flags |= IPT_LOG_OPT_IPOPT;
+               break;
+
+       case '4':
+               if (*flags & IPT_LOG_OPT_UID)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-uid twice");
+
+               loginfo->logflags |= IPT_LOG_UID;
+               *flags |= IPT_LOG_OPT_UID;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_log_info *loginfo
+               = (const struct ipt_log_info *)target->data;
+       unsigned int i = 0;
+
+       printf("LOG ");
+       if (numeric)
+               printf("flags %u level %u ",
+                      loginfo->logflags, loginfo->level);
+       else {
+               for (i = 0;
+                    i < sizeof(ipt_log_names) / sizeof(struct ipt_log_names);
+                    i++) {
+                       if (loginfo->level == ipt_log_names[i].level) {
+                               printf("level %s ", ipt_log_names[i].name);
+                               break;
+                       }
+               }
+               if (i == sizeof(ipt_log_names) / sizeof(struct ipt_log_names))
+                       printf("UNKNOWN level %u ", loginfo->level);
+               if (loginfo->logflags & IPT_LOG_TCPSEQ)
+                       printf("tcp-sequence ");
+               if (loginfo->logflags & IPT_LOG_TCPOPT)
+                       printf("tcp-options ");
+               if (loginfo->logflags & IPT_LOG_IPOPT)
+                       printf("ip-options ");
+               if (loginfo->logflags & IPT_LOG_UID)
+                       printf("uid ");
+               if (loginfo->logflags & ~(IPT_LOG_MASK))
+                       printf("unknown-flags ");
+       }
+
+       if (strcmp(loginfo->prefix, "") != 0)
+               printf("prefix `%s' ", loginfo->prefix);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_log_info *loginfo
+               = (const struct ipt_log_info *)target->data;
+
+       if (strcmp(loginfo->prefix, "") != 0)
+               printf("--log-prefix \"%s\" ", loginfo->prefix);
+
+       if (loginfo->level != LOG_DEFAULT_LEVEL)
+               printf("--log-level %d ", loginfo->level);
+
+       if (loginfo->logflags & IPT_LOG_TCPSEQ)
+               printf("--log-tcp-sequence ");
+       if (loginfo->logflags & IPT_LOG_TCPOPT)
+               printf("--log-tcp-options ");
+       if (loginfo->logflags & IPT_LOG_IPOPT)
+               printf("--log-ip-options ");
+       if (loginfo->logflags & IPT_LOG_UID)
+               printf("--log-uid ");
+}
+
+static
+struct iptables_target log
+= {
+    .name          = "LOG",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_log_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_log_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_target(&log);
+}
diff --git a/extensions/libipt_LOG.man b/extensions/libipt_LOG.man
new file mode 100644 (file)
index 0000000..597ba3f
--- /dev/null
@@ -0,0 +1,31 @@
+Turn on kernel logging of matching packets.  When this option is set
+for a rule, the Linux kernel will print some information on all
+matching packets (like most IP header fields) via the kernel log
+(where it can be read with
+.I dmesg
+or 
+.IR syslogd (8)).
+This is a "non-terminating target", i.e. rule traversal continues at
+the next rule.  So if you want to LOG the packets you refuse, use two
+separate rules with the same matching criteria, first using target LOG
+then DROP (or REJECT).
+.TP
+.BI "--log-level " "level"
+Level of logging (numeric or see \fIsyslog.conf\fP(5)).
+.TP
+.BI "--log-prefix " "prefix"
+Prefix log messages with the specified prefix; up to 29 letters long,
+and useful for distinguishing messages in the logs.
+.TP
+.B --log-tcp-sequence
+Log TCP sequence numbers. This is a security risk if the log is
+readable by users.
+.TP
+.B --log-tcp-options
+Log options from the TCP packet header.
+.TP
+.B --log-ip-options
+Log options from the IP packet header.
+.TP
+.B --log-uid
+Log the userid of the process which generated the packet.
diff --git a/extensions/libipt_MARK.c b/extensions/libipt_MARK.c
new file mode 100644 (file)
index 0000000..56b71b0
--- /dev/null
@@ -0,0 +1,254 @@
+/* Shared library add-on to iptables to add MARK target support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv4/ipt_MARK.h"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"MARK target v%s options:\n"
+"  --set-mark value                   Set nfmark value\n"
+"  --and-mark value                   Binary AND the nfmark with value\n"
+"  --or-mark  value                   Binary OR  the nfmark with value\n"
+"  --copy-xid                         Set nfmark to be the connection xid (PlanetLab specific)\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "set-mark", 1, 0, '1' },
+       { "and-mark", 1, 0, '2' },
+       { "or-mark", 1, 0, '3' },
+       { "copy-xid", 1, 0, '4' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse_v0(int c, char **argv, int invert, unsigned int *flags,
+        const struct ipt_entry *entry,
+        struct ipt_entry_target **target)
+{
+       struct ipt_mark_target_info *markinfo
+               = (struct ipt_mark_target_info *)(*target)->data;
+
+       switch (c) {
+       case '1':
+#ifdef KERNEL_64_USERSPACE_32
+               if (string_to_number_ll(optarg, 0, 0, 
+                                    &markinfo->mark))
+#else
+               if (string_to_number_l(optarg, 0, 0, 
+                                    &markinfo->mark))
+#endif
+                       exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "MARK target: Can't specify --set-mark twice");
+               *flags = 1;
+               break;
+       case '2':
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK target: kernel too old for --and-mark");
+       case '3':
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK target: kernel too old for --or-mark");
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK target: Parameter --set/and/or-mark"
+                          " is required");
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse_v1(int c, char **argv, int invert, unsigned int *flags,
+        const struct ipt_entry *entry,
+        struct ipt_entry_target **target)
+{
+       struct ipt_mark_target_info_v1 *markinfo
+               = (struct ipt_mark_target_info_v1 *)(*target)->data;
+
+       switch (c) {
+       case '1':
+               markinfo->mode = IPT_MARK_SET;
+               break;
+       case '2':
+               markinfo->mode = IPT_MARK_AND;
+               break;
+       case '3':
+               markinfo->mode = IPT_MARK_OR;
+               break;
+       case '4':
+               markinfo->mode = IPT_MARK_COPYXID;
+               break;
+       default:
+               return 0;
+       }
+
+#ifdef KERNEL_64_USERSPACE_32
+       if (string_to_number_ll(optarg, 0, 0,  &markinfo->mark))
+#else
+       if (string_to_number_l(optarg, 0, 0, &markinfo->mark))
+#endif
+               exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
+
+       if (*flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK target: Can't specify --set-mark twice");
+
+       *flags = 1;
+       return 1;
+}
+
+#ifdef KERNEL_64_USERSPACE_32
+static void
+print_mark(unsigned long long mark)
+{
+       printf("0x%llx ", mark);
+}
+#else
+static void
+print_mark(unsigned long mark)
+{
+       printf("0x%lx ", mark);
+}
+#endif
+
+/* Prints out the targinfo. */
+static void
+print_v0(const struct ipt_ip *ip,
+        const struct ipt_entry_target *target,
+        int numeric)
+{
+       const struct ipt_mark_target_info *markinfo =
+               (const struct ipt_mark_target_info *)target->data;
+       printf("MARK set ");
+       print_mark(markinfo->mark);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save_v0(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_mark_target_info *markinfo =
+               (const struct ipt_mark_target_info *)target->data;
+
+       printf("--set-mark ");
+       print_mark(markinfo->mark);
+}
+
+/* Prints out the targinfo. */
+static void
+print_v1(const struct ipt_ip *ip,
+        const struct ipt_entry_target *target,
+        int numeric)
+{
+       const struct ipt_mark_target_info_v1 *markinfo =
+               (const struct ipt_mark_target_info_v1 *)target->data;
+
+       switch (markinfo->mode) {
+       case IPT_MARK_SET:
+               printf("MARK set ");
+               break;
+       case IPT_MARK_AND:
+               printf("MARK and ");
+               break;
+       case IPT_MARK_OR: 
+               printf("MARK or ");
+               break;
+       case IPT_MARK_COPYXID: 
+               printf("MARK copyxid ");
+               break;
+       }
+       print_mark(markinfo->mark);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save_v1(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_mark_target_info_v1 *markinfo =
+               (const struct ipt_mark_target_info_v1 *)target->data;
+
+       switch (markinfo->mode) {
+       case IPT_MARK_SET:
+               printf("--set-mark ");
+               break;
+       case IPT_MARK_AND:
+               printf("--and-mark ");
+               break;
+       case IPT_MARK_OR: 
+               printf("--or-mark ");
+               break;
+       case IPT_MARK_COPYXID: 
+               printf("--copy-xid ");
+               break;
+       }
+       print_mark(markinfo->mark);
+}
+
+static
+struct iptables_target mark_v0 = {
+       .next           = NULL,
+       .name           = "MARK",
+       .version        = IPTABLES_VERSION,
+       .revision       = 0,
+       .size           = IPT_ALIGN(sizeof(struct ipt_mark_target_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_mark_target_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse_v0,
+       .final_check    = &final_check,
+       .print          = &print_v0,
+       .save           = &save_v0,
+       .extra_opts     = opts
+};
+
+static
+struct iptables_target mark_v1 = {
+       .next           = NULL,
+       .name           = "MARK",
+       .version        = IPTABLES_VERSION,
+       .revision       = 1,
+       .size           = IPT_ALIGN(sizeof(struct ipt_mark_target_info_v1)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_mark_target_info_v1)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse_v1,
+       .final_check    = &final_check,
+       .print          = &print_v1,
+       .save           = &save_v1,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&mark_v0);
+       register_target(&mark_v1);
+}
diff --git a/extensions/libipt_MARK.man b/extensions/libipt_MARK.man
new file mode 100644 (file)
index 0000000..7ddf23e
--- /dev/null
@@ -0,0 +1,13 @@
+This is used to set the netfilter mark value associated with the
+packet.  It is only valid in the
+.B mangle
+table.  It can for example be used in conjunction with iproute2.
+.TP
+.BI "--set-mark " "value"
+Set nfmark value
+.TP
+.BI "--and-mark " "value"
+Binary AND the nfmark with value
+.TP
+.BI "--or-mark " "value"
+Binary OR  the nfmark with value
diff --git a/extensions/libipt_MASQUERADE.c b/extensions/libipt_MASQUERADE.c
new file mode 100644 (file)
index 0000000..c24bb32
--- /dev/null
@@ -0,0 +1,180 @@
+/* Shared library add-on to iptables to add masquerade support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter/nf_nat.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"MASQUERADE v%s options:\n"
+" --to-ports <port>[-<port>]\n"
+"                              Port (range) to map to.\n"
+" --random\n"
+"                              Randomize source port.\n"
+"\n"
+,
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "to-ports", 1, 0, '1' },
+       { "random", 0, 0, '2' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+       struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *)t->data;
+
+       /* Actually, it's 0, but it's ignored at the moment. */
+       mr->rangesize = 1;
+
+}
+
+/* Parses ports */
+static void
+parse_ports(const char *arg, struct ip_nat_multi_range *mr)
+{
+       const char *dash;
+       int port;
+
+       mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+
+       port = atoi(arg);
+       if (port <= 0 || port > 65535)
+               exit_error(PARAMETER_PROBLEM, "Port `%s' not valid\n", arg);
+
+       dash = strchr(arg, '-');
+       if (!dash) {
+               mr->range[0].min.tcp.port
+                       = mr->range[0].max.tcp.port
+                       = htons(port);
+       } else {
+               int maxport;
+
+               maxport = atoi(dash + 1);
+               if (maxport == 0 || maxport > 65535)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Port `%s' not valid\n", dash+1);
+               if (maxport < port)
+                       /* People are stupid.  Present reader excepted. */
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Port range `%s' funky\n", arg);
+               mr->range[0].min.tcp.port = htons(port);
+               mr->range[0].max.tcp.port = htons(maxport);
+       }
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       int portok;
+       struct ip_nat_multi_range *mr
+               = (struct ip_nat_multi_range *)(*target)->data;
+
+       if (entry->ip.proto == IPPROTO_TCP
+           || entry->ip.proto == IPPROTO_UDP
+           || entry->ip.proto == IPPROTO_ICMP)
+               portok = 1;
+       else
+               portok = 0;
+
+       switch (c) {
+       case '1':
+               if (!portok)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need TCP or UDP with port specification");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --to-ports");
+
+               parse_ports(optarg, mr);
+               return 1;
+
+       case '2':
+               mr->range[0].flags |=  IP_NAT_RANGE_PROTO_RANDOM;
+               return 1;
+
+       default:
+               return 0;
+       }
+}
+
+/* Final check; don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       struct ip_nat_multi_range *mr
+               = (struct ip_nat_multi_range *)target->data;
+       struct ip_nat_range *r = &mr->range[0];
+
+       if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+               printf("masq ports: ");
+               printf("%hu", ntohs(r->min.tcp.port));
+               if (r->max.tcp.port != r->min.tcp.port)
+                       printf("-%hu", ntohs(r->max.tcp.port));
+               printf(" ");
+       }
+
+       if (r->flags & IP_NAT_RANGE_PROTO_RANDOM)
+               printf("random ");
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       struct ip_nat_multi_range *mr
+               = (struct ip_nat_multi_range *)target->data;
+       struct ip_nat_range *r = &mr->range[0];
+
+       if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+               printf("--to-ports %hu", ntohs(r->min.tcp.port));
+               if (r->max.tcp.port != r->min.tcp.port)
+                       printf("-%hu", ntohs(r->max.tcp.port));
+               printf(" ");
+       }
+
+       if (r->flags & IP_NAT_RANGE_PROTO_RANDOM)
+               printf("--random ");
+}
+
+static struct iptables_target masq = { NULL,
+       .name           = "MASQUERADE",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&masq);
+}
diff --git a/extensions/libipt_MASQUERADE.man b/extensions/libipt_MASQUERADE.man
new file mode 100644 (file)
index 0000000..ea3c8de
--- /dev/null
@@ -0,0 +1,30 @@
+This target is only valid in the
+.B nat
+table, in the
+.B POSTROUTING
+chain.  It should only be used with dynamically assigned IP (dialup)
+connections: if you have a static IP address, you should use the SNAT
+target.  Masquerading is equivalent to specifying a mapping to the IP
+address of the interface the packet is going out, but also has the
+effect that connections are
+.I forgotten
+when the interface goes down.  This is the correct behavior when the
+next dialup is unlikely to have the same interface address (and hence
+any established connections are lost anyway).  It takes one option:
+.TP
+.BR "--to-ports " "\fIport\fP[-\fIport\fP]"
+This specifies a range of source ports to use, overriding the default
+.B SNAT
+source port-selection heuristics (see above).  This is only valid
+if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" .
+.TP
+.BR "--random"
+Randomize source port mapping
+If option
+.B "--random"
+is used then port mapping will be randomized (kernel >= 2.6.21).
+.RS
+.PP
diff --git a/extensions/libipt_MIRROR.c b/extensions/libipt_MIRROR.c
new file mode 100644 (file)
index 0000000..7e61703
--- /dev/null
@@ -0,0 +1,62 @@
+/* Shared library add-on to iptables to add MIRROR target support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"MIRROR target v%s takes no options\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       return 0;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+static struct iptables_target mirror = {
+       .next           = NULL,
+       .name           = "MIRROR",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(0),
+       .userspacesize  = IPT_ALIGN(0),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = NULL,
+       .save           = NULL,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&mirror);
+}
diff --git a/extensions/libipt_MIRROR.man b/extensions/libipt_MIRROR.man
new file mode 100644 (file)
index 0000000..7b720bc
--- /dev/null
@@ -0,0 +1,12 @@
+This is an experimental demonstration target which inverts the source
+and destination fields in the IP header and retransmits the packet.
+It is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains, and user-defined chains which are only called from those
+chains.  Note that the outgoing packets are
+.B NOT
+seen by any packet filtering chains, connection tracking or NAT, to
+avoid loops and other problems.
diff --git a/extensions/libipt_NETMAP.c b/extensions/libipt_NETMAP.c
new file mode 100644 (file)
index 0000000..4b4b14d
--- /dev/null
@@ -0,0 +1,199 @@
+/* Shared library add-on to iptables to add static NAT support.
+   Author: Svenning Soerensen <svenning@post5.tele.dk>
+*/
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter/nf_nat.h>
+
+#define MODULENAME "NETMAP"
+
+static struct option opts[] = {
+       { "to", 1, 0, '1' },
+       { 0 }
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(MODULENAME" v%s options:\n"
+              "  --%s address[/mask]\n"
+              "                                Network address to map to.\n\n",
+              IPTABLES_VERSION, opts[0].name);
+}
+
+static u_int32_t
+bits2netmask(int bits)
+{
+       u_int32_t netmask, bm;
+
+       if (bits >= 32 || bits < 0)
+               return(~0);
+       for (netmask = 0, bm = 0x80000000; bits; bits--, bm >>= 1)
+               netmask |= bm;
+       return htonl(netmask);
+}
+
+static int
+netmask2bits(u_int32_t netmask)
+{
+       u_int32_t bm;
+       int bits;
+
+       netmask = ntohl(netmask);
+       for (bits = 0, bm = 0x80000000; netmask & bm; netmask <<= 1)
+               bits++;
+       if (netmask)
+               return -1; /* holes in netmask */
+       return bits;
+}
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+       struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *)t->data;
+
+       /* Actually, it's 0, but it's ignored at the moment. */
+       mr->rangesize = 1;
+
+}
+
+/* Parses network address */
+static void
+parse_to(char *arg, struct ip_nat_range *range)
+{
+       char *slash;
+       struct in_addr *ip;
+       u_int32_t netmask;
+       unsigned int bits;
+
+       range->flags |= IP_NAT_RANGE_MAP_IPS;
+       slash = strchr(arg, '/');
+       if (slash)
+               *slash = '\0';
+
+       ip = dotted_to_addr(arg);
+       if (!ip)
+               exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
+                          arg);
+       range->min_ip = ip->s_addr;
+       if (slash) {
+               if (strchr(slash+1, '.')) {
+                       ip = dotted_to_mask(slash+1);
+                       if (!ip)
+                               exit_error(PARAMETER_PROBLEM, "Bad netmask `%s'\n",
+                                          slash+1);
+                       netmask = ip->s_addr;
+               }
+               else {
+                       if (string_to_number(slash+1, 0, 32, &bits) == -1)
+                               exit_error(PARAMETER_PROBLEM, "Bad netmask `%s'\n",
+                                          slash+1);
+                       netmask = bits2netmask(bits);
+               }
+               /* Don't allow /0 (/1 is probably insane, too) */
+               if (netmask == 0)
+                       exit_error(PARAMETER_PROBLEM, "Netmask needed\n");
+       }
+       else
+               netmask = ~0;
+
+       if (range->min_ip & ~netmask) {
+               if (slash)
+                       *slash = '/';
+               exit_error(PARAMETER_PROBLEM, "Bad network address `%s'\n",
+                          arg);
+       }
+       range->max_ip = range->min_ip | ~netmask;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ip_nat_multi_range *mr
+               = (struct ip_nat_multi_range *)(*target)->data;
+
+       switch (c) {
+       case '1':
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --%s", opts[0].name);
+
+               parse_to(optarg, &mr->range[0]);
+               *flags = 1;
+               return 1;
+
+       default:
+               return 0;
+       }
+}
+
+/* Final check; need --to */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          MODULENAME" needs --%s", opts[0].name);
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       struct ip_nat_multi_range *mr
+               = (struct ip_nat_multi_range *)target->data;
+       struct ip_nat_range *r = &mr->range[0];
+       struct in_addr a;
+       int bits;
+
+       a.s_addr = r->min_ip;
+       printf("%s", addr_to_dotted(&a));
+       a.s_addr = ~(r->min_ip ^ r->max_ip);
+       bits = netmask2bits(a.s_addr);
+       if (bits < 0)
+               printf("/%s", addr_to_dotted(&a));
+       else
+               printf("/%d", bits);
+}
+
+/* Saves the targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       printf("--%s ", opts[0].name);
+       print(ip, target, 0);
+}
+
+static struct iptables_target target_module = {
+       .next           = NULL,
+       .name           = MODULENAME,
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&target_module);
+}
+
diff --git a/extensions/libipt_NETMAP.man b/extensions/libipt_NETMAP.man
new file mode 100644 (file)
index 0000000..d49a025
--- /dev/null
@@ -0,0 +1,9 @@
+This target allows you to statically map a whole network of addresses onto
+another network of addresses.  It can only be used from rules in the
+.B nat
+table.
+.TP
+.BI "--to "  "address[/mask]"
+Network address to map to.  The resulting address will be constructed in the
+following way: All 'one' bits in the mask are filled in from the new `address'.
+All bits that are zero in the mask are filled in from the original address.
diff --git a/extensions/libipt_NFLOG.c b/extensions/libipt_NFLOG.c
new file mode 100644 (file)
index 0000000..ae5c62a
--- /dev/null
@@ -0,0 +1,161 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <iptables.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter/xt_NFLOG.h>
+
+enum {
+       NFLOG_GROUP     = 0x1,
+       NFLOG_PREFIX    = 0x2,
+       NFLOG_RANGE     = 0x4,
+       NFLOG_THRESHOLD = 0x8,
+};
+
+static struct option opts[] = {
+       { "nflog-group",     1, 0, NFLOG_GROUP },
+       { "nflog-prefix",    1, 0, NFLOG_PREFIX },
+       { "nflog-range",     1, 0, NFLOG_RANGE },
+       { "nflog-threshold", 1, 0, NFLOG_THRESHOLD },
+};
+
+static void help(void)
+{
+       printf("NFLOG v%s options:\n"
+              " --nflog-group NUM              NETLINK group used for logging\n"
+              " --nflog-range NUM              Number of byte to copy\n"
+              " --nflog-threshold NUM          Message threshold of in-kernel queue\n"
+              " --nflog-prefix STRING          Prefix string for log messages\n\n",
+              IPTABLES_VERSION);
+}
+
+static void init(struct xt_entry_target *t, unsigned int *nfcache)
+{
+       struct xt_nflog_info *info = (struct xt_nflog_info *)t->data;
+
+       info->group     = 0;
+       info->threshold = XT_NFLOG_DEFAULT_THRESHOLD;
+}
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+                const struct ipt_entry *entry,
+                struct xt_entry_target **target)
+{
+       struct xt_nflog_info *info = (struct xt_nflog_info *)(*target)->data;
+       int n;
+
+       switch (c) {
+       case NFLOG_GROUP:
+               if (*flags & NFLOG_GROUP)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --nflog-group twice");
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --nflog-group");
+
+               n = atoi(optarg);
+               if (n < 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--nflog-group can not be negative");
+               info->group = n;
+               break;
+       case NFLOG_PREFIX:
+               if (*flags & NFLOG_PREFIX)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --nflog-prefix twice");
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --nflog-prefix");
+
+               n = strlen(optarg);
+               if (n == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "No prefix specified for --nflog-prefix");
+               if (n >= sizeof(info->prefix))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--nflog-prefix too long, max %Zu characters",
+                                  sizeof(info->prefix) - 1);
+               if (n != strlen(strtok(optarg, "\n")))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Newlines are not allowed in --nflog-prefix");
+               strcpy(info->prefix, optarg);
+               break;
+       case NFLOG_RANGE:
+               if (*flags & NFLOG_RANGE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --nflog-range twice");
+               n = atoi(optarg);
+               if (n < 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid --nflog-range, must be >= 0");
+               info->len = n;
+               break;
+       case NFLOG_THRESHOLD:
+               if (*flags & NFLOG_THRESHOLD)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --nflog-threshold twice");
+               n = atoi(optarg);
+               if (n < 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid --nflog-threshold, must be >= 1");
+               info->threshold = n;
+               break;
+       default:
+               return 0;
+       }
+       *flags |= c;
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       return;
+}
+
+static void nflog_print(const struct xt_nflog_info *info, char *prefix)
+{
+       if (info->prefix[0] != '\0')
+               printf("%snflog-prefix \"%s\" ", prefix, info->prefix);
+       if (info->group)
+               printf("%snflog-group %u ", prefix, info->group);
+       if (info->len)
+               printf("%snflog-range %u ", prefix, info->len);
+       if (info->threshold != XT_NFLOG_DEFAULT_THRESHOLD)
+               printf("%snflog-threshold %u ", prefix, info->threshold);
+}
+
+static void print(const struct ipt_ip *ip, const struct xt_entry_target *target,
+                 int numeric)
+{
+       const struct xt_nflog_info *info = (struct xt_nflog_info *)target->data;
+
+       nflog_print(info, "");
+}
+
+static void save(const struct ipt_ip *ip, const struct xt_entry_target *target)
+{
+       const struct xt_nflog_info *info = (struct xt_nflog_info *)target->data;
+
+       nflog_print(info, "--");
+}
+
+static struct iptables_target nflog = {
+       .name           = "NFLOG",
+       .version        = IPTABLES_VERSION,
+       .size           = XT_ALIGN(sizeof(struct xt_nflog_info)),
+       .userspacesize  = XT_ALIGN(sizeof(struct xt_nflog_info)),
+       .help           = help,
+       .init           = init,
+       .parse          = parse,
+       .final_check    = final_check,
+       .print          = print,
+       .save           = save,
+       .extra_opts     = opts,
+};
+
+void _init(void)
+{
+       register_target(&nflog);
+}
diff --git a/extensions/libipt_NFQUEUE.c b/extensions/libipt_NFQUEUE.c
new file mode 100644 (file)
index 0000000..bc4e82f
--- /dev/null
@@ -0,0 +1,114 @@
+/* Shared library add-on to iptables for NFQ
+ *
+ * (C) 2005 by Harald Welte <laforge@netfilter.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_NFQUEUE.h>
+
+static void init(struct ipt_entry_target *t, unsigned int *nfcache) 
+{
+}
+
+static void help(void) 
+{
+       printf(
+"NFQUEUE target options\n"
+"  --queue-num value           Send packet to QUEUE number <value>.\n"
+"                              Valid queue numbers are 0-65535\n"
+);
+}
+
+static struct option opts[] = {
+       { "queue-num", 1, 0, 'F' },
+       { 0 }
+};
+
+static void
+parse_num(const char *s, struct ipt_NFQ_info *tinfo)
+{
+       unsigned int num;
+       
+       if (string_to_number(s, 0, 65535, &num) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid queue number `%s'\n", s);
+
+       tinfo->queuenum = num & 0xffff;
+       return;
+}
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_NFQ_info *tinfo
+               = (struct ipt_NFQ_info *)(*target)->data;
+
+       switch (c) {
+       case 'F':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM, "NFQUEUE target: "
+                                  "Only use --queue-num ONCE!");
+               parse_num(optarg, tinfo);
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_NFQ_info *tinfo =
+               (const struct ipt_NFQ_info *)target->data;
+       printf("NFQUEUE num %u", tinfo->queuenum);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_NFQ_info *tinfo =
+               (const struct ipt_NFQ_info *)target->data;
+
+       printf("--queue-num %u ", tinfo->queuenum);
+}
+
+static struct iptables_target nfqueue = { 
+       .next           = NULL,
+       .name           = "NFQUEUE",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_NFQ_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_NFQ_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&nfqueue);
+}
diff --git a/extensions/libipt_NFQUEUE.man b/extensions/libipt_NFQUEUE.man
new file mode 100644 (file)
index 0000000..c4e9d11
--- /dev/null
@@ -0,0 +1,12 @@
+This target is an extension of the QUEUE target. As opposed to QUEUE, it allows
+you to put a packet into any specific queue, identified by its 16-bit queue
+number.  
+.TP
+.BR "--queue-num " "\fIvalue"
+This specifies the QUEUE number to use. Valud queue numbers are 0 to 65535. The default value is 0.
+.TP
+It can only be used with Kernel versions 2.6.14 or later, since it requires
+the
+.B
+nfnetlink_queue
+kernel support.
diff --git a/extensions/libipt_NOTRACK.c b/extensions/libipt_NOTRACK.c
new file mode 100644 (file)
index 0000000..39489ae
--- /dev/null
@@ -0,0 +1,63 @@
+/* Shared library add-on to iptables to add NOTRACK target support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"NOTRACK target v%s takes no options\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       return 0;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+static
+struct iptables_target notrack 
+= {    .next = NULL,
+       .name = "NOTRACK",
+       .version = IPTABLES_VERSION,
+       .size = IPT_ALIGN(0),
+       .userspacesize = IPT_ALIGN(0),
+       .help = &help,
+       .init = &init,
+       .parse = &parse,
+       .final_check = &final_check,
+       .print = NULL, /* print */
+       .save = NULL, /* save */
+       .extra_opts = opts
+};
+
+void _init(void)
+{
+       register_target(&notrack);
+}
diff --git a/extensions/libipt_NOTRACK.man b/extensions/libipt_NOTRACK.man
new file mode 100644 (file)
index 0000000..30e830a
--- /dev/null
@@ -0,0 +1,5 @@
+This target disables connection tracking for all packets matching that rule.
+.TP
+It can only be used in the
+.B raw
+table.
diff --git a/extensions/libipt_REDIRECT.c b/extensions/libipt_REDIRECT.c
new file mode 100644 (file)
index 0000000..6b38716
--- /dev/null
@@ -0,0 +1,189 @@
+/* Shared library add-on to iptables to add redirect support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter/nf_nat.h>
+
+#define IPT_REDIRECT_OPT_DEST  0x01
+#define IPT_REDIRECT_OPT_RANDOM        0x02
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"REDIRECT v%s options:\n"
+" --to-ports <port>[-<port>]\n"
+"                              Port (range) to map to.\n\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "to-ports", 1, 0, '1' },
+       { "random", 1, 0, '2' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+       struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *)t->data;
+
+       /* Actually, it's 0, but it's ignored at the moment. */
+       mr->rangesize = 1;
+
+}
+
+/* Parses ports */
+static void
+parse_ports(const char *arg, struct ip_nat_multi_range *mr)
+{
+       const char *dash;
+       int port;
+
+       mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+
+       if (strchr(arg, '.'))
+               exit_error(PARAMETER_PROBLEM, "IP address not permitted\n");
+
+       port = atoi(arg);
+       if (port == 0 || port > 65535)
+               exit_error(PARAMETER_PROBLEM, "Port `%s' not valid\n", arg);
+
+       dash = strchr(arg, '-');
+       if (!dash) {
+               mr->range[0].min.tcp.port
+                       = mr->range[0].max.tcp.port
+                       = htons(port);
+       } else {
+               int maxport;
+
+               maxport = atoi(dash + 1);
+               if (maxport == 0 || maxport > 65535)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Port `%s' not valid\n", dash+1);
+               if (maxport < port)
+                       /* People are stupid. */
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Port range `%s' funky\n", arg);
+               mr->range[0].min.tcp.port = htons(port);
+               mr->range[0].max.tcp.port = htons(maxport);
+       }
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ip_nat_multi_range *mr
+               = (struct ip_nat_multi_range *)(*target)->data;
+       int portok;
+
+       if (entry->ip.proto == IPPROTO_TCP
+           || entry->ip.proto == IPPROTO_UDP
+           || entry->ip.proto == IPPROTO_ICMP)
+               portok = 1;
+       else
+               portok = 0;
+
+       switch (c) {
+       case '1':
+               if (!portok)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need TCP or UDP with port specification");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --to-ports");
+
+               parse_ports(optarg, mr);
+               if (*flags & IPT_REDIRECT_OPT_RANDOM)
+                       mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
+               *flags |= IPT_REDIRECT_OPT_DEST;
+               return 1;
+
+       case '2':
+               if (*flags & IPT_REDIRECT_OPT_DEST) {
+                       mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
+                       *flags |= IPT_REDIRECT_OPT_RANDOM;
+               } else
+                       *flags |= IPT_REDIRECT_OPT_RANDOM;
+               return 1;
+
+       default:
+               return 0;
+       }
+}
+
+/* Final check; don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       struct ip_nat_multi_range *mr
+               = (struct ip_nat_multi_range *)target->data;
+       struct ip_nat_range *r = &mr->range[0];
+
+       if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+               printf("redir ports ");
+               printf("%hu", ntohs(r->min.tcp.port));
+               if (r->max.tcp.port != r->min.tcp.port)
+                       printf("-%hu", ntohs(r->max.tcp.port));
+               printf(" ");
+               if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM)
+                       printf("random ");
+       }
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       struct ip_nat_multi_range *mr
+               = (struct ip_nat_multi_range *)target->data;
+       struct ip_nat_range *r = &mr->range[0];
+
+       if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+               printf("--to-ports ");
+               printf("%hu", ntohs(r->min.tcp.port));
+               if (r->max.tcp.port != r->min.tcp.port)
+                       printf("-%hu", ntohs(r->max.tcp.port));
+               printf(" ");
+               if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM)
+                       printf("--random ");
+       }
+}
+
+static struct iptables_target redir = { 
+       .next           = NULL,
+       .name           = "REDIRECT",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&redir);
+}
diff --git a/extensions/libipt_REDIRECT.man b/extensions/libipt_REDIRECT.man
new file mode 100644 (file)
index 0000000..93e2982
--- /dev/null
@@ -0,0 +1,26 @@
+This target is only valid in the
+.B nat
+table, in the
+.B PREROUTING
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains.  It redirects the packet to the machine itself by changing the
+destination IP to the primary address of the incoming interface
+(locally-generated packets are mapped to the 127.0.0.1 address).  It
+takes one option:
+.TP
+.BR "--to-ports " "\fIport\fP[-\fIport\fP]"
+This specifies a destination port or range of ports to use: without
+this, the destination port is never altered.  This is only valid
+if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" .
+.TP
+.BR "--random"
+If option
+.B "--random"
+is used then port mapping will be randomized (kernel >= 2.6.22).
+.RS
+.PP
diff --git a/extensions/libipt_REJECT.c b/extensions/libipt_REJECT.c
new file mode 100644 (file)
index 0000000..70859eb
--- /dev/null
@@ -0,0 +1,189 @@
+/* Shared library add-on to iptables to add customized REJECT support.
+ *
+ * (C) 2000 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_REJECT.h>
+#include <linux/version.h>
+
+/* If we are compiling against a kernel that does not support
+ * IPT_ICMP_ADMIN_PROHIBITED, we are emulating it.
+ * The result will be a plain DROP of the packet instead of
+ * reject. -- Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ */
+#ifndef IPT_ICMP_ADMIN_PROHIBITED
+#define IPT_ICMP_ADMIN_PROHIBITED      IPT_TCP_RESET + 1
+#endif
+
+struct reject_names {
+       const char *name;
+       const char *alias;
+       enum ipt_reject_with with;
+       const char *desc;
+};
+
+static const struct reject_names reject_table[] = {
+       {"icmp-net-unreachable", "net-unreach",
+               IPT_ICMP_NET_UNREACHABLE, "ICMP network unreachable"},
+       {"icmp-host-unreachable", "host-unreach",
+               IPT_ICMP_HOST_UNREACHABLE, "ICMP host unreachable"},
+       {"icmp-proto-unreachable", "proto-unreach",
+               IPT_ICMP_PROT_UNREACHABLE, "ICMP protocol unreachable"},
+       {"icmp-port-unreachable", "port-unreach",
+               IPT_ICMP_PORT_UNREACHABLE, "ICMP port unreachable (default)"},
+#if 0
+       {"echo-reply", "echoreply",
+        IPT_ICMP_ECHOREPLY, "for ICMP echo only: faked ICMP echo reply"},
+#endif
+       {"icmp-net-prohibited", "net-prohib",
+        IPT_ICMP_NET_PROHIBITED, "ICMP network prohibited"},
+       {"icmp-host-prohibited", "host-prohib",
+        IPT_ICMP_HOST_PROHIBITED, "ICMP host prohibited"},
+       {"tcp-reset", "tcp-rst",
+        IPT_TCP_RESET, "TCP RST packet"},
+       {"icmp-admin-prohibited", "admin-prohib",
+        IPT_ICMP_ADMIN_PROHIBITED, "ICMP administratively prohibited (*)"}
+};
+
+static void
+print_reject_types()
+{
+       unsigned int i;
+
+       printf("Valid reject types:\n");
+
+       for (i = 0; i < sizeof(reject_table)/sizeof(struct reject_names); i++) {
+               printf("    %-25s\t%s\n", reject_table[i].name, reject_table[i].desc);
+               printf("    %-25s\talias\n", reject_table[i].alias);
+       }
+       printf("\n");
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"REJECT options:\n"
+"--reject-with type              drop input packet and send back\n"
+"                                a reply packet according to type:\n");
+
+       print_reject_types();
+
+       printf("(*) See man page or read the INCOMPATIBILITES file for compatibility issues.\n");
+}
+
+static struct option opts[] = {
+       { "reject-with", 1, 0, '1' },
+       { 0 }
+};
+
+/* Allocate and initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+       struct ipt_reject_info *reject = (struct ipt_reject_info *)t->data;
+
+       /* default */
+       reject->with = IPT_ICMP_PORT_UNREACHABLE;
+
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_reject_info *reject = (struct ipt_reject_info *)(*target)->data;
+       unsigned int limit = sizeof(reject_table)/sizeof(struct reject_names);
+       unsigned int i;
+
+       switch(c) {
+       case '1':
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --reject-with");
+               for (i = 0; i < limit; i++) {
+                       if ((strncasecmp(reject_table[i].name, optarg, strlen(optarg)) == 0)
+                           || (strncasecmp(reject_table[i].alias, optarg, strlen(optarg)) == 0)) {
+                               reject->with = reject_table[i].with;
+                               return 1;
+                       }
+               }
+               /* This due to be dropped late in 2.4 pre-release cycle --RR */
+               if (strncasecmp("echo-reply", optarg, strlen(optarg)) == 0
+                   || strncasecmp("echoreply", optarg, strlen(optarg)) == 0)
+                       fprintf(stderr, "--reject-with echo-reply no longer"
+                               " supported\n");
+               exit_error(PARAMETER_PROBLEM, "unknown reject type `%s'",optarg);
+       default:
+               /* Fall through */
+               break;
+       }
+       return 0;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out ipt_reject_info. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_reject_info *reject
+               = (const struct ipt_reject_info *)target->data;
+       unsigned int i;
+
+       for (i = 0; i < sizeof(reject_table)/sizeof(struct reject_names); i++) {
+               if (reject_table[i].with == reject->with)
+                       break;
+       }
+       printf("reject-with %s ", reject_table[i].name);
+}
+
+/* Saves ipt_reject in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_reject_info *reject
+               = (const struct ipt_reject_info *)target->data;
+       unsigned int i;
+
+       for (i = 0; i < sizeof(reject_table)/sizeof(struct reject_names); i++)
+               if (reject_table[i].with == reject->with)
+                       break;
+
+       printf("--reject-with %s ", reject_table[i].name);
+}
+
+static struct iptables_target reject = { 
+       .next           = NULL,
+       .name           = "REJECT",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_reject_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_reject_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&reject);
+}
diff --git a/extensions/libipt_REJECT.man b/extensions/libipt_REJECT.man
new file mode 100644 (file)
index 0000000..174bf7b
--- /dev/null
@@ -0,0 +1,34 @@
+This is used to send back an error packet in response to the matched
+packet: otherwise it is equivalent to
+.B DROP
+so it is a terminating TARGET, ending rule traversal.
+This target is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains.  The following option controls the nature of the error packet
+returned:
+.TP
+.BI "--reject-with " "type"
+The type given can be
+.nf
+.B " icmp-net-unreachable"
+.B " icmp-host-unreachable"
+.B " icmp-port-unreachable"
+.B " icmp-proto-unreachable"
+.B " icmp-net-prohibited"
+.B " icmp-host-prohibited or"
+.B " icmp-admin-prohibited (*)"
+.fi
+which return the appropriate ICMP error message (\fBport-unreachable\fP is
+the default).  The option
+.B tcp-reset
+can be used on rules which only match the TCP protocol: this causes a
+TCP RST packet to be sent back.  This is mainly useful for blocking 
+.I ident
+(113/tcp) probes which frequently occur when sending mail to broken mail
+hosts (which won't accept your mail otherwise).
+.TP
+(*) Using icmp-admin-prohibited with kernels that do not support it will result in a plain DROP instead of REJECT
diff --git a/extensions/libipt_SAME.c b/extensions/libipt_SAME.c
new file mode 100644 (file)
index 0000000..7211f60
--- /dev/null
@@ -0,0 +1,236 @@
+/* Shared library add-on to iptables to add simple non load-balancing SNAT support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter/nf_nat.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv4/ipt_SAME.h"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"SAME v%s options:\n"
+" --to <ipaddr>-<ipaddr>\n"
+"                              Addresses to map source to.\n"
+"                               May be specified more than\n"
+"                                once for multiple ranges.\n"
+" --nodst\n"
+"                              Don't use destination-ip in\n"
+"                                         source selection\n"
+" --random\n"
+"                              Randomize source port\n"
+,
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "to", 1, 0, '1' },
+       { "nodst", 0, 0, '2'},
+       { "random", 0, 0, '3' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+       struct ipt_same_info *mr = (struct ipt_same_info *)t->data;
+
+       /* Set default to 0 */
+       mr->rangesize = 0;
+       mr->info = 0;
+       mr->ipnum = 0;
+       
+}
+
+/* Parses range of IPs */
+static void
+parse_to(char *arg, struct ip_nat_range *range)
+{
+       char *dash;
+       struct in_addr *ip;
+
+       range->flags |= IP_NAT_RANGE_MAP_IPS;
+       dash = strchr(arg, '-');
+
+       if (dash)
+               *dash = '\0';
+
+       ip = dotted_to_addr(arg);
+       if (!ip)
+               exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
+                          arg);
+       range->min_ip = ip->s_addr;
+
+       if (dash) {
+               ip = dotted_to_addr(dash+1);
+               if (!ip)
+                       exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
+                                  dash+1);
+       }
+       range->max_ip = ip->s_addr;
+       if (dash)
+               if (range->min_ip > range->max_ip)
+                       exit_error(PARAMETER_PROBLEM, "Bad IP range `%s-%s'\n", 
+                                  arg, dash+1);
+}
+
+#define IPT_SAME_OPT_TO                        0x01
+#define IPT_SAME_OPT_NODST             0x02
+#define IPT_SAME_OPT_RANDOM            0x04
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_same_info *mr
+               = (struct ipt_same_info *)(*target)->data;
+       int count;
+
+       switch (c) {
+       case '1':
+               if (mr->rangesize == IPT_SAME_MAX_RANGE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Too many ranges specified, maximum "
+                                  "is %i ranges.\n",
+                                  IPT_SAME_MAX_RANGE);
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --to");
+
+               parse_to(optarg, &mr->range[mr->rangesize]);
+               /* WTF do we need this for? */
+               if (*flags & IPT_SAME_OPT_RANDOM)
+                       mr->range[mr->rangesize].flags 
+                               |= IP_NAT_RANGE_PROTO_RANDOM;
+               mr->rangesize++;
+               *flags |= IPT_SAME_OPT_TO;
+               break;
+               
+       case '2':
+               if (*flags & IPT_SAME_OPT_NODST)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --nodst twice");
+               
+               mr->info |= IPT_SAME_NODST;
+               *flags |= IPT_SAME_OPT_NODST;
+               break;
+
+       case '3':       
+               *flags |= IPT_SAME_OPT_RANDOM;
+               for (count=0; count < mr->rangesize; count++)
+                       mr->range[count].flags |= IP_NAT_RANGE_PROTO_RANDOM;
+               break;
+
+       default:
+               return 0;
+       }
+       
+       return 1;
+}
+
+/* Final check; need --to. */
+static void final_check(unsigned int flags)
+{
+       if (!(flags & IPT_SAME_OPT_TO))
+               exit_error(PARAMETER_PROBLEM,
+                          "SAME needs --to");
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       int count;
+       struct ipt_same_info *mr
+               = (struct ipt_same_info *)target->data;
+       int random = 0;
+       
+       printf("same:");
+       
+       for (count = 0; count < mr->rangesize; count++) {
+               struct ip_nat_range *r = &mr->range[count];
+               struct in_addr a;
+
+               a.s_addr = r->min_ip;
+
+               printf("%s", addr_to_dotted(&a));
+               a.s_addr = r->max_ip;
+               
+               if (r->min_ip == r->max_ip)
+                       printf(" ");
+               else
+                       printf("-%s ", addr_to_dotted(&a));
+               if (r->flags & IP_NAT_RANGE_PROTO_RANDOM) 
+                       random = 1;
+       }
+       
+       if (mr->info & IPT_SAME_NODST)
+               printf("nodst ");
+
+       if (random)
+               printf("random ");
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       int count;
+       struct ipt_same_info *mr
+               = (struct ipt_same_info *)target->data;
+       int random = 0;
+
+       for (count = 0; count < mr->rangesize; count++) {
+               struct ip_nat_range *r = &mr->range[count];
+               struct in_addr a;
+
+               a.s_addr = r->min_ip;
+               printf("--to %s", addr_to_dotted(&a));
+               a.s_addr = r->max_ip;
+
+               if (r->min_ip == r->max_ip)
+                       printf(" ");
+               else
+                       printf("-%s ", addr_to_dotted(&a));
+               if (r->flags & IP_NAT_RANGE_PROTO_RANDOM) 
+                       random = 1;
+       }
+       
+       if (mr->info & IPT_SAME_NODST)
+               printf("--nodst ");
+
+       if (random)
+               printf("--random ");
+}
+
+static struct iptables_target same = {
+       .next           = NULL,
+       .name           = "SAME",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_same_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_same_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&same);
+}
diff --git a/extensions/libipt_SAME.man b/extensions/libipt_SAME.man
new file mode 100644 (file)
index 0000000..4e88d48
--- /dev/null
@@ -0,0 +1,15 @@
+Similar to SNAT/DNAT depending on chain: it takes a range of addresses
+(`--to 1.2.3.4-1.2.3.7') and gives a client the same
+source-/destination-address for each connection.
+.TP
+.BI "--to " "<ipaddr>-<ipaddr>"
+Addresses to map source to. May be specified more than once for
+multiple ranges.
+.TP
+.B "--nodst"
+Don't use the destination-ip in the calculations when selecting the
+new source-ip
+.TP
+.B "--random"
+Port mapping will be forcely randomized to avoid attacks based on 
+port prediction (kernel >= 2.6.21).
diff --git a/extensions/libipt_SECMARK.c b/extensions/libipt_SECMARK.c
new file mode 100644 (file)
index 0000000..89a2b2a
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Shared library add-on to iptables to add SECMARK target support.
+ *
+ * Based on the MARK target.
+ *
+ * Copyright (C) 2006 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter/xt_SECMARK.h>
+
+#define PFX "SECMARK target: "
+
+static void help(void)
+{
+       printf(
+"SECMARK target v%s options:\n"
+"  --selctx value                     Set the SELinux security context\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "selctx", 1, 0, '1' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void init(struct ipt_entry_target *t, unsigned int *nfcache)
+{ }
+
+/*
+ * Function which parses command options; returns true if it
+ * ate an option.
+ */
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+                 const struct ipt_entry *entry, struct ipt_entry_target **target)
+{
+       struct xt_secmark_target_info *info =
+               (struct xt_secmark_target_info*)(*target)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & SECMARK_MODE_SEL)
+                       exit_error(PARAMETER_PROBLEM, PFX
+                                  "Can't specify --selctx twice");
+               info->mode = SECMARK_MODE_SEL;
+
+               if (strlen(optarg) > SECMARK_SELCTX_MAX-1)
+                       exit_error(PARAMETER_PROBLEM, PFX
+                                  "Maximum length %u exceeded by --selctx"
+                                  " parameter (%zu)",
+                                  SECMARK_SELCTX_MAX-1, strlen(optarg));
+
+               strcpy(info->u.sel.selctx, optarg);
+               *flags |= SECMARK_MODE_SEL;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, PFX "parameter required");
+}
+
+static void print_secmark(struct xt_secmark_target_info *info)
+{
+       switch (info->mode) {
+       case SECMARK_MODE_SEL:
+               printf("selctx %s ", info->u.sel.selctx);\
+               break;
+       
+       default:
+               exit_error(OTHER_PROBLEM, PFX "invalid mode %hhu\n", info->mode);
+       }
+}
+
+static void print(const struct ipt_ip *ip,
+                 const struct ipt_entry_target *target, int numeric)
+{
+       struct xt_secmark_target_info *info =
+               (struct xt_secmark_target_info*)(target)->data;
+
+       printf("SECMARK ");
+       print_secmark(info);
+}
+
+/* Saves the target info in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       struct xt_secmark_target_info *info =
+               (struct xt_secmark_target_info*)target->data;
+
+       printf("--");
+       print_secmark(info);
+}
+
+static struct iptables_target secmark = {
+       .next           = NULL,
+       .name           = "SECMARK",
+       .version        = IPTABLES_VERSION,
+       .revision       = 0,
+       .size           = IPT_ALIGN(sizeof(struct xt_secmark_target_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct xt_secmark_target_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&secmark);
+}
diff --git a/extensions/libipt_SECMARK.man b/extensions/libipt_SECMARK.man
new file mode 100644 (file)
index 0000000..f892de9
--- /dev/null
@@ -0,0 +1,7 @@
+This is used to set the security mark value associated with the
+packet for use by security subsystems such as SELinux.  It is only
+valid in the
+.B mangle
+table.
+.TP
+.BI "--selctx " "security_context"
diff --git a/extensions/libipt_SET.c b/extensions/libipt_SET.c
new file mode 100644 (file)
index 0000000..1d8ace6
--- /dev/null
@@ -0,0 +1,179 @@
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
+ *                         Patrick Schaaf <bof@bof.de>
+ *                         Martin Josefsson <gandalf@wlug.westbo.se>
+ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.  
+ */
+
+/* Shared library add-on to iptables to add IP set mangling target. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_set.h>
+#include <linux/netfilter_ipv4/ipt_set.h>
+#include "libipt_set.h"
+
+/* Function which prints out usage message. */
+static void help(void)
+{
+       printf("SET v%s options:\n"
+              " --add-set name flags\n"
+              " --del-set name flags\n"
+              "                add/del src/dst IP/port from/to named sets,\n"
+              "                where flags are the comma separated list of\n"
+              "                'src' and 'dst'.\n"
+              "\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       {"add-set",   1, 0, '1'},
+       {"del-set",   1, 0, '2'},
+       {0}
+};
+
+/* Initialize the target. */
+static void init(struct ipt_entry_target *target, unsigned int *nfcache)
+{
+       struct ipt_set_info_target *info =
+           (struct ipt_set_info_target *) target->data;
+
+       memset(info, 0, sizeof(struct ipt_set_info_target));
+       info->add_set.index =
+       info->del_set.index = IP_SET_INVALID_ID;
+
+}
+
+static void
+parse_target(char **argv, int invert, unsigned int *flags,
+             struct ipt_set_info *info, const char *what)
+{
+       if (info->flags[0])
+               exit_error(PARAMETER_PROBLEM,
+                          "--%s can be specified only once", what);
+
+       if (check_inverse(optarg, &invert, NULL, 0))
+               exit_error(PARAMETER_PROBLEM,
+                          "Unexpected `!' after --%s", what);
+
+       if (!argv[optind]
+           || argv[optind][0] == '-' || argv[optind][0] == '!')
+               exit_error(PARAMETER_PROBLEM,
+                          "--%s requires two args.", what);
+
+       if (strlen(argv[optind-1]) > IP_SET_MAXNAMELEN - 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "setname `%s' too long, max %d characters.",
+                          argv[optind-1], IP_SET_MAXNAMELEN - 1);
+
+       get_set_byname(argv[optind - 1], info);
+       parse_bindings(argv[optind], info);
+       optind++;
+       
+       *flags = 1;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry, struct ipt_entry_target **target)
+{
+       struct ipt_set_info_target *myinfo =
+           (struct ipt_set_info_target *) (*target)->data;
+
+       switch (c) {
+       case '1':               /* --add-set <set> <flags> */
+               parse_target(argv, invert, flags,
+                            &myinfo->add_set, "add-set");
+               break;
+       case '2':               /* --del-set <set>[:<flags>] <flags> */
+               parse_target(argv, invert, flags,
+                            &myinfo->del_set, "del-set");
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+/* Final check; must specify at least one. */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "You must specify either `--add-set' or `--del-set'");
+}
+
+static void
+print_target(const char *prefix, const struct ipt_set_info *info)
+{
+       int i;
+       char setname[IP_SET_MAXNAMELEN];
+
+       if (info->index == IP_SET_INVALID_ID)
+               return;
+       get_set_byid(setname, info->index);
+       printf("%s %s", prefix, setname);
+       for (i = 0; i < IP_SET_MAX_BINDINGS; i++) {
+               if (!info->flags[i])
+                       break;          
+               printf("%s%s",
+                      i == 0 ? " " : ",",
+                      info->flags[i] & IPSET_SRC ? "src" : "dst");
+       }
+       printf(" ");
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target, int numeric)
+{
+       struct ipt_set_info_target *info =
+           (struct ipt_set_info_target *) target->data;
+
+       print_target("add-set", &info->add_set);
+       print_target("del-set", &info->del_set);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       struct ipt_set_info_target *info =
+           (struct ipt_set_info_target *) target->data;
+
+       print_target("--add-set", &info->add_set);
+       print_target("--del-set", &info->del_set);
+}
+
+static
+struct iptables_target ipt_set_target 
+= {
+       .name           = "SET",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_set_info_target)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_set_info_target)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&ipt_set_target);
+}
diff --git a/extensions/libipt_SET.man b/extensions/libipt_SET.man
new file mode 100644 (file)
index 0000000..8f25bea
--- /dev/null
@@ -0,0 +1,16 @@
+This modules adds and/or deletes entries from IP sets which can be defined 
+by ipset(8).
+.TP
+.BR "--add-set " "setname flag[,flag...]"
+add the address(es)/port(s) of the packet to the sets
+.TP
+.BR "--del-set " "setname flag[,flag...]"
+delete the address(es)/port(s) of the packet from the sets,
+where flags are
+.BR "src"
+and/or
+.BR "dst"
+and there can be no more than six of them.
+.TP
+The bindings to follow must previously be defined in order to use 
+multilevel adding/deleting by the SET target.
diff --git a/extensions/libipt_SNAT.c b/extensions/libipt_SNAT.c
new file mode 100644 (file)
index 0000000..0a665a0
--- /dev/null
@@ -0,0 +1,269 @@
+/* Shared library add-on to iptables to add source-NAT support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter/nf_nat.h>
+
+#define IPT_SNAT_OPT_SOURCE 0x01
+#define IPT_SNAT_OPT_RANDOM 0x02
+
+/* Source NAT data consists of a multi-range, indicating where to map
+   to. */
+struct ipt_natinfo
+{
+       struct ipt_entry_target t;
+       struct ip_nat_multi_range mr;
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"SNAT v%s options:\n"
+" --to-source <ipaddr>[-<ipaddr>][:port-port]\n"
+"                              Address to map source to.\n"
+"[--random]\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "to-source", 1, 0, '1' },
+       { "random", 0, 0, '2' },
+       { 0 }
+};
+
+static struct ipt_natinfo *
+append_range(struct ipt_natinfo *info, const struct ip_nat_range *range)
+{
+       unsigned int size;
+
+       /* One rangesize already in struct ipt_natinfo */
+       size = IPT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
+
+       info = realloc(info, size);
+       if (!info)
+               exit_error(OTHER_PROBLEM, "Out of memory\n");
+
+       info->t.u.target_size = size;
+       info->mr.range[info->mr.rangesize] = *range;
+       info->mr.rangesize++;
+
+       return info;
+}
+
+/* Ranges expected in network order. */
+static struct ipt_entry_target *
+parse_to(char *arg, int portok, struct ipt_natinfo *info)
+{
+       struct ip_nat_range range;
+       char *colon, *dash, *error;
+       struct in_addr *ip;
+
+       memset(&range, 0, sizeof(range));
+       colon = strchr(arg, ':');
+
+       if (colon) {
+               int port;
+
+               if (!portok)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need TCP or UDP with port specification");
+
+               range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+
+               port = atoi(colon+1);
+               if (port <= 0 || port > 65535)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Port `%s' not valid\n", colon+1);
+
+               error = strchr(colon+1, ':');
+               if (error)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid port:port syntax - use dash\n");
+
+               dash = strchr(colon, '-');
+               if (!dash) {
+                       range.min.tcp.port
+                               = range.max.tcp.port
+                               = htons(port);
+               } else {
+                       int maxport;
+
+                       maxport = atoi(dash + 1);
+                       if (maxport <= 0 || maxport > 65535)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Port `%s' not valid\n", dash+1);
+                       if (maxport < port)
+                               /* People are stupid. */
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Port range `%s' funky\n", colon+1);
+                       range.min.tcp.port = htons(port);
+                       range.max.tcp.port = htons(maxport);
+               }
+               /* Starts with a colon? No IP info...*/
+               if (colon == arg)
+                       return &(append_range(info, &range)->t);
+               *colon = '\0';
+       }
+
+       range.flags |= IP_NAT_RANGE_MAP_IPS;
+       dash = strchr(arg, '-');
+       if (colon && dash && dash > colon)
+               dash = NULL;
+
+       if (dash)
+               *dash = '\0';
+
+       ip = dotted_to_addr(arg);
+       if (!ip)
+               exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
+                          arg);
+       range.min_ip = ip->s_addr;
+       if (dash) {
+               ip = dotted_to_addr(dash+1);
+               if (!ip)
+                       exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
+                                  dash+1);
+               range.max_ip = ip->s_addr;
+       } else
+               range.max_ip = range.min_ip;
+
+       return &(append_range(info, &range)->t);
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_natinfo *info = (void *)*target;
+       int portok;
+
+       if (entry->ip.proto == IPPROTO_TCP
+           || entry->ip.proto == IPPROTO_UDP
+           || entry->ip.proto == IPPROTO_ICMP)
+               portok = 1;
+       else
+               portok = 0;
+
+       switch (c) {
+       case '1':
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --to-source");
+
+               if (*flags & IPT_SNAT_OPT_SOURCE) {
+                       if (!kernel_version)
+                               get_kernel_version();
+                       if (kernel_version > LINUX_VERSION(2, 6, 10))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Multiple --to-source not supported");
+               }
+               *target = parse_to(optarg, portok, info);
+               /* WTF do we need this for?? */
+               if (*flags & IPT_SNAT_OPT_RANDOM)
+                       info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
+               *flags |= IPT_SNAT_OPT_SOURCE;
+               return 1;
+
+       case '2':
+               if (*flags & IPT_SNAT_OPT_SOURCE) {
+                       info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
+                       *flags |= IPT_SNAT_OPT_RANDOM;
+               } else
+                       *flags |= IPT_SNAT_OPT_RANDOM;
+               return 1;
+
+       default:
+               return 0;
+       }
+}
+
+/* Final check; must have specfied --to-source. */
+static void final_check(unsigned int flags)
+{
+       if (!(flags & IPT_SNAT_OPT_SOURCE))
+               exit_error(PARAMETER_PROBLEM,
+                          "You must specify --to-source");
+}
+
+static void print_range(const struct ip_nat_range *r)
+{
+       if (r->flags & IP_NAT_RANGE_MAP_IPS) {
+               struct in_addr a;
+
+               a.s_addr = r->min_ip;
+               printf("%s", addr_to_dotted(&a));
+               if (r->max_ip != r->min_ip) {
+                       a.s_addr = r->max_ip;
+                       printf("-%s", addr_to_dotted(&a));
+               }
+       }
+       if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+               printf(":");
+               printf("%hu", ntohs(r->min.tcp.port));
+               if (r->max.tcp.port != r->min.tcp.port)
+                       printf("-%hu", ntohs(r->max.tcp.port));
+       }
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       struct ipt_natinfo *info = (void *)target;
+       unsigned int i = 0;
+
+       printf("to:");
+       for (i = 0; i < info->mr.rangesize; i++) {
+               print_range(&info->mr.range[i]);
+               printf(" ");
+               if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
+                       printf("random ");
+       }
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       struct ipt_natinfo *info = (void *)target;
+       unsigned int i = 0;
+
+       for (i = 0; i < info->mr.rangesize; i++) {
+               printf("--to-source ");
+               print_range(&info->mr.range[i]);
+               printf(" ");
+               if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
+                       printf("--random ");
+       }
+}
+
+static struct iptables_target snat = {
+       .next           = NULL,
+       .name           = "SNAT",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&snat);
+}
diff --git a/extensions/libipt_SNAT.man b/extensions/libipt_SNAT.man
new file mode 100644 (file)
index 0000000..7b34799
--- /dev/null
@@ -0,0 +1,34 @@
+This target is only valid in the
+.B nat
+table, in the
+.B POSTROUTING
+chain.  It specifies that the source address of the packet should be
+modified (and all future packets in this connection will also be
+mangled), and rules should cease being examined.  It takes one type
+of option:
+.TP
+.BR "--to-source  " "\fIipaddr\fP[-\fIipaddr\fP][:\fIport\fP-\fIport\fP]"
+which can specify a single new source IP address, an inclusive range
+of IP addresses, and optionally, a port range (which is only valid if
+the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" ).
+If no port range is specified, then source ports below 512 will be
+mapped to other ports below 512: those between 512 and 1023 inclusive
+will be mapped to ports below 1024, and other ports will be mapped to
+1024 or above. Where possible, no port alteration will
+
+In Kernels up to 2.6.10, you can add several --to-source options.  For those
+kernels, if you specify more than one source address, either via an address
+range or multiple --to-source options, a simple round-robin (one after another
+in cycle) takes place between these addresses.
+Later Kernels (>= 2.6.11-rc1) don't have the ability to NAT to multiple ranges
+anymore.
+.TP
+.BR "--random"
+If option
+.B "--random"
+is used then port mapping will be randomized (kernel >= 2.6.21).
+.RS
+.PP
diff --git a/extensions/libipt_TARPIT.c b/extensions/libipt_TARPIT.c
new file mode 100644 (file)
index 0000000..b12cbc2
--- /dev/null
@@ -0,0 +1,58 @@
+/* Shared library add-on to iptables for TARPIT support */
+#include <stdio.h>
+#include <getopt.h>
+#include <iptables.h>
+
+static void
+help(void)
+{
+       fputs(
+"TARPIT takes no options\n"
+"\n", stdout);
+}
+
+static struct option opts[] = {
+       { 0 }
+};
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       return 0;
+}
+
+static void final_check(unsigned int flags)
+{
+}
+
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+}
+
+static void save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+}
+
+static struct iptables_target tarpit = {
+       .next           = NULL,
+       .name           = "TARPIT",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(0),
+       .userspacesize  = IPT_ALIGN(0),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&tarpit);
+}
diff --git a/extensions/libipt_TARPIT.man b/extensions/libipt_TARPIT.man
new file mode 100644 (file)
index 0000000..26526b7
--- /dev/null
@@ -0,0 +1,34 @@
+Captures and holds incoming TCP connections using no local
+per-connection resources. Connections are accepted, but immediately
+switched to the persist state (0 byte window), in which the remote
+side stops sending data and asks to continue every 60-240 seconds.
+Attempts to close the connection are ignored, forcing the remote side
+to time out the connection in 12-24 minutes.
+
+This offers similar functionality to LaBrea
+<http://www.hackbusters.net/LaBrea/> but doesn't require dedicated
+hardware or IPs. Any TCP port that you would normally DROP or REJECT
+can instead become a tarpit.
+
+To tarpit connections to TCP port 80 destined for the current machine:
+.IP
+iptables -A INPUT -p tcp -m tcp --dport 80 -j TARPIT
+.P
+To significantly slow down Code Red/Nimda-style scans of unused address
+space, forward unused ip addresses to a Linux box not acting as a router
+(e.g. "ip route 10.0.0.0 255.0.0.0 ip.of.linux.box" on a Cisco), enable IP
+forwarding on the Linux box, and add:
+.IP
+iptables -A FORWARD -p tcp -j TARPIT
+.IP
+iptables -A FORWARD -j DROP
+.TP
+NOTE:
+If you use the conntrack module while you are using TARPIT, you should
+also use the NOTRACK target, or the kernel will unnecessarily allocate
+resources for each TARPITted connection. To TARPIT incoming
+connections to the standard IRC port while using conntrack, you could:
+.IP
+iptables -t raw -A PREROUTING -p tcp --dport 6667 -j NOTRACK
+.IP
+iptables -A INPUT -p tcp --dport 6667 -j TARPIT
diff --git a/extensions/libipt_TCPMSS.c b/extensions/libipt_TCPMSS.c
new file mode 100644 (file)
index 0000000..c3256f0
--- /dev/null
@@ -0,0 +1,134 @@
+/* Shared library add-on to iptables to add TCPMSS target support.
+ *
+ * Copyright (c) 2000 Marc Boucher
+*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_TCPMSS.h>
+
+struct mssinfo {
+       struct ipt_entry_target t;
+       struct ipt_tcpmss_info mss;
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"TCPMSS target v%s mutually-exclusive options:\n"
+"  --set-mss value               explicitly set MSS option to specified value\n"
+"  --clamp-mss-to-pmtu           automatically clamp MSS value to (path_MTU - 40)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "set-mss", 1, 0, '1' },
+       { "clamp-mss-to-pmtu", 0, 0, '2' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_tcpmss_info *mssinfo
+               = (struct ipt_tcpmss_info *)(*target)->data;
+
+       switch (c) {
+               unsigned int mssval;
+
+       case '1':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "TCPMSS target: Only one option may be specified");
+               if (string_to_number(optarg, 0, 65535 - 40, &mssval) == -1)
+                       exit_error(PARAMETER_PROBLEM, "Bad TCPMSS value `%s'", optarg);
+               
+               mssinfo->mss = mssval;
+               *flags = 1;
+               break;
+
+       case '2':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "TCPMSS target: Only one option may be specified");
+               mssinfo->mss = IPT_TCPMSS_CLAMP_PMTU;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "TCPMSS target: At least one parameter is required");
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_tcpmss_info *mssinfo =
+               (const struct ipt_tcpmss_info *)target->data;
+       if(mssinfo->mss == IPT_TCPMSS_CLAMP_PMTU)
+               printf("TCPMSS clamp to PMTU ");
+       else
+               printf("TCPMSS set %u ", mssinfo->mss);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_tcpmss_info *mssinfo =
+               (const struct ipt_tcpmss_info *)target->data;
+
+       if(mssinfo->mss == IPT_TCPMSS_CLAMP_PMTU)
+               printf("--clamp-mss-to-pmtu ");
+       else
+               printf("--set-mss %u ", mssinfo->mss);
+}
+
+static struct iptables_target mss = {
+       .next           = NULL,
+       .name           = "TCPMSS",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_tcpmss_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_tcpmss_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&mss);
+}
diff --git a/extensions/libipt_TCPMSS.man b/extensions/libipt_TCPMSS.man
new file mode 100644 (file)
index 0000000..30668b0
--- /dev/null
@@ -0,0 +1,41 @@
+This target allows to alter the MSS value of TCP SYN packets, to control
+the maximum size for that connection (usually limiting it to your
+outgoing interface's MTU minus 40).  Of course, it can only be used
+in conjunction with
+.BR "-p tcp" .
+It is only valid in the
+.BR mangle
+table.
+.br
+This target is used to overcome criminally braindead ISPs or servers
+which block ICMP Fragmentation Needed packets.  The symptoms of this
+problem are that everything works fine from your Linux
+firewall/router, but machines behind it can never exchange large
+packets:
+.PD 0
+.RS 0.1i
+.TP 0.3i
+1)
+Web browsers connect, then hang with no data received.
+.TP
+2)
+Small mail works fine, but large emails hang.
+.TP
+3)
+ssh works fine, but scp hangs after initial handshaking.
+.RE
+.PD
+Workaround: activate this option and add a rule to your firewall
+configuration like:
+.nf
+ iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \\
+             -j TCPMSS --clamp-mss-to-pmtu
+.fi
+.TP
+.BI "--set-mss " "value"
+Explicitly set MSS option to specified value.
+.TP
+.B "--clamp-mss-to-pmtu"
+Automatically clamp MSS value to (path_MTU - 40).
+.TP
+These options are mutually exclusive.
diff --git a/extensions/libipt_TOS.c b/extensions/libipt_TOS.c
new file mode 100644 (file)
index 0000000..999f7b0
--- /dev/null
@@ -0,0 +1,174 @@
+/* Shared library add-on to iptables to add TOS target support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_TOS.h>
+
+struct tosinfo {
+       struct ipt_entry_target t;
+       struct ipt_tos_target_info tos;
+};
+
+/* TOS names and values. */
+static
+struct TOS_value
+{
+       unsigned char TOS;
+       const char *name;
+} TOS_values[] = {
+       { IPTOS_LOWDELAY,    "Minimize-Delay" },
+       { IPTOS_THROUGHPUT,  "Maximize-Throughput" },
+       { IPTOS_RELIABILITY, "Maximize-Reliability" },
+       { IPTOS_MINCOST,     "Minimize-Cost" },
+       { IPTOS_NORMALSVC,   "Normal-Service" },
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       unsigned int i;
+
+       printf(
+"TOS target v%s options:\n"
+"  --set-tos value                   Set Type of Service field to one of the\n"
+"                                following numeric or descriptive values:\n",
+IPTABLES_VERSION);
+
+       for (i = 0; i < sizeof(TOS_values)/sizeof(struct TOS_value);i++)
+               printf("                                     %s %u (0x%02x)\n",
+                      TOS_values[i].name,
+                       TOS_values[i].TOS,
+                       TOS_values[i].TOS);
+       fputc('\n', stdout);
+}
+
+static struct option opts[] = {
+       { "set-tos", 1, 0, '1' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+static void
+parse_tos(const char *s, struct ipt_tos_target_info *info)
+{
+       unsigned int i, tos;
+
+       if (string_to_number(s, 0, 255, &tos) != -1) {
+               if (tos == IPTOS_LOWDELAY
+                   || tos == IPTOS_THROUGHPUT
+                   || tos == IPTOS_RELIABILITY
+                   || tos == IPTOS_MINCOST
+                   || tos == IPTOS_NORMALSVC) {
+                       info->tos = (u_int8_t )tos;
+                       return;
+               }
+       } else {
+               for (i = 0; i<sizeof(TOS_values)/sizeof(struct TOS_value); i++)
+                       if (strcasecmp(s,TOS_values[i].name) == 0) {
+                               info->tos = TOS_values[i].TOS;
+                               return;
+                       }
+       }
+       exit_error(PARAMETER_PROBLEM, "Bad TOS value `%s'", s);
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_tos_target_info *tosinfo
+               = (struct ipt_tos_target_info *)(*target)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "TOS target: Cant specify --set-tos twice");
+               parse_tos(optarg, tosinfo);
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "TOS target: Parameter --set-tos is required");
+}
+
+static void
+print_tos(u_int8_t tos, int numeric)
+{
+       unsigned int i;
+
+       if (!numeric) {
+               for (i = 0; i<sizeof(TOS_values)/sizeof(struct TOS_value); i++)
+                       if (TOS_values[i].TOS == tos) {
+                               printf("%s ", TOS_values[i].name);
+                               return;
+                       }
+       }
+       printf("0x%02x ", tos);
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_tos_target_info *tosinfo =
+               (const struct ipt_tos_target_info *)target->data;
+       printf("TOS set ");
+       print_tos(tosinfo->tos, numeric);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_tos_target_info *tosinfo =
+               (const struct ipt_tos_target_info *)target->data;
+
+       printf("--set-tos 0x%02x ", tosinfo->tos);
+}
+
+static struct iptables_target tos = {
+       .next           = NULL,
+       .name           = "TOS",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_tos_target_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_tos_target_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&tos);
+}
diff --git a/extensions/libipt_TOS.man b/extensions/libipt_TOS.man
new file mode 100644 (file)
index 0000000..c31b068
--- /dev/null
@@ -0,0 +1,11 @@
+This is used to set the 8-bit Type of Service field in the IP header.
+It is only valid in the
+.B mangle
+table.
+.TP
+.BI "--set-tos " "tos"
+You can use a numeric TOS values, or use
+.nf
+ iptables -j TOS -h
+.fi
+to see the list of valid TOS names.
diff --git a/extensions/libipt_TTL.c b/extensions/libipt_TTL.c
new file mode 100644 (file)
index 0000000..a2a28bd
--- /dev/null
@@ -0,0 +1,166 @@
+/* Shared library add-on to iptables for the TTL target
+ * (C) 2000 by Harald Welte <laforge@gnumonks.org>
+ *
+ * $Id: libipt_TTL.c 3507 2004-12-28 13:11:59Z /C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=rusty/emailAddress=rusty@netfilter.org $
+ *
+ * This program is distributed under the terms of GNU GPL
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_TTL.h>
+
+#define IPT_TTL_USED   1
+
+static void init(struct ipt_entry_target *t, unsigned int *nfcache) 
+{
+}
+
+static void help(void) 
+{
+       printf(
+"TTL target v%s options\n"
+"  --ttl-set value             Set TTL to <value 0-255>\n"
+"  --ttl-dec value             Decrement TTL by <value 1-255>\n"
+"  --ttl-inc value             Increment TTL by <value 1-255>\n"
+, IPTABLES_VERSION);
+}
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+               const struct ipt_entry *entry,
+               struct ipt_entry_target **target)
+{
+       struct ipt_TTL_info *info = (struct ipt_TTL_info *) (*target)->data;
+       unsigned int value;
+
+       if (*flags & IPT_TTL_USED) {
+               exit_error(PARAMETER_PROBLEM, 
+                               "Can't specify TTL option twice");
+       }
+
+       if (!optarg) 
+               exit_error(PARAMETER_PROBLEM, 
+                               "TTL: You must specify a value");
+
+       if (check_inverse(optarg, &invert, NULL, 0))
+               exit_error(PARAMETER_PROBLEM,
+                               "TTL: unexpected `!'");
+       
+       if (string_to_number(optarg, 0, 255, &value) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "TTL: Expected value between 0 and 255");
+
+       switch (c) {
+
+               case '1':
+                       info->mode = IPT_TTL_SET;
+                       break;
+
+               case '2':
+                       if (value == 0) {
+                               exit_error(PARAMETER_PROBLEM,
+                                       "TTL: decreasing by 0?");
+                       }
+
+                       info->mode = IPT_TTL_DEC;
+                       break;
+
+               case '3':
+                       if (value == 0) {
+                               exit_error(PARAMETER_PROBLEM,
+                                       "TTL: increasing by 0?");
+                       }
+
+                       info->mode = IPT_TTL_INC;
+                       break;
+
+               default:
+                       return 0;
+
+       }
+       
+       info->ttl = value;
+       *flags |= IPT_TTL_USED;
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!(flags & IPT_TTL_USED))
+               exit_error(PARAMETER_PROBLEM,
+                               "TTL: You must specify an action");
+}
+
+static void save(const struct ipt_ip *ip,
+               const struct ipt_entry_target *target)
+{
+       const struct ipt_TTL_info *info = 
+               (struct ipt_TTL_info *) target->data;
+
+       switch (info->mode) {
+               case IPT_TTL_SET:
+                       printf("--ttl-set ");
+                       break;
+               case IPT_TTL_DEC:
+                       printf("--ttl-dec ");
+                       break;
+
+               case IPT_TTL_INC:
+                       printf("--ttl-inc ");
+                       break;
+       }
+       printf("%u ", info->ttl);
+}
+
+static void print(const struct ipt_ip *ip,
+               const struct ipt_entry_target *target, int numeric)
+{
+       const struct ipt_TTL_info *info =
+               (struct ipt_TTL_info *) target->data;
+
+       printf("TTL ");
+       switch (info->mode) {
+               case IPT_TTL_SET:
+                       printf("set to ");
+                       break;
+               case IPT_TTL_DEC:
+                       printf("decrement by ");
+                       break;
+               case IPT_TTL_INC:
+                       printf("increment by ");
+                       break;
+       }
+       printf("%u ", info->ttl);
+}
+
+static struct option opts[] = {
+       { "ttl-set", 1, 0, '1' },
+       { "ttl-dec", 1, 0, '2' },
+       { "ttl-inc", 1, 0, '3' },
+       { 0 }
+};
+
+static struct iptables_target TTL = {
+       .next           = NULL, 
+       .name           = "TTL",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_TTL_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_TTL_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts 
+};
+
+void _init(void)
+{
+       register_target(&TTL);
+}
diff --git a/extensions/libipt_TTL.man b/extensions/libipt_TTL.man
new file mode 100644 (file)
index 0000000..97c46c4
--- /dev/null
@@ -0,0 +1,19 @@
+This is used to modify the IPv4 TTL header field.  The TTL field determines
+how many hops (routers) a packet can traverse until it's time to live is
+exceeded.
+.TP
+Setting or incrementing the TTL field can potentially be very dangerous,
+so it should be avoided at any cost.  
+.TP
+.B Don't ever set or increment the value on packets that leave your local network!
+.B mangle
+table.
+.TP
+.BI "--ttl-set " "value"
+Set the TTL value to `value'.
+.TP
+.BI "--ttl-dec " "value"
+Decrement the TTL value `value' times.
+.TP
+.BI "--ttl-inc " "value"
+Increment the TTL value `value' times.
diff --git a/extensions/libipt_ULOG.c b/extensions/libipt_ULOG.c
new file mode 100644 (file)
index 0000000..a73b685
--- /dev/null
@@ -0,0 +1,237 @@
+/* Shared library add-on to iptables to add ULOG support.
+ * 
+ * (C) 2000 by Harald Welte <laforge@gnumonks.org>
+ *
+ * multipart netlink support based on ideas by Sebastian Zander 
+ *                                             <zander@fokus.gmd.de>
+ *
+ * This software is released under the terms of GNU GPL
+ * 
+ * libipt_ULOG.c,v 1.7 2001/01/30 11:55:02 laforge Exp
+ */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv4/ipt_ULOG.h"
+
+
+void print_groups(unsigned int gmask)
+{
+       int b;
+       unsigned int test;
+
+       for (b = 31; b >= 0; b--) {
+               test = (1 << b);
+               if (gmask & test)
+                       printf("%d ", b + 1);
+       }
+}
+
+/* Function which prints out usage message. */
+static void help(void)
+{
+       printf("ULOG v%s options:\n"
+              " --ulog-nlgroup nlgroup         NETLINK group used for logging\n"
+              " --ulog-cprange size            Bytes of each packet to be passed\n"
+              " --ulog-qthreshold              Threshold of in-kernel queue\n"
+              " --ulog-prefix prefix           Prefix log messages with this prefix.\n\n",
+              IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       {"ulog-nlgroup", 1, 0, '!'},
+       {"ulog-prefix", 1, 0, '#'},
+       {"ulog-cprange", 1, 0, 'A'},
+       {"ulog-qthreshold", 1, 0, 'B'},
+       {0}
+};
+
+/* Initialize the target. */
+static void init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+       struct ipt_ulog_info *loginfo = (struct ipt_ulog_info *) t->data;
+
+       loginfo->nl_group = ULOG_DEFAULT_NLGROUP;
+       loginfo->qthreshold = ULOG_DEFAULT_QTHRESHOLD;
+
+}
+
+#define IPT_LOG_OPT_NLGROUP 0x01
+#define IPT_LOG_OPT_PREFIX 0x02
+#define IPT_LOG_OPT_CPRANGE 0x04
+#define IPT_LOG_OPT_QTHRESHOLD 0x08
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+                const struct ipt_entry *entry,
+                struct ipt_entry_target **target)
+{
+       struct ipt_ulog_info *loginfo =
+           (struct ipt_ulog_info *) (*target)->data;
+       int group_d;
+
+       switch (c) {
+       case '!':
+               if (*flags & IPT_LOG_OPT_NLGROUP)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ulog-nlgroup twice");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --ulog-nlgroup");
+               group_d = atoi(optarg);
+               if (group_d > 32 || group_d < 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--ulog-nlgroup has to be between 1 and 32");
+
+               loginfo->nl_group = (1 << (group_d - 1));
+
+               *flags |= IPT_LOG_OPT_NLGROUP;
+               break;
+
+       case '#':
+               if (*flags & IPT_LOG_OPT_PREFIX)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ulog-prefix twice");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --ulog-prefix");
+
+               if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Maximum prefix length %u for --ulog-prefix",
+                                  (unsigned int)sizeof(loginfo->prefix) - 1);
+
+               if (strlen(optarg) == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "No prefix specified for --ulog-prefix");
+
+               if (strlen(optarg) != strlen(strtok(optarg, "\n")))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Newlines not allowed in --ulog-prefix");
+
+               strcpy(loginfo->prefix, optarg);
+               *flags |= IPT_LOG_OPT_PREFIX;
+               break;
+       case 'A':
+               if (*flags & IPT_LOG_OPT_CPRANGE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ulog-cprange twice");
+               if (atoi(optarg) < 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Negative copy range?");
+#ifdef KERNEL_64_USERSPACE_32
+               loginfo->copy_range = (unsigned long long)atoll(optarg);
+#else
+               loginfo->copy_range = atoi(optarg);
+#endif
+               *flags |= IPT_LOG_OPT_CPRANGE;
+               break;
+       case 'B':
+               if (*flags & IPT_LOG_OPT_QTHRESHOLD)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ulog-qthreshold twice");
+               if (atoi(optarg) < 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Negative or zero queue threshold ?");
+               if (atoi(optarg) > ULOG_MAX_QLEN)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Maximum queue length exceeded");
+#ifdef KERNEL_64_USERSPACE_32
+               loginfo->qthreshold = (unsigned long long)atoll(optarg);
+#else
+               loginfo->qthreshold = atoi(optarg);
+#endif
+               *flags |= IPT_LOG_OPT_QTHRESHOLD;
+               break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip,
+                const struct ipt_entry_target *target)
+{
+       const struct ipt_ulog_info *loginfo
+           = (const struct ipt_ulog_info *) target->data;
+
+       if (strcmp(loginfo->prefix, "") != 0)
+               printf("--ulog-prefix \"%s\" ", loginfo->prefix);
+
+       if (loginfo->nl_group != ULOG_DEFAULT_NLGROUP) {
+               printf("--ulog-nlgroup ");
+               print_groups(loginfo->nl_group);
+       }
+#ifdef KERNEL_64_USERSPACE_32
+       if (loginfo->copy_range)
+               printf("--ulog-cprange %llu ", loginfo->copy_range);
+
+       if (loginfo->qthreshold != ULOG_DEFAULT_QTHRESHOLD)
+               printf("--ulog-qthreshold %llu ", loginfo->qthreshold);
+#else
+       if (loginfo->copy_range)
+               printf("--ulog-cprange %u ", (unsigned int)loginfo->copy_range);
+
+       if (loginfo->qthreshold != ULOG_DEFAULT_QTHRESHOLD)
+               printf("--ulog-qthreshold %u ", (unsigned int)loginfo->qthreshold);
+#endif
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target, int numeric)
+{
+       const struct ipt_ulog_info *loginfo
+           = (const struct ipt_ulog_info *) target->data;
+
+       printf("ULOG ");
+#ifdef KERNEL_64_USERSPACE_32
+       printf("copy_range %llu nlgroup ", loginfo->copy_range);
+#else
+       printf("copy_range %u nlgroup ", (unsigned int)loginfo->copy_range);
+#endif
+       print_groups(loginfo->nl_group);
+       if (strcmp(loginfo->prefix, "") != 0)
+               printf("prefix `%s' ", loginfo->prefix);
+#ifdef KERNEL_64_USERSPACE_32
+       printf("queue_threshold %llu ", loginfo->qthreshold);
+#else
+       printf("queue_threshold %u ", (unsigned int)loginfo->qthreshold);
+#endif
+}
+
+static struct iptables_target ulog = {
+       .next           = NULL,
+       .name           = "ULOG",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_ulog_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_ulog_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&ulog);
+}
diff --git a/extensions/libipt_ULOG.man b/extensions/libipt_ULOG.man
new file mode 100644 (file)
index 0000000..51aa619
--- /dev/null
@@ -0,0 +1,27 @@
+This target provides userspace logging of matching packets.  When this
+target is set for a rule, the Linux kernel will multicast this packet
+through a
+.IR netlink 
+socket. One or more userspace processes may then subscribe to various 
+multicast groups and receive the packets.
+Like LOG, this is a "non-terminating target", i.e. rule traversal
+continues at the next rule.
+.TP
+.BI "--ulog-nlgroup " "nlgroup"
+This specifies the netlink group (1-32) to which the packet is sent.
+Default value is 1.
+.TP
+.BI "--ulog-prefix " "prefix"
+Prefix log messages with the specified prefix; up to 32 characters
+long, and useful for distinguishing messages in the logs.
+.TP
+.BI "--ulog-cprange " "size"
+Number of bytes to be copied to userspace.  A value of 0 always copies
+the entire packet, regardless of its size.  Default is 0.
+.TP
+.BI "--ulog-qthreshold " "size"
+Number of packet to queue inside kernel.  Setting this value to, e.g. 10
+accumulates ten packets inside the kernel and transmits them as one
+netlink multipart message to userspace.  Default is 1 (for backwards
+compatibility).
+.br
diff --git a/extensions/libipt_addrtype.c b/extensions/libipt_addrtype.c
new file mode 100644 (file)
index 0000000..d8e1929
--- /dev/null
@@ -0,0 +1,207 @@
+/* Shared library add-on to iptables to add addrtype matching support 
+ * 
+ * This program is released under the terms of GNU GPL */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <iptables.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_addrtype.h>
+
+/* from linux/rtnetlink.h, must match order of enumeration */
+static char *rtn_names[] = {
+       "UNSPEC",
+       "UNICAST",
+       "LOCAL",
+       "BROADCAST",
+       "ANYCAST",
+       "MULTICAST",
+       "BLACKHOLE",
+       "UNREACHABLE",
+       "PROHIBIT",
+       "THROW",
+       "NAT",
+       "XRESOLVE",
+       NULL
+};
+
+static void help_types(void)
+{
+       int i;
+
+       for (i = 0; rtn_names[i]; i++)
+               printf("                                %s\n", rtn_names[i]);
+}
+
+static void help(void) 
+{
+       printf(
+"Address type match v%s options:\n"
+" [!] --src-type type[,...]      Match source address type\n"
+" [!] --dst-type type[,...]      Match destination address type\n"
+"\n"
+"Valid types:           \n"
+, IPTABLES_VERSION);
+       help_types();
+}
+
+static int
+parse_type(const char *name, size_t strlen, u_int16_t *mask)
+{
+       int i;
+
+       for (i = 0; rtn_names[i]; i++)
+               if (strncasecmp(name, rtn_names[i], strlen) == 0) {
+                       /* build up bitmask for kernel module */
+                       *mask |= (1 << i);
+                       return 1;
+               }
+
+       return 0;
+}
+
+static void parse_types(const char *arg, u_int16_t *mask)
+{
+       const char *comma;
+
+       while ((comma = strchr(arg, ',')) != NULL) {
+               if (comma == arg || !parse_type(arg, comma-arg, mask))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "addrtype: bad type `%s'", arg);
+               arg = comma + 1;
+       }
+
+       if (strlen(arg) == 0 || !parse_type(arg, strlen(arg), mask))
+               exit_error(PARAMETER_PROBLEM, "addrtype: bad type `%s'", arg);
+}
+       
+#define IPT_ADDRTYPE_OPT_SRCTYPE       0x1
+#define IPT_ADDRTYPE_OPT_DSTTYPE       0x2
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+               const struct ipt_entry *entry, unsigned int *nfcache,
+               struct ipt_entry_match **match)
+{
+       struct ipt_addrtype_info *info =
+               (struct ipt_addrtype_info *) (*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags&IPT_ADDRTYPE_OPT_SRCTYPE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "addrtype: can't specify src-type twice");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_types(argv[optind-1], &info->source);
+               if (invert)
+                       info->invert_source = 1;
+               *flags |= IPT_ADDRTYPE_OPT_SRCTYPE;
+               break;
+       case '2':
+               if (*flags&IPT_ADDRTYPE_OPT_DSTTYPE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "addrtype: can't specify dst-type twice");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_types(argv[optind-1], &info->dest);
+               if (invert)
+                       info->invert_dest = 1;
+               *flags |= IPT_ADDRTYPE_OPT_DSTTYPE;
+               break;
+       default:
+               return 0;
+       }
+       
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!(flags & (IPT_ADDRTYPE_OPT_SRCTYPE|IPT_ADDRTYPE_OPT_DSTTYPE)))
+               exit_error(PARAMETER_PROBLEM,
+                          "addrtype: you must specify --src-type or --dst-type");
+}
+
+static void print_types(u_int16_t mask)
+{
+       const char *sep = "";
+       int i;
+
+       for (i = 0; rtn_names[i]; i++)
+               if (mask & (1 << i)) {
+                       printf("%s%s", sep, rtn_names[i]);
+                       sep = ",";
+               }
+
+       printf(" ");
+}
+
+static void print(const struct ipt_ip *ip, 
+               const struct ipt_entry_match *match,
+               int numeric)
+{
+       const struct ipt_addrtype_info *info = 
+               (struct ipt_addrtype_info *) match->data;
+
+       printf("ADDRTYPE match ");
+       if (info->source) {
+               printf("src-type ");
+               if (info->invert_source)
+                       printf("!");
+               print_types(info->source);
+       }
+       if (info->dest) {
+               printf("dst-type ");
+               if (info->invert_dest)
+                       printf("!");
+               print_types(info->dest);
+       }
+}
+
+static void save(const struct ipt_ip *ip, 
+               const struct ipt_entry_match *match)
+{
+       const struct ipt_addrtype_info *info =
+               (struct ipt_addrtype_info *) match->data;
+
+       if (info->source) {
+               printf("--src-type ");
+               if (info->invert_source)
+                       printf("! ");
+               print_types(info->source);
+       }
+       if (info->dest) {
+               printf("--dst-type ");
+               if (info->invert_dest)
+                       printf("! ");
+               print_types(info->dest);
+       }
+}
+
+static struct option opts[] = {
+       { "src-type", 1, 0, '1' },
+       { "dst-type", 1, 0, '2' },
+       { 0 }
+};
+
+static
+struct iptables_match addrtype = {
+       .next           = NULL,
+       .name           = "addrtype",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_addrtype_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_addrtype_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+
+void _init(void) 
+{
+       register_match(&addrtype);
+}
diff --git a/extensions/libipt_addrtype.man b/extensions/libipt_addrtype.man
new file mode 100644 (file)
index 0000000..2c3bbab
--- /dev/null
@@ -0,0 +1,37 @@
+This module matches packets based on their 
+.B address type.
+Address types are used within the kernel networking stack and categorize
+addresses into various groups.  The exact definition of that group depends on the specific layer three protocol.
+.TP
+The following address types are possible:
+.TP
+.BI "UNSPEC"
+an unspecified address (i.e. 0.0.0.0)
+.BI "UNICAST"
+an unicast address
+.BI "LOCAL"
+a local address
+.BI "BROADCAST"
+a broadcast address
+.BI "ANYCAST"
+an anycast packet
+.BI "MULTICAST"
+a multicast address
+.BI "BLACKHOLE"
+a blackhole address
+.BI "UNREACHABLE"
+an unreachable address
+.BI "PROHIBIT"
+a prohibited address
+.BI "THROW"
+FIXME
+.BI "NAT"
+FIXME
+.BI "XRESOLVE"
+FIXME
+.TP
+.BI "--src-type " "type"
+Matches if the source address is of given type
+.TP
+.BI "--dst-type " "type"
+Matches if the destination address is of given type
diff --git a/extensions/libipt_ah.c b/extensions/libipt_ah.c
new file mode 100644 (file)
index 0000000..443c9f8
--- /dev/null
@@ -0,0 +1,190 @@
+/* Shared library add-on to iptables to add AH support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_ah.h>
+                                        
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"AH v%s options:\n"
+" --ahspi [!] spi[:spi]\n"
+"                              match spi (range)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "ahspi", 1, 0, '1' },
+       {0}
+};
+
+static u_int32_t
+parse_ah_spi(const char *spistr)
+{
+       unsigned long int spi;
+       char* ep;
+
+       spi =  strtoul(spistr,&ep,0) ;
+
+       if ( spistr == ep ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "AH no valid digits in spi `%s'", spistr);
+       }
+       if ( spi == ULONG_MAX  && errno == ERANGE ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "spi `%s' specified too big: would overflow", spistr);
+       }       
+       if ( *spistr != '\0'  && *ep != '\0' ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "AH error parsing spi `%s'", spistr);
+       }
+       return (u_int32_t) spi;
+}
+
+static void
+parse_ah_spis(const char *spistring, u_int32_t *spis)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(spistring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               spis[0] = spis[1] = parse_ah_spi(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               spis[0] = buffer[0] ? parse_ah_spi(buffer) : 0;
+               spis[1] = cp[0] ? parse_ah_spi(cp) : 0xFFFFFFFF;
+       }
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_ah *ahinfo = (struct ipt_ah *)m->data;
+
+       ahinfo->spis[1] = 0xFFFFFFFF;
+}
+
+#define AH_SPI 0x01
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_ah *ahinfo = (struct ipt_ah *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & AH_SPI)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--ahspi' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_ah_spis(argv[optind-1], ahinfo->spis);
+               if (invert)
+                       ahinfo->invflags |= IPT_AH_INV_SPI;
+               *flags |= AH_SPI;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_spis(const char *name, u_int32_t min, u_int32_t max,
+           int invert)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFFFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       printf("%u", min);
+               } else {
+                       printf("s:%s", inv);
+                       printf("%u",min);
+                       printf(":");
+                       printf("%u",max);
+               }
+               printf(" ");
+       }
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match, int numeric)
+{
+       const struct ipt_ah *ah = (struct ipt_ah *)match->data;
+
+       printf("ah ");
+       print_spis("spi", ah->spis[0], ah->spis[1],
+                   ah->invflags & IPT_AH_INV_SPI);
+       if (ah->invflags & ~IPT_AH_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      ah->invflags & ~IPT_AH_INV_MASK);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_ah *ahinfo = (struct ipt_ah *)match->data;
+
+       if (!(ahinfo->spis[0] == 0
+           && ahinfo->spis[1] == 0xFFFFFFFF)) {
+               printf("--ahspi %s", 
+                       (ahinfo->invflags & IPT_AH_INV_SPI) ? "! " : "");
+               if (ahinfo->spis[0]
+                   != ahinfo->spis[1])
+                       printf("%u:%u ",
+                              ahinfo->spis[0],
+                              ahinfo->spis[1]);
+               else
+                       printf("%u ",
+                              ahinfo->spis[0]);
+       }
+
+}
+
+static struct iptables_match ah = { 
+       .next           = NULL,
+       .name           = "ah",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_ah)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_ah)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void
+_init(void)
+{
+       register_match(&ah);
+}
diff --git a/extensions/libipt_ah.man b/extensions/libipt_ah.man
new file mode 100644 (file)
index 0000000..7300c18
--- /dev/null
@@ -0,0 +1,3 @@
+This module matches the SPIs in Authentication header of IPsec packets.
+.TP
+.BR "--ahspi " "[!] \fIspi\fP[:\fIspi\fP]"
diff --git a/extensions/libipt_comment.c b/extensions/libipt_comment.c
new file mode 100644 (file)
index 0000000..692acca
--- /dev/null
@@ -0,0 +1,119 @@
+/* Shared library add-on to iptables to add comment match support.
+ *
+ * ChangeLog
+ *     2003-05-13: Brad Fisher <brad@info-link.net>
+ *         Initial comment match
+ *     2004-05-12: Brad Fisher <brad@info-link.net>
+ *         Port to patch-o-matic-ng
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_comment.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+               "COMMENT match options:\n"
+               "--comment COMMENT             Attach a comment to a rule\n\n"
+               );
+}
+
+static struct option opts[] = {
+       { "comment", 1, 0, '1' },
+       {0}
+};
+
+static void
+parse_comment(const char *s, struct ipt_comment_info *info)
+{      
+       int slen = strlen(s);
+
+       if (slen >= IPT_MAX_COMMENT_LEN) {
+               exit_error(PARAMETER_PROBLEM,
+                       "COMMENT must be shorter than %i characters", IPT_MAX_COMMENT_LEN);
+       }
+       strcpy((char *)info->comment, s);
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_comment_info *commentinfo = (struct ipt_comment_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               if (invert) {
+                       exit_error(PARAMETER_PROBLEM,
+                                       "Sorry, you can't have an inverted comment");
+               }
+               parse_comment(argv[optind-1], commentinfo);
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+/* Final check; must have specified --comment. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "COMMENT match: You must specify `--comment'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_comment_info *commentinfo = (struct ipt_comment_info *)match->data;
+
+       commentinfo->comment[IPT_MAX_COMMENT_LEN-1] = '\0';
+       printf("/* %s */ ", commentinfo->comment);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_comment_info *commentinfo = (struct ipt_comment_info *)match->data;
+
+       commentinfo->comment[IPT_MAX_COMMENT_LEN-1] = '\0';
+       printf("--comment \"%s\" ", commentinfo->comment);
+}
+
+static struct iptables_match comment = {
+    .next              = NULL,
+    .name              = "comment",
+    .version           = IPTABLES_VERSION,
+    .size              = IPT_ALIGN(sizeof(struct ipt_comment_info)),
+    .userspacesize     = IPT_ALIGN(sizeof(struct ipt_comment_info)),
+    .help              = &help,
+    .parse             = &parse,
+    .final_check       = &final_check,
+    .print             = &print,
+    .save              = &save,
+    .extra_opts                = opts
+};
+
+void _init(void)
+{
+       register_match(&comment);
+}
diff --git a/extensions/libipt_comment.man b/extensions/libipt_comment.man
new file mode 100644 (file)
index 0000000..2f4ce55
--- /dev/null
@@ -0,0 +1,6 @@
+Allows you to add comments (up to 256 characters) to any rule.
+.TP
+.BI "--comment " "comment"
+.TP
+Example:
+iptables -A INPUT -s 192.168.0.0/16 -m comment --comment "A privatized IP block"
diff --git a/extensions/libipt_condition.c b/extensions/libipt_condition.c
new file mode 100644 (file)
index 0000000..16558fe
--- /dev/null
@@ -0,0 +1,106 @@
+/* Shared library add-on to iptables for condition match */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <iptables.h>
+
+#include<linux/netfilter_ipv4/ip_tables.h>
+#include<linux/netfilter_ipv4/ipt_condition.h>
+
+
+static void
+help(void)
+{
+       printf("condition match v%s options:\n"
+              "--condition [!] filename       "
+              "Match on boolean value stored in /proc file\n",
+              IPTABLES_VERSION);
+}
+
+
+static struct option opts[] = {
+       { .name = "condition", .has_arg = 1, .flag = 0, .val = 'X' },
+       { .name = 0 }
+};
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry, unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct condition_info *info =
+           (struct condition_info *) (*match)->data;
+
+       if (c == 'X') {
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify multiple conditions");
+
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (strlen(argv[optind - 1]) < CONDITION_NAME_LEN)
+                       strcpy(info->name, argv[optind - 1]);
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "File name too long");
+
+               info->invert = invert;
+               *flags = 1;
+               return 1;
+       }
+
+       return 0;
+}
+
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "Condition match: must specify --condition");
+}
+
+
+static void
+print(const struct ipt_ip *ip,
+                 const struct ipt_entry_match *match, int numeric)
+{
+       const struct condition_info *info =
+           (const struct condition_info *) match->data;
+
+       printf("condition %s%s ", (info->invert) ? "!" : "", info->name);
+}
+
+
+static void
+save(const struct ipt_ip *ip,
+                const struct ipt_entry_match *match)
+{
+       const struct condition_info *info =
+           (const struct condition_info *) match->data;
+
+       printf("--condition %s\"%s\" ", (info->invert) ? "! " : "", info->name);
+}
+
+
+static struct iptables_match condition = {
+       .name           = "condition",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct condition_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct condition_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+
+void
+_init(void)
+{
+       register_match(&condition);
+}
diff --git a/extensions/libipt_condition.man b/extensions/libipt_condition.man
new file mode 100644 (file)
index 0000000..ce2aa95
--- /dev/null
@@ -0,0 +1,4 @@
+This matches if a specific /proc filename is '0' or '1'.
+.TP
+.BI "--condition " "[!] \fIfilename\fP"
+Match on boolean value stored in /proc/net/ipt_condition/filename file
diff --git a/extensions/libipt_connbytes.c b/extensions/libipt_connbytes.c
new file mode 100644 (file)
index 0000000..f6439c2
--- /dev/null
@@ -0,0 +1,205 @@
+/* Shared library add-on to iptables to add byte tracking support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <linux/netfilter_ipv4/ipt_connbytes.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"connbytes v%s options:\n"
+" [!] --connbytes from:[to]\n"
+"     --connbytes-dir [original, reply, both]\n"
+"     --connbytes-mode [packets, bytes, avgpkt]\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "connbytes", 1, 0, '1' },
+       { "connbytes-dir", 1, 0, '2' },
+       { "connbytes-mode", 1, 0, '3' },
+       {0}
+};
+
+static void
+parse_range(const char *arg, struct ipt_connbytes_info *si)
+{
+       char *colon,*p;
+
+       si->count.from = strtoul(arg,&colon,10);
+       if (*colon != ':') 
+               exit_error(PARAMETER_PROBLEM, "Bad range `%s'", arg);
+       si->count.to = strtoul(colon+1,&p,10);
+       if (p == colon+1) {
+               /* second number omited */
+               si->count.to = 0xffffffff;
+       }
+       if (si->count.from > si->count.to)
+               exit_error(PARAMETER_PROBLEM, "%llu should be less than %llu",
+                          si->count.from, si->count.to);
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_connbytes_info *sinfo = (struct ipt_connbytes_info *)(*match)->data;
+       unsigned long i;
+
+       switch (c) {
+       case '1':
+               if (check_inverse(optarg, &invert, &optind, 0))
+                       optind++;
+
+               parse_range(argv[optind-1], sinfo);
+               if (invert) {
+                       i = sinfo->count.from;
+                       sinfo->count.from = sinfo->count.to;
+                       sinfo->count.to = i;
+               }
+               *flags |= 1;
+               break;
+       case '2':
+               if (!strcmp(optarg, "original"))
+                       sinfo->direction = IPT_CONNBYTES_DIR_ORIGINAL;
+               else if (!strcmp(optarg, "reply"))
+                       sinfo->direction = IPT_CONNBYTES_DIR_REPLY;
+               else if (!strcmp(optarg, "both"))
+                       sinfo->direction = IPT_CONNBYTES_DIR_BOTH;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unknown --connbytes-dir `%s'", optarg);
+
+               *flags |= 2;
+               break;
+       case '3':
+               if (!strcmp(optarg, "packets"))
+                       sinfo->what = IPT_CONNBYTES_PKTS;
+               else if (!strcmp(optarg, "bytes"))
+                       sinfo->what = IPT_CONNBYTES_BYTES;
+               else if (!strcmp(optarg, "avgpkt"))
+                       sinfo->what = IPT_CONNBYTES_AVGPKT;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unknown --connbytes-mode `%s'", optarg);
+               *flags |= 4;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (flags != 7)
+               exit_error(PARAMETER_PROBLEM, "You must specify `--connbytes'"
+                          "`--connbytes-dir' and `--connbytes-mode'");
+}
+
+static void print_mode(struct ipt_connbytes_info *sinfo)
+{
+       switch (sinfo->what) {
+               case IPT_CONNBYTES_PKTS:
+                       fputs("packets ", stdout);
+                       break;
+               case IPT_CONNBYTES_BYTES:
+                       fputs("bytes ", stdout);
+                       break;
+               case IPT_CONNBYTES_AVGPKT:
+                       fputs("avgpkt ", stdout);
+                       break;
+               default:
+                       fputs("unknown ", stdout);
+                       break;
+       }
+}
+
+static void print_direction(struct ipt_connbytes_info *sinfo)
+{
+       switch (sinfo->direction) {
+               case IPT_CONNBYTES_DIR_ORIGINAL:
+                       fputs("original ", stdout);
+                       break;
+               case IPT_CONNBYTES_DIR_REPLY:
+                       fputs("reply ", stdout);
+                       break;
+               case IPT_CONNBYTES_DIR_BOTH:
+                       fputs("both ", stdout);
+                       break;
+               default:
+                       fputs("unknown ", stdout);
+                       break;
+       }
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_connbytes_info *sinfo = (struct ipt_connbytes_info *)match->data;
+
+       if (sinfo->count.from > sinfo->count.to) 
+               printf("connbytes ! %llu:%llu ", sinfo->count.to,
+                       sinfo->count.from);
+       else
+               printf("connbytes %llu:%llu ",sinfo->count.from,
+                       sinfo->count.to);
+
+       fputs("connbytes mode ", stdout);
+       print_mode(sinfo);
+
+       fputs("connbytes direction ", stdout);
+       print_direction(sinfo);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_connbytes_info *sinfo = (struct ipt_connbytes_info *)match->data;
+
+       if (sinfo->count.from > sinfo->count.to) 
+               printf("! --connbytes %llu:%llu ", sinfo->count.to,
+                       sinfo->count.from);
+       else
+               printf("--connbytes %llu:%llu ", sinfo->count.from,
+                       sinfo->count.to);
+
+       fputs("--connbytes-mode ", stdout);
+       print_mode(sinfo);
+
+       fputs("--connbytes-dir ", stdout);
+       print_direction(sinfo);
+}
+
+static struct iptables_match state = {
+       .next           = NULL,
+       .name           = "connbytes",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_connbytes_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_connbytes_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&state);
+}
diff --git a/extensions/libipt_connbytes.man b/extensions/libipt_connbytes.man
new file mode 100644 (file)
index 0000000..ce7b665
--- /dev/null
@@ -0,0 +1,30 @@
+Match by how many bytes or packets a connection (or one of the two
+flows constituting the connection) have tranferred so far, or by
+average bytes per packet.
+
+The counters are 64bit and are thus not expected to overflow ;)
+
+The primary use is to detect long-lived downloads and mark them to be
+scheduled using a lower priority band in traffic control.
+
+The transfered bytes per connection can also be viewed through
+/proc/net/ip_conntrack and accessed via ctnetlink
+.TP
+[\fB!\fR]\fB --connbytes \fIfrom\fB:\fR[\fIto\fR]
+match packets from a connection whose packets/bytes/average packet
+size is more than FROM and less than TO bytes/packets. if TO is
+omitted only FROM check is done. "!" is used to match packets not
+falling in the range.
+.TP
+\fB--connbytes-dir\fR [\fBoriginal\fR|\fBreply\fR|\fBboth\fR]
+which packets to consider
+.TP
+\fB--connbytes-mode\fR [\fBpackets\fR|\fBbytes\fR|\fBavgpkt\fR]
+whether to check the amount of packets, number of bytes transferred or
+the average size (in bytes) of all packets received so far. Note that
+when "both" is used together with "avgpkt", and data is going (mainly)
+only in one direction (for example HTTP), the average packet size will
+be about half of the actual data packets.
+.TP
+Example:
+iptables .. -m connbytes --connbytes 10000:100000 --connbytes-dir both --connbytes-mode bytes ...
diff --git a/extensions/libipt_connmark.c b/extensions/libipt_connmark.c
new file mode 100644 (file)
index 0000000..bc15f0d
--- /dev/null
@@ -0,0 +1,151 @@
+/* Shared library add-on to iptables to add connmark matching support.
+ *
+ * (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * Version 1.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include "../include/linux/netfilter_ipv4/ipt_connmark.h"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"CONNMARK match v%s options:\n"
+"[!] --mark value[/mask]         Match nfmark value with optional mask\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "mark", 1, 0, '1' },
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       /* Can't cache this. */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_connmark_info *markinfo = (struct ipt_connmark_info *)(*match)->data;
+
+       switch (c) {
+               char *end;
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               markinfo->mark = strtoul(optarg, &end, 0);
+               markinfo->mask = 0xffffffffUL;
+               
+               if (*end == '/')
+                       markinfo->mask = strtoul(end+1, &end, 0);
+
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
+               if (invert)
+                       markinfo->invert = 1;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+print_mark(unsigned long mark, unsigned long mask, int numeric)
+{
+       if(mask != 0xffffffffUL)
+               printf("0x%lx/0x%lx ", mark, mask);
+       else
+               printf("0x%lx ", mark);
+}
+
+/* Final check; must have specified --mark. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK match: You must specify `--mark'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_connmark_info *info = (struct ipt_connmark_info *)match->data;
+
+       printf("CONNMARK match ");
+       if (info->invert)
+               printf("!");
+       print_mark(info->mark, info->mask, numeric);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_connmark_info *info = (struct ipt_connmark_info *)match->data;
+
+       if (info->invert)
+               printf("! ");
+
+       printf("--mark ");
+       print_mark(info->mark, info->mask, 0);
+}
+
+static struct iptables_match connmark_match = {
+    .name          = "connmark",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_connmark_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_connmark_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_match(&connmark_match);
+}
diff --git a/extensions/libipt_connmark.man b/extensions/libipt_connmark.man
new file mode 100644 (file)
index 0000000..a8e0600
--- /dev/null
@@ -0,0 +1,9 @@
+This module matches the netfilter mark field associated with a connection
+(which can be set using the
+.B CONNMARK
+target below).
+.TP
+.BI "--mark " "value[/mask]"
+Matches packets in connections with the given mark value (if a mask is
+specified, this is logically ANDed with the mark before the
+comparison).
diff --git a/extensions/libipt_connrate.c b/extensions/libipt_connrate.c
new file mode 100644 (file)
index 0000000..5abe3c4
--- /dev/null
@@ -0,0 +1,179 @@
+/* Shared library add-on to iptables to add connection rate tracking
+ * support.
+ *
+ * Copyright (c) 2004 Nuutti Kotivuori <naked@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ **/
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <linux/netfilter_ipv4/ipt_connrate.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"connrate v%s options:\n"
+" --connrate [!] [from]:[to]\n"
+"                              Match connection transfer rate in bytes\n"
+"                              per second. `inf' can be used for maximum\n"
+"                              expressible value.\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "connrate", 1, 0, '1' },
+       {0}
+};
+
+static u_int32_t
+parse_value(const char *arg, u_int32_t def)
+{
+       char *end;
+       size_t len;
+       u_int32_t value;
+
+       len = strlen(arg);
+       if(len == 0)
+               return def;
+       if(strcmp(arg, "inf") == 0)
+               return 0xFFFFFFFF;
+       value = strtoul(arg, &end, 0);
+       if(*end != '\0')
+               exit_error(PARAMETER_PROBLEM,
+                          "Bad value in range `%s'", arg);
+       return value;
+}
+
+static void
+parse_range(const char *arg, struct ipt_connrate_info *si)
+{
+       char *buffer;
+       char *colon;
+
+       buffer = strdup(arg);
+       if ((colon = strchr(buffer, ':')) == NULL)
+               exit_error(PARAMETER_PROBLEM, "Bad range `%s'", arg);
+       *colon = '\0';
+       si->from = parse_value(buffer, 0);
+       si->to = parse_value(colon+1, 0xFFFFFFFF);
+       if (si->from > si->to)
+               exit_error(PARAMETER_PROBLEM, "%u should be less than %u", si->from,si->to);
+       free(buffer);
+}
+
+#define CONNRATE_OPT 0x01
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_connrate_info *sinfo = (struct ipt_connrate_info *)(*match)->data;
+       u_int32_t tmp;
+
+       switch (c) {
+       case '1':
+               if (*flags & CONNRATE_OPT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--connrate' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_range(argv[optind-1], sinfo);
+               if (invert) {
+                       tmp = sinfo->from;
+                       sinfo->from = sinfo->to;
+                       sinfo->to = tmp;
+               }
+               *flags |= CONNRATE_OPT;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!(flags & CONNRATE_OPT))
+               exit_error(PARAMETER_PROBLEM,
+                          "connrate match: You must specify `--connrate'");
+}
+
+static void
+print_value(u_int32_t value)
+{
+       if(value == 0xFFFFFFFF)
+               printf("inf");
+       else
+               printf("%u", value);
+}
+
+static void
+print_range(struct ipt_connrate_info *sinfo)
+{
+       if (sinfo->from > sinfo->to) {
+               printf("! ");
+               print_value(sinfo->to);
+               printf(":");
+               print_value(sinfo->from);
+       } else {
+               print_value(sinfo->from);
+               printf(":");
+               print_value(sinfo->to);
+       }
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_connrate_info *sinfo = (struct ipt_connrate_info *)match->data;
+
+       printf("connrate ");
+       print_range(sinfo);
+       printf(" ");
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_connrate_info *sinfo = (struct ipt_connrate_info *)match->data;
+
+       printf("--connrate ");
+       print_range(sinfo);
+       printf(" ");
+}
+
+static struct iptables_match state = { 
+       .next           = NULL,
+       .name           = "connrate",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_connrate_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_connrate_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&state);
+}
diff --git a/extensions/libipt_connrate.man b/extensions/libipt_connrate.man
new file mode 100644 (file)
index 0000000..45cba9d
--- /dev/null
@@ -0,0 +1,6 @@
+This module matches the current transfer rate in a connection.
+.TP
+.BI "--connrate " "[!] [\fIfrom\fP]:[\fIto\fP]"
+Match against the current connection transfer rate being within 'from'
+and 'to' bytes per second. When the "!" argument is used before the
+range, the sense of the match is inverted.
diff --git a/extensions/libipt_conntrack.c b/extensions/libipt_conntrack.c
new file mode 100644 (file)
index 0000000..2b62a8c
--- /dev/null
@@ -0,0 +1,549 @@
+/* Shared library add-on to iptables for conntrack matching support.
+ * GPL (C) 2001  Marc Boucher (marc@mbsi.ca).
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <iptables.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv4/ipt_conntrack.h"
+
+#ifndef IPT_CONNTRACK_STATE_UNTRACKED
+#define IPT_CONNTRACK_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 3))
+#endif
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"conntrack match v%s options:\n"
+" [!] --ctstate [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT][,...]\n"
+"                              State(s) to match\n"
+" [!] --ctproto        proto           Protocol to match; by number or name, eg. `tcp'\n"
+"     --ctorigsrc  [!] address[/mask]\n"
+"                              Original source specification\n"
+"     --ctorigdst  [!] address[/mask]\n"
+"                              Original destination specification\n"
+"     --ctreplsrc  [!] address[/mask]\n"
+"                              Reply source specification\n"
+"     --ctrepldst  [!] address[/mask]\n"
+"                              Reply destination specification\n"
+" [!] --ctstatus [NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED][,...]\n"
+"                              Status(es) to match\n"
+" [!] --ctexpire time[:time]   Match remaining lifetime in seconds against\n"
+"                              value or range of values (inclusive)\n"
+"\n", IPTABLES_VERSION);
+}
+
+
+
+static struct option opts[] = {
+       { "ctstate", 1, 0, '1' },
+       { "ctproto", 1, 0, '2' },
+       { "ctorigsrc", 1, 0, '3' },
+       { "ctorigdst", 1, 0, '4' },
+       { "ctreplsrc", 1, 0, '5' },
+       { "ctrepldst", 1, 0, '6' },
+       { "ctstatus", 1, 0, '7' },
+       { "ctexpire", 1, 0, '8' },
+       {0}
+};
+
+static int
+parse_state(const char *state, size_t strlen, struct ipt_conntrack_info *sinfo)
+{
+       if (strncasecmp(state, "INVALID", strlen) == 0)
+               sinfo->statemask |= IPT_CONNTRACK_STATE_INVALID;
+       else if (strncasecmp(state, "NEW", strlen) == 0)
+               sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_NEW);
+       else if (strncasecmp(state, "ESTABLISHED", strlen) == 0)
+               sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
+       else if (strncasecmp(state, "RELATED", strlen) == 0)
+               sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
+       else if (strncasecmp(state, "UNTRACKED", strlen) == 0)
+               sinfo->statemask |= IPT_CONNTRACK_STATE_UNTRACKED;
+       else if (strncasecmp(state, "SNAT", strlen) == 0)
+               sinfo->statemask |= IPT_CONNTRACK_STATE_SNAT;
+       else if (strncasecmp(state, "DNAT", strlen) == 0)
+               sinfo->statemask |= IPT_CONNTRACK_STATE_DNAT;
+       else
+               return 0;
+       return 1;
+}
+
+static void
+parse_states(const char *arg, struct ipt_conntrack_info *sinfo)
+{
+       const char *comma;
+
+       while ((comma = strchr(arg, ',')) != NULL) {
+               if (comma == arg || !parse_state(arg, comma-arg, sinfo))
+                       exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
+               arg = comma+1;
+       }
+
+       if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
+               exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
+}
+
+static int
+parse_status(const char *status, size_t strlen, struct ipt_conntrack_info *sinfo)
+{
+       if (strncasecmp(status, "NONE", strlen) == 0)
+               sinfo->statusmask |= 0;
+       else if (strncasecmp(status, "EXPECTED", strlen) == 0)
+               sinfo->statusmask |= IPS_EXPECTED;
+       else if (strncasecmp(status, "SEEN_REPLY", strlen) == 0)
+               sinfo->statusmask |= IPS_SEEN_REPLY;
+       else if (strncasecmp(status, "ASSURED", strlen) == 0)
+               sinfo->statusmask |= IPS_ASSURED;
+#ifdef IPS_CONFIRMED
+       else if (strncasecmp(status, "CONFIRMED", strlen) == 0)
+               sinfo->stausmask |= IPS_CONFIRMED;
+#endif
+       else
+               return 0;
+       return 1;
+}
+
+static void
+parse_statuses(const char *arg, struct ipt_conntrack_info *sinfo)
+{
+       const char *comma;
+
+       while ((comma = strchr(arg, ',')) != NULL) {
+               if (comma == arg || !parse_status(arg, comma-arg, sinfo))
+                       exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
+               arg = comma+1;
+       }
+
+       if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo))
+               exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
+}
+
+#ifdef KERNEL_64_USERSPACE_32
+static unsigned long long
+parse_expire(const char *s)
+{
+       unsigned long long len;
+       
+       if (string_to_number_ll(s, 0, 0, &len) == -1)
+               exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
+       else
+               return len;
+}
+#else
+static unsigned long
+parse_expire(const char *s)
+{
+       unsigned int len;
+       
+       if (string_to_number(s, 0, 0, &len) == -1)
+               exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
+       else
+               return len;
+}
+#endif
+
+/* If a single value is provided, min and max are both set to the value */
+static void
+parse_expires(const char *s, struct ipt_conntrack_info *sinfo)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(s);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               sinfo->expires_min = sinfo->expires_max = parse_expire(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0;
+               sinfo->expires_max = cp[0] ? parse_expire(cp) : -1;
+       }
+       free(buffer);
+       
+       if (sinfo->expires_min > sinfo->expires_max)
+               exit_error(PARAMETER_PROBLEM,
+#ifdef KERNEL_64_USERSPACE_32
+                          "expire min. range value `%llu' greater than max. "
+                          "range value `%llu'", sinfo->expires_min, sinfo->expires_max);
+#else
+                          "expire min. range value `%lu' greater than max. "
+                          "range value `%lu'", sinfo->expires_min, sinfo->expires_max);
+#endif
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_conntrack_info *sinfo = (struct ipt_conntrack_info *)(*match)->data;
+       char *protocol = NULL;
+       unsigned int naddrs = 0;
+       struct in_addr *addrs = NULL;
+
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               parse_states(argv[optind-1], sinfo);
+               if (invert) {
+                       sinfo->invflags |= IPT_CONNTRACK_STATE;
+               }
+               sinfo->flags |= IPT_CONNTRACK_STATE;
+               break;
+
+       case '2':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if(invert)
+                       sinfo->invflags |= IPT_CONNTRACK_PROTO;
+
+               /* Canonicalize into lower case */
+               for (protocol = argv[optind-1]; *protocol; protocol++)
+                       *protocol = tolower(*protocol);
+
+               protocol = argv[optind-1];
+               sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = parse_protocol(protocol);
+
+               if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
+                   && (sinfo->invflags & IPT_INV_PROTO))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "rule would never match protocol");
+
+               sinfo->flags |= IPT_CONNTRACK_PROTO;
+               break;
+
+       case '3':
+               check_inverse(optarg, &invert, &optind, 9);
+
+               if (invert)
+                       sinfo->invflags |= IPT_CONNTRACK_ORIGSRC;
+
+               parse_hostnetworkmask(argv[optind-1], &addrs,
+                                       &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
+                                       &naddrs);
+               if(naddrs > 1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "multiple IP addresses not allowed");
+
+               if(naddrs == 1) {
+                       sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr;
+               }
+
+               sinfo->flags |= IPT_CONNTRACK_ORIGSRC;
+               break;
+
+       case '4':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (invert)
+                       sinfo->invflags |= IPT_CONNTRACK_ORIGDST;
+
+               parse_hostnetworkmask(argv[optind-1], &addrs,
+                                       &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
+                                       &naddrs);
+               if(naddrs > 1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "multiple IP addresses not allowed");
+
+               if(naddrs == 1) {
+                       sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr;
+               }
+
+               sinfo->flags |= IPT_CONNTRACK_ORIGDST;
+               break;
+
+       case '5':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (invert)
+                       sinfo->invflags |= IPT_CONNTRACK_REPLSRC;
+
+               parse_hostnetworkmask(argv[optind-1], &addrs,
+                                       &sinfo->sipmsk[IP_CT_DIR_REPLY],
+                                       &naddrs);
+               if(naddrs > 1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "multiple IP addresses not allowed");
+
+               if(naddrs == 1) {
+                       sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr;
+               }
+
+               sinfo->flags |= IPT_CONNTRACK_REPLSRC;
+               break;
+
+       case '6':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (invert)
+                       sinfo->invflags |= IPT_CONNTRACK_REPLDST;
+
+               parse_hostnetworkmask(argv[optind-1], &addrs,
+                                       &sinfo->dipmsk[IP_CT_DIR_REPLY],
+                                       &naddrs);
+               if(naddrs > 1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "multiple IP addresses not allowed");
+
+               if(naddrs == 1) {
+                       sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr;
+               }
+
+               sinfo->flags |= IPT_CONNTRACK_REPLDST;
+               break;
+
+       case '7':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               parse_statuses(argv[optind-1], sinfo);
+               if (invert) {
+                       sinfo->invflags |= IPT_CONNTRACK_STATUS;
+               }
+               sinfo->flags |= IPT_CONNTRACK_STATUS;
+               break;
+
+       case '8':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               parse_expires(argv[optind-1], sinfo);
+               if (invert) {
+                       sinfo->invflags |= IPT_CONNTRACK_EXPIRES;
+               }
+               sinfo->flags |= IPT_CONNTRACK_EXPIRES;
+               break;
+
+       default:
+               return 0;
+       }
+
+       *flags = sinfo->flags;
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, "You must specify one or more options");
+}
+
+static void
+print_state(unsigned int statemask)
+{
+       const char *sep = "";
+
+       if (statemask & IPT_CONNTRACK_STATE_INVALID) {
+               printf("%sINVALID", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
+               printf("%sNEW", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
+               printf("%sRELATED", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
+               printf("%sESTABLISHED", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_CONNTRACK_STATE_UNTRACKED) {
+               printf("%sUNTRACKED", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_CONNTRACK_STATE_SNAT) {
+               printf("%sSNAT", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_CONNTRACK_STATE_DNAT) {
+               printf("%sDNAT", sep);
+               sep = ",";
+       }
+       printf(" ");
+}
+
+static void
+print_status(unsigned int statusmask)
+{
+       const char *sep = "";
+
+       if (statusmask & IPS_EXPECTED) {
+               printf("%sEXPECTED", sep);
+               sep = ",";
+       }
+       if (statusmask & IPS_SEEN_REPLY) {
+               printf("%sSEEN_REPLY", sep);
+               sep = ",";
+       }
+       if (statusmask & IPS_ASSURED) {
+               printf("%sASSURED", sep);
+               sep = ",";
+       }
+#ifdef IPS_CONFIRMED
+       if (statusmask & IPS_CONFIRMED) {
+               printf("%sCONFIRMED", sep);
+               sep =",";
+       }
+#endif
+       if (statusmask == 0) {
+               printf("%sNONE", sep);
+               sep = ",";
+       }
+       printf(" ");
+}
+
+static void
+print_addr(struct in_addr *addr, struct in_addr *mask, int inv, int numeric)
+{
+       char buf[BUFSIZ];
+
+        if (inv) 
+                       printf("! ");
+
+       if (mask->s_addr == 0L && !numeric)
+               printf("%s ", "anywhere");
+       else {
+               if (numeric)
+                       sprintf(buf, "%s", addr_to_dotted(addr));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(addr));
+               strcat(buf, mask_to_dotted(mask));
+               printf("%s ", buf);
+       }
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void
+matchinfo_print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric, const char *optpfx)
+{
+       struct ipt_conntrack_info *sinfo = (struct ipt_conntrack_info *)match->data;
+
+       if(sinfo->flags & IPT_CONNTRACK_STATE) {
+               printf("%sctstate ", optpfx);
+               if (sinfo->invflags & IPT_CONNTRACK_STATE)
+                       printf("! ");
+               print_state(sinfo->statemask);
+       }
+
+       if(sinfo->flags & IPT_CONNTRACK_PROTO) {
+               printf("%sctproto ", optpfx);
+               if (sinfo->invflags & IPT_CONNTRACK_PROTO)
+                       printf("! ");
+               printf("%u ", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
+       }
+
+       if(sinfo->flags & IPT_CONNTRACK_ORIGSRC) {
+               printf("%sctorigsrc ", optpfx);
+
+               print_addr(
+                   (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
+                   &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
+                   sinfo->invflags & IPT_CONNTRACK_ORIGSRC,
+                   numeric);
+       }
+
+       if(sinfo->flags & IPT_CONNTRACK_ORIGDST) {
+               printf("%sctorigdst ", optpfx);
+
+               print_addr(
+                   (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
+                   &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
+                   sinfo->invflags & IPT_CONNTRACK_ORIGDST,
+                   numeric);
+       }
+
+       if(sinfo->flags & IPT_CONNTRACK_REPLSRC) {
+               printf("%sctreplsrc ", optpfx);
+
+               print_addr(
+                   (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
+                   &sinfo->sipmsk[IP_CT_DIR_REPLY],
+                   sinfo->invflags & IPT_CONNTRACK_REPLSRC,
+                   numeric);
+       }
+
+       if(sinfo->flags & IPT_CONNTRACK_REPLDST) {
+               printf("%sctrepldst ", optpfx);
+
+               print_addr(
+                   (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
+                   &sinfo->dipmsk[IP_CT_DIR_REPLY],
+                   sinfo->invflags & IPT_CONNTRACK_REPLDST,
+                   numeric);
+       }
+
+       if(sinfo->flags & IPT_CONNTRACK_STATUS) {
+               printf("%sctstatus ", optpfx);
+               if (sinfo->invflags & IPT_CONNTRACK_STATUS)
+                       printf("! ");
+               print_status(sinfo->statusmask);
+       }
+
+       if(sinfo->flags & IPT_CONNTRACK_EXPIRES) {
+               printf("%sctexpire ", optpfx);
+               if (sinfo->invflags & IPT_CONNTRACK_EXPIRES)
+                       printf("! ");
+
+#ifdef KERNEL_64_USERSPACE_32
+               if (sinfo->expires_max == sinfo->expires_min)
+                       printf("%llu ", sinfo->expires_min);
+               else
+                       printf("%llu:%llu ", sinfo->expires_min, sinfo->expires_max);
+#else
+               if (sinfo->expires_max == sinfo->expires_min)
+                       printf("%lu ", sinfo->expires_min);
+               else
+                       printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max);
+#endif
+       }
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       matchinfo_print(ip, match, numeric, "");
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       matchinfo_print(ip, match, 1, "--");
+}
+
+static struct iptables_match conntrack = { 
+       .next           = NULL,
+       .name           = "conntrack",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&conntrack);
+}
diff --git a/extensions/libipt_conntrack.man b/extensions/libipt_conntrack.man
new file mode 100644 (file)
index 0000000..b732b28
--- /dev/null
@@ -0,0 +1,49 @@
+This module, when combined with connection tracking, allows access to
+more connection tracking information than the "state" match.
+(this module is present only if iptables was compiled under a kernel
+supporting this feature)
+.TP
+.BI "--ctstate " "state"
+Where state is a comma separated list of the connection states to
+match.  Possible states are
+.B INVALID
+meaning that the packet is associated with no known connection,
+.B ESTABLISHED
+meaning that the packet is associated with a connection which has seen
+packets in both directions,
+.B NEW
+meaning that the packet has started a new connection, or otherwise
+associated with a connection which has not seen packets in both
+directions, and
+.B RELATED
+meaning that the packet is starting a new connection, but is
+associated with an existing connection, such as an FTP data transfer,
+or an ICMP error.
+.B SNAT
+A virtual state, matching if the original source address differs from
+the reply destination.
+.B DNAT
+A virtual state, matching if the original destination differs from the
+reply source.
+.TP
+.BI "--ctproto " "proto"
+Protocol to match (by number or name)
+.TP
+.BI "--ctorigsrc " "[!] \fIaddress\fP[/\fImask\fP]"
+Match against original source address
+.TP
+.BI "--ctorigdst " "[!] \fIaddress\fP[/\fImask\fP]"
+Match against original destination address
+.TP
+.BI "--ctreplsrc " "[!] \fIaddress\fP[/\fImask\fP]"
+Match against reply source address
+.TP
+.BI "--ctrepldst " "[!] \fIaddress\fB[/\fImask\fP]"
+Match against reply destination address
+.TP
+.BI "--ctstatus " "[\fINONE|EXPECTED|SEEN_REPLY|ASSURED\fP][,...]"
+Match against internal conntrack states
+.TP
+.BI "--ctexpire " "\fItime\fP[\fI:time\fP]"
+Match remaining lifetime in seconds against given value
+or range of values (inclusive)
diff --git a/extensions/libipt_dccp.c b/extensions/libipt_dccp.c
new file mode 100644 (file)
index 0000000..e5782a8
--- /dev/null
@@ -0,0 +1,374 @@
+/* Shared library add-on to iptables for DCCP matching
+ *
+ * (C) 2005 by Harald Welte <laforge@netfilter.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <ctype.h>
+
+#include <iptables.h>
+#include <linux/dccp.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_dccp.h>
+
+#if 0
+#define DEBUGP(format, first...) printf(format, ##first)
+#define static
+#else
+#define DEBUGP(format, fist...) 
+#endif
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, 
+     unsigned int *nfcache)
+{
+       struct ipt_dccp_info *einfo = (struct ipt_dccp_info *)m->data;
+
+       memset(einfo, 0, sizeof(struct ipt_dccp_info));
+}
+
+static void help(void)
+{
+       printf(
+"DCCP match v%s options\n"
+" --source-port [!] port[:port]                          match source port(s)\n"
+" --sport ...\n"
+" --destination-port [!] port[:port]                     match destination port(s)\n"
+" --dport ...\n"
+,
+       IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "source-port", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "sport", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "destination-port", .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "dport", .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "dccp-types", .has_arg = 1, .flag = 0, .val = '3' },
+       { .name = "dccp-option", .has_arg = 1, .flag = 0, .val = '4' },
+       { .name = 0 }
+};
+
+static void
+parse_dccp_ports(const char *portstring, 
+                u_int16_t *ports)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(portstring);
+       DEBUGP("%s\n", portstring);
+       if ((cp = strchr(buffer, ':')) == NULL) {
+               ports[0] = ports[1] = parse_port(buffer, "dccp");
+       }
+       else {
+               *cp = '\0';
+               cp++;
+
+               ports[0] = buffer[0] ? parse_port(buffer, "dccp") : 0;
+               ports[1] = cp[0] ? parse_port(cp, "dccp") : 0xFFFF;
+
+               if (ports[0] > ports[1])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "invalid portrange (min > max)");
+       }
+       free(buffer);
+}
+
+static char *dccp_pkt_types[] = {
+       [DCCP_PKT_REQUEST]      = "REQUEST",
+       [DCCP_PKT_RESPONSE]     = "RESPONSE",
+       [DCCP_PKT_DATA]         = "DATA",
+       [DCCP_PKT_ACK]          = "ACK",
+       [DCCP_PKT_DATAACK]      = "DATAACK",
+       [DCCP_PKT_CLOSEREQ]     = "CLOSEREQ",
+       [DCCP_PKT_CLOSE]        = "CLOSE",
+       [DCCP_PKT_RESET]        = "RESET",
+       [DCCP_PKT_SYNC]         = "SYNC",
+       [DCCP_PKT_SYNCACK]      = "SYNCACK",
+       [DCCP_PKT_INVALID]      = "INVALID",
+};
+
+static u_int16_t
+parse_dccp_types(const char *typestring)
+{
+       u_int16_t typemask = 0;
+       char *ptr, *buffer;
+
+       buffer = strdup(typestring);
+
+       for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
+               unsigned int i;
+               for (i = 0; i < sizeof(dccp_pkt_types)/sizeof(char *); i++) {
+                       if (!strcasecmp(dccp_pkt_types[i], ptr)) {
+                               typemask |= (1 << i);
+                               break;
+                       }
+               }
+               if (i == sizeof(dccp_pkt_types)/sizeof(char *))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unknown DCCP type `%s'", ptr);
+       }
+
+       free(buffer);
+       return typemask;
+}
+
+static u_int8_t parse_dccp_option(char *optstring)
+{
+       unsigned int ret;
+
+       if (string_to_number(optstring, 1, 255, &ret) == -1)
+               exit_error(PARAMETER_PROBLEM, "Bad DCCP option `%s'",
+                          optstring);
+
+       return (u_int8_t)ret;
+}
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_dccp_info *einfo
+               = (struct ipt_dccp_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IPT_DCCP_SRC_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--source-port' allowed");
+               einfo->flags |= IPT_DCCP_SRC_PORTS;
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_dccp_ports(argv[optind-1], einfo->spts);
+               if (invert)
+                       einfo->invflags |= IPT_DCCP_SRC_PORTS;
+               *flags |= IPT_DCCP_SRC_PORTS;
+               break;
+
+       case '2':
+               if (*flags & IPT_DCCP_DEST_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--destination-port' allowed");
+               einfo->flags |= IPT_DCCP_DEST_PORTS;
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_dccp_ports(argv[optind-1], einfo->dpts);
+               if (invert)
+                       einfo->invflags |= IPT_DCCP_DEST_PORTS;
+               *flags |= IPT_DCCP_DEST_PORTS;
+               break;
+
+       case '3':
+               if (*flags & IPT_DCCP_TYPE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--dccp-types' allowed");
+               einfo->flags |= IPT_DCCP_TYPE;
+               check_inverse(optarg, &invert, &optind, 0);
+               einfo->typemask = parse_dccp_types(argv[optind-1]);
+               if (invert)
+                       einfo->invflags |= IPT_DCCP_TYPE;
+               *flags |= IPT_DCCP_TYPE;
+               break;
+
+       case '4':
+               if (*flags & IPT_DCCP_OPTION)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--dccp-option' allowed");
+               einfo->flags |= IPT_DCCP_OPTION;
+               check_inverse(optarg, &invert, &optind, 0);
+               einfo->option = parse_dccp_option(argv[optind-1]);
+               if (invert)
+                       einfo->invflags |= IPT_DCCP_OPTION;
+               *flags |= IPT_DCCP_OPTION;
+               break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+static char *
+port_to_service(int port)
+{
+       struct servent *service;
+
+       if ((service = getservbyport(htons(port), "dccp")))
+               return service->s_name;
+
+       return NULL;
+}
+
+static void
+print_port(u_int16_t port, int numeric)
+{
+       char *service;
+
+       if (numeric || (service = port_to_service(port)) == NULL)
+               printf("%u", port);
+       else
+               printf("%s", service);
+}
+
+static void
+print_ports(const char *name, u_int16_t min, u_int16_t max,
+           int invert, int numeric)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       print_port(min, numeric);
+               } else {
+                       printf("s:%s", inv);
+                       print_port(min, numeric);
+                       printf(":");
+                       print_port(max, numeric);
+               }
+               printf(" ");
+       }
+}
+
+static void
+print_types(u_int16_t types, int inverted, int numeric)
+{
+       int have_type = 0;
+
+       if (inverted)
+               printf("! ");
+
+       while (types) {
+               unsigned int i;
+
+               for (i = 0; !(types & (1 << i)); i++);
+
+               if (have_type)
+                       printf(",");
+               else
+                       have_type = 1;
+
+               if (numeric)
+                       printf("%u", i);
+               else
+                       printf("%s", dccp_pkt_types[i]);
+
+               types &= ~(1 << i);
+       }
+}
+
+static void
+print_option(u_int8_t option, int invert, int numeric)
+{
+       if (option || invert)
+               printf("option=%s%u ", invert ? "!" : "", option);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_dccp_info *einfo =
+               (const struct ipt_dccp_info *)match->data;
+
+       printf("dccp ");
+
+       if (einfo->flags & IPT_DCCP_SRC_PORTS) {
+               print_ports("spt", einfo->spts[0], einfo->spts[1],
+                       einfo->invflags & IPT_DCCP_SRC_PORTS,
+                       numeric);
+       }
+
+       if (einfo->flags & IPT_DCCP_DEST_PORTS) {
+               print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
+                       einfo->invflags & IPT_DCCP_DEST_PORTS,
+                       numeric);
+       }
+
+       if (einfo->flags & IPT_DCCP_TYPE) {
+               print_types(einfo->typemask,
+                          einfo->invflags & IPT_DCCP_TYPE,
+                          numeric);
+       }
+
+       if (einfo->flags & IPT_DCCP_OPTION) {
+               print_option(einfo->option,
+                            einfo->invflags & IPT_DCCP_OPTION, numeric);
+       }
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, 
+     const struct ipt_entry_match *match)
+{
+       const struct ipt_dccp_info *einfo =
+               (const struct ipt_dccp_info *)match->data;
+
+       if (einfo->flags & IPT_DCCP_SRC_PORTS) {
+               if (einfo->invflags & IPT_DCCP_SRC_PORTS)
+                       printf("! ");
+               if (einfo->spts[0] != einfo->spts[1])
+                       printf("--sport %u:%u ", 
+                              einfo->spts[0], einfo->spts[1]);
+               else
+                       printf("--sport %u ", einfo->spts[0]);
+       }
+
+       if (einfo->flags & IPT_DCCP_DEST_PORTS) {
+               if (einfo->invflags & IPT_DCCP_DEST_PORTS)
+                       printf("! ");
+               if (einfo->dpts[0] != einfo->dpts[1])
+                       printf("--dport %u:%u ",
+                              einfo->dpts[0], einfo->dpts[1]);
+               else
+                       printf("--dport %u ", einfo->dpts[0]);
+       }
+
+       if (einfo->flags & IPT_DCCP_TYPE) {
+               printf("--dccp-type ");
+               print_types(einfo->typemask, einfo->invflags & IPT_DCCP_TYPE,0);
+       }
+
+       if (einfo->flags & IPT_DCCP_OPTION) {
+               printf("--dccp-option %s%u ", 
+                       einfo->typemask & IPT_DCCP_OPTION ? "! " : "",
+                       einfo->option);
+       }
+}
+
+static
+struct iptables_match dccp
+= { .name          = "dccp",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_dccp_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_dccp_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_match(&dccp);
+}
+
diff --git a/extensions/libipt_dccp.man b/extensions/libipt_dccp.man
new file mode 100644 (file)
index 0000000..6443ec3
--- /dev/null
@@ -0,0 +1,12 @@
+.TP
+\fB--source-port\fR,\fB--sport \fR[\fB!\fR] \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB--destination-port\fR,\fB--dport \fR[\fB!\fR] \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB--dccp-types\fR [\fB!\fR] \fImask\fP
+Match when the DCCP packet type is one of 'mask'. 'mask' is a comma-separated
+list of packet types.  Packet types are: 
+.BR "REQUEST RESPONSE DATA ACK DATAACK CLOSEREQ CLOSE RESET SYNC SYNCACK INVALID" .
+.TP
+\fB--dccp-option\fR [\fB!\fR\] \fInumber\fP
+Match if DCP option set.
diff --git a/extensions/libipt_dscp.c b/extensions/libipt_dscp.c
new file mode 100644 (file)
index 0000000..bb19bed
--- /dev/null
@@ -0,0 +1,172 @@
+/* Shared library add-on to iptables for DSCP
+ *
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_dscp.c borrowed heavily from libipt_tos.c
+ *
+ * --class support added by Iain Barnes
+ * 
+ * For a list of DSCP codepoints see 
+ * http://www.iana.org/assignments/dscp-registry
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_dscp.h>
+
+/* This is evil, but it's my code - HW*/
+#include "libipt_dscp_helper.c"
+
+static void help(void) 
+{
+       printf(
+"DSCP match v%s options\n"
+"[!] --dscp value              Match DSCP codepoint with numerical value\n"
+"                              This value can be in decimal (ex: 32)\n"
+"                              or in hex (ex: 0x20)\n"
+"[!] --dscp-class name         Match the DiffServ class. This value may\n"
+"                              be any of the BE,EF, AFxx or CSx classes\n"
+"\n"
+"                              These two options are mutually exclusive !\n"
+                               , IPTABLES_VERSION
+);
+}
+
+static struct option opts[] = {
+       { "dscp", 1, 0, 'F' },
+       { "dscp-class", 1, 0, 'G' },
+       { 0 }
+};
+
+static void
+parse_dscp(const char *s, struct ipt_dscp_info *dinfo)
+{
+       unsigned int dscp;
+       
+       if (string_to_number(s, 0, 255, &dscp) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid dscp `%s'\n", s);
+
+       if (dscp > IPT_DSCP_MAX)
+               exit_error(PARAMETER_PROBLEM,
+                          "DSCP `%d` out of range\n", dscp);
+
+       dinfo->dscp = (u_int8_t )dscp;
+       return;
+}
+
+
+static void
+parse_class(const char *s, struct ipt_dscp_info *dinfo)
+{
+       unsigned int dscp = class_to_dscp(s);
+
+       /* Assign the value */
+       dinfo->dscp = (u_int8_t)dscp;
+}
+
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_dscp_info *dinfo
+               = (struct ipt_dscp_info *)(*match)->data;
+
+       switch (c) {
+       case 'F':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "DSCP match: Only use --dscp ONCE!");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_dscp(argv[optind-1], dinfo);
+               if (invert)
+                       dinfo->invert = 1;
+               *flags = 1;
+               break;
+
+       case 'G':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                       "DSCP match: Only use --dscp-class ONCE!");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_class(argv[optind - 1], dinfo);
+               if (invert)
+                       dinfo->invert = 1;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "DSCP match: Parameter --dscp is required");
+}
+
+static void
+print_dscp(u_int8_t dscp, int invert, int numeric)
+{
+       if (invert)
+               fputc('!', stdout);
+
+       printf("0x%02x ", dscp);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_dscp_info *dinfo =
+               (const struct ipt_dscp_info *)match->data;
+       printf("DSCP match ");
+       print_dscp(dinfo->dscp, dinfo->invert, numeric);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_dscp_info *dinfo =
+               (const struct ipt_dscp_info *)match->data;
+
+       printf("--dscp ");
+       print_dscp(dinfo->dscp, dinfo->invert, 1);
+}
+
+static struct iptables_match dscp = { 
+       .next           = NULL,
+       .name           = "dscp",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_dscp_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_dscp_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&dscp);
+}
diff --git a/extensions/libipt_dscp.man b/extensions/libipt_dscp.man
new file mode 100644 (file)
index 0000000..4a84210
--- /dev/null
@@ -0,0 +1,10 @@
+This module matches the 6 bit DSCP field within the TOS field in the
+IP header.  DSCP has superseded TOS within the IETF.
+.TP
+.BI "--dscp " "value"
+Match against a numeric (decimal or hex) value [0-32].
+.TP
+.BI "--dscp-class " "\fIDiffServ Class\fP"
+Match the DiffServ class. This value may be any of the
+BE, EF, AFxx or CSx classes.  It will then be converted
+into it's according numeric value.
diff --git a/extensions/libipt_dscp_helper.c b/extensions/libipt_dscp_helper.c
new file mode 100644 (file)
index 0000000..31adb6c
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * DiffServ classname <-> DiffServ codepoint mapping functions.
+ *
+ * The latest list of the mappings can be found at:
+ * <http://www.iana.org/assignments/dscp-registry>
+ *
+ * This code is released under the GNU GPL v2, 1991
+ * 
+ * Author: Iain Barnes
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <iptables_common.h>
+
+
+
+static struct ds_class
+{
+       const char *name;
+       unsigned int dscp;
+} ds_classes[] = 
+{
+       { "CS0", 0x00 },
+       { "CS1", 0x08 },
+       { "CS2", 0x10 },
+       { "CS3", 0x18 },
+       { "CS4", 0x20 },
+       { "CS5", 0x28 },
+       { "CS6", 0x30 },
+       { "CS7", 0x38 },
+       { "BE", 0x00 },
+       { "AF11", 0x0a },
+       { "AF12", 0x0c },
+       { "AF13", 0x0e },
+       { "AF21", 0x12 },
+       { "AF22", 0x14 },
+       { "AF23", 0x16 },
+       { "AF31", 0x1a },
+       { "AF32", 0x1c },
+       { "AF33", 0x1e },
+       { "AF41", 0x22 },
+       { "AF42", 0x24 },
+       { "AF43", 0x26 },
+       { "EF", 0x2e }
+};
+
+
+
+static unsigned int 
+class_to_dscp(const char *name)
+{
+       int i;
+
+       for (i = 0; i < sizeof(ds_classes) / sizeof(struct ds_class); i++) {
+               if (!strncasecmp(name, ds_classes[i].name,
+                                       strlen(ds_classes[i].name))) 
+                       return ds_classes[i].dscp;
+       }
+
+       exit_error(PARAMETER_PROBLEM, 
+                       "Invalid DSCP value `%s'\n", name);
+}
+
+
+#if 0
+static const char *
+dscp_to_name(unsigned int dscp)
+{
+       int i;
+
+       for (i = 0; i < sizeof(ds_classes) / sizeof(struct ds_class); i++) {
+               if (dscp == ds_classes[i].dscp)
+                       return ds_classes[i].name;
+       }
+
+       
+       exit_error(PARAMETER_PROBLEM,
+                       "Invalid DSCP value `%d'\n", dscp);
+}
+#endif
+
diff --git a/extensions/libipt_ecn.c b/extensions/libipt_ecn.c
new file mode 100644 (file)
index 0000000..97e839d
--- /dev/null
@@ -0,0 +1,171 @@
+/* Shared library add-on to iptables for ECN matching
+ *
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_ecn.c borrowed heavily from libipt_dscp.c
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_ecn.h>
+
+static void help(void) 
+{
+       printf(
+"ECN match v%s options\n"
+"[!] --ecn-tcp-cwr             Match CWR bit of TCP header\n"
+"[!] --ecn-tcp-ece             Match ECE bit of TCP header\n"
+"[!] --ecn-ip-ect [0..3]       Match ECN codepoint in IPv4 header\n",
+       IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "ecn-tcp-cwr", .has_arg = 0, .flag = 0, .val = 'F' },
+       { .name = "ecn-tcp-ece", .has_arg = 0, .flag = 0, .val = 'G' },
+       { .name = "ecn-ip-ect",  .has_arg = 1, .flag = 0, .val = 'H' },
+       { .name = 0 }
+};
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       unsigned int result;
+       struct ipt_ecn_info *einfo
+               = (struct ipt_ecn_info *)(*match)->data;
+
+       switch (c) {
+       case 'F':
+               if (*flags & IPT_ECN_OP_MATCH_CWR)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN match: can only use parameter ONCE!");
+               check_inverse(optarg, &invert, &optind, 0);
+               einfo->operation |= IPT_ECN_OP_MATCH_CWR;
+               if (invert)
+                       einfo->invert |= IPT_ECN_OP_MATCH_CWR;
+               *flags |= IPT_ECN_OP_MATCH_CWR;
+               break;
+
+       case 'G':
+               if (*flags & IPT_ECN_OP_MATCH_ECE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN match: can only use parameter ONCE!");
+               check_inverse(optarg, &invert, &optind, 0);
+               einfo->operation |= IPT_ECN_OP_MATCH_ECE;
+               if (invert)
+                       einfo->invert |= IPT_ECN_OP_MATCH_ECE;
+               *flags |= IPT_ECN_OP_MATCH_ECE;
+               break;
+
+       case 'H':
+               if (*flags & IPT_ECN_OP_MATCH_IP)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN match: can only use parameter ONCE!");
+               check_inverse(optarg, &invert, &optind, 0);
+               if (invert)
+                       einfo->invert |= IPT_ECN_OP_MATCH_IP;
+               *flags |= IPT_ECN_OP_MATCH_IP;
+               einfo->operation |= IPT_ECN_OP_MATCH_IP;
+               if (string_to_number(optarg, 0, 3, &result))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN match: Value out of range");
+               einfo->ip_ect = result;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "ECN match: some option required");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_ecn_info *einfo =
+               (const struct ipt_ecn_info *)match->data;
+
+       printf("ECN match ");
+
+       if (einfo->operation & IPT_ECN_OP_MATCH_ECE) {
+               if (einfo->invert & IPT_ECN_OP_MATCH_ECE)
+                       fputc('!', stdout);
+               printf("ECE ");
+       }
+
+       if (einfo->operation & IPT_ECN_OP_MATCH_CWR) {
+               if (einfo->invert & IPT_ECN_OP_MATCH_CWR)
+                       fputc('!', stdout);
+               printf("CWR ");
+       }
+
+       if (einfo->operation & IPT_ECN_OP_MATCH_IP) {
+               if (einfo->invert & IPT_ECN_OP_MATCH_IP)
+                       fputc('!', stdout);
+               printf("ECT=%d ", einfo->ip_ect);
+       }
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_ecn_info *einfo =
+               (const struct ipt_ecn_info *)match->data;
+       
+       if (einfo->operation & IPT_ECN_OP_MATCH_ECE) {
+               if (einfo->invert & IPT_ECN_OP_MATCH_ECE)
+                       printf("! ");
+               printf("--ecn-tcp-ece ");
+       }
+
+       if (einfo->operation & IPT_ECN_OP_MATCH_CWR) {
+               if (einfo->invert & IPT_ECN_OP_MATCH_CWR)
+                       printf("! ");
+               printf("--ecn-tcp-cwr ");
+       }
+
+       if (einfo->operation & IPT_ECN_OP_MATCH_IP) {
+               if (einfo->invert & IPT_ECN_OP_MATCH_IP)
+                       printf("! ");
+               printf("--ecn-ip-ect %d", einfo->ip_ect);
+       }
+}
+
+static
+struct iptables_match ecn
+= { .name          = "ecn",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_ecn_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_ecn_info)),
+    .help          = &help,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_match(&ecn);
+}
diff --git a/extensions/libipt_ecn.man b/extensions/libipt_ecn.man
new file mode 100644 (file)
index 0000000..8ecfef5
--- /dev/null
@@ -0,0 +1,11 @@
+This allows you to match the ECN bits of the IPv4 and TCP header.  ECN is the Explicit Congestion Notification mechanism as specified in RFC3168
+.TP
+.BI "--ecn-tcp-cwr"
+This matches if the TCP ECN CWR (Congestion Window Received) bit is set.
+.TP
+.BI "--ecn-tcp-ece"
+This matches if the TCP ECN ECE (ECN Echo) bit is set.
+.TP
+.BI "--ecn-ip-ect " "num"
+This matches a particular IPv4 ECT (ECN-Capable Transport). You have to specify
+a number between `0' and `3'.
diff --git a/extensions/libipt_esp.c b/extensions/libipt_esp.c
new file mode 100644 (file)
index 0000000..21e912b
--- /dev/null
@@ -0,0 +1,193 @@
+/* Shared library add-on to iptables to add ESP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_esp.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"ESP v%s options:\n"
+" --espspi [!] spi[:spi]\n"
+"                              match spi (range)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "espspi", 1, 0, '1' },
+       {0}
+};
+
+static u_int32_t
+parse_esp_spi(const char *spistr)
+{
+       unsigned long int spi;
+       char* ep;
+
+       spi =  strtoul(spistr,&ep,0) ;
+
+       if ( spistr == ep ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "ESP no valid digits in spi `%s'", spistr);
+       }
+       if ( spi == ULONG_MAX  && errno == ERANGE ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "spi `%s' specified too big: would overflow", spistr);
+       }       
+       if ( *spistr != '\0'  && *ep != '\0' ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "ESP error parsing spi `%s'", spistr);
+       }
+       return (u_int32_t) spi;
+}
+
+static void
+parse_esp_spis(const char *spistring, u_int32_t *spis)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(spistring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               spis[0] = spis[1] = parse_esp_spi(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               spis[0] = buffer[0] ? parse_esp_spi(buffer) : 0;
+               spis[1] = cp[0] ? parse_esp_spi(cp) : 0xFFFFFFFF;
+               if (spis[0] > spis[1])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid ESP spi range: %s", spistring);
+       }
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_esp *espinfo = (struct ipt_esp *)m->data;
+
+       espinfo->spis[1] = 0xFFFFFFFF;
+}
+
+#define ESP_SPI 0x01
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_esp *espinfo = (struct ipt_esp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & ESP_SPI)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--espspi' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_esp_spis(argv[optind-1], espinfo->spis);
+               if (invert)
+                       espinfo->invflags |= IPT_ESP_INV_SPI;
+               *flags |= ESP_SPI;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_spis(const char *name, u_int32_t min, u_int32_t max,
+           int invert)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFFFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       printf("%u", min);
+               } else {
+                       printf("s:%s", inv);
+                       printf("%u",min);
+                       printf(":");
+                       printf("%u",max);
+               }
+               printf(" ");
+       }
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match, int numeric)
+{
+       const struct ipt_esp *esp = (struct ipt_esp *)match->data;
+
+       printf("esp ");
+       print_spis("spi", esp->spis[0], esp->spis[1],
+                   esp->invflags & IPT_ESP_INV_SPI);
+       if (esp->invflags & ~IPT_ESP_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      esp->invflags & ~IPT_ESP_INV_MASK);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_esp *espinfo = (struct ipt_esp *)match->data;
+
+       if (!(espinfo->spis[0] == 0
+           && espinfo->spis[1] == 0xFFFFFFFF)) {
+               printf("--espspi %s", 
+                       (espinfo->invflags & IPT_ESP_INV_SPI) ? "! " : "");
+               if (espinfo->spis[0]
+                   != espinfo->spis[1])
+                       printf("%u:%u ",
+                              espinfo->spis[0],
+                              espinfo->spis[1]);
+               else
+                       printf("%u ",
+                              espinfo->spis[0]);
+       }
+
+}
+
+static struct iptables_match esp = { 
+       .next           = NULL,
+       .name           = "esp",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_esp)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_esp)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void
+_init(void)
+{
+       register_match(&esp);
+}
diff --git a/extensions/libipt_esp.man b/extensions/libipt_esp.man
new file mode 100644 (file)
index 0000000..7898e02
--- /dev/null
@@ -0,0 +1,3 @@
+This module matches the SPIs in ESP header of IPsec packets.
+.TP
+.BR "--espspi " "[!] \fIspi\fP[:\fIspi\fP]"
diff --git a/extensions/libipt_hashlimit.c b/extensions/libipt_hashlimit.c
new file mode 100644 (file)
index 0000000..6fb0ecc
--- /dev/null
@@ -0,0 +1,369 @@
+/* iptables match extension for limiting packets per destination
+ *
+ * (C) 2003-2004 by Harald Welte <laforge@netfilter.org>
+ *
+ * Development of this code was funded by Astaro AG, http://www.astaro.com/
+ *
+ * Based on ipt_limit.c by
+ * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
+ * Hervé Eychenne    <rv@wallfire.org>
+ * 
+ * Error corections by nmalykh@bilim.com (22.01.2005)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <stddef.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_hashlimit.h>
+
+#define IPT_HASHLIMIT_BURST    5
+
+/* miliseconds */
+#define IPT_HASHLIMIT_GCINTERVAL       1000
+#define IPT_HASHLIMIT_EXPIRE   10000
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"hashlimit v%s options:\n"
+"--hashlimit <avg>             max average match rate\n"
+"                                [Packets per second unless followed by \n"
+"                                /sec /minute /hour /day postfixes]\n"
+"--hashlimit-mode <mode>               mode is a comma-separated list of\n"
+"                                      dstip,srcip,dstport,srcport\n"
+"--hashlimit-name <name>               name for /proc/net/ipt_hashlimit/\n"
+"[--hashlimit-burst <num>]     number to match in a burst, default %u\n"
+"[--hashlimit-htable-size <num>]       number of hashtable buckets\n"
+"[--hashlimit-htable-max <num>]        number of hashtable entries\n"
+"[--hashlimit-htable-gcinterval]       interval between garbage collection runs\n"
+"[--hashlimit-htable-expire]   after which time are idle entries expired?\n"
+"\n", IPTABLES_VERSION, IPT_HASHLIMIT_BURST);
+}
+
+static struct option opts[] = {
+       { "hashlimit", 1, 0, '%' },
+       { "hashlimit-burst", 1, 0, '$' },
+       { "hashlimit-htable-size", 1, 0, '&' },
+       { "hashlimit-htable-max", 1, 0, '*' },
+       { "hashlimit-htable-gcinterval", 1, 0, '(' },
+       { "hashlimit-htable-expire", 1, 0, ')' },
+       { "hashlimit-mode", 1, 0, '_' },
+       { "hashlimit-name", 1, 0, '"' },
+       { 0 }
+};
+
+static
+int parse_rate(const char *rate, u_int32_t *val)
+{
+       const char *delim;
+       u_int32_t r;
+       u_int32_t mult = 1;  /* Seconds by default. */
+
+       delim = strchr(rate, '/');
+       if (delim) {
+               if (strlen(delim+1) == 0)
+                       return 0;
+
+               if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
+                       mult = 1;
+               else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
+                       mult = 60;
+               else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
+                       mult = 60*60;
+               else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
+                       mult = 24*60*60;
+               else
+                       return 0;
+       }
+       r = atoi(rate);
+       if (!r)
+               return 0;
+
+       /* This would get mapped to infinite (1/day is minimum they
+           can specify, so we're ok at that end). */
+       if (r / mult > IPT_HASHLIMIT_SCALE)
+               exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
+
+       *val = IPT_HASHLIMIT_SCALE * mult / r;
+       return 1;
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_hashlimit_info *r = (struct ipt_hashlimit_info *)m->data;
+
+       r->cfg.burst = IPT_HASHLIMIT_BURST;
+       r->cfg.gc_interval = IPT_HASHLIMIT_GCINTERVAL;
+       r->cfg.expire = IPT_HASHLIMIT_EXPIRE;
+
+}
+
+
+/* Parse a 'mode' parameter into the required bitmask */
+static int parse_mode(struct ipt_hashlimit_info *r, char *optarg)
+{
+       char *tok;
+       char *arg = strdup(optarg);
+
+       if (!arg)
+               return -1;
+
+       r->cfg.mode = 0;
+
+       for (tok = strtok(arg, ",|");
+            tok;
+            tok = strtok(NULL, ",|")) {
+               if (!strcmp(tok, "dstip"))
+                       r->cfg.mode |= IPT_HASHLIMIT_HASH_DIP;
+               else if (!strcmp(tok, "srcip"))
+                       r->cfg.mode |= IPT_HASHLIMIT_HASH_SIP;
+               else if (!strcmp(tok, "srcport"))
+                       r->cfg.mode |= IPT_HASHLIMIT_HASH_SPT;
+               else if (!strcmp(tok, "dstport"))
+                       r->cfg.mode |= IPT_HASHLIMIT_HASH_DPT;
+               else {
+                       free(arg);
+                       return -1;
+               }
+       }
+       free(arg);
+       return 0;
+}
+
+#define PARAM_LIMIT            0x00000001
+#define PARAM_BURST            0x00000002
+#define PARAM_MODE             0x00000004
+#define PARAM_NAME             0x00000008
+#define PARAM_SIZE             0x00000010
+#define PARAM_MAX              0x00000020
+#define PARAM_GCINTERVAL       0x00000040
+#define PARAM_EXPIRE           0x00000080
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_hashlimit_info *r = 
+                       (struct ipt_hashlimit_info *)(*match)->data;
+       unsigned int num;
+
+       switch(c) {
+       case '%':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (!parse_rate(optarg, &r->cfg.avg))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "bad rate `%s'", optarg);
+               *flags |= PARAM_LIMIT;
+               break;
+
+       case '$':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 10000, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "bad --hashlimit-burst `%s'", optarg);
+               r->cfg.burst = num;
+               *flags |= PARAM_BURST;
+               break;
+       case '&':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --hashlimit-htable-size: `%s'", optarg);
+               r->cfg.size = num;
+               *flags |= PARAM_SIZE;
+               break;
+       case '*':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --hashlimit-htable-max: `%s'", optarg);
+               r->cfg.max = num;
+               *flags |= PARAM_MAX;
+               break;
+       case '(':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --hashlimit-htable-gcinterval: `%s'", 
+                               optarg);
+               /* FIXME: not HZ dependent!! */
+               r->cfg.gc_interval = num;
+               *flags |= PARAM_GCINTERVAL;
+               break;
+       case ')':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "bad --hashlimit-htable-expire: `%s'", optarg);
+               /* FIXME: not HZ dependent */
+               r->cfg.expire = num;
+               *flags |= PARAM_EXPIRE;
+               break;
+       case '_':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (parse_mode(r, optarg) < 0)
+                       exit_error(PARAMETER_PROBLEM, 
+                                  "bad --hashlimit-mode: `%s'\n", optarg);
+               *flags |= PARAM_MODE;
+               break;
+       case '"':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (strlen(optarg) == 0)
+                       exit_error(PARAMETER_PROBLEM, "Zero-length name?");
+               strncpy(r->name, optarg, sizeof(r->name));
+               *flags |= PARAM_NAME;
+               break;
+       default:
+               return 0;
+       }
+
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "hashlimit does not support invert");
+
+       return 1;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+       if (!(flags & PARAM_LIMIT))
+               exit_error(PARAMETER_PROBLEM,
+                               "You have to specify --hashlimit");
+       if (!(flags & PARAM_MODE))
+               exit_error(PARAMETER_PROBLEM,
+                               "You have to specify --hashlimit-mode");
+       if (!(flags & PARAM_NAME))
+               exit_error(PARAMETER_PROBLEM,
+                               "You have to specify --hashlimit-name");
+}
+
+static struct rates
+{
+       const char *name;
+       u_int32_t mult;
+} rates[] = { { "day", IPT_HASHLIMIT_SCALE*24*60*60 },
+             { "hour", IPT_HASHLIMIT_SCALE*60*60 },
+             { "min", IPT_HASHLIMIT_SCALE*60 },
+             { "sec", IPT_HASHLIMIT_SCALE } };
+
+static void print_rate(u_int32_t period)
+{
+       unsigned int i;
+
+       for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
+               if (period > rates[i].mult
+            || rates[i].mult/period < rates[i].mult%period)
+                       break;
+       }
+
+       printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
+}
+
+static void print_mode(const struct ipt_hashlimit_info *r, char separator)
+{
+       int prevmode = 0;
+
+       if (r->cfg.mode & IPT_HASHLIMIT_HASH_SIP) {
+               if (prevmode)
+                       putchar(separator);
+               fputs("srcip", stdout);
+               prevmode = 1;
+       }
+       if (r->cfg.mode & IPT_HASHLIMIT_HASH_SPT) {
+               if (prevmode)
+                       putchar(separator);
+               fputs("srcport", stdout);
+               prevmode = 1;
+       }
+       if (r->cfg.mode & IPT_HASHLIMIT_HASH_DIP) {
+               if (prevmode)
+                       putchar(separator);
+               fputs("dstip", stdout);
+               prevmode = 1;
+       }
+       if (r->cfg.mode & IPT_HASHLIMIT_HASH_DPT) {
+               if (prevmode)
+                       putchar(separator);
+               fputs("dstport", stdout);
+       }
+       putchar(' ');
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_hashlimit_info *r = 
+               (struct ipt_hashlimit_info *)match->data;
+       fputs("limit: avg ", stdout); print_rate(r->cfg.avg);
+       printf("burst %u ", r->cfg.burst);
+       fputs("mode ", stdout);
+       print_mode(r, '-');
+       if (r->cfg.size)
+               printf("htable-size %u ", r->cfg.size);
+       if (r->cfg.max)
+               printf("htable-max %u ", r->cfg.max);
+       if (r->cfg.gc_interval != IPT_HASHLIMIT_GCINTERVAL)
+               printf("htable-gcinterval %u ", r->cfg.gc_interval);
+       if (r->cfg.expire != IPT_HASHLIMIT_EXPIRE)
+               printf("htable-expire %u ", r->cfg.expire);
+}
+
+/* FIXME: Make minimalist: only print rate if not default --RR */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_hashlimit_info *r = 
+               (struct ipt_hashlimit_info *)match->data;
+
+       fputs("--hashlimit ", stdout); print_rate(r->cfg.avg);
+       if (r->cfg.burst != IPT_HASHLIMIT_BURST)
+               printf("--hashlimit-burst %u ", r->cfg.burst);
+
+       fputs("--hashlimit-mode ", stdout);
+       print_mode(r, ',');
+       
+       printf("--hashlimit-name %s ", r->name);
+
+       if (r->cfg.size)
+               printf("--hashlimit-htable-size %u ", r->cfg.size);
+       if (r->cfg.max)
+               printf("--hashlimit-htable-max %u ", r->cfg.max);
+       if (r->cfg.gc_interval != IPT_HASHLIMIT_GCINTERVAL)
+               printf("--hashlimit-htable-gcinterval %u", r->cfg.gc_interval);
+       if (r->cfg.expire != IPT_HASHLIMIT_EXPIRE)
+               printf("--hashlimit-htable-expire %u ", r->cfg.expire);
+}
+
+static struct iptables_match hashlimit = { NULL,
+       .name           = "hashlimit",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_hashlimit_info)),
+       .userspacesize  = offsetof(struct ipt_hashlimit_info, hinfo),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&hashlimit);
+}
diff --git a/extensions/libipt_hashlimit.man b/extensions/libipt_hashlimit.man
new file mode 100644 (file)
index 0000000..c8128ec
--- /dev/null
@@ -0,0 +1,35 @@
+This patch adds a new match called 'hashlimit'.
+The idea is to have something like 'limit', but either per
+destination-ip or per (destip,destport) tuple.
+
+It gives you the ability to express
+.IP
+ '1000 packets per second for every host in 192.168.0.0/16'
+.IP
+ '100 packets per second for every service of 192.168.1.1'
+.P
+with a single iptables rule.
+.TP
+.BI "--hashlimit " "rate"
+A rate just like the limit match
+.TP
+.BI "--hashlimit-burst " "num"
+Burst value, just like limit match
+.TP
+.BI "--hashlimit-mode " "dstip,srcip,dstport,srcport"
+A comma-separated list of objects to take into consideration
+.TP
+.BI "--hashlimit-name " "foo"
+The name for the /proc/net/ipt_hashlimit/foo entry
+.TP
+.BI "--hashlimit-htable-size " "num"
+The number of buckets of the hash table
+.TP
+.BI "--hashlimit-htable-max " "num"
+Maximum entries in the hash
+.TP
+.BI "--hashlimit-htable-expire " "num"
+After how many miliseconds do hash entries expire
+.TP
+.BI "--hashlimit-htable-gcinterval " "num"
+How many miliseconds between garbage collection intervals
diff --git a/extensions/libipt_helper.c b/extensions/libipt_helper.c
new file mode 100644 (file)
index 0000000..7c9f3e3
--- /dev/null
@@ -0,0 +1,101 @@
+/* Shared library add-on to iptables to add related packet matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_helper.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"helper match v%s options:\n"
+"[!] --helper string        Match helper identified by string\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "helper", 1, 0, '1' },
+       {0}
+};
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_helper_info *info = (struct ipt_helper_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                       "helper match: Only use --helper ONCE!");
+               check_inverse(optarg, &invert, &invert, 0);
+               strncpy(info->name, optarg, 29);
+               info->name[29] = '\0';
+               if (invert)
+                       info->invert = 1;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+/* Final check; must have specified --helper. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "helper match: You must specify `--helper'");
+}
+
+/* Prints out the info. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_helper_info *info = (struct ipt_helper_info *)match->data;
+
+       printf("helper match %s\"%s\" ", info->invert ? "! " : "", info->name);
+}
+
+/* Saves the union ipt_info in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_helper_info *info = (struct ipt_helper_info *)match->data;
+
+       printf("%s--helper \"%s\" ",info->invert ? "! " : "", info->name);
+}
+
+static struct iptables_match helper = { 
+       .next           = NULL,
+       .name           = "helper",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_helper_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&helper);
+}
diff --git a/extensions/libipt_helper.man b/extensions/libipt_helper.man
new file mode 100644 (file)
index 0000000..c3221ad
--- /dev/null
@@ -0,0 +1,11 @@
+This module matches packets related to a specific conntrack-helper.
+.TP
+.BI "--helper " "string"
+Matches packets related to the specified conntrack-helper.
+.RS
+.PP
+string can be "ftp" for packets related to a ftp-session on default port.
+For other ports append -portnr to the value, ie. "ftp-2121".
+.PP
+Same rules apply for other conntrack-helpers.
+.RE
diff --git a/extensions/libipt_icmp.c b/extensions/libipt_icmp.c
new file mode 100644 (file)
index 0000000..a6e642a
--- /dev/null
@@ -0,0 +1,307 @@
+/* Shared library add-on to iptables to add ICMP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+/* special hack for icmp-type 'any': 
+ * Up to kernel <=2.4.20 the problem was:
+ * '-p icmp ' matches all icmp packets
+ * '-p icmp -m icmp' matches _only_ ICMP type 0 :(
+ * This is now fixed by initializing the field * to icmp type 0xFF
+ * See: https://bugzilla.netfilter.org/cgi-bin/bugzilla/show_bug.cgi?id=37
+ */
+
+struct icmp_names {
+       const char *name;
+       u_int8_t type;
+       u_int8_t code_min, code_max;
+};
+
+static const struct icmp_names icmp_codes[] = {
+       { "any", 0xFF, 0, 0xFF },
+       { "echo-reply", 0, 0, 0xFF },
+       /* Alias */ { "pong", 0, 0, 0xFF },
+
+       { "destination-unreachable", 3, 0, 0xFF },
+       {   "network-unreachable", 3, 0, 0 },
+       {   "host-unreachable", 3, 1, 1 },
+       {   "protocol-unreachable", 3, 2, 2 },
+       {   "port-unreachable", 3, 3, 3 },
+       {   "fragmentation-needed", 3, 4, 4 },
+       {   "source-route-failed", 3, 5, 5 },
+       {   "network-unknown", 3, 6, 6 },
+       {   "host-unknown", 3, 7, 7 },
+       {   "network-prohibited", 3, 9, 9 },
+       {   "host-prohibited", 3, 10, 10 },
+       {   "TOS-network-unreachable", 3, 11, 11 },
+       {   "TOS-host-unreachable", 3, 12, 12 },
+       {   "communication-prohibited", 3, 13, 13 },
+       {   "host-precedence-violation", 3, 14, 14 },
+       {   "precedence-cutoff", 3, 15, 15 },
+
+       { "source-quench", 4, 0, 0xFF },
+
+       { "redirect", 5, 0, 0xFF },
+       {   "network-redirect", 5, 0, 0 },
+       {   "host-redirect", 5, 1, 1 },
+       {   "TOS-network-redirect", 5, 2, 2 },
+       {   "TOS-host-redirect", 5, 3, 3 },
+
+       { "echo-request", 8, 0, 0xFF },
+       /* Alias */ { "ping", 8, 0, 0xFF },
+
+       { "router-advertisement", 9, 0, 0xFF },
+
+       { "router-solicitation", 10, 0, 0xFF },
+
+       { "time-exceeded", 11, 0, 0xFF },
+       /* Alias */ { "ttl-exceeded", 11, 0, 0xFF },
+       {   "ttl-zero-during-transit", 11, 0, 0 },
+       {   "ttl-zero-during-reassembly", 11, 1, 1 },
+
+       { "parameter-problem", 12, 0, 0xFF },
+       {   "ip-header-bad", 12, 0, 0 },
+       {   "required-option-missing", 12, 1, 1 },
+
+       { "timestamp-request", 13, 0, 0xFF },
+
+       { "timestamp-reply", 14, 0, 0xFF },
+
+       { "address-mask-request", 17, 0, 0xFF },
+
+       { "address-mask-reply", 18, 0, 0xFF }
+};
+
+static void
+print_icmptypes()
+{
+       unsigned int i;
+       printf("Valid ICMP Types:");
+
+       for (i = 0; i < sizeof(icmp_codes)/sizeof(struct icmp_names); i++) {
+               if (i && icmp_codes[i].type == icmp_codes[i-1].type) {
+                       if (icmp_codes[i].code_min == icmp_codes[i-1].code_min
+                           && (icmp_codes[i].code_max
+                               == icmp_codes[i-1].code_max))
+                               printf(" (%s)", icmp_codes[i].name);
+                       else
+                               printf("\n   %s", icmp_codes[i].name);
+               }
+               else
+                       printf("\n%s", icmp_codes[i].name);
+       }
+       printf("\n");
+}
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"ICMP v%s options:\n"
+" --icmp-type [!] typename     match icmp type\n"
+"                              (or numeric type or type/code)\n"
+"\n", IPTABLES_VERSION);
+       print_icmptypes();
+}
+
+static struct option opts[] = {
+       { "icmp-type", 1, 0, '1' },
+       {0}
+};
+
+static void 
+parse_icmp(const char *icmptype, u_int8_t *type, u_int8_t code[])
+{
+       unsigned int limit = sizeof(icmp_codes)/sizeof(struct icmp_names);
+       unsigned int match = limit;
+       unsigned int i;
+
+       for (i = 0; i < limit; i++) {
+               if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype))
+                   == 0) {
+                       if (match != limit)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Ambiguous ICMP type `%s':"
+                                          " `%s' or `%s'?",
+                                          icmptype,
+                                          icmp_codes[match].name,
+                                          icmp_codes[i].name);
+                       match = i;
+               }
+       }
+
+       if (match != limit) {
+               *type = icmp_codes[match].type;
+               code[0] = icmp_codes[match].code_min;
+               code[1] = icmp_codes[match].code_max;
+       } else {
+               char *slash;
+               char buffer[strlen(icmptype) + 1];
+               unsigned int number;
+
+               strcpy(buffer, icmptype);
+               slash = strchr(buffer, '/');
+
+               if (slash)
+                       *slash = '\0';
+
+               if (string_to_number(buffer, 0, 255, &number) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid ICMP type `%s'\n", buffer);
+               *type = number;
+               if (slash) {
+                       if (string_to_number(slash+1, 0, 255, &number) == -1)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Invalid ICMP code `%s'\n",
+                                          slash+1);
+                       code[0] = code[1] = number;
+               } else {
+                       code[0] = 0;
+                       code[1] = 0xFF;
+               }
+       }
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data;
+
+       icmpinfo->type = 0xFF;
+       icmpinfo->code[1] = 0xFF;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_icmp *icmpinfo = (struct ipt_icmp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags == 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "icmp match: only use --icmp-type once!");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_icmp(argv[optind-1], &icmpinfo->type, 
+                          icmpinfo->code);
+               if (invert)
+                       icmpinfo->invflags |= IPT_ICMP_INV;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void print_icmptype(u_int8_t type,
+                          u_int8_t code_min, u_int8_t code_max,
+                          int invert,
+                          int numeric)
+{
+       if (!numeric) {
+               unsigned int i;
+
+               for (i = 0;
+                    i < sizeof(icmp_codes)/sizeof(struct icmp_names);
+                    i++) {
+                       if (icmp_codes[i].type == type
+                           && icmp_codes[i].code_min == code_min
+                           && icmp_codes[i].code_max == code_max)
+                               break;
+               }
+
+               if (i != sizeof(icmp_codes)/sizeof(struct icmp_names)) {
+                       printf("%s%s ",
+                              invert ? "!" : "",
+                              icmp_codes[i].name);
+                       return;
+               }
+       }
+
+       if (invert)
+               printf("!");
+
+       printf("type %u", type);
+       if (code_min == 0 && code_max == 0xFF)
+               printf(" ");
+       else if (code_min == code_max)
+               printf(" code %u ", code_min);
+       else
+               printf(" codes %u-%u ", code_min, code_max);
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
+
+       printf("icmp ");
+       print_icmptype(icmp->type, icmp->code[0], icmp->code[1],
+                      icmp->invflags & IPT_ICMP_INV,
+                      numeric);
+
+       if (icmp->invflags & ~IPT_ICMP_INV)
+               printf("Unknown invflags: 0x%X ",
+                      icmp->invflags & ~IPT_ICMP_INV);
+}
+
+/* Saves the match in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
+
+       if (icmp->invflags & IPT_ICMP_INV)
+               printf("! ");
+
+       /* special hack for 'any' case */
+       if (icmp->type == 0xFF) {
+               printf("--icmp-type any ");
+       } else {
+               printf("--icmp-type %u", icmp->type);
+               if (icmp->code[0] != 0 || icmp->code[1] != 0xFF)
+                       printf("/%u", icmp->code[0]);
+               printf(" ");
+       }
+}
+
+/* Final check; we don't care. We can pass 0xFF to match any type */
+static void final_check(unsigned int flags)
+{
+}
+
+static struct iptables_match icmp = { 
+       .next           = NULL,
+       .name           = "icmp",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_icmp)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_icmp)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&icmp);
+}
diff --git a/extensions/libipt_icmp.man b/extensions/libipt_icmp.man
new file mode 100644 (file)
index 0000000..55d24b4
--- /dev/null
@@ -0,0 +1,9 @@
+This extension can be used if `--protocol icmp' is specified.  It
+provides the following option:
+.TP
+.BR "--icmp-type " "[!] \fItypename\fP"
+This allows specification of the ICMP type, which can be a numeric
+ICMP type, or one of the ICMP type names shown by the command
+.nf
+ iptables -p icmp -h
+.fi
diff --git a/extensions/libipt_iprange.c b/extensions/libipt_iprange.c
new file mode 100644 (file)
index 0000000..e696702
--- /dev/null
@@ -0,0 +1,184 @@
+/* Shared library add-on to iptables to add IP range matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_iprange.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"iprange match v%s options:\n"
+"[!] --src-range ip-ip        Match source IP in the specified range\n"
+"[!] --dst-range ip-ip        Match destination IP in the specified range\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "src-range", 1, 0, '1' },
+       { "dst-range", 1, 0, '2' },
+       {0}
+};
+
+static void
+parse_iprange(char *arg, struct ipt_iprange *range)
+{
+       char *dash;
+       struct in_addr *ip;
+
+       dash = strchr(arg, '-');
+       if (dash)
+               *dash = '\0';
+               
+       ip = dotted_to_addr(arg);
+       if (!ip)
+               exit_error(PARAMETER_PROBLEM, "iprange match: Bad IP address `%s'\n", 
+                          arg);
+       range->min_ip = ip->s_addr;
+
+       if (dash) {
+               ip = dotted_to_addr(dash+1);
+               if (!ip)
+                       exit_error(PARAMETER_PROBLEM, "iprange match: Bad IP address `%s'\n",
+                                  dash+1);
+               range->max_ip = ip->s_addr;
+       } else
+               range->max_ip = range->min_ip;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_iprange_info *info = (struct ipt_iprange_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IPRANGE_SRC)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "iprange match: Only use --src-range ONCE!");
+               *flags |= IPRANGE_SRC;
+
+               info->flags |= IPRANGE_SRC;
+               check_inverse(optarg, &invert, &optind, 0);
+               if (invert) {
+                       info->flags |= IPRANGE_SRC_INV;
+               }
+               parse_iprange(optarg, &info->src);              
+
+               break;
+
+       case '2':
+               if (*flags & IPRANGE_DST)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "iprange match: Only use --dst-range ONCE!");
+               *flags |= IPRANGE_DST;
+
+               info->flags |= IPRANGE_DST;
+               check_inverse(optarg, &invert, &optind, 0);
+               if (invert)
+                       info->flags |= IPRANGE_DST_INV;
+
+               parse_iprange(optarg, &info->dst);              
+
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+/* Final check; must have specified --src-range or --dst-range. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "iprange match: You must specify `--src-range' or `--dst-range'");
+}
+
+static void
+print_iprange(const struct ipt_iprange *range)
+{
+       const unsigned char *byte_min, *byte_max;
+
+       byte_min = (const unsigned char *) &(range->min_ip);
+       byte_max = (const unsigned char *) &(range->max_ip);
+       printf("%d.%d.%d.%d-%d.%d.%d.%d ", 
+               byte_min[0], byte_min[1], byte_min[2], byte_min[3],
+               byte_max[0], byte_max[1], byte_max[2], byte_max[3]);
+}
+
+/* Prints out the info. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_iprange_info *info = (struct ipt_iprange_info *)match->data;
+
+       if (info->flags & IPRANGE_SRC) {
+               printf("source IP range ");
+               if (info->flags & IPRANGE_SRC_INV)
+                       printf("! ");
+               print_iprange(&info->src);
+       }
+       if (info->flags & IPRANGE_DST) {
+               printf("destination IP range ");
+               if (info->flags & IPRANGE_DST_INV)
+                       printf("! ");
+               print_iprange(&info->dst);
+       }
+}
+
+/* Saves the union ipt_info in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_iprange_info *info = (struct ipt_iprange_info *)match->data;
+
+       if (info->flags & IPRANGE_SRC) {
+               if (info->flags & IPRANGE_SRC_INV)
+                       printf("! ");
+               printf("--src-range ");
+               print_iprange(&info->src);
+               if (info->flags & IPRANGE_DST)
+                       fputc(' ', stdout);
+       }
+       if (info->flags & IPRANGE_DST) {
+               if (info->flags & IPRANGE_DST_INV)
+                       printf("! ");
+               printf("--dst-range ");
+               print_iprange(&info->dst);
+       }
+}
+
+static struct iptables_match iprange = { 
+       .next           = NULL,
+       .name           = "iprange",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_iprange_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_iprange_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&iprange);
+}
diff --git a/extensions/libipt_iprange.man b/extensions/libipt_iprange.man
new file mode 100644 (file)
index 0000000..57e1cff
--- /dev/null
@@ -0,0 +1,7 @@
+This matches on a given arbitrary range of IPv4 addresses
+.TP
+.BI "[!]" "--src-range " "ip-ip"
+Match source IP in the specified range.
+.TP
+.BI "[!]" "--dst-range " "ip-ip"
+Match destination IP in the specified range.
diff --git a/extensions/libipt_ipv4options.c b/extensions/libipt_ipv4options.c
new file mode 100644 (file)
index 0000000..90183e5
--- /dev/null
@@ -0,0 +1,310 @@
+/* Shared library add-on to iptables to add ipv4 options matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_ipv4options.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"ipv4options v%s options:\n"
+"      --ssrr    (match strict source routing flag)\n"
+"      --lsrr    (match loose  source routing flag)\n"
+"      --no-srr  (match packets with no source routing)\n\n"
+"  [!] --rr      (match record route flag)\n\n"
+"  [!] --ts      (match timestamp flag)\n\n"
+"  [!] --ra      (match router-alert option)\n\n"
+"  [!] --any-opt (match any option or no option at all if used with '!')\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "ssrr", 0, 0, '1' },
+       { "lsrr", 0, 0, '2' },
+       { "no-srr", 0, 0, '3'},
+       { "rr", 0, 0, '4'},
+       { "ts", 0, 0, '5'},
+       { "ra", 0, 0, '6'},
+       { "any-opt", 0, 0, '7'},
+       {0}
+};
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_match **match)
+{
+       struct ipt_ipv4options_info *info = (struct ipt_ipv4options_info *)(*match)->data;
+
+       switch (c)
+       {
+               /* strict-source-routing */
+       case '1':
+               if (invert) 
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ipv4options: unexpected `!' with --ssrr");
+               if (*flags & IPT_IPV4OPTION_MATCH_SSRR)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --ssrr twice");
+               if (*flags & IPT_IPV4OPTION_MATCH_LSRR)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ssrr with --lsrr");
+               if (*flags & IPT_IPV4OPTION_DONT_MATCH_SRR)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ssrr with --no-srr");
+
+               info->options |= IPT_IPV4OPTION_MATCH_SSRR;
+               *flags |= IPT_IPV4OPTION_MATCH_SSRR;
+               break;
+
+               /* loose-source-routing */
+       case '2':
+               if (invert) 
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ipv4options: unexpected `!' with --lsrr");
+               if (*flags & IPT_IPV4OPTION_MATCH_SSRR)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --lsrr twice");
+               if (*flags & IPT_IPV4OPTION_MATCH_LSRR)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --lsrr with --ssrr");
+               if (*flags & IPT_IPV4OPTION_DONT_MATCH_SRR)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --lsrr with --no-srr");
+               info->options |= IPT_IPV4OPTION_MATCH_LSRR;
+               *flags |= IPT_IPV4OPTION_MATCH_LSRR;
+               break;
+
+               /* no-source-routing */
+       case '3':
+               if (invert) 
+                       exit_error(PARAMETER_PROBLEM,
+                                          "ipv4options: unexpected `!' with --no-srr");
+               if (*flags & IPT_IPV4OPTION_DONT_MATCH_SRR)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --no-srr twice");
+               if (*flags & IPT_IPV4OPTION_MATCH_SSRR)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --no-srr with --ssrr");
+               if (*flags & IPT_IPV4OPTION_MATCH_LSRR)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --no-srr with --lsrr");
+               info->options |= IPT_IPV4OPTION_DONT_MATCH_SRR;
+               *flags |= IPT_IPV4OPTION_DONT_MATCH_SRR;
+               break;
+
+               /* record-route */
+       case '4':
+               if ((!invert) && (*flags & IPT_IPV4OPTION_MATCH_RR))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --rr twice"); 
+               if (invert && (*flags & IPT_IPV4OPTION_DONT_MATCH_RR))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --rr twice");
+               if ((!invert) && (*flags & IPT_IPV4OPTION_DONT_MATCH_RR))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --rr with ! --rr");
+               if (invert && (*flags & IPT_IPV4OPTION_MATCH_RR))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --rr with --rr");
+               if (invert) {
+                       info->options |= IPT_IPV4OPTION_DONT_MATCH_RR;
+                       *flags |= IPT_IPV4OPTION_DONT_MATCH_RR;
+               }
+               else {
+                       info->options |= IPT_IPV4OPTION_MATCH_RR;
+                       *flags |= IPT_IPV4OPTION_MATCH_RR;
+               }
+               break;
+
+               /* timestamp */
+       case '5':
+               if ((!invert) && (*flags & IPT_IPV4OPTION_MATCH_TIMESTAMP))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ts twice"); 
+               if (invert && (*flags & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --ts twice");
+               if ((!invert) && (*flags & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ts with ! --ts");
+               if (invert && (*flags & IPT_IPV4OPTION_MATCH_TIMESTAMP))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --ts with --ts");
+               if (invert) {
+                       info->options |= IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP;
+                       *flags |= IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP;
+               }
+               else {
+                       info->options |= IPT_IPV4OPTION_MATCH_TIMESTAMP;
+                       *flags |= IPT_IPV4OPTION_MATCH_TIMESTAMP;
+               }
+               break;
+
+               /* router-alert  */
+       case '6':
+               if ((!invert) && (*flags & IPT_IPV4OPTION_MATCH_ROUTER_ALERT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ra twice"); 
+               if (invert && (*flags & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --rr twice");
+               if ((!invert) && (*flags & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ra with ! --ra");
+               if (invert && (*flags & IPT_IPV4OPTION_MATCH_ROUTER_ALERT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --ra with --ra");
+               if (invert) {
+                       info->options |= IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT;
+                       *flags |= IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT;
+               }
+               else {
+                       info->options |= IPT_IPV4OPTION_MATCH_ROUTER_ALERT;
+                       *flags |= IPT_IPV4OPTION_MATCH_ROUTER_ALERT;
+               }
+               break;
+
+               /* any option */
+       case '7' :
+               if ((!invert) && (*flags & IPT_IPV4OPTION_MATCH_ANY_OPT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --any-opt twice");
+               if (invert && (*flags & IPT_IPV4OPTION_MATCH_ANY_OPT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --any-opt with --any-opt");
+               if (invert && (*flags & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --any-opt twice");
+               if ((!invert) &&
+                   ((*flags & IPT_IPV4OPTION_DONT_MATCH_SRR)       ||
+                    (*flags & IPT_IPV4OPTION_DONT_MATCH_RR)        ||
+                    (*flags & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) ||
+                    (*flags & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT)))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --any-opt with any other negative ipv4options match");
+               if (invert &&
+                   ((*flags & IPT_IPV4OPTION_MATCH_LSRR)      ||
+                    (*flags & IPT_IPV4OPTION_MATCH_SSRR)      ||
+                    (*flags & IPT_IPV4OPTION_MATCH_RR)        ||
+                    (*flags & IPT_IPV4OPTION_MATCH_TIMESTAMP) ||
+                    (*flags & IPT_IPV4OPTION_MATCH_ROUTER_ALERT)))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --any-opt with any other positive ipv4options match");
+               if (invert) {
+                       info->options |= IPT_IPV4OPTION_DONT_MATCH_ANY_OPT;
+                       *flags |= IPT_IPV4OPTION_DONT_MATCH_ANY_OPT;    
+               }
+               else {
+                       info->options |= IPT_IPV4OPTION_MATCH_ANY_OPT;
+                       *flags |= IPT_IPV4OPTION_MATCH_ANY_OPT;
+               }
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (flags == 0)
+               exit_error(PARAMETER_PROBLEM,
+                          "ipv4options match: you must specify some parameters. See iptables -m ipv4options --help for help.'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_ipv4options_info *info = ((struct ipt_ipv4options_info *)match->data);
+
+       printf(" IPV4OPTS");
+       if (info->options & IPT_IPV4OPTION_MATCH_SSRR)
+               printf(" SSRR");
+       else if (info->options & IPT_IPV4OPTION_MATCH_LSRR)
+               printf(" LSRR");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_SRR)
+               printf(" !SRR");
+       if (info->options & IPT_IPV4OPTION_MATCH_RR)
+               printf(" RR");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_RR)
+               printf(" !RR");
+       if (info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP)
+               printf(" TS");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP)
+               printf(" !TS");
+       if (info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT)
+               printf(" RA");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT)
+               printf(" !RA");
+       if (info->options & IPT_IPV4OPTION_MATCH_ANY_OPT)
+               printf(" ANYOPT ");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_ANY_OPT)
+               printf(" NOOPT");
+
+       printf(" ");
+}
+
+/* Saves the data in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_ipv4options_info *info = ((struct ipt_ipv4options_info *)match->data);
+
+       if (info->options & IPT_IPV4OPTION_MATCH_SSRR)
+               printf(" --ssrr");
+       else if (info->options & IPT_IPV4OPTION_MATCH_LSRR)
+               printf(" --lsrr");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_SRR)
+               printf(" --no-srr");
+       if (info->options & IPT_IPV4OPTION_MATCH_RR)
+               printf(" --rr");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_RR)
+               printf(" ! --rr");
+       if (info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP)
+               printf(" --ts");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP)
+               printf(" ! --ts");
+       if (info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT)
+               printf(" --ra");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT)
+               printf(" ! --ra");
+       if (info->options & IPT_IPV4OPTION_MATCH_ANY_OPT)
+               printf(" --any-opt");
+       if (info->options & IPT_IPV4OPTION_DONT_MATCH_ANY_OPT)
+               printf(" ! --any-opt");
+
+       printf(" ");
+}
+
+static struct iptables_match ipv4options_struct = { 
+       .next           = NULL,
+       .name           = "ipv4options",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_ipv4options_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_ipv4options_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&ipv4options_struct);
+}
diff --git a/extensions/libipt_ipv4options.man b/extensions/libipt_ipv4options.man
new file mode 100644 (file)
index 0000000..122dc68
--- /dev/null
@@ -0,0 +1,32 @@
+Match on IPv4 header options like source routing, record route,
+timestamp and router-alert.
+.TP
+.B "--ssrr"
+To match packets with the flag strict source routing.
+.TP
+.B "--lsrr"
+To match packets with the flag loose source routing.
+.TP
+.B "--no-srr"
+To match packets with no flag for source routing.
+.TP
+.B "\fR[\fB!\fR]\fB --rr"
+To match packets with the RR flag.
+.TP
+.B "\fR[\fB!\fR]\fB --ts"
+To match packets with the TS flag.
+.TP
+.B "\fR[\fB!\fR]\fB --ra"
+To match packets with the router-alert option.
+.TP
+.B "\fR[\fB!\fR]\fB --any-opt"
+To match a packet with at least one IP option, or no IP option
+at all if ! is chosen.
+.TP
+Examples:
+.TP
+$ iptables -A input -m ipv4options --rr -j DROP
+will drop packets with the record-route flag.
+.TP
+$ iptables -A input -m ipv4options --ts -j DROP
+will drop packets with the timestamp flag.
diff --git a/extensions/libipt_length.c b/extensions/libipt_length.c
new file mode 100644 (file)
index 0000000..cfac1c5
--- /dev/null
@@ -0,0 +1,151 @@
+/* Shared library add-on to iptables to add packet length matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_length.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"length v%s options:\n"
+"[!] --length length[:length]    Match packet length against value or range\n"
+"                                of values (inclusive)\n",
+IPTABLES_VERSION);
+
+}
+  
+static struct option opts[] = {
+       { "length", 1, 0, '1' },
+       {0}
+};
+
+static u_int16_t
+parse_length(const char *s)
+{
+       unsigned int len;
+       
+       if (string_to_number(s, 0, 0xFFFF, &len) == -1)
+               exit_error(PARAMETER_PROBLEM, "length invalid: `%s'\n", s);
+       else
+               return (u_int16_t )len;
+}
+
+/* If a single value is provided, min and max are both set to the value */
+static void
+parse_lengths(const char *s, struct ipt_length_info *info)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(s);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               info->min = info->max = parse_length(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               info->min = buffer[0] ? parse_length(buffer) : 0;
+               info->max = cp[0] ? parse_length(cp) : 0xFFFF;
+       }
+       free(buffer);
+       
+       if (info->min > info->max)
+               exit_error(PARAMETER_PROBLEM,
+                          "length min. range value `%u' greater than max. "
+                          "range value `%u'", info->min, info->max);
+       
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_length_info *info = (struct ipt_length_info *)(*match)->data;
+
+       switch (c) {
+               case '1':
+                       if (*flags)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "length: `--length' may only be "
+                                          "specified once");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       parse_lengths(argv[optind-1], info);
+                       if (invert)
+                               info->invert = 1;
+                       *flags = 1;
+                       break;
+                       
+               default:
+                       return 0;
+       }
+       return 1;
+}
+
+/* Final check; must have specified --length. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "length: You must specify `--length'");
+}
+
+/* Common match printing code. */
+static void
+print_length(struct ipt_length_info *info)
+{
+       if (info->invert)
+               printf("! ");
+       
+       if (info->max == info->min)
+               printf("%u ", info->min);
+       else
+               printf("%u:%u ", info->min, info->max);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       printf("length ");
+       print_length((struct ipt_length_info *)match->data);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       printf("--length ");
+       print_length((struct ipt_length_info *)match->data);
+}
+
+static struct iptables_match length = { 
+       .next           = NULL,
+       .name           = "length",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_length_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_length_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&length);
+}
diff --git a/extensions/libipt_length.man b/extensions/libipt_length.man
new file mode 100644 (file)
index 0000000..43bbdcf
--- /dev/null
@@ -0,0 +1,4 @@
+This module matches the length of a packet against a specific value
+or range of values.
+.TP
+.BR "--length " "[!] \fIlength\fP[:\fIlength\fP]"
diff --git a/extensions/libipt_limit.c b/extensions/libipt_limit.c
new file mode 100644 (file)
index 0000000..7f0337a
--- /dev/null
@@ -0,0 +1,196 @@
+/* Shared library add-on to iptables to add limit support.
+ *
+ * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
+ * Hervé Eychenne    <rv@wallfire.org>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <stddef.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv4/ipt_limit.h"
+
+#define IPT_LIMIT_AVG  "3/hour"
+#define IPT_LIMIT_BURST        5
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"limit v%s options:\n"
+"--limit avg                   max average match rate: default "IPT_LIMIT_AVG"\n"
+"                                [Packets per second unless followed by \n"
+"                                /sec /minute /hour /day postfixes]\n"
+"--limit-burst number          number to match in a burst, default %u\n"
+"\n", IPTABLES_VERSION, IPT_LIMIT_BURST);
+}
+
+static struct option opts[] = {
+       { "limit", 1, 0, '%' },
+       { "limit-burst", 1, 0, '$' },
+       { 0 }
+};
+
+static
+int parse_rate(const char *rate, u_int32_t *val)
+{
+       const char *delim;
+       u_int32_t r;
+       u_int32_t mult = 1;  /* Seconds by default. */
+
+       delim = strchr(rate, '/');
+       if (delim) {
+               if (strlen(delim+1) == 0)
+                       return 0;
+
+               if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
+                       mult = 1;
+               else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
+                       mult = 60;
+               else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
+                       mult = 60*60;
+               else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
+                       mult = 24*60*60;
+               else
+                       return 0;
+       }
+       r = atoi(rate);
+       if (!r)
+               return 0;
+
+       /* This would get mapped to infinite (1/day is minimum they
+           can specify, so we're ok at that end). */
+       if (r / mult > IPT_LIMIT_SCALE)
+               exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
+
+       *val = IPT_LIMIT_SCALE * mult / r;
+       return 1;
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_rateinfo *r = (struct ipt_rateinfo *)m->data;
+
+       parse_rate(IPT_LIMIT_AVG, &r->avg);
+       r->burst = IPT_LIMIT_BURST;
+
+}
+
+/* FIXME: handle overflow:
+       if (r->avg*r->burst/r->burst != r->avg)
+               exit_error(PARAMETER_PROBLEM,
+                          "Sorry: burst too large for that avg rate.\n");
+*/
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_rateinfo *r = (struct ipt_rateinfo *)(*match)->data;
+       unsigned int num;
+
+       switch(c) {
+       case '%':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (!parse_rate(optarg, &r->avg))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "bad rate `%s'", optarg);
+               break;
+
+       case '$':
+               if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+               if (string_to_number(optarg, 0, 10000, &num) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "bad --limit-burst `%s'", optarg);
+               r->burst = num;
+               break;
+
+       default:
+               return 0;
+       }
+
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "limit does not support invert");
+
+       return 1;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+}
+
+static struct rates
+{
+       const char *name;
+       u_int32_t mult;
+} rates[] = { { "day", IPT_LIMIT_SCALE*24*60*60 },
+             { "hour", IPT_LIMIT_SCALE*60*60 },
+             { "min", IPT_LIMIT_SCALE*60 },
+             { "sec", IPT_LIMIT_SCALE } };
+
+static void print_rate(u_int32_t period)
+{
+       unsigned int i;
+
+       for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
+               if (period > rates[i].mult
+            || rates[i].mult/period < rates[i].mult%period)
+                       break;
+       }
+
+       printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_rateinfo *r = (struct ipt_rateinfo *)match->data;
+       printf("limit: avg "); print_rate(r->avg);
+       printf("burst %u ", r->burst);
+}
+
+/* FIXME: Make minimalist: only print rate if not default --RR */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_rateinfo *r = (struct ipt_rateinfo *)match->data;
+
+       printf("--limit "); print_rate(r->avg);
+       if (r->burst != IPT_LIMIT_BURST)
+               printf("--limit-burst %u ", r->burst);
+}
+
+static struct iptables_match limit = { 
+       .next           = NULL,
+       .name           = "limit",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_rateinfo)),
+       .userspacesize  = offsetof(struct ipt_rateinfo, prev),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&limit);
+}
diff --git a/extensions/libipt_limit.man b/extensions/libipt_limit.man
new file mode 100644 (file)
index 0000000..84b63d4
--- /dev/null
@@ -0,0 +1,15 @@
+This module matches at a limited rate using a token bucket filter.
+A rule using this extension will match until this limit is reached
+(unless the `!' flag is used).  It can be used in combination with the
+.B LOG
+target to give limited logging, for example.
+.TP
+.BI "--limit " "rate"
+Maximum average matching rate: specified as a number, with an optional
+`/second', `/minute', `/hour', or `/day' suffix; the default is
+3/hour.
+.TP
+.BI "--limit-burst " "number"
+Maximum initial number of packets to match: this number gets
+recharged by one every time the limit specified above is not reached,
+up to this number; the default is 5.
diff --git a/extensions/libipt_mac.c b/extensions/libipt_mac.c
new file mode 100644 (file)
index 0000000..bac8512
--- /dev/null
@@ -0,0 +1,140 @@
+/* Shared library add-on to iptables to add MAC address support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_mac.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"MAC v%s options:\n"
+" --mac-source [!] XX:XX:XX:XX:XX:XX\n"
+"                              Match source MAC address\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "mac-source", 1, 0, '1' },
+       {0}
+};
+
+static void
+parse_mac(const char *mac, struct ipt_mac_info *info)
+{
+       unsigned int i = 0;
+
+       if (strlen(mac) != ETH_ALEN*3-1)
+               exit_error(PARAMETER_PROBLEM, "Bad mac address `%s'", mac);
+
+       for (i = 0; i < ETH_ALEN; i++) {
+               long number;
+               char *end;
+
+               number = strtol(mac + i*3, &end, 16);
+
+               if (end == mac + i*3 + 2
+                   && number >= 0
+                   && number <= 255)
+                       info->srcaddr[i] = number;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Bad mac address `%s'", mac);
+       }
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_mac_info *macinfo = (struct ipt_mac_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_mac(argv[optind-1], macinfo);
+               if (invert)
+                       macinfo->invert = 1;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void print_mac(unsigned char macaddress[ETH_ALEN])
+{
+       unsigned int i;
+
+       printf("%02X", macaddress[0]);
+       for (i = 1; i < ETH_ALEN; i++)
+               printf(":%02X", macaddress[i]);
+       printf(" ");
+}
+
+/* Final check; must have specified --mac. */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "You must specify `--mac-source'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       printf("MAC ");
+
+       if (((struct ipt_mac_info *)match->data)->invert)
+               printf("! ");
+       
+       print_mac(((struct ipt_mac_info *)match->data)->srcaddr);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       if (((struct ipt_mac_info *)match->data)->invert)
+               printf("! ");
+
+       printf("--mac-source ");
+       print_mac(((struct ipt_mac_info *)match->data)->srcaddr);
+}
+
+static struct iptables_match mac = { 
+       .next           = NULL,
+       .name           = "mac",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_mac_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_mac_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&mac);
+}
diff --git a/extensions/libipt_mac.man b/extensions/libipt_mac.man
new file mode 100644 (file)
index 0000000..5321ca1
--- /dev/null
@@ -0,0 +1,10 @@
+.TP
+.BR "--mac-source " "[!] \fIaddress\fP"
+Match source MAC address.  It must be of the form XX:XX:XX:XX:XX:XX.
+Note that this only makes sense for packets coming from an Ethernet device
+and entering the
+.BR PREROUTING ,
+.B FORWARD
+or
+.B INPUT
+chains.
diff --git a/extensions/libipt_mark.c b/extensions/libipt_mark.c
new file mode 100644 (file)
index 0000000..1922768
--- /dev/null
@@ -0,0 +1,143 @@
+/* Shared library add-on to iptables to add NFMARK matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+/* For 64bit kernel / 32bit userspace */
+#include "../include/linux/netfilter_ipv4/ipt_mark.h"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"MARK match v%s options:\n"
+"[!] --mark value[/mask]         Match nfmark value with optional mask\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "mark", 1, 0, '1' },
+       {0}
+};
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_mark_info *markinfo = (struct ipt_mark_info *)(*match)->data;
+
+       switch (c) {
+               char *end;
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+#ifdef KERNEL_64_USERSPACE_32
+               markinfo->mark = strtoull(optarg, &end, 0);
+               if (*end == '/') {
+                       markinfo->mask = strtoull(end+1, &end, 0);
+               } else
+                       markinfo->mask = 0xffffffffffffffffULL;
+#else
+               markinfo->mark = strtoul(optarg, &end, 0);
+               if (*end == '/') {
+                       markinfo->mask = strtoul(end+1, &end, 0);
+               } else
+                       markinfo->mask = 0xffffffff;
+#endif
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
+               if (invert)
+                       markinfo->invert = 1;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+#ifdef KERNEL_64_USERSPACE_32
+static void
+print_mark(unsigned long long mark, unsigned long long mask, int numeric)
+{
+       if(mask != 0xffffffffffffffffULL)
+               printf("0x%llx/0x%llx ", mark, mask);
+       else
+               printf("0x%llx ", mark);
+}
+#else
+static void
+print_mark(unsigned long mark, unsigned long mask, int numeric)
+{
+       if(mask != 0xffffffff)
+               printf("0x%lx/0x%lx ", mark, mask);
+       else
+               printf("0x%lx ", mark);
+}
+#endif
+
+/* Final check; must have specified --mark. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK match: You must specify `--mark'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_mark_info *info = (struct ipt_mark_info *)match->data;
+
+       printf("MARK match ");
+
+       if (info->invert)
+               printf("!");
+       
+       print_mark(info->mark, info->mask, numeric);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_mark_info *info = (struct ipt_mark_info *)match->data;
+
+       if (info->invert)
+               printf("! ");
+       
+       printf("--mark ");
+       print_mark(info->mark, info->mask, 0);
+}
+
+static struct iptables_match mark = { 
+       .next           = NULL,
+       .name           = "mark",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_mark_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_mark_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&mark);
+}
diff --git a/extensions/libipt_mark.man b/extensions/libipt_mark.man
new file mode 100644 (file)
index 0000000..a2a1395
--- /dev/null
@@ -0,0 +1,9 @@
+This module matches the netfilter mark field associated with a packet
+(which can be set using the
+.B MARK
+target below).
+.TP
+.BR "--mark " "\fIvalue\fP[/\fImask\fP]"
+Matches packets with the given unsigned mark value (if a \fImask\fP is
+specified, this is logically ANDed with the \fImask\fP before the
+comparison).
diff --git a/extensions/libipt_multiport.c b/extensions/libipt_multiport.c
new file mode 100644 (file)
index 0000000..58b6a0e
--- /dev/null
@@ -0,0 +1,467 @@
+/* Shared library add-on to iptables to add multiple TCP port support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+/* To ensure that iptables compiles with an old kernel */
+#include "../include/linux/netfilter_ipv4/ipt_multiport.h"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"multiport v%s options:\n"
+" --source-ports port[,port,port...]\n"
+" --sports ...\n"
+"                              match source port(s)\n"
+" --destination-ports port[,port,port...]\n"
+" --dports ...\n"
+"                              match destination port(s)\n"
+" --ports port[,port,port]\n"
+"                              match both source and destination port(s)\n"
+" NOTE: this kernel does not support port ranges in multiport.\n",
+IPTABLES_VERSION);
+}
+
+static void
+help_v1(void)
+{
+       printf(
+"multiport v%s options:\n"
+" --source-ports [!] port[,port:port,port...]\n"
+" --sports ...\n"
+"                              match source port(s)\n"
+" --destination-ports [!] port[,port:port,port...]\n"
+" --dports ...\n"
+"                              match destination port(s)\n"
+" --ports [!] port[,port:port,port]\n"
+"                              match both source and destination port(s)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "source-ports", 1, 0, '1' },
+       { "sports", 1, 0, '1' }, /* synonym */
+       { "destination-ports", 1, 0, '2' },
+       { "dports", 1, 0, '2' }, /* synonym */
+       { "ports", 1, 0, '3' },
+       {0}
+};
+
+static char *
+proto_to_name(u_int8_t proto)
+{
+       switch (proto) {
+       case IPPROTO_TCP:
+               return "tcp";
+       case IPPROTO_UDP:
+               return "udp";
+       case IPPROTO_UDPLITE:
+               return "udplite";
+       case IPPROTO_SCTP:
+               return "sctp";
+       case IPPROTO_DCCP:
+               return "dccp";
+       default:
+               return NULL;
+       }
+}
+
+static unsigned int
+parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto)
+{
+       char *buffer, *cp, *next;
+       unsigned int i;
+
+       buffer = strdup(portstring);
+       if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
+
+       for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next,i++)
+       {
+               next=strchr(cp, ',');
+               if (next) *next++='\0';
+               ports[i] = parse_port(cp, proto);
+       }
+       if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
+       free(buffer);
+       return i;
+}
+
+static void
+parse_multi_ports_v1(const char *portstring, 
+                    struct ipt_multiport_v1 *multiinfo,
+                    const char *proto)
+{
+       char *buffer, *cp, *next, *range;
+       unsigned int i;
+       u_int16_t m;
+
+       buffer = strdup(portstring);
+       if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
+
+       for (i=0; i<IPT_MULTI_PORTS; i++)
+               multiinfo->pflags[i] = 0;
+       for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next, i++) {
+               next=strchr(cp, ',');
+               if (next) *next++='\0';
+               range = strchr(cp, ':');
+               if (range) {
+                       if (i == IPT_MULTI_PORTS-1)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "too many ports specified");
+                       *range++ = '\0';
+               }
+               multiinfo->ports[i] = parse_port(cp, proto);
+               if (range) {
+                       multiinfo->pflags[i] = 1;
+                       multiinfo->ports[++i] = parse_port(range, proto);
+                       if (multiinfo->ports[i-1] >= multiinfo->ports[i])
+                               exit_error(PARAMETER_PROBLEM,
+                                          "invalid portrange specified");
+                       m <<= 1;
+               }
+       }
+       multiinfo->count = i;
+       if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+}
+
+static const char *
+check_proto(const struct ipt_entry *entry)
+{
+       char *proto;
+
+       if (entry->ip.invflags & IPT_INV_PROTO)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
+
+       if ((proto = proto_to_name(entry->ip.proto)) != NULL)
+               return proto;
+       else if (!entry->ip.proto)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport needs `-p tcp', `-p udp', `-p udplite', "
+                          "`-p sctp' or `-p dccp'");
+       else
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       const char *proto;
+       struct ipt_multiport *multiinfo
+               = (struct ipt_multiport *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               multiinfo->count = parse_multi_ports(argv[optind-1],
+                                                    multiinfo->ports, proto);
+               multiinfo->flags = IPT_MULTIPORT_SOURCE;
+               break;
+
+       case '2':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               multiinfo->count = parse_multi_ports(argv[optind-1],
+                                                    multiinfo->ports, proto);
+               multiinfo->flags = IPT_MULTIPORT_DESTINATION;
+               break;
+
+       case '3':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               multiinfo->count = parse_multi_ports(argv[optind-1],
+                                                    multiinfo->ports, proto);
+               multiinfo->flags = IPT_MULTIPORT_EITHER;
+               break;
+
+       default:
+               return 0;
+       }
+
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport does not support invert");
+
+       if (*flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport can only have one option");
+       *flags = 1;
+       return 1;
+}
+
+static int
+parse_v1(int c, char **argv, int invert, unsigned int *flags,
+        const struct ipt_entry *entry,
+        unsigned int *nfcache,
+        struct ipt_entry_match **match)
+{
+       const char *proto;
+       struct ipt_multiport_v1 *multiinfo
+               = (struct ipt_multiport_v1 *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
+               multiinfo->flags = IPT_MULTIPORT_SOURCE;
+               break;
+
+       case '2':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
+               multiinfo->flags = IPT_MULTIPORT_DESTINATION;
+               break;
+
+       case '3':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
+               multiinfo->flags = IPT_MULTIPORT_EITHER;
+               break;
+
+       default:
+               return 0;
+       }
+
+       if (invert)
+               multiinfo->invert = 1;
+
+       if (*flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport can only have one option");
+       *flags = 1;
+       return 1;
+}
+
+/* Final check; must specify something. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, "multiport expection an option");
+}
+
+static char *
+port_to_service(int port, u_int8_t proto)
+{
+       struct servent *service;
+
+       if ((service = getservbyport(htons(port), proto_to_name(proto))))
+               return service->s_name;
+
+       return NULL;
+}
+
+static void
+print_port(u_int16_t port, u_int8_t protocol, int numeric)
+{
+       char *service;
+
+       if (numeric || (service = port_to_service(port, protocol)) == NULL)
+               printf("%u", port);
+       else
+               printf("%s", service);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_multiport *multiinfo
+               = (const struct ipt_multiport *)match->data;
+       unsigned int i;
+
+       printf("multiport ");
+
+       switch (multiinfo->flags) {
+       case IPT_MULTIPORT_SOURCE:
+               printf("sports ");
+               break;
+
+       case IPT_MULTIPORT_DESTINATION:
+               printf("dports ");
+               break;
+
+       case IPT_MULTIPORT_EITHER:
+               printf("ports ");
+               break;
+
+       default:
+               printf("ERROR ");
+               break;
+       }
+
+       for (i=0; i < multiinfo->count; i++) {
+               printf("%s", i ? "," : "");
+               print_port(multiinfo->ports[i], ip->proto, numeric);
+       }
+       printf(" ");
+}
+
+static void
+print_v1(const struct ipt_ip *ip,
+        const struct ipt_entry_match *match,
+        int numeric)
+{
+       const struct ipt_multiport_v1 *multiinfo
+               = (const struct ipt_multiport_v1 *)match->data;
+       unsigned int i;
+
+       printf("multiport ");
+
+       switch (multiinfo->flags) {
+       case IPT_MULTIPORT_SOURCE:
+               printf("sports ");
+               break;
+
+       case IPT_MULTIPORT_DESTINATION:
+               printf("dports ");
+               break;
+
+       case IPT_MULTIPORT_EITHER:
+               printf("ports ");
+               break;
+
+       default:
+               printf("ERROR ");
+               break;
+       }
+
+       if (multiinfo->invert)
+               printf("! ");
+
+       for (i=0; i < multiinfo->count; i++) {
+               printf("%s", i ? "," : "");
+               print_port(multiinfo->ports[i], ip->proto, numeric);
+               if (multiinfo->pflags[i]) {
+                       printf(":");
+                       print_port(multiinfo->ports[++i], ip->proto, numeric);
+               }
+       }
+       printf(" ");
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_multiport *multiinfo
+               = (const struct ipt_multiport *)match->data;
+       unsigned int i;
+
+       switch (multiinfo->flags) {
+       case IPT_MULTIPORT_SOURCE:
+               printf("--sports ");
+               break;
+
+       case IPT_MULTIPORT_DESTINATION:
+               printf("--dports ");
+               break;
+
+       case IPT_MULTIPORT_EITHER:
+               printf("--ports ");
+               break;
+       }
+
+       for (i=0; i < multiinfo->count; i++) {
+               printf("%s", i ? "," : "");
+               print_port(multiinfo->ports[i], ip->proto, 1);
+       }
+       printf(" ");
+}
+
+static void save_v1(const struct ipt_ip *ip, 
+                   const struct ipt_entry_match *match)
+{
+       const struct ipt_multiport_v1 *multiinfo
+               = (const struct ipt_multiport_v1 *)match->data;
+       unsigned int i;
+
+       switch (multiinfo->flags) {
+       case IPT_MULTIPORT_SOURCE:
+               printf("--sports ");
+               break;
+
+       case IPT_MULTIPORT_DESTINATION:
+               printf("--dports ");
+               break;
+
+       case IPT_MULTIPORT_EITHER:
+               printf("--ports ");
+               break;
+       }
+
+       if (multiinfo->invert)
+               printf("! ");
+
+       for (i=0; i < multiinfo->count; i++) {
+               printf("%s", i ? "," : "");
+               print_port(multiinfo->ports[i], ip->proto, 1);
+               if (multiinfo->pflags[i]) {
+                       printf(":");
+                       print_port(multiinfo->ports[++i], ip->proto, 1);
+               }
+       }
+       printf(" ");
+}
+
+static struct iptables_match multiport = { 
+       .next           = NULL,
+       .name           = "multiport",
+       .revision       = 0,
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_multiport)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_multiport)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+static struct iptables_match multiport_v1 = { 
+       .next           = NULL,
+       .name           = "multiport",
+       .version        = IPTABLES_VERSION,
+       .revision       = 1,
+       .size           = IPT_ALIGN(sizeof(struct ipt_multiport_v1)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_multiport_v1)),
+       .help           = &help_v1,
+       .init           = &init,
+       .parse          = &parse_v1,
+       .final_check    = &final_check,
+       .print          = &print_v1,
+       .save           = &save_v1,
+       .extra_opts     = opts
+};
+
+void
+_init(void)
+{
+       register_match(&multiport);
+       register_match(&multiport_v1);
+}
diff --git a/extensions/libipt_multiport.man b/extensions/libipt_multiport.man
new file mode 100644 (file)
index 0000000..ba760e9
--- /dev/null
@@ -0,0 +1,20 @@
+This module matches a set of source or destination ports.  Up to 15
+ports can be specified.  A port range (port:port) counts as two
+ports.  It can only be used in conjunction with
+.B "-p tcp"
+or
+.BR "-p udp" .
+.TP
+.BR "--source-ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if the source port is one of the given ports.  The flag
+.B --sports
+is a convenient alias for this option.
+.TP
+.BR "--destination-ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if the destination port is one of the given ports.  The flag
+.B --dports
+is a convenient alias for this option.
+.TP
+.BR "--ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if either the source or destination ports are equal to one of
+the given ports.
diff --git a/extensions/libipt_owner.c b/extensions/libipt_owner.c
new file mode 100644 (file)
index 0000000..cf13cb9
--- /dev/null
@@ -0,0 +1,250 @@
+/* Shared library add-on to iptables to add OWNER matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_owner.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+#ifdef IPT_OWNER_COMM
+       printf(
+"OWNER match v%s options:\n"
+"[!] --uid-owner userid     Match local uid\n"
+"[!] --gid-owner groupid    Match local gid\n"
+"[!] --pid-owner processid  Match local pid\n"
+"[!] --sid-owner sessionid  Match local sid\n"
+"[!] --cmd-owner name       Match local command name\n"
+"NOTE: pid, sid and command matching are broken on SMP\n"
+"\n",
+IPTABLES_VERSION);
+#else
+       printf(
+"OWNER match v%s options:\n"
+"[!] --uid-owner userid     Match local uid\n"
+"[!] --gid-owner groupid    Match local gid\n"
+"[!] --pid-owner processid  Match local pid\n"
+"[!] --sid-owner sessionid  Match local sid\n"
+"NOTE: pid and sid matching are broken on SMP\n"
+"\n",
+IPTABLES_VERSION);
+#endif /* IPT_OWNER_COMM */
+}
+
+static struct option opts[] = {
+       { "uid-owner", 1, 0, '1' },
+       { "gid-owner", 1, 0, '2' },
+       { "pid-owner", 1, 0, '3' },
+       { "sid-owner", 1, 0, '4' },
+#ifdef IPT_OWNER_COMM
+       { "cmd-owner", 1, 0, '5' },
+#endif
+       {0}
+};
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_owner_info *ownerinfo = (struct ipt_owner_info *)(*match)->data;
+
+       switch (c) {
+               char *end;
+               struct passwd *pwd;
+               struct group *grp;
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               if ((pwd = getpwnam(optarg)))
+                       ownerinfo->uid = pwd->pw_uid;
+               else {
+                       ownerinfo->uid = strtoul(optarg, &end, 0);
+                       if (*end != '\0' || end == optarg)
+                               exit_error(PARAMETER_PROBLEM, "Bad OWNER UID value `%s'", optarg);
+               }
+               if (invert)
+                       ownerinfo->invert |= IPT_OWNER_UID;
+               ownerinfo->match |= IPT_OWNER_UID;
+               *flags = 1;
+               break;
+
+       case '2':
+               check_inverse(optarg, &invert, &optind, 0);
+               if ((grp = getgrnam(optarg)))
+                       ownerinfo->gid = grp->gr_gid;
+               else {
+                       ownerinfo->gid = strtoul(optarg, &end, 0);
+                       if (*end != '\0' || end == optarg)
+                               exit_error(PARAMETER_PROBLEM, "Bad OWNER GID value `%s'", optarg);
+               }
+               if (invert)
+                       ownerinfo->invert |= IPT_OWNER_GID;
+               ownerinfo->match |= IPT_OWNER_GID;
+               *flags = 1;
+               break;
+
+       case '3':
+               check_inverse(optarg, &invert, &optind, 0);
+               ownerinfo->pid = strtoul(optarg, &end, 0);
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad OWNER PID value `%s'", optarg);
+               if (invert)
+                       ownerinfo->invert |= IPT_OWNER_PID;
+               ownerinfo->match |= IPT_OWNER_PID;
+               *flags = 1;
+               break;
+
+       case '4':
+               check_inverse(optarg, &invert, &optind, 0);
+               ownerinfo->sid = strtoul(optarg, &end, 0);
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad OWNER SID value `%s'", optarg);
+               if (invert)
+                       ownerinfo->invert |= IPT_OWNER_SID;
+               ownerinfo->match |= IPT_OWNER_SID;
+               *flags = 1;
+               break;
+
+#ifdef IPT_OWNER_COMM
+       case '5':
+               check_inverse(optarg, &invert, &optind, 0);
+               if(strlen(optarg) > sizeof(ownerinfo->comm))
+                       exit_error(PARAMETER_PROBLEM, "OWNER CMD `%s' too long, max %u characters", optarg, (unsigned int)sizeof(ownerinfo->comm));
+
+               strncpy(ownerinfo->comm, optarg, sizeof(ownerinfo->comm));
+               ownerinfo->comm[sizeof(ownerinfo->comm)-1] = '\0';
+
+               if (invert)
+                       ownerinfo->invert |= IPT_OWNER_COMM;
+               ownerinfo->match |= IPT_OWNER_COMM;
+               *flags = 1;
+               break;
+#endif
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+print_item(struct ipt_owner_info *info, u_int8_t flag, int numeric, char *label)
+{
+       if(info->match & flag) {
+
+               if (info->invert & flag)
+                       printf("! ");
+
+               printf(label);
+
+               switch(info->match & flag) {
+               case IPT_OWNER_UID:
+                       if(!numeric) {
+                               struct passwd *pwd = getpwuid(info->uid);
+
+                               if(pwd && pwd->pw_name) {
+                                       printf("%s ", pwd->pw_name);
+                                       break;
+                               }
+                               /* FALLTHROUGH */
+                       }
+                       printf("%u ", info->uid);
+                       break;
+               case IPT_OWNER_GID:
+                       if(!numeric) {
+                               struct group *grp = getgrgid(info->gid);
+
+                               if(grp && grp->gr_name) {
+                                       printf("%s ", grp->gr_name);
+                                       break;
+                               }
+                               /* FALLTHROUGH */
+                       }
+                       printf("%u ", info->gid);
+                       break;
+               case IPT_OWNER_PID:
+                       printf("%u ", info->pid);
+                       break;
+               case IPT_OWNER_SID:
+                       printf("%u ", info->sid);
+                       break;
+#ifdef IPT_OWNER_COMM
+               case IPT_OWNER_COMM:
+                       printf("%.*s ", (int)sizeof(info->comm), info->comm);
+                       break;
+#endif
+               default:
+                       break;
+               }
+       }
+}
+
+/* Final check; must have specified --own. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "OWNER match: You must specify one or more options");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_owner_info *info = (struct ipt_owner_info *)match->data;
+
+       print_item(info, IPT_OWNER_UID, numeric, "OWNER UID match ");
+       print_item(info, IPT_OWNER_GID, numeric, "OWNER GID match ");
+       print_item(info, IPT_OWNER_PID, numeric, "OWNER PID match ");
+       print_item(info, IPT_OWNER_SID, numeric, "OWNER SID match ");
+#ifdef IPT_OWNER_COMM
+       print_item(info, IPT_OWNER_COMM, numeric, "OWNER CMD match ");
+#endif
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_owner_info *info = (struct ipt_owner_info *)match->data;
+
+       print_item(info, IPT_OWNER_UID, 0, "--uid-owner ");
+       print_item(info, IPT_OWNER_GID, 0, "--gid-owner ");
+       print_item(info, IPT_OWNER_PID, 0, "--pid-owner ");
+       print_item(info, IPT_OWNER_SID, 0, "--sid-owner ");
+#ifdef IPT_OWNER_COMM
+       print_item(info, IPT_OWNER_COMM, 0, "--cmd-owner ");
+#endif
+}
+
+static struct iptables_match owner = { 
+       .next           = NULL,
+       .name           = "owner",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_owner_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_owner_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&owner);
+}
diff --git a/extensions/libipt_owner.man b/extensions/libipt_owner.man
new file mode 100644 (file)
index 0000000..b635e7d
--- /dev/null
@@ -0,0 +1,28 @@
+This module attempts to match various characteristics of the packet
+creator, for locally-generated packets.  It is only valid in the
+.B OUTPUT
+chain, and even this some packets (such as ICMP ping responses) may
+have no owner, and hence never match.
+.TP
+.BI "--uid-owner " "userid"
+Matches if the packet was created by a process with the given
+effective user id.
+.TP
+.BI "--gid-owner " "groupid"
+Matches if the packet was created by a process with the given
+effective group id.
+.TP
+.BI "--pid-owner " "processid"
+Matches if the packet was created by a process with the given
+process id.
+.TP
+.BI "--sid-owner " "sessionid"
+Matches if the packet was created by a process in the given session
+group.
+.TP
+.BI "--cmd-owner " "name"
+Matches if the packet was created by a process with the given command name.
+(this option is present only if iptables was compiled under a kernel
+supporting this feature)
+.TP
+.B NOTE: pid, sid and command matching are broken on SMP
diff --git a/extensions/libipt_physdev.c b/extensions/libipt_physdev.c
new file mode 100644 (file)
index 0000000..28ee827
--- /dev/null
@@ -0,0 +1,193 @@
+/* Shared library add-on to iptables to add bridge port matching support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_physdev.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+static void
+help(void)
+{
+       printf(
+"physdev v%s options:\n"
+" --physdev-in [!] input name[+]               bridge port name ([+] for wildcard)\n"
+" --physdev-out [!] output name[+]     bridge port name ([+] for wildcard)\n"
+" [!] --physdev-is-in                  arrived on a bridge device\n"
+" [!] --physdev-is-out                 will leave on a bridge device\n"
+" [!] --physdev-is-bridged             it's a bridged packet\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "physdev-in", 1, 0, '1' },
+       { "physdev-out", 1, 0, '2' },
+       { "physdev-is-in", 0, 0, '3' },
+       { "physdev-is-out", 0, 0, '4' },
+       { "physdev-is-bridged", 0, 0, '5' },
+       {0}
+};
+
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+}
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_physdev_info *info =
+               (struct ipt_physdev_info*)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IPT_PHYSDEV_OP_IN)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_interface(argv[optind-1], info->physindev,
+                               (unsigned char *)info->in_mask);
+               if (invert)
+                       info->invert |= IPT_PHYSDEV_OP_IN;
+               info->bitmask |= IPT_PHYSDEV_OP_IN;
+               *flags |= IPT_PHYSDEV_OP_IN;
+               break;
+
+       case '2':
+               if (*flags & IPT_PHYSDEV_OP_OUT)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_interface(argv[optind-1], info->physoutdev,
+                               (unsigned char *)info->out_mask);
+               if (invert)
+                       info->invert |= IPT_PHYSDEV_OP_OUT;
+               info->bitmask |= IPT_PHYSDEV_OP_OUT;
+               *flags |= IPT_PHYSDEV_OP_OUT;
+               break;
+
+       case '3':
+               if (*flags & IPT_PHYSDEV_OP_ISIN)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               info->bitmask |= IPT_PHYSDEV_OP_ISIN;
+               if (invert)
+                       info->invert |= IPT_PHYSDEV_OP_ISIN;
+               *flags |= IPT_PHYSDEV_OP_ISIN;
+               break;
+
+       case '4':
+               if (*flags & IPT_PHYSDEV_OP_ISOUT)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               info->bitmask |= IPT_PHYSDEV_OP_ISOUT;
+               if (invert)
+                       info->invert |= IPT_PHYSDEV_OP_ISOUT;
+               *flags |= IPT_PHYSDEV_OP_ISOUT;
+               break;
+
+       case '5':
+               if (*flags & IPT_PHYSDEV_OP_BRIDGED)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               if (invert)
+                       info->invert |= IPT_PHYSDEV_OP_BRIDGED;
+               *flags |= IPT_PHYSDEV_OP_BRIDGED;
+               info->bitmask |= IPT_PHYSDEV_OP_BRIDGED;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+multiple_use:
+       exit_error(PARAMETER_PROBLEM,
+          "multiple use of the same physdev option is not allowed");
+
+}
+
+static void final_check(unsigned int flags)
+{
+       if (flags == 0)
+               exit_error(PARAMETER_PROBLEM, "PHYSDEV: no physdev option specified");
+}
+
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_physdev_info *info =
+               (struct ipt_physdev_info*)match->data;
+
+       printf("PHYSDEV match");
+       if (info->bitmask & IPT_PHYSDEV_OP_ISIN)
+               printf("%s --physdev-is-in",
+                      info->invert & IPT_PHYSDEV_OP_ISIN ? " !":"");
+       if (info->bitmask & IPT_PHYSDEV_OP_IN)
+               printf("%s --physdev-in %s",
+               (info->invert & IPT_PHYSDEV_OP_IN) ? " !":"", info->physindev);
+
+       if (info->bitmask & IPT_PHYSDEV_OP_ISOUT)
+               printf("%s --physdev-is-out",
+                      info->invert & IPT_PHYSDEV_OP_ISOUT ? " !":"");
+       if (info->bitmask & IPT_PHYSDEV_OP_OUT)
+               printf("%s --physdev-out %s",
+               (info->invert & IPT_PHYSDEV_OP_OUT) ? " !":"", info->physoutdev);
+       if (info->bitmask & IPT_PHYSDEV_OP_BRIDGED)
+               printf("%s --physdev-is-bridged",
+                      info->invert & IPT_PHYSDEV_OP_BRIDGED ? " !":"");
+       printf(" ");
+}
+
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_physdev_info *info =
+               (struct ipt_physdev_info*)match->data;
+
+       if (info->bitmask & IPT_PHYSDEV_OP_ISIN)
+               printf("%s --physdev-is-in",
+                      info->invert & IPT_PHYSDEV_OP_ISIN ? " !":"");
+       if (info->bitmask & IPT_PHYSDEV_OP_IN)
+               printf("%s --physdev-in %s",
+               (info->invert & IPT_PHYSDEV_OP_IN) ? " !":"", info->physindev);
+
+       if (info->bitmask & IPT_PHYSDEV_OP_ISOUT)
+               printf("%s --physdev-is-out",
+                      info->invert & IPT_PHYSDEV_OP_ISOUT ? " !":"");
+       if (info->bitmask & IPT_PHYSDEV_OP_OUT)
+               printf("%s --physdev-out %s",
+               (info->invert & IPT_PHYSDEV_OP_OUT) ? " !":"", info->physoutdev);
+       if (info->bitmask & IPT_PHYSDEV_OP_BRIDGED)
+               printf("%s --physdev-is-bridged",
+                      info->invert & IPT_PHYSDEV_OP_BRIDGED ? " !":"");
+       printf(" ");
+}
+
+static struct iptables_match physdev = { 
+       .next           = NULL,
+       .name           = "physdev",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_physdev_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_physdev_info)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&physdev);
+}
diff --git a/extensions/libipt_physdev.man b/extensions/libipt_physdev.man
new file mode 100644 (file)
index 0000000..1e635fc
--- /dev/null
@@ -0,0 +1,42 @@
+This module matches on the bridge port input and output devices enslaved
+to a bridge device. This module is a part of the infrastructure that enables
+a transparent bridging IP firewall and is only useful for kernel versions
+above version 2.5.44.
+.TP
+.BR --physdev-in " [!] \fIname\fP"
+Name of a bridge port via which a packet is received (only for
+packets entering the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains). If the interface name ends in a "+", then any
+interface which begins with this name will match. If the packet didn't arrive
+through a bridge device, this packet won't match this option, unless '!' is used.
+.TP
+.BR --physdev-out " [!] \fIname\fP"
+Name of a bridge port via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD ,
+.B OUTPUT
+and
+.B POSTROUTING
+chains).  If the interface name ends in a "+", then any
+interface which begins with this name will match. Note that in the
+.BR nat " and " mangle
+.B OUTPUT
+chains one cannot match on the bridge output port, however one can in the
+.B "filter OUTPUT"
+chain. If the packet won't leave by a bridge device or it is yet unknown what
+the output device will be, then the packet won't match this option, unless
+'!' is used.
+.TP
+.RB "[!] " --physdev-is-in
+Matches if the packet has entered through a bridge interface.
+.TP
+.RB "[!] " --physdev-is-out
+Matches if the packet will leave through a bridge interface.
+.TP
+.RB "[!] " --physdev-is-bridged
+Matches if the packet is being bridged and therefore is not being routed.
+This is only useful in the FORWARD and POSTROUTING chains.
diff --git a/extensions/libipt_pkttype.c b/extensions/libipt_pkttype.c
new file mode 100644 (file)
index 0000000..ea6439e
--- /dev/null
@@ -0,0 +1,167 @@
+/* 
+ * Shared library add-on to iptables to match 
+ * packets by their type (BROADCAST, UNICAST, MULTICAST). 
+ *
+ * Michal Ludvig <michal@logix.cz>
+ */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+#include <iptables.h>
+#include <linux/if_packet.h>
+#include <linux/netfilter_ipv4/ipt_pkttype.h>
+
+#define        PKTTYPE_VERSION "0.1"
+
+struct pkttypes {
+       const char *name;
+       unsigned char pkttype;
+       unsigned char printhelp;
+       const char *help;
+};
+
+static const struct pkttypes supported_types[] = {
+       {"unicast", PACKET_HOST, 1, "to us"},
+       {"broadcast", PACKET_BROADCAST, 1, "to all"},
+       {"multicast", PACKET_MULTICAST, 1, "to group"},
+/*
+       {"otherhost", PACKET_OTHERHOST, 1, "to someone else"},
+       {"outgoing", PACKET_OUTGOING, 1, "outgoing of any type"},
+*/
+       /* aliases */
+       {"bcast", PACKET_BROADCAST, 0, NULL},
+       {"mcast", PACKET_MULTICAST, 0, NULL},
+       {"host", PACKET_HOST, 0, NULL}
+};
+
+static void print_types()
+{
+       unsigned int    i;
+       
+       printf("Valid packet types:\n");
+       for (i = 0; i < sizeof(supported_types)/sizeof(struct pkttypes); i++)
+       {
+               if(supported_types[i].printhelp == 1)
+                       printf("\t%-14s\t\t%s\n", supported_types[i].name, supported_types[i].help);
+       }
+       printf("\n");
+}
+
+/* Function which prints out usage message. */
+static void help(void)
+{
+       printf(
+"pkt_type v%s options:\n"
+"  --pkt-type [!] packettype\tmatch packet type\n"
+"\n", PKTTYPE_VERSION);
+       print_types();
+}
+
+static struct option opts[] = {
+       {"pkt-type", 1, 0, '1'},
+       {0}
+};
+
+static void parse_pkttype(const char *pkttype, struct ipt_pkttype_info *info)
+{
+       unsigned int    i;
+       
+       for (i = 0; i < sizeof(supported_types)/sizeof(struct pkttypes); i++)
+       {
+               if(strcasecmp(pkttype, supported_types[i].name)==0)
+               {
+                       info->pkttype=supported_types[i].pkttype;
+                       return;
+               }
+       }
+       
+       exit_error(PARAMETER_PROBLEM, "Bad packet type '%s'", pkttype);
+}
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_pkttype_info *info = (struct ipt_pkttype_info *)(*match)->data;
+       
+       switch(c)
+       {
+               case '1':
+                       check_inverse(optarg, &invert, &optind, 0);
+                       parse_pkttype(argv[optind-1], info);
+                       if(invert)
+                               info->invert=1;
+                       *flags=1;
+                       break;
+
+               default: 
+                       return 0;
+       }
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, "You must specify `--pkt-type'");
+}
+
+static void print_pkttype(struct ipt_pkttype_info *info)
+{
+       unsigned int    i;
+       
+       for (i = 0; i < sizeof(supported_types)/sizeof(struct pkttypes); i++)
+       {
+               if(supported_types[i].pkttype==info->pkttype)
+               {
+                       printf("%s ", supported_types[i].name);
+                       return;
+               }
+       }
+
+       printf("%d ", info->pkttype);   /* in case we didn't find an entry in named-packtes */
+}
+
+static void print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric)
+{
+       struct ipt_pkttype_info *info = (struct ipt_pkttype_info *)match->data;
+       
+       printf("PKTTYPE %s= ", info->invert?"!":"");
+       print_pkttype(info);
+}
+
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_pkttype_info *info = (struct ipt_pkttype_info *)match->data;
+       
+       printf("--pkt-type %s", info->invert?"! ":"");
+       print_pkttype(info);
+}
+
+static struct iptables_match pkttype = {
+       .next           = NULL,
+       .name           = "pkttype",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_pkttype_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_pkttype_info)),
+       .help           = &help,
+       .parse          = &parse, 
+       .final_check    = &final_check, 
+       .print          = &print,
+       .save           = &save, 
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&pkttype);
+}
diff --git a/extensions/libipt_pkttype.man b/extensions/libipt_pkttype.man
new file mode 100644 (file)
index 0000000..b52810b
--- /dev/null
@@ -0,0 +1,3 @@
+This module matches the link-layer packet type.
+.TP
+.BI "--pkt-type " "[\fIunicast\fP|\fIbroadcast\fP|\fImulticast\fP]"
diff --git a/extensions/libipt_policy.c b/extensions/libipt_policy.c
new file mode 100644 (file)
index 0000000..681995a
--- /dev/null
@@ -0,0 +1,436 @@
+/* Shared library add-on to iptables to add policy support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <iptables.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include "../include/linux/netfilter_ipv4/ipt_policy.h"
+
+/*
+ * HACK: global pointer to current matchinfo for making
+ * final checks and adjustments in final_check.
+ */
+static struct ipt_policy_info *policy_info;
+
+static void help(void)
+{
+       printf(
+"policy v%s options:\n"
+"  --dir in|out                        match policy applied during decapsulation/\n"
+"                              policy to be applied during encapsulation\n"
+"  --pol none|ipsec            match policy\n"
+"  --strict                    match entire policy instead of single element\n"
+"                              at any position\n"
+"[!] --reqid reqid             match reqid\n"
+"[!] --spi spi                 match SPI\n"
+"[!] --proto proto             match protocol (ah/esp/ipcomp)\n"
+"[!] --mode mode               match mode (transport/tunnel)\n"
+"[!] --tunnel-src addr/mask    match tunnel source\n"
+"[!] --tunnel-dst addr/mask    match tunnel destination\n"
+"  --next                      begin next element in policy\n",
+       IPTABLES_VERSION);
+}
+
+static struct option opts[] =
+{
+       {
+               .name           = "dir",
+               .has_arg        = 1,
+               .val            = '1',
+       },
+       {
+               .name           = "pol",
+               .has_arg        = 1,
+               .val            = '2',
+       },
+       {
+               .name           = "strict",
+               .val            = '3'
+       },
+       {
+               .name           = "reqid",
+               .has_arg        = 1,
+               .val            = '4',
+       },
+       {
+               .name           = "spi",
+               .has_arg        = 1,
+               .val            = '5'
+       },
+       {
+               .name           = "tunnel-src",
+               .has_arg        = 1,
+               .val            = '6'
+       },
+       {
+               .name           = "tunnel-dst",
+               .has_arg        = 1,
+               .val            = '7'
+       },
+       {
+               .name           = "proto",
+               .has_arg        = 1,
+               .val            = '8'
+       },
+       {
+               .name           = "mode",
+               .has_arg        = 1,
+               .val            = '9'
+       },
+       {
+               .name           = "next",
+               .val            = 'a'
+       },
+       { }
+};
+
+static void init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       *nfcache |= NFC_UNKNOWN;
+}
+
+static int parse_direction(char *s)
+{
+       if (strcmp(s, "in") == 0)
+               return IPT_POLICY_MATCH_IN;
+       if (strcmp(s, "out") == 0)
+               return IPT_POLICY_MATCH_OUT;
+       exit_error(PARAMETER_PROBLEM, "policy_match: invalid dir `%s'", s);
+}
+
+static int parse_policy(char *s)
+{
+       if (strcmp(s, "none") == 0)
+               return IPT_POLICY_MATCH_NONE;
+       if (strcmp(s, "ipsec") == 0)
+               return 0;
+       exit_error(PARAMETER_PROBLEM, "policy match: invalid policy `%s'", s);
+}
+
+static int parse_mode(char *s)
+{
+       if (strcmp(s, "transport") == 0)
+               return IPT_POLICY_MODE_TRANSPORT;
+       if (strcmp(s, "tunnel") == 0)
+               return IPT_POLICY_MODE_TUNNEL;
+       exit_error(PARAMETER_PROBLEM, "policy match: invalid mode `%s'", s);
+}
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+                 const struct ipt_entry *entry,
+                 unsigned int *nfcache,
+                 struct ipt_entry_match **match)
+{
+       struct ipt_policy_info *info = (void *)(*match)->data;
+       struct ipt_policy_elem *e = &info->pol[info->len];
+       struct in_addr *addr = NULL, mask;
+       unsigned int naddr = 0;
+       int mode;
+
+       check_inverse(optarg, &invert, &optind, 0);
+
+       switch (c) {
+       case '1':
+               if (info->flags & (IPT_POLICY_MATCH_IN|IPT_POLICY_MATCH_OUT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --dir option");
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: can't invert --dir option");
+
+               info->flags |= parse_direction(argv[optind-1]);
+               break;
+       case '2':
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: can't invert --policy option");
+
+               info->flags |= parse_policy(argv[optind-1]);
+               break;
+       case '3':
+               if (info->flags & IPT_POLICY_MATCH_STRICT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --strict option");
+
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: can't invert --strict option");
+
+               info->flags |= IPT_POLICY_MATCH_STRICT;
+               break;
+       case '4':
+               if (e->match.reqid)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --reqid option");
+
+               e->match.reqid = 1;
+               e->invert.reqid = invert;
+               e->reqid = strtol(argv[optind-1], NULL, 10);
+               break;
+       case '5':
+               if (e->match.spi)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --spi option");
+
+               e->match.spi = 1;
+               e->invert.spi = invert;
+               e->spi = strtol(argv[optind-1], NULL, 0x10);
+               break;
+       case '6':
+               if (e->match.saddr)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --tunnel-src option");
+
+               parse_hostnetworkmask(argv[optind-1], &addr, &mask, &naddr);
+               if (naddr > 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: name resolves to multiple IPs");
+
+               e->match.saddr = 1;
+               e->invert.saddr = invert;
+               e->saddr.a4 = addr[0];
+               e->smask.a4 = mask;
+                break;
+       case '7':
+               if (e->match.daddr)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --tunnel-dst option");
+
+               parse_hostnetworkmask(argv[optind-1], &addr, &mask, &naddr);
+               if (naddr > 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: name resolves to multiple IPs");
+
+               e->match.daddr = 1;
+               e->invert.daddr = invert;
+               e->daddr.a4 = addr[0];
+               e->dmask.a4 = mask;
+               break;
+       case '8':
+               if (e->match.proto)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --proto option");
+
+               e->proto = parse_protocol(argv[optind-1]);
+               if (e->proto != IPPROTO_AH && e->proto != IPPROTO_ESP &&
+                   e->proto != IPPROTO_COMP)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: protocol must ah/esp/ipcomp");
+               e->match.proto = 1;
+               e->invert.proto = invert;
+               break;
+       case '9':
+               if (e->match.mode)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: double --mode option");
+
+               mode = parse_mode(argv[optind-1]);
+               e->match.mode = 1;
+               e->invert.mode = invert;
+               e->mode = mode;
+               break;
+       case 'a':
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: can't invert --next option");
+
+               if (++info->len == IPT_POLICY_MAX_ELEM)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: maximum policy depth reached");
+               break;
+       default:
+               return 0;
+       }
+
+       policy_info = info;
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       struct ipt_policy_info *info = policy_info;
+       struct ipt_policy_elem *e;
+       int i;
+
+       if (info == NULL)
+               exit_error(PARAMETER_PROBLEM,
+                          "policy match: no parameters given");
+
+       if (!(info->flags & (IPT_POLICY_MATCH_IN|IPT_POLICY_MATCH_OUT)))
+               exit_error(PARAMETER_PROBLEM,
+                          "policy match: neither --in nor --out specified");
+
+       if (info->flags & IPT_POLICY_MATCH_NONE) {
+               if (info->flags & IPT_POLICY_MATCH_STRICT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: policy none but --strict given");
+
+               if (info->len != 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: policy none but policy given");
+       } else
+               info->len++;    /* increase len by 1, no --next after last element */
+
+       if (!(info->flags & IPT_POLICY_MATCH_STRICT) && info->len > 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "policy match: multiple elements but no --strict");
+
+       for (i = 0; i < info->len; i++) {
+               e = &info->pol[i];
+
+               if (info->flags & IPT_POLICY_MATCH_STRICT &&
+                   !(e->match.reqid || e->match.spi || e->match.saddr ||
+                     e->match.daddr || e->match.proto || e->match.mode))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: empty policy element");
+
+               if ((e->match.saddr || e->match.daddr)
+                   && ((e->mode == IPT_POLICY_MODE_TUNNEL && e->invert.mode) ||
+                       (e->mode == IPT_POLICY_MODE_TRANSPORT && !e->invert.mode)))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "policy match: --tunnel-src/--tunnel-dst "
+                                  "is only valid in tunnel mode");
+       }
+}
+
+static void print_mode(char *prefix, u_int8_t mode, int numeric)
+{
+       printf("%smode ", prefix);
+
+       switch (mode) {
+       case IPT_POLICY_MODE_TRANSPORT:
+               printf("transport ");
+               break;
+       case IPT_POLICY_MODE_TUNNEL:
+               printf("tunnel ");
+               break;
+       default:
+               printf("??? ");
+               break;
+       }
+}
+
+static void print_proto(char *prefix, u_int8_t proto, int numeric)
+{
+       struct protoent *p = NULL;
+
+       printf("%sproto ", prefix);
+       if (!numeric)
+               p = getprotobynumber(proto);
+       if (p != NULL)
+               printf("%s ", p->p_name);
+       else
+               printf("%u ", proto);
+}
+
+#define PRINT_INVERT(x)                \
+do {                           \
+       if (x)                  \
+               printf("! ");   \
+} while(0)
+
+static void print_entry(char *prefix, const struct ipt_policy_elem *e,
+                        int numeric)
+{
+       if (e->match.reqid) {
+               PRINT_INVERT(e->invert.reqid);
+               printf("%sreqid %u ", prefix, e->reqid);
+       }
+       if (e->match.spi) {
+               PRINT_INVERT(e->invert.spi);
+               printf("%sspi 0x%x ", prefix, e->spi);
+       }
+       if (e->match.proto) {
+               PRINT_INVERT(e->invert.proto);
+               print_proto(prefix, e->proto, numeric);
+       }
+       if (e->match.mode) {
+               PRINT_INVERT(e->invert.mode);
+               print_mode(prefix, e->mode, numeric);
+       }
+       if (e->match.daddr) {
+               PRINT_INVERT(e->invert.daddr);
+               printf("%stunnel-dst %s%s ", prefix,
+                      addr_to_dotted((struct in_addr *)&e->daddr),
+                      mask_to_dotted((struct in_addr *)&e->dmask));
+       }
+       if (e->match.saddr) {
+               PRINT_INVERT(e->invert.saddr);
+               printf("%stunnel-src %s%s ", prefix,
+                      addr_to_dotted((struct in_addr *)&e->saddr),
+                      mask_to_dotted((struct in_addr *)&e->smask));
+       }
+}
+
+static void print_flags(char *prefix, const struct ipt_policy_info *info)
+{
+       if (info->flags & IPT_POLICY_MATCH_IN)
+               printf("%sdir in ", prefix);
+       else
+               printf("%sdir out ", prefix);
+
+       if (info->flags & IPT_POLICY_MATCH_NONE)
+               printf("%spol none ", prefix);
+       else
+               printf("%spol ipsec ", prefix);
+
+       if (info->flags & IPT_POLICY_MATCH_STRICT)
+               printf("%sstrict ", prefix);
+}
+
+static void print(const struct ipt_ip *ip,
+                  const struct ipt_entry_match *match,
+                 int numeric)
+{
+       const struct ipt_policy_info *info = (void *)match->data;
+       unsigned int i;
+
+       printf("policy match ");
+       print_flags("", info);
+       for (i = 0; i < info->len; i++) {
+               if (info->len > 1)
+                       printf("[%u] ", i);
+               print_entry("", &info->pol[i], numeric);
+       }
+}
+
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_policy_info *info = (void *)match->data;
+       unsigned int i;
+
+       print_flags("--", info);
+       for (i = 0; i < info->len; i++) {
+               print_entry("--", &info->pol[i], 0);
+               if (i + 1 < info->len)
+                       printf("--next ");
+       }
+}
+
+struct iptables_match policy = {
+       .name           = "policy",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_policy_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_policy_info)),
+       .help           = help,
+       .init           = init,
+       .parse          = parse,
+       .final_check    = final_check,
+       .print          = print,
+       .save           = save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&policy);
+}
diff --git a/extensions/libipt_policy.man b/extensions/libipt_policy.man
new file mode 100644 (file)
index 0000000..eed163e
--- /dev/null
@@ -0,0 +1,48 @@
+This modules matches the policy used by IPsec for handling a packet.
+.TP
+.BI "--dir " "in|out"
+Used to select whether to match the policy used for decapsulation or the
+policy that will be used for encapsulation.
+.B in
+is valid in the
+.B PREROUTING, INPUT and FORWARD
+chains,
+.B out
+is valid in the
+.B POSTROUTING, OUTPUT and FORWARD
+chains.
+.TP
+.BI "--pol " "none|ipsec"
+Matches if the packet is subject to IPsec processing.
+.TP
+.BI "--strict"
+Selects whether to match the exact policy or match if any rule of
+the policy matches the given policy.
+.TP
+.BI "--reqid " "id"
+Matches the reqid of the policy rule. The reqid can be specified with
+.B setkey(8)
+using
+.B unique:id
+as level.
+.TP
+.BI "--spi " "spi"
+Matches the SPI of the SA.
+.TP
+.BI "--proto " "ah|esp|ipcomp"
+Matches the encapsulation protocol.
+.TP
+.BI "--mode " "tunnel|transport"
+Matches the encapsulation mode.
+.TP
+.BI "--tunnel-src " "addr[/mask]"
+Matches the source end-point address of a tunnel mode SA.
+Only valid with --mode tunnel.
+.TP
+.BI "--tunnel-dst " "addr[/mask]"
+Matches the destination end-point address of a tunnel mode SA.
+Only valid with --mode tunnel.
+.TP
+.BI "--next"
+Start the next element in the policy specification. Can only be used with
+--strict
diff --git a/extensions/libipt_pool.c.orig b/extensions/libipt_pool.c.orig
new file mode 100644 (file)
index 0000000..666599d
--- /dev/null
@@ -0,0 +1,144 @@
+/* Shared library add-on to iptables to add IP address pool matching. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ipt_pool.h>
+
+#include <libippool/ip_pool_support.h>
+
+/* FIXME --RR */
+#include "../ippool/libippool.c"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"pool v%s options:\n"
+" [!] --srcpool NAME|INDEX\n"
+" [!] --dstpool NAME|INDEX\n"
+"                      Pool index (or name from %s) to match\n"
+"\n", IPTABLES_VERSION, IPPOOL_CONF);
+}
+
+static struct option opts[] = {
+       { "srcpool", 1, 0, '1' },
+       { "dstpool", 1, 0, '2' },
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *match, unsigned int *nfcache)
+{
+       struct ipt_pool_info *info =
+               (struct ipt_pool_info *)match->data;
+
+       info->src = IP_POOL_NONE;
+       info->dst = IP_POOL_NONE;
+       info->flags = 0;
+       /* Can't cache this - XXX */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_pool_info *info =
+               (struct ipt_pool_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               info->src = ip_pool_get_index(argv[optind-1]);
+               if (invert) info->flags |= IPT_POOL_INV_SRC;
+               *flags = 1;
+               break;
+       case '2':
+               check_inverse(optarg, &invert, &optind, 0);
+               info->dst = ip_pool_get_index(argv[optind-1]);
+               if (invert) info->flags |= IPT_POOL_INV_DST;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; must have specified --srcpool or --dstpool. */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, "You must specify either `--srcpool or --dstpool'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       char buf[256];
+       struct ipt_pool_info *info =
+               (struct ipt_pool_info *)match->data;
+
+       if (info->src != IP_POOL_NONE)
+               printf("%ssrcpool %s ",
+                       (info->flags & IPT_POOL_INV_SRC) ? "!" : "",
+                       ip_pool_get_name(buf, sizeof(buf), info->src, 0));
+       if (info->dst != IP_POOL_NONE)
+               printf("%sdstpool %s ",
+                       (info->flags & IPT_POOL_INV_DST) ? "!" : "",
+                       ip_pool_get_name(buf, sizeof(buf), info->dst, 0));
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       char buf[256];
+       struct ipt_pool_info *info =
+               (struct ipt_pool_info *)match->data;
+
+       if (info->src != IP_POOL_NONE)
+               printf("%s--srcpool %s",
+                       (info->flags & IPT_POOL_INV_SRC) ? "! " : "",
+                       ip_pool_get_name(buf, sizeof(buf), info->src, 0));
+       if (info->dst != IP_POOL_NONE)
+               printf("%s--dstpool %s",
+                       (info->flags & IPT_POOL_INV_DST) ? "! " : "",
+                       ip_pool_get_name(buf, sizeof(buf), info->dst, 0));
+}
+
+static
+struct iptables_match pool
+= { NULL,
+    "pool",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_pool_info)),
+    IPT_ALIGN(sizeof(struct ipt_pool_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&pool);
+}
diff --git a/extensions/libipt_quota.c b/extensions/libipt_quota.c
new file mode 100644 (file)
index 0000000..8380754
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Shared library add-on to iptables to add quota support
+ *
+ * Sam Johnston <samj@samj.net>
+ */
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+
+#include <linux/netfilter/xt_quota.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+static struct option opts[] = {
+        {"quota", 1, 0, '1'},
+        {0}
+};
+
+/* print usage */
+static void
+help(void)
+{
+        printf("quota options:\n"
+               " --quota quota                 quota (bytes)\n" "\n");
+}
+
+/* print matchinfo */
+static void
+print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric)
+{
+        struct xt_quota_info *q = (struct xt_quota_info *) match->data;
+        printf("quota: %llu bytes", (unsigned long long) q->quota);
+}
+
+/* save matchinfo */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+        struct xt_quota_info *q = (struct xt_quota_info *) match->data;
+        printf("--quota %llu ", (unsigned long long) q->quota);
+}
+
+/* parse quota option */
+static int
+parse_quota(const char *s, u_int64_t * quota)
+{
+        *quota = strtoull(s, (char **) NULL, 10);
+
+#ifdef DEBUG_IPT_QUOTA
+        printf("Quota: %llu\n", *quota);
+#endif
+
+        if (*quota == -1)
+                exit_error(PARAMETER_PROBLEM, "quota invalid: '%s'\n", s);
+        else
+                return 1;
+}
+
+/* parse all options, returning true if we found any for us */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache, struct ipt_entry_match **match)
+{
+        struct xt_quota_info *info = (struct xt_quota_info *) (*match)->data;
+
+        switch (c) {
+        case '1':
+                if (check_inverse(optarg, &invert, NULL, 0))
+                        exit_error(PARAMETER_PROBLEM, "quota: unexpected '!'");
+                if (!parse_quota(optarg, &info->quota))
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad quota: '%s'", optarg);
+                break;
+
+        default:
+                return 0;
+        }
+        return 1;
+}
+
+/* no final check */
+static void
+final_check(unsigned int flags)
+{
+}
+
+struct iptables_match quota = { 
+       .next           = NULL,
+       .name           = "quota",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof (struct xt_quota_info)),
+       .userspacesize  = offsetof(struct xt_quota_info, quota),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void
+_init(void)
+{
+        register_match(&quota);
+}
diff --git a/extensions/libipt_quota.man b/extensions/libipt_quota.man
new file mode 100644 (file)
index 0000000..7945bee
--- /dev/null
@@ -0,0 +1,6 @@
+Implements network quotas by decrementing a byte counter with each
+packet.
+.TP
+.BI "--quota " "bytes"
+The quota in bytes.
+.P
diff --git a/extensions/libipt_realm.c b/extensions/libipt_realm.c
new file mode 100644 (file)
index 0000000..1564677
--- /dev/null
@@ -0,0 +1,272 @@
+/* Shared library add-on to iptables to add realm matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <getopt.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_realm.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"realm v%s options:\n"
+" --realm [!] value[/mask]\n"
+"                              Match realm\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "realm", 1, 0, '1' },
+       {0}
+};
+
+struct realmname { 
+       int     id;
+       char*   name;
+       int     len;
+       struct realmname* next;
+};
+
+/* array of realms from /etc/iproute2/rt_realms */
+static struct realmname *realms = NULL;
+/* 1 if loading failed */
+static int rdberr = 0;
+
+
+void load_realms()
+{
+       const char* rfnm = "/etc/iproute2/rt_realms";
+       char buf[512];
+       FILE *fil;
+       char *cur, *nxt;
+       int id;
+       struct realmname *oldnm = NULL, *newnm = NULL;
+
+       fil = fopen(rfnm, "r");
+       if (!fil) {
+               rdberr = 1;
+               return;
+       }
+
+       while (fgets(buf, sizeof(buf), fil)) {
+               cur = buf;
+               while ((*cur == ' ') || (*cur == '\t'))
+                       cur++;
+               if ((*cur == '#') || (*cur == '\n') || (*cur == 0))
+                       continue;
+
+               /* iproute2 allows hex and dec format */
+               errno = 0;
+               id = strtoul(cur, &nxt, strncmp(cur, "0x", 2) ? 10 : 16);
+               if ((nxt == cur) || errno)
+                       continue;
+
+               /* same boundaries as in iproute2 */
+               if (id < 0 || id > 255)
+                       continue;
+               cur = nxt;
+
+               if (!isspace(*cur))
+                       continue;
+               while ((*cur == ' ') || (*cur == '\t'))
+                       cur++;
+               if ((*cur == '#') || (*cur == '\n') || (*cur == 0))
+                       continue;
+               nxt = cur;
+               while ((*nxt != 0) && !isspace(*nxt))
+                       nxt++;
+               if (nxt == cur)
+                       continue;
+
+               /* found valid data */
+               newnm = (struct realmname*)malloc(sizeof(struct realmname));
+               if (newnm == NULL) {
+                       perror("libipt_realm: malloc failed");
+                       exit(1);
+               }
+               newnm->id = id;
+               newnm->len = nxt - cur;
+               newnm->name = (char*)malloc(newnm->len + 1);
+               if (newnm->name == NULL) {
+                       perror("libipt_realm: malloc failed");
+                       exit(1);
+               }
+               strncpy(newnm->name, cur, newnm->len);
+               newnm->name[newnm->len] = 0;
+               newnm->next = NULL;
+
+               if (oldnm)
+                       oldnm->next = newnm;
+               else
+                       realms = newnm;
+               oldnm = newnm;
+       }
+
+       fclose(fil);
+}
+
+/* get realm id for name, -1 if error/not found */
+int realm_name2id(const char* name)
+{
+       struct realmname* cur;
+
+       if ((realms == NULL) && (rdberr == 0))
+               load_realms();
+       cur = realms;
+       if (cur == NULL)
+               return -1;
+       while (cur) {
+               if (!strncmp(name, cur->name, cur->len + 1))
+                       return cur->id;
+               cur = cur->next;
+       }
+       return -1;
+}
+
+/* get realm name for id, NULL if error/not found */
+const char* realm_id2name(int id)
+{
+       struct realmname* cur;
+
+       if ((realms == NULL) && (rdberr == 0))
+               load_realms();
+       cur = realms;
+       if (cur == NULL)
+               return NULL;
+       while (cur) {
+               if (id == cur->id)
+                       return cur->name;
+               cur = cur->next;
+       }
+       return NULL;
+}
+
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_realm_info *realminfo = (struct ipt_realm_info *)(*match)->data;
+       int id;
+
+       switch (c) {
+               char *end;
+       case '1':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               end = optarg = argv[optind-1];
+               realminfo->id = strtoul(optarg, &end, 0);
+               if (end != optarg && (*end == '/' || *end == '\0')) {
+                       if (*end == '/')
+                               realminfo->mask = strtoul(end+1, &end, 0);
+                       else
+                               realminfo->mask = 0xffffffff;
+                       if (*end != '\0' || end == optarg)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Bad realm value `%s'", optarg);
+               } else {
+                       id = realm_name2id(optarg);
+                       if (id == -1)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Realm `%s' not found", optarg);
+                       realminfo->id = (u_int32_t)id;
+                       realminfo->mask = 0xffffffff;
+               }
+               if (invert)
+                       realminfo->invert = 1;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+print_realm(unsigned long id, unsigned long mask, int numeric)
+{
+       const char* name = NULL;
+
+       if (mask != 0xffffffff)
+               printf("0x%lx/0x%lx ", id, mask);
+       else {
+               if (numeric == 0)
+                       name = realm_id2name(id);
+               if (name)
+                       printf("%s ", name);
+               else
+                       printf("0x%lx ", id);
+       }
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_realm_info *ri = (struct ipt_realm_info *) match->data;
+
+       if (ri->invert)
+               printf("! ");
+
+       printf("realm ");
+       print_realm(ri->id, ri->mask, numeric);
+}
+
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_realm_info *ri = (struct ipt_realm_info *) match->data;
+
+       if (ri->invert)
+               printf("! ");
+
+       printf("--realm ");
+       print_realm(ri->id, ri->mask, 0);
+}
+
+/* Final check; must have specified --mark. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "realm match: You must specify `--realm'");
+}
+
+static struct iptables_match realm = { NULL,
+       .name           = "realm",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_realm_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_realm_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&realm);
+}
+
+
diff --git a/extensions/libipt_realm.man b/extensions/libipt_realm.man
new file mode 100644 (file)
index 0000000..b33da0e
--- /dev/null
@@ -0,0 +1,7 @@
+This matches the routing realm.  Routing realms are used in complex routing
+setups involving dynamic routing protocols like BGP.
+.TP
+.BI "--realm " "[!] " "value[/mask]"
+Matches a given realm number (and optionally mask). If not a number, value
+can be a named realm from /etc/iproute2/rt_realms (mask can not be used in
+that case).
diff --git a/extensions/libipt_recent.c b/extensions/libipt_recent.c
new file mode 100644 (file)
index 0000000..0b0ed2d
--- /dev/null
@@ -0,0 +1,240 @@
+/* Shared library add-on to iptables to add recent matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_recent.h>
+
+/* Need these in order to not fail when compiling against an older kernel. */
+#ifndef RECENT_NAME
+#define RECENT_NAME    "ipt_recent"
+#endif /* RECENT_NAME */
+
+#ifndef RECENT_VER
+#define RECENT_VER     "unknown"
+#endif /* RECENT_VER */
+
+#ifndef IPT_RECENT_NAME_LEN
+#define IPT_RECENT_NAME_LEN    200
+#endif /* IPT_RECENT_NAME_LEN */
+
+/* Options for this module */
+static struct option opts[] = {
+       { .name = "set",      .has_arg = 0, .flag = 0, .val = 201 }, 
+       { .name = "rcheck",   .has_arg = 0, .flag = 0, .val = 202 }, 
+       { .name = "update",   .has_arg = 0, .flag = 0, .val = 203 },
+       { .name = "seconds",  .has_arg = 1, .flag = 0, .val = 204 }, 
+       { .name = "hitcount", .has_arg = 1, .flag = 0, .val = 205 },
+       { .name = "remove",   .has_arg = 0, .flag = 0, .val = 206 },
+       { .name = "rttl",     .has_arg = 0, .flag = 0, .val = 207 },
+       { .name = "name",     .has_arg = 1, .flag = 0, .val = 208 },
+       { .name = "rsource",  .has_arg = 0, .flag = 0, .val = 209 },
+       { .name = "rdest",    .has_arg = 0, .flag = 0, .val = 210 },
+       { .name = 0,          .has_arg = 0, .flag = 0, .val = 0   }
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"recent v%s options:\n"
+"[!] --set                       Add source address to list, always matches.\n"
+"[!] --rcheck                    Match if source address in list.\n"
+"[!] --update                    Match if source address in list, also update last-seen time.\n"
+"[!] --remove                    Match if source address in list, also removes that address from list.\n"
+"    --seconds seconds           For check and update commands above.\n"
+"                                Specifies that the match will only occur if source address last seen within\n"
+"                                the last 'seconds' seconds.\n"
+"    --hitcount hits             For check and update commands above.\n"
+"                                Specifies that the match will only occur if source address seen hits times.\n"
+"                                May be used in conjunction with the seconds option.\n"
+"    --rttl                      For check and update commands above.\n"
+"                                Specifies that the match will only occur if the source address and the TTL\n"
+"                                match between this packet and the one which was set.\n"
+"                                Useful if you have problems with people spoofing their source address in order\n"
+"                                to DoS you via this module.\n"
+"    --name name                 Name of the recent list to be used.  DEFAULT used if none given.\n"
+"    --rsource                   Match/Save the source address of each packet in the recent list table (default).\n"
+"    --rdest                     Match/Save the destination address of each packet in the recent list table.\n"
+RECENT_NAME " " RECENT_VER ": Stephen Frost <sfrost@snowman.net>.  http://snowman.net/projects/ipt_recent/\n"
+,
+IPTABLES_VERSION);
+
+}
+  
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *match, unsigned int *nfcache)
+{
+       struct ipt_recent_info *info = (struct ipt_recent_info *)(match)->data;
+
+
+       strncpy(info->name,"DEFAULT",IPT_RECENT_NAME_LEN);
+       /* eventhough IPT_RECENT_NAME_LEN is currently defined as 200,
+        * better be safe, than sorry */
+       info->name[IPT_RECENT_NAME_LEN-1] = '\0';
+       info->side = IPT_RECENT_SOURCE;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_recent_info *info = (struct ipt_recent_info *)(*match)->data;
+       switch (c) {
+               case 201:
+                       if (*flags) exit_error(PARAMETER_PROBLEM,
+                                       "recent: only one of `--set', `--rcheck' "
+                                       "`--update' or `--remove' may be set");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       info->check_set |= IPT_RECENT_SET;
+                       if (invert) info->invert = 1;
+                       *flags = 1;
+                       break;
+                       
+               case 202:
+                       if (*flags) exit_error(PARAMETER_PROBLEM,
+                                       "recent: only one of `--set', `--rcheck' "
+                                       "`--update' or `--remove' may be set");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       info->check_set |= IPT_RECENT_CHECK;
+                       if(invert) info->invert = 1;
+                       *flags = 1;
+                       break;
+
+               case 203:
+                       if (*flags) exit_error(PARAMETER_PROBLEM,
+                                       "recent: only one of `--set', `--rcheck' "
+                                       "`--update' or `--remove' may be set");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       info->check_set |= IPT_RECENT_UPDATE;
+                       if (invert) info->invert = 1;
+                       *flags = 1;
+                       break;
+
+               case 206:
+                       if (*flags) exit_error(PARAMETER_PROBLEM,
+                                       "recent: only one of `--set', `--rcheck' "
+                                       "`--update' or `--remove' may be set");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       info->check_set |= IPT_RECENT_REMOVE;
+                       if (invert) info->invert = 1;
+                       *flags = 1;
+                       break;
+
+               case 204:
+                       info->seconds = atoi(optarg);
+                       break;
+
+               case 205:
+                       info->hit_count = atoi(optarg);
+                       break;
+
+               case 207:
+                       info->check_set |= IPT_RECENT_TTL;
+                       break;
+
+               case 208:
+                       strncpy(info->name,optarg,IPT_RECENT_NAME_LEN);
+                       info->name[IPT_RECENT_NAME_LEN-1] = '\0';
+                       break;
+
+               case 209:
+                       info->side = IPT_RECENT_SOURCE;
+                       break;
+
+               case 210:
+                       info->side = IPT_RECENT_DEST;
+                       break;
+
+               default:
+                       return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; must have specified a specific option. */
+static void
+final_check(unsigned int flags)
+{
+
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                       "recent: you must specify one of `--set', `--rcheck' "
+                       "`--update' or `--remove'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_recent_info *info = (struct ipt_recent_info *)match->data;
+
+       if (info->invert)
+               fputc('!', stdout);
+
+       printf("recent: ");
+       if(info->check_set & IPT_RECENT_SET) printf("SET ");
+       if(info->check_set & IPT_RECENT_CHECK) printf("CHECK ");
+       if(info->check_set & IPT_RECENT_UPDATE) printf("UPDATE ");
+       if(info->check_set & IPT_RECENT_REMOVE) printf("REMOVE ");
+       if(info->seconds) printf("seconds: %d ",info->seconds);
+       if(info->hit_count) printf("hit_count: %d ",info->hit_count);
+       if(info->check_set & IPT_RECENT_TTL) printf("TTL-Match ");
+       if(info->name) printf("name: %s ",info->name);
+       if(info->side == IPT_RECENT_SOURCE) printf("side: source ");
+       if(info->side == IPT_RECENT_DEST) printf("side: dest");
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_recent_info *info = (struct ipt_recent_info *)match->data;
+
+       if (info->invert)
+               printf("! ");
+
+       if(info->check_set & IPT_RECENT_SET) printf("--set ");
+       if(info->check_set & IPT_RECENT_CHECK) printf("--rcheck ");
+       if(info->check_set & IPT_RECENT_UPDATE) printf("--update ");
+       if(info->check_set & IPT_RECENT_REMOVE) printf("--remove ");
+       if(info->seconds) printf("--seconds %d ",info->seconds);
+       if(info->hit_count) printf("--hitcount %d ",info->hit_count);
+       if(info->check_set & IPT_RECENT_TTL) printf("--rttl ");
+       if(info->name) printf("--name %s ",info->name);
+       if(info->side == IPT_RECENT_SOURCE) printf("--rsource ");
+       if(info->side == IPT_RECENT_DEST) printf("--rdest ");
+}
+
+/* Structure for iptables to use to communicate with module */
+static struct iptables_match recent = { 
+    .next          = NULL,
+    .name          = "recent",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_recent_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_recent_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_match(&recent);
+}
diff --git a/extensions/libipt_recent.man b/extensions/libipt_recent.man
new file mode 100644 (file)
index 0000000..bf5d710
--- /dev/null
@@ -0,0 +1,93 @@
+Allows you to dynamically create a list of IP addresses and then match
+against that list in a few different ways.
+
+For example, you can create a `badguy' list out of people attempting
+to connect to port 139 on your firewall and then DROP all future
+packets from them without considering them.
+.TP
+.BI "--name " "name"
+Specify the list to use for the commands. If no name is given then 'DEFAULT'
+will be used.
+.TP
+[\fB!\fR] \fB--set\fR
+This will add the source address of the packet to the list. If the
+source address is already in the list, this will update the existing
+entry. This will always return success (or failure if `!' is passed
+in).
+.TP
+[\fB!\fR] \fB--rcheck\fR
+Check if the source address of the packet is currently in
+the list.
+.TP
+[\fB!\fR] \fB--update\fR
+Like \fB--rcheck\fR, except it will update the "last seen" timestamp if it
+matches.
+.TP
+[\fB!\fR] \fB--remove\fR
+Check if the source address of the packet is currently in the list and
+if so that address will be removed from the list and the rule will
+return true. If the address is not found, false is returned.
+.TP
+[\fB!\fR] \fB--seconds \fIseconds\fR
+This option must be used in conjunction with one of \fB--rcheck\fR or
+\fB--update\fR. When used, this will narrow the match to only happen
+when the address is in the list and was seen within the last given
+number of seconds.
+.TP
+[\fB!\fR] \fB--hitcount \fIhits\fR
+This option must be used in conjunction with one of \fB--rcheck\fR or
+\fB--update\fR. When used, this will narrow the match to only happen
+when the address is in the list and packets had been received greater
+than or equal to the given value. This option may be used along with
+\fB--seconds\fR to create an even narrower match requiring a certain
+number of hits within a specific time frame.
+.TP
+\fB--rttl\fR
+This option must be used in conjunction with one of \fB--rcheck\fR or
+\fB--update\fR. When used, this will narrow the match to only happen
+when the address is in the list and the TTL of the current packet
+matches that of the packet which hit the \fB--set\fR rule. This may be
+useful if you have problems with people faking their source address in
+order to DoS you via this module by disallowing others access to your
+site by sending bogus packets to you.
+.P
+Examples:
+.IP
+# iptables -A FORWARD -m recent --name badguy --rcheck --seconds 60 -j DROP
+
+# iptables -A FORWARD -p tcp -i eth0 --dport 139 -m recent --name badguy --set -j DROP
+.P
+Official website (http://snowman.net/projects/ipt_recent/) also has
+some examples of usage.
+
+/proc/net/ipt_recent/* are the current lists of addresses and information 
+about each entry of each list.
+
+Each file in /proc/net/ipt_recent/ can be read from to see the current list
+or written two using the following commands to modify the list:
+.TP
+echo xx.xx.xx.xx > /proc/net/ipt_recent/DEFAULT
+to Add to the DEFAULT list
+.TP
+echo -xx.xx.xx.xx > /proc/net/ipt_recent/DEFAULT
+to Remove from the DEFAULT list
+.TP
+echo clear > /proc/net/ipt_recent/DEFAULT
+to empty the DEFAULT list.
+.P
+The module itself accepts parameters, defaults shown:
+.TP
+.BI "ip_list_tot=" "100"
+Number of addresses remembered per table
+.TP
+.BI "ip_pkt_list_tot=" "20"
+Number of packets per address remembered
+.TP
+.BI "ip_list_hash_size=" "0"
+Hash table size. 0 means to calculate it based on ip_list_tot, default: 512
+.TP
+.BI "ip_list_perms=" "0644"
+Permissions for /proc/net/ipt_recent/* files
+.TP
+.BI "debug=" "0"
+Set to 1 to get lots of debugging info
diff --git a/extensions/libipt_sctp.c b/extensions/libipt_sctp.c
new file mode 100644 (file)
index 0000000..0354d19
--- /dev/null
@@ -0,0 +1,550 @@
+/* Shared library add-on to iptables for SCTP matching
+ *
+ * (C) 2003 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_ecn.c borrowed heavily from libipt_dscp.c
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <ctype.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+#include <linux/netfilter_ipv4/ipt_sctp.h>
+
+/* Some ZS!#@:$%*#$! has replaced the ELEMCOUNT macro in ipt_sctp.h with
+ * ARRAY_SIZE without noticing that this file is used from userserspace,
+ * and userspace doesn't have ARRAY_SIZE */
+
+#ifndef ELEMCOUNT
+#define ELEMCOUNT ARRAY_SIZE
+#endif
+
+#if 0
+#define DEBUGP(format, first...) printf(format, ##first)
+#define static
+#else
+#define DEBUGP(format, fist...) 
+#endif
+
+static void
+print_chunk(u_int32_t chunknum, int numeric);
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, 
+     unsigned int *nfcache)
+{
+       int i;
+       struct ipt_sctp_info *einfo = (struct ipt_sctp_info *)m->data;
+
+       memset(einfo, 0, sizeof(struct ipt_sctp_info));
+
+       for (i = 0; i < IPT_NUM_SCTP_FLAGS; i++) {
+               einfo->flag_info[i].chunktype = -1;
+       }
+}
+
+static void help(void)
+{
+       printf(
+"SCTP match v%s options\n"
+" --source-port [!] port[:port]                          match source port(s)\n"
+" --sport ...\n"
+" --destination-port [!] port[:port]                     match destination port(s)\n"
+" --dport ...\n" 
+" --chunk-types [!] (all|any|none) (chunktype[:flags])+        match if all, any or none of\n"
+"                                                      chunktypes are present\n"
+"chunktypes - DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK ALL NONE\n",
+       IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "source-port", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "sport", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "destination-port", .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "dport", .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "chunk-types", .has_arg = 1, .flag = 0, .val = '3' },
+       { .name = 0 }
+};
+
+static void
+parse_sctp_ports(const char *portstring, 
+                u_int16_t *ports)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(portstring);
+       DEBUGP("%s\n", portstring);
+       if ((cp = strchr(buffer, ':')) == NULL) {
+               ports[0] = ports[1] = parse_port(buffer, "sctp");
+       }
+       else {
+               *cp = '\0';
+               cp++;
+
+               ports[0] = buffer[0] ? parse_port(buffer, "sctp") : 0;
+               ports[1] = cp[0] ? parse_port(cp, "sctp") : 0xFFFF;
+
+               if (ports[0] > ports[1])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "invalid portrange (min > max)");
+       }
+       free(buffer);
+}
+
+struct sctp_chunk_names {
+       const char *name;
+       unsigned int chunk_type;
+       const char *valid_flags;
+};
+
+/*'ALL' and 'NONE' will be treated specially. */
+static struct sctp_chunk_names sctp_chunk_names[]
+= { { .name = "DATA",          .chunk_type = 0,   .valid_flags = "-----UBE"},
+    { .name = "INIT",          .chunk_type = 1,   .valid_flags = "--------"},
+    { .name = "INIT_ACK",      .chunk_type = 2,   .valid_flags = "--------"},
+    { .name = "SACK",          .chunk_type = 3,   .valid_flags = "--------"},
+    { .name = "HEARTBEAT",     .chunk_type = 4,   .valid_flags = "--------"},
+    { .name = "HEARTBEAT_ACK", .chunk_type = 5,   .valid_flags = "--------"},
+    { .name = "ABORT",         .chunk_type = 6,   .valid_flags = "-------T"},
+    { .name = "SHUTDOWN",      .chunk_type = 7,   .valid_flags = "--------"},
+    { .name = "SHUTDOWN_ACK",  .chunk_type = 8,   .valid_flags = "--------"},
+    { .name = "ERROR",         .chunk_type = 9,   .valid_flags = "--------"},
+    { .name = "COOKIE_ECHO",   .chunk_type = 10,  .valid_flags = "--------"},
+    { .name = "COOKIE_ACK",    .chunk_type = 11,  .valid_flags = "--------"},
+    { .name = "ECN_ECNE",      .chunk_type = 12,  .valid_flags = "--------"},
+    { .name = "ECN_CWR",       .chunk_type = 13,  .valid_flags = "--------"},
+    { .name = "SHUTDOWN_COMPLETE", .chunk_type = 14,  .valid_flags = "-------T"},
+    { .name = "ASCONF",                .chunk_type = 31,  .valid_flags = "--------"},
+    { .name = "ASCONF_ACK",    .chunk_type = 30,  .valid_flags = "--------"},
+};
+
+static void
+save_chunk_flag_info(struct ipt_sctp_flag_info *flag_info,
+                    int *flag_count,
+                    int chunktype, 
+                    int bit, 
+                    int set)
+{
+       int i;
+
+       for (i = 0; i < *flag_count; i++) {
+               if (flag_info[i].chunktype == chunktype) {
+                       DEBUGP("Previous match found\n");
+                       flag_info[i].chunktype = chunktype;
+                       flag_info[i].flag_mask |= (1 << bit);
+                       if (set) {
+                               flag_info[i].flag |= (1 << bit);
+                       }
+
+                       return;
+               }
+       }
+       
+       if (*flag_count == IPT_NUM_SCTP_FLAGS) {
+               exit_error (PARAMETER_PROBLEM,
+                       "Number of chunk types with flags exceeds currently allowed limit."
+                       "Increasing this limit involves changing IPT_NUM_SCTP_FLAGS and"
+                       "recompiling both the kernel space and user space modules\n");
+       }
+
+       flag_info[*flag_count].chunktype = chunktype;
+       flag_info[*flag_count].flag_mask |= (1 << bit);
+       if (set) {
+               flag_info[*flag_count].flag |= (1 << bit);
+       }
+       (*flag_count)++;
+}
+
+static void
+parse_sctp_chunk(struct ipt_sctp_info *einfo, 
+                const char *chunks)
+{
+       char *ptr;
+       char *buffer;
+       unsigned int i, j;
+       int found = 0;
+       char *chunk_flags;
+
+       buffer = strdup(chunks);
+       DEBUGP("Buffer: %s\n", buffer);
+
+       SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+
+       if (!strcasecmp(buffer, "ALL")) {
+               SCTP_CHUNKMAP_SET_ALL(einfo->chunkmap);
+               goto out;
+       }
+       
+       if (!strcasecmp(buffer, "NONE")) {
+               SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+               goto out;
+       }
+
+       for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
+               found = 0;
+               DEBUGP("Next Chunk type %s\n", ptr);
+               
+               if ((chunk_flags = strchr(ptr, ':')) != NULL) {
+                       *chunk_flags++ = 0;
+               }
+               
+               for (i = 0; i < ELEMCOUNT(sctp_chunk_names); i++) {
+                       if (strcasecmp(sctp_chunk_names[i].name, ptr) == 0) {
+                               DEBUGP("Chunk num %d\n", sctp_chunk_names[i].chunk_type);
+                               SCTP_CHUNKMAP_SET(einfo->chunkmap, 
+                                       sctp_chunk_names[i].chunk_type);
+                               found = 1;
+                               break;
+                       }
+               }
+               if (!found)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unknown sctp chunk `%s'", ptr);
+
+               if (chunk_flags) {
+                       DEBUGP("Chunk flags %s\n", chunk_flags);
+                       for (j = 0; j < strlen(chunk_flags); j++) {
+                               char *p;
+                               int bit;
+
+                               if ((p = strchr(sctp_chunk_names[i].valid_flags, 
+                                               toupper(chunk_flags[j]))) != NULL) {
+                                       bit = p - sctp_chunk_names[i].valid_flags;
+                                       bit = 7 - bit;
+
+                                       save_chunk_flag_info(einfo->flag_info, 
+                                               &(einfo->flag_count), i, bit, 
+                                               isupper(chunk_flags[j]));
+                               } else {
+                                       exit_error(PARAMETER_PROBLEM, 
+                                               "Invalid flags for chunk type %d\n", i);
+                               }
+                       }
+               }
+       }
+out:
+       free(buffer);
+}
+
+static void
+parse_sctp_chunks(struct ipt_sctp_info *einfo,
+                 const char *match_type,
+                 const char *chunks)
+{
+       DEBUGP("Match type: %s Chunks: %s\n", match_type, chunks);
+       if (!strcasecmp(match_type, "ANY")) {
+               einfo->chunk_match_type = SCTP_CHUNK_MATCH_ANY;
+       } else  if (!strcasecmp(match_type, "ALL")) {
+               einfo->chunk_match_type = SCTP_CHUNK_MATCH_ALL;
+       } else  if (!strcasecmp(match_type, "ONLY")) {
+               einfo->chunk_match_type = SCTP_CHUNK_MATCH_ONLY;
+       } else {
+               exit_error (PARAMETER_PROBLEM, 
+                       "Match type has to be one of \"ALL\", \"ANY\" or \"ONLY\"");
+       }
+
+       SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+       parse_sctp_chunk(einfo, chunks);
+}
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_sctp_info *einfo
+               = (struct ipt_sctp_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IPT_SCTP_SRC_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--source-port' allowed");
+               einfo->flags |= IPT_SCTP_SRC_PORTS;
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_sctp_ports(argv[optind-1], einfo->spts);
+               if (invert)
+                       einfo->invflags |= IPT_SCTP_SRC_PORTS;
+               *flags |= IPT_SCTP_SRC_PORTS;
+               break;
+
+       case '2':
+               if (*flags & IPT_SCTP_DEST_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--destination-port' allowed");
+               einfo->flags |= IPT_SCTP_DEST_PORTS;
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_sctp_ports(argv[optind-1], einfo->dpts);
+               if (invert)
+                       einfo->invflags |= IPT_SCTP_DEST_PORTS;
+               *flags |= IPT_SCTP_DEST_PORTS;
+               break;
+
+       case '3':
+               if (*flags & IPT_SCTP_CHUNK_TYPES)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--chunk-types' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (!argv[optind] 
+                   || argv[optind][0] == '-' || argv[optind][0] == '!')
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--chunk-types requires two args");
+
+               einfo->flags |= IPT_SCTP_CHUNK_TYPES;
+               parse_sctp_chunks(einfo, argv[optind-1], argv[optind]);
+               if (invert)
+                       einfo->invflags |= IPT_SCTP_CHUNK_TYPES;
+               optind++;
+               *flags |= IPT_SCTP_CHUNK_TYPES;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+static char *
+port_to_service(int port)
+{
+       struct servent *service;
+
+       if ((service = getservbyport(htons(port), "sctp")))
+               return service->s_name;
+
+       return NULL;
+}
+
+static void
+print_port(u_int16_t port, int numeric)
+{
+       char *service;
+
+       if (numeric || (service = port_to_service(port)) == NULL)
+               printf("%u", port);
+       else
+               printf("%s", service);
+}
+
+static void
+print_ports(const char *name, u_int16_t min, u_int16_t max,
+           int invert, int numeric)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       print_port(min, numeric);
+               } else {
+                       printf("s:%s", inv);
+                       print_port(min, numeric);
+                       printf(":");
+                       print_port(max, numeric);
+               }
+               printf(" ");
+       }
+}
+
+static void
+print_chunk_flags(u_int32_t chunknum, u_int8_t chunk_flags, u_int8_t chunk_flags_mask)
+{
+       int i;
+
+       DEBUGP("type: %d\tflags: %x\tflag mask: %x\n", chunknum, chunk_flags, 
+                       chunk_flags_mask);
+
+       if (chunk_flags_mask) {
+               printf(":");
+       }
+
+       for (i = 7; i >= 0; i--) {
+               if (chunk_flags_mask & (1 << i)) {
+                       if (chunk_flags & (1 << i)) {
+                               printf("%c", sctp_chunk_names[chunknum].valid_flags[7-i]);
+                       } else {
+                               printf("%c", tolower(sctp_chunk_names[chunknum].valid_flags[7-i]));
+                       }
+               }
+       }
+}
+
+static void
+print_chunk(u_int32_t chunknum, int numeric)
+{
+       if (numeric) {
+               printf("0x%04X", chunknum);
+       }
+       else {
+               int i;
+
+               for (i = 0; i < ELEMCOUNT(sctp_chunk_names); i++) {
+                       if (sctp_chunk_names[i].chunk_type == chunknum)
+                               printf("%s", sctp_chunk_names[chunknum].name);
+               }
+       }
+}
+
+static void
+print_chunks(u_int32_t chunk_match_type, 
+            const u_int32_t *chunkmap, 
+            const struct ipt_sctp_flag_info *flag_info,
+            int flag_count,
+            int numeric)
+{
+       int i, j;
+       int flag;
+
+       switch (chunk_match_type) {
+               case SCTP_CHUNK_MATCH_ANY:      printf("any "); break;
+               case SCTP_CHUNK_MATCH_ALL:      printf("all "); break;
+               case SCTP_CHUNK_MATCH_ONLY:     printf("only "); break;
+               default:        printf("Never reach herer\n"); break;
+       }
+
+       if (SCTP_CHUNKMAP_IS_CLEAR(chunkmap)) {
+               printf("NONE ");
+               goto out;
+       }
+       
+       if (SCTP_CHUNKMAP_IS_ALL_SET(chunkmap)) {
+               printf("ALL ");
+               goto out;
+       }
+       
+       flag = 0;
+       for (i = 0; i < 256; i++) {
+               if (SCTP_CHUNKMAP_IS_SET(chunkmap, i)) {
+                       if (flag)
+                               printf(",");
+                       flag = 1;
+                       print_chunk(i, numeric);
+                       for (j = 0; j < flag_count; j++) {
+                               if (flag_info[j].chunktype == i) {
+                                       print_chunk_flags(i, flag_info[j].flag,
+                                               flag_info[j].flag_mask);
+                               }
+                       }
+               }
+       }
+
+       if (flag)
+               printf(" ");
+out:
+       return;
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_sctp_info *einfo =
+               (const struct ipt_sctp_info *)match->data;
+
+       printf("sctp ");
+
+       if (einfo->flags & IPT_SCTP_SRC_PORTS) {
+               print_ports("spt", einfo->spts[0], einfo->spts[1],
+                       einfo->invflags & IPT_SCTP_SRC_PORTS,
+                       numeric);
+       }
+
+       if (einfo->flags & IPT_SCTP_DEST_PORTS) {
+               print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
+                       einfo->invflags & IPT_SCTP_DEST_PORTS,
+                       numeric);
+       }
+
+       if (einfo->flags & IPT_SCTP_CHUNK_TYPES) {
+               /* FIXME: print_chunks() is used in save() where the printing of '!'
+               s taken care of, so we need to do that here as well */
+               if (einfo->invflags & IPT_SCTP_CHUNK_TYPES) {
+                       printf("! ");
+               }
+               print_chunks(einfo->chunk_match_type, einfo->chunkmap,
+                       einfo->flag_info, einfo->flag_count, numeric);
+       }
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, 
+     const struct ipt_entry_match *match)
+{
+       const struct ipt_sctp_info *einfo =
+               (const struct ipt_sctp_info *)match->data;
+
+       if (einfo->flags & IPT_SCTP_SRC_PORTS) {
+               if (einfo->invflags & IPT_SCTP_SRC_PORTS)
+                       printf("! ");
+               if (einfo->spts[0] != einfo->spts[1])
+                       printf("--sport %u:%u ", 
+                              einfo->spts[0], einfo->spts[1]);
+               else
+                       printf("--sport %u ", einfo->spts[0]);
+       }
+
+       if (einfo->flags & IPT_SCTP_DEST_PORTS) {
+               if (einfo->invflags & IPT_SCTP_DEST_PORTS)
+                       printf("! ");
+               if (einfo->dpts[0] != einfo->dpts[1])
+                       printf("--dport %u:%u ",
+                              einfo->dpts[0], einfo->dpts[1]);
+               else
+                       printf("--dport %u ", einfo->dpts[0]);
+       }
+
+       if (einfo->flags & IPT_SCTP_CHUNK_TYPES) {
+               if (einfo->invflags & IPT_SCTP_CHUNK_TYPES)
+                       printf("! ");
+               printf("--chunk-types ");
+
+               print_chunks(einfo->chunk_match_type, einfo->chunkmap, 
+                       einfo->flag_info, einfo->flag_count, 0);
+       }
+}
+
+static
+struct iptables_match sctp
+= { .name          = "sctp",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_sctp_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_sctp_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_match(&sctp);
+}
+
diff --git a/extensions/libipt_sctp.man b/extensions/libipt_sctp.man
new file mode 100644 (file)
index 0000000..97b467d
--- /dev/null
@@ -0,0 +1,28 @@
+.TP
+\fB--source-port\fR,\fB--sport \fR[\fB!\fR] \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB--destination-port\fR,\fB--dport \fR[\fB!\fR] \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB--chunk-types\fR [\fB!\fR] \fBall\fR|\fBany\fR|\fBonly \fIchunktype\fR[\fB:\fIflags\fR] [...]
+The flag letter in upper case indicates that the flag is to match if set,
+in the lower case indicates to match if unset.
+
+Chunk types: DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK
+
+chunk type            available flags      
+.br
+DATA                  U B E u b e         
+.br
+ABORT                 T t                 
+.br
+SHUTDOWN_COMPLETE     T t                 
+
+(lowercase means flag should be "off", uppercase means "on")
+.P
+Examples:
+
+iptables -A INPUT -p sctp --dport 80 -j DROP
+
+iptables -A INPUT -p sctp --chunk-types any DATA,INIT -j DROP
+
+iptables -A INPUT -p sctp --chunk-types any DATA:Be -j ACCEPT
diff --git a/extensions/libipt_set.c b/extensions/libipt_set.c
new file mode 100644 (file)
index 0000000..cf5d19d
--- /dev/null
@@ -0,0 +1,166 @@
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
+ *                         Patrick Schaaf <bof@bof.de>
+ *                         Martin Josefsson <gandalf@wlug.westbo.se>
+ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.  
+ */
+
+/* Shared library add-on to iptables to add IP set matching. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_set.h>
+#include "libipt_set.h"
+
+/* Function which prints out usage message. */
+static void help(void)
+{
+       printf("set v%s options:\n"
+              " [!] --set     name flags\n"
+              "                'name' is the set name from to match,\n" 
+              "                'flags' are the comma separated list of\n"
+              "                'src' and 'dst'.\n"
+              "\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       {"set", 1, 0, '1'},
+       {0}
+};
+
+/* Initialize the match. */
+static void init(struct ipt_entry_match *match, unsigned int *nfcache)
+{
+       struct ipt_set_info_match *info = 
+               (struct ipt_set_info_match *) match->data;
+       
+
+       memset(info, 0, sizeof(struct ipt_set_info_match));
+
+}
+
+/* Function which parses command options; returns true if it ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache, struct ipt_entry_match **match)
+{
+       struct ipt_set_info_match *myinfo = 
+               (struct ipt_set_info_match *) (*match)->data;
+       struct ipt_set_info *info = &myinfo->match_set;
+
+       switch (c) {
+       case '1':               /* --set <set> <flag>[,<flag> */
+               if (info->flags[0])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--set can be specified only once");
+
+               check_inverse(optarg, &invert, &optind, 0);
+               if (invert)
+                       info->flags[0] |= IPSET_MATCH_INV;
+
+               if (!argv[optind]
+                   || argv[optind][0] == '-'
+                   || argv[optind][0] == '!')
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--set requires two args.");
+
+               if (strlen(argv[optind-1]) > IP_SET_MAXNAMELEN - 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "setname `%s' too long, max %d characters.",
+                                  argv[optind-1], IP_SET_MAXNAMELEN - 1);
+
+               get_set_byname(argv[optind - 1], info);
+               parse_bindings(argv[optind], info);
+               DEBUGP("parse: set index %u\n", info->index);
+               optind++;
+               
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; must have specified --set. */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "You must specify `--set' with proper arguments");
+       DEBUGP("final check OK\n");
+}
+
+static void
+print_match(const char *prefix, const struct ipt_set_info *info)
+{
+       int i;
+       char setname[IP_SET_MAXNAMELEN];
+
+       get_set_byid(setname, info->index);
+       printf("%s%s %s", 
+              (info->flags[0] & IPSET_MATCH_INV) ? "! " : "",
+              prefix,
+              setname); 
+       for (i = 0; i < IP_SET_MAX_BINDINGS; i++) {
+               if (!info->flags[i])
+                       break;          
+               printf("%s%s",
+                      i == 0 ? " " : ",",
+                      info->flags[i] & IPSET_SRC ? "src" : "dst");
+       }
+       printf(" ");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match, int numeric)
+{
+       struct ipt_set_info_match *info = 
+               (struct ipt_set_info_match *) match->data;
+
+       print_match("set", &info->match_set);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip,
+                const struct ipt_entry_match *match)
+{
+       struct ipt_set_info_match *info = 
+               (struct ipt_set_info_match *) match->data;
+
+       print_match("--set", &info->match_set);
+}
+
+static
+struct iptables_match set = {
+       .name           = "set",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_set_info_match)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_set_info_match)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&set);
+}
diff --git a/extensions/libipt_set.h b/extensions/libipt_set.h
new file mode 100644 (file)
index 0000000..02de0fa
--- /dev/null
@@ -0,0 +1,104 @@
+#ifndef _LIBIPT_SET_H
+#define _LIBIPT_SET_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...) 
+#endif
+
+static void
+parse_bindings(const char *optarg, struct ipt_set_info *info)
+{
+       char *saved = strdup(optarg);
+       char *ptr, *tmp = saved;
+       int i = 0;
+       
+       while (i < (IP_SET_MAX_BINDINGS - 1) && tmp != NULL) {
+               ptr = strsep(&tmp, ",");
+               if (strncmp(ptr, "src", 3) == 0)
+                       info->flags[i++] |= IPSET_SRC;
+               else if (strncmp(ptr, "dst", 3) == 0)
+                       info->flags[i++] |= IPSET_DST;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "You must spefify (the comma separated list of) 'src' or 'dst'.");
+       }
+
+       if (tmp)
+               exit_error(PARAMETER_PROBLEM,
+                          "Can't follow bindings deeper than %i.", 
+                          IP_SET_MAX_BINDINGS - 1);
+
+       free(saved);
+}
+
+static int get_set_getsockopt(void *data, socklen_t * size)
+{
+       int sockfd = -1;
+       sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0)
+               exit_error(OTHER_PROBLEM,
+                          "Can't open socket to ipset.\n");
+       /* Send! */
+       return getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+}
+
+static void get_set_byname(const char *setname, struct ipt_set_info *info)
+{
+       struct ip_set_req_get_set req;
+       socklen_t size = sizeof(struct ip_set_req_get_set);
+       int res;
+
+       req.op = IP_SET_OP_GET_BYNAME;
+       req.version = IP_SET_PROTOCOL_VERSION;
+       strncpy(req.set.name, setname, IP_SET_MAXNAMELEN);
+       req.set.name[IP_SET_MAXNAMELEN - 1] = '\0';
+       res = get_set_getsockopt(&req, &size);
+       if (res != 0)
+               exit_error(OTHER_PROBLEM,
+                          "Problem when communicating with ipset, errno=%d.\n",
+                          errno);
+       if (size != sizeof(struct ip_set_req_get_set))
+               exit_error(OTHER_PROBLEM,
+                          "Incorrect return size from kernel during ipset lookup, "
+                          "(want %ld, got %ld)\n",
+                          sizeof(struct ip_set_req_get_set), size);
+       if (req.set.index == IP_SET_INVALID_ID)
+               exit_error(PARAMETER_PROBLEM,
+                          "Set %s doesn't exist.\n", setname);
+
+       info->index = req.set.index;
+}
+
+static void get_set_byid(char * setname, ip_set_id_t index)
+{
+       struct ip_set_req_get_set req;
+       socklen_t size = sizeof(struct ip_set_req_get_set);
+       int res;
+
+       req.op = IP_SET_OP_GET_BYINDEX;
+       req.version = IP_SET_PROTOCOL_VERSION;
+       req.set.index = index;
+       res = get_set_getsockopt(&req, &size);
+       if (res != 0)
+               exit_error(OTHER_PROBLEM,
+                          "Problem when communicating with ipset, errno=%d.\n",
+                          errno);
+       if (size != sizeof(struct ip_set_req_get_set))
+               exit_error(OTHER_PROBLEM,
+                          "Incorrect return size from kernel during ipset lookup, "
+                          "(want %ld, got %ld)\n",
+                          sizeof(struct ip_set_req_get_set), size);
+       if (req.set.name[0] == '\0')
+               exit_error(PARAMETER_PROBLEM,
+                          "Set id %i in kernel doesn't exist.\n", index);
+
+       strncpy(setname, req.set.name, IP_SET_MAXNAMELEN);
+}
+
+#endif /*_LIBIPT_SET_H*/
diff --git a/extensions/libipt_set.man b/extensions/libipt_set.man
new file mode 100644 (file)
index 0000000..d280577
--- /dev/null
@@ -0,0 +1,17 @@
+This modules macthes IP sets which can be defined by ipset(8).
+.TP
+.BR "--set " "setname flag[,flag...]"
+where flags are
+.BR "src"
+and/or
+.BR "dst" 
+and there can be no more than six of them. Hence the command
+.nf
+ iptables -A FORWARD -m set --set test src,dst
+.fi
+will match packets, for which (depending on the type of the set) the source
+address or port number of the packet can be found in the specified set. If 
+there is a binding belonging to the mached set element or there is a default 
+binding for the given set, then the rule will match the packet only if 
+additionally (depending on the type of the set) the destination address or 
+port number of the packet can be found in the set according to the binding.
diff --git a/extensions/libipt_standard.c b/extensions/libipt_standard.c
new file mode 100644 (file)
index 0000000..4c5a3f5
--- /dev/null
@@ -0,0 +1,69 @@
+/* Shared library add-on to iptables for standard target support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <getopt.h>
+#include <iptables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"Standard v%s options:\n"
+"(If target is DROP, ACCEPT, RETURN or nothing)\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       {0}
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       return 0;
+}
+
+/* Final check; don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Saves the targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+}
+
+static
+struct iptables_target standard = { 
+       .next           = NULL,
+       .name           = "standard",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(int)),
+       .userspacesize  = IPT_ALIGN(sizeof(int)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = NULL,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_target(&standard);
+}
diff --git a/extensions/libipt_state.c b/extensions/libipt_state.c
new file mode 100644 (file)
index 0000000..6a784ff
--- /dev/null
@@ -0,0 +1,163 @@
+/* Shared library add-on to iptables to add state tracking support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <linux/netfilter_ipv4/ipt_state.h>
+
+#ifndef IPT_STATE_UNTRACKED
+#define IPT_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 1))
+#endif
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"state v%s options:\n"
+" [!] --state [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED][,...]\n"
+"                              State(s) to match\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "state", 1, 0, '1' },
+       {0}
+};
+
+static int
+parse_state(const char *state, size_t strlen, struct ipt_state_info *sinfo)
+{
+       if (strncasecmp(state, "INVALID", strlen) == 0)
+               sinfo->statemask |= IPT_STATE_INVALID;
+       else if (strncasecmp(state, "NEW", strlen) == 0)
+               sinfo->statemask |= IPT_STATE_BIT(IP_CT_NEW);
+       else if (strncasecmp(state, "ESTABLISHED", strlen) == 0)
+               sinfo->statemask |= IPT_STATE_BIT(IP_CT_ESTABLISHED);
+       else if (strncasecmp(state, "RELATED", strlen) == 0)
+               sinfo->statemask |= IPT_STATE_BIT(IP_CT_RELATED);
+       else if (strncasecmp(state, "UNTRACKED", strlen) == 0)
+               sinfo->statemask |= IPT_STATE_UNTRACKED;
+       else
+               return 0;
+       return 1;
+}
+
+static void
+parse_states(const char *arg, struct ipt_state_info *sinfo)
+{
+       const char *comma;
+
+       while ((comma = strchr(arg, ',')) != NULL) {
+               if (comma == arg || !parse_state(arg, comma-arg, sinfo))
+                       exit_error(PARAMETER_PROBLEM, "Bad state `%s'", arg);
+               arg = comma+1;
+       }
+
+       if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
+               exit_error(PARAMETER_PROBLEM, "Bad state `%s'", arg);
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_state_info *sinfo = (struct ipt_state_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               parse_states(argv[optind-1], sinfo);
+               if (invert)
+                       sinfo->statemask = ~sinfo->statemask;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; must have specified --state. */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, "You must specify `--state'");
+}
+
+static void print_state(unsigned int statemask)
+{
+       const char *sep = "";
+
+       if (statemask & IPT_STATE_INVALID) {
+               printf("%sINVALID", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_STATE_BIT(IP_CT_NEW)) {
+               printf("%sNEW", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_STATE_BIT(IP_CT_RELATED)) {
+               printf("%sRELATED", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_STATE_BIT(IP_CT_ESTABLISHED)) {
+               printf("%sESTABLISHED", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_STATE_UNTRACKED) {
+               printf("%sUNTRACKED", sep);
+               sep = ",";
+       }
+       printf(" ");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_state_info *sinfo = (struct ipt_state_info *)match->data;
+
+       printf("state ");
+       print_state(sinfo->statemask);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_state_info *sinfo = (struct ipt_state_info *)match->data;
+
+       printf("--state ");
+       print_state(sinfo->statemask);
+}
+
+static struct iptables_match state = { 
+       .next           = NULL,
+       .name           = "state",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_state_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_state_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&state);
+}
diff --git a/extensions/libipt_state.man b/extensions/libipt_state.man
new file mode 100644 (file)
index 0000000..7107868
--- /dev/null
@@ -0,0 +1,21 @@
+This module, when combined with connection tracking, allows access to
+the connection tracking state for this packet.
+.TP
+.BI "--state " "state"
+Where state is a comma separated list of the connection states to
+match.  Possible states are
+.B INVALID
+meaning that the packet could not be identified for some reason which
+includes running out of memory and ICMP errors which don't correspond to any
+known connection,
+.B ESTABLISHED
+meaning that the packet is associated with a connection which has seen
+packets in both directions,
+.B NEW
+meaning that the packet has started a new connection, or otherwise
+associated with a connection which has not seen packets in both
+directions, and
+.B RELATED
+meaning that the packet is starting a new connection, but is
+associated with an existing connection, such as an FTP data transfer,
+or an ICMP error.
diff --git a/extensions/libipt_statistic.c b/extensions/libipt_statistic.c
new file mode 100644 (file)
index 0000000..4ed1813
--- /dev/null
@@ -0,0 +1,175 @@
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter/xt_statistic.h>
+
+static void
+help(void)
+{
+       printf(
+"statistic match v%s options:\n"
+" --mode mode                    Match mode (random, nth)\n"
+" random mode:\n"
+" --probability p               Probability\n"
+" nth mode:\n"
+" --every n                     Match every nth packet\n"
+" --packet p                    Initial counter value (0 <= p <= n-1, default 0)\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "mode", 1, 0, '1' },
+       { "probability", 1, 0, '2' },
+       { "every", 1, 0, '3' },
+       { "packet", 1, 0, '4' },
+       { 0 }
+};
+
+static struct xt_statistic_info *info;
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       double prob;
+
+       info = (void *)(*match)->data;
+
+       if (invert)
+               info->flags |= XT_STATISTIC_INVERT;
+
+       switch (c) {
+       case '1':
+               if (*flags & 0x1)
+                       exit_error(PARAMETER_PROBLEM, "double --mode");
+               if (!strcmp(optarg, "random"))
+                       info->mode = XT_STATISTIC_MODE_RANDOM;
+               else if (!strcmp(optarg, "nth"))
+                       info->mode = XT_STATISTIC_MODE_NTH;
+               else
+                       exit_error(PARAMETER_PROBLEM, "Bad mode `%s'", optarg);
+               *flags |= 0x1;
+               break;
+       case '2':
+               if (*flags & 0x2)
+                       exit_error(PARAMETER_PROBLEM, "double --probability");
+               prob = atof(optarg);
+               if (prob < 0 || prob > 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--probability must be between 0 and 1");
+               info->u.random.probability = 0x80000000 * prob;
+               *flags |= 0x2;
+               break;
+       case '3':
+               if (*flags & 0x4)
+                       exit_error(PARAMETER_PROBLEM, "double --every");
+               if (string_to_number(optarg, 0, 0xFFFFFFFF,
+                                    &info->u.nth.every) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "cannot parse --every `%s'", optarg);
+               if (info->u.nth.every == 0)
+                       exit_error(PARAMETER_PROBLEM, "--every cannot be 0");
+               info->u.nth.every--;
+               *flags |= 0x4;
+               break;
+       case '4':
+               if (*flags & 0x8)
+                       exit_error(PARAMETER_PROBLEM, "double --packet");
+               if (string_to_number(optarg, 0, 0xFFFFFFFF,
+                                    &info->u.nth.packet) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "cannot parse --packet `%s'", optarg);
+               *flags |= 0x8;
+               break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+/* Final check; must have specified --mark. */
+static void
+final_check(unsigned int flags)
+{
+       if (!(flags & 0x1))
+               exit_error(PARAMETER_PROBLEM, "no mode specified");
+       if ((flags & 0x2) && (flags & (0x4 | 0x8)))
+               exit_error(PARAMETER_PROBLEM,
+                          "both nth and random parameters given");
+       if (flags & 0x2 && info->mode != XT_STATISTIC_MODE_RANDOM)
+               exit_error(PARAMETER_PROBLEM,
+                          "--probability can only be used in random mode");
+       if (flags & 0x4 && info->mode != XT_STATISTIC_MODE_NTH)
+               exit_error(PARAMETER_PROBLEM,
+                          "--every can only be used in nth mode");
+       if (flags & 0x8 && info->mode != XT_STATISTIC_MODE_NTH)
+               exit_error(PARAMETER_PROBLEM,
+                          "--packet can only be used in nth mode");
+       info->u.nth.count = info->u.nth.every - info->u.nth.packet;
+}
+
+/* Prints out the matchinfo. */
+static void print_match(const struct xt_statistic_info *info, char *prefix)
+{
+       if (info->flags & XT_STATISTIC_INVERT)
+               printf("! ");
+
+       switch (info->mode) {
+       case XT_STATISTIC_MODE_RANDOM:
+               printf("%smode random %sprobability %f ", prefix, prefix,
+                      1.0 * info->u.random.probability / 0x80000000);
+               break;
+       case XT_STATISTIC_MODE_NTH:
+               printf("%smode nth %severy %u ", prefix, prefix,
+                      info->u.nth.every + 1);
+               if (info->u.nth.packet)
+                       printf("%spacket %u ", prefix, info->u.nth.packet);
+               break;
+       }
+}
+
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct xt_statistic_info *info = (struct xt_statistic_info *)match->data;
+
+       printf("statistic ");
+       print_match(info, "");
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct xt_statistic_info *info = (struct xt_statistic_info *)match->data;
+
+       print_match(info, "--");
+}
+
+static struct iptables_match statistic = { 
+       .name           = "statistic",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct xt_statistic_info)),
+       .userspacesize  = offsetof(struct xt_statistic_info, u.nth.count),
+       .help           = help,
+       .parse          = parse,
+       .final_check    = final_check,
+       .print          = print,
+       .save           = save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&statistic);
+}
diff --git a/extensions/libipt_string.c b/extensions/libipt_string.c
new file mode 100644 (file)
index 0000000..266b3e5
--- /dev/null
@@ -0,0 +1,354 @@
+/* Shared library add-on to iptables to add string matching support. 
+ * 
+ * Copyright (C) 2000 Emmanuel Roger  <winfield@freegates.be>
+ *
+ * 2005-08-05 Pablo Neira Ayuso <pablo@eurodev.net>
+ *     - reimplemented to use new string matching iptables match
+ *     - add functionality to match packets by using window offsets
+ *     - add functionality to select the string matching algorithm
+ *
+ * ChangeLog
+ *     29.12.2003: Michael Rash <mbr@cipherdyne.org>
+ *             Fixed iptables save/restore for ascii strings
+ *             that contain space chars, and hex strings that
+ *             contain embedded NULL chars.  Updated to print
+ *             strings in hex mode if any non-printable char
+ *             is contained within the string.
+ *
+ *     27.01.2001: Gianni Tedesco <gianni@ecsc.co.uk>
+ *             Changed --tos to --string in save(). Also
+ *             updated to work with slightly modified
+ *             ipt_string_info.
+ */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <iptables.h>
+#include <stddef.h>
+#include <linux/netfilter_ipv4/ipt_string.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"STRING match v%s options:\n"
+"--from                       Offset to start searching from\n"
+"--to                         Offset to stop searching\n"
+"--algo                              Algorithm\n"
+"--string [!] string          Match a string in a packet\n"
+"--hex-string [!] string      Match a hex string in a packet\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "from", 1, 0, '1' },
+       { "to", 1, 0, '2' },
+       { "algo", 1, 0, '3' },
+       { "string", 1, 0, '4' },
+       { "hex-string", 1, 0, '5' },
+       {0}
+};
+
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_string_info *i = (struct ipt_string_info *) m->data;
+
+       if (i->to_offset == 0)
+               i->to_offset = (u_int16_t) ~0UL;
+}
+
+static void
+parse_string(const char *s, struct ipt_string_info *info)
+{      
+       if (strlen(s) <= IPT_STRING_MAX_PATTERN_SIZE) {
+               strncpy(info->pattern, s, IPT_STRING_MAX_PATTERN_SIZE);
+               info->patlen = strlen(s);
+               return;
+       }
+       exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s);
+}
+
+static void
+parse_algo(const char *s, struct ipt_string_info *info)
+{
+       if (strlen(s) <= IPT_STRING_MAX_ALGO_NAME_SIZE) {
+               strncpy(info->algo, s, IPT_STRING_MAX_ALGO_NAME_SIZE);
+               return;
+       }
+       exit_error(PARAMETER_PROBLEM, "ALGO too long `%s'", s);
+}
+
+static void
+parse_hex_string(const char *s, struct ipt_string_info *info)
+{
+       int i=0, slen, sindex=0, schar;
+       short hex_f = 0, literal_f = 0;
+       char hextmp[3];
+
+       slen = strlen(s);
+
+       if (slen == 0) {
+               exit_error(PARAMETER_PROBLEM,
+                       "STRING must contain at least one char");
+       }
+
+       while (i < slen) {
+               if (s[i] == '\\' && !hex_f) {
+                       literal_f = 1;
+               } else if (s[i] == '\\') {
+                       exit_error(PARAMETER_PROBLEM,
+                               "Cannot include literals in hex data");
+               } else if (s[i] == '|') {
+                       if (hex_f)
+                               hex_f = 0;
+                       else {
+                               hex_f = 1;
+                               /* get past any initial whitespace just after the '|' */
+                               while (s[i+1] == ' ')
+                                       i++;
+                       }
+                       if (i+1 >= slen)
+                               break;
+                       else
+                               i++;  /* advance to the next character */
+               }
+
+               if (literal_f) {
+                       if (i+1 >= slen) {
+                               exit_error(PARAMETER_PROBLEM,
+                                       "Bad literal placement at end of string");
+                       }
+                       info->pattern[sindex] = s[i+1];
+                       i += 2;  /* skip over literal char */
+                       literal_f = 0;
+               } else if (hex_f) {
+                       if (i+1 >= slen) {
+                               exit_error(PARAMETER_PROBLEM,
+                                       "Odd number of hex digits");
+                       }
+                       if (i+2 >= slen) {
+                               /* must end with a "|" */
+                               exit_error(PARAMETER_PROBLEM, "Invalid hex block");
+                       }
+                       if (! isxdigit(s[i])) /* check for valid hex char */
+                               exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i]);
+                       if (! isxdigit(s[i+1])) /* check for valid hex char */
+                               exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i+1]);
+                       hextmp[0] = s[i];
+                       hextmp[1] = s[i+1];
+                       hextmp[2] = '\0';
+                       if (! sscanf(hextmp, "%x", &schar))
+                               exit_error(PARAMETER_PROBLEM,
+                                       "Invalid hex char `%c'", s[i]);
+                       info->pattern[sindex] = (char) schar;
+                       if (s[i+2] == ' ')
+                               i += 3;  /* spaces included in the hex block */
+                       else
+                               i += 2;
+               } else {  /* the char is not part of hex data, so just copy */
+                       info->pattern[sindex] = s[i];
+                       i++;
+               }
+               if (sindex > IPT_STRING_MAX_PATTERN_SIZE)
+                       exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s);
+               sindex++;
+       }
+       info->patlen = sindex;
+}
+
+#define STRING 0x1
+#define ALGO   0x2
+#define FROM   0x4
+#define TO     0x8
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_string_info *stringinfo = (struct ipt_string_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & FROM)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify multiple --from");
+               stringinfo->from_offset = atoi(optarg);
+               *flags |= FROM;
+               break;
+       case '2':
+               if (*flags & TO)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify multiple --to");
+               stringinfo->to_offset = atoi(optarg);
+               *flags |= TO;
+               break;
+       case '3':
+               if (*flags & ALGO)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify multiple --algo");
+               parse_algo(optarg, stringinfo);
+               *flags |= ALGO;
+               break;
+       case '4':
+               if (*flags & STRING)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify multiple --string");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_string(argv[optind-1], stringinfo);
+               if (invert)
+                       stringinfo->invert = 1;
+               stringinfo->patlen=strlen((char *)&stringinfo->pattern);
+               *flags |= STRING;
+               break;
+
+       case '5':
+               if (*flags & STRING)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify multiple --hex-string");
+
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_hex_string(argv[optind-1], stringinfo);  /* sets length */
+               if (invert)
+                       stringinfo->invert = 1;
+               *flags |= STRING;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+
+/* Final check; must have specified --string. */
+static void
+final_check(unsigned int flags)
+{
+       if (!(flags & STRING))
+               exit_error(PARAMETER_PROBLEM,
+                          "STRING match: You must specify `--string' or "
+                          "`--hex-string'");
+       if (!(flags & ALGO))
+               exit_error(PARAMETER_PROBLEM,
+                          "STRING match: You must specify `--algo'");
+}
+
+/* Test to see if the string contains non-printable chars or quotes */
+static unsigned short int
+is_hex_string(const char *str, const unsigned short int len)
+{
+       unsigned int i;
+       for (i=0; i < len; i++)
+               if (! isprint(str[i]))
+                       return 1;  /* string contains at least one non-printable char */
+       /* use hex output if the last char is a "\" */
+       if ((unsigned char) str[len-1] == 0x5c)
+               return 1;
+       return 0;
+}
+
+/* Print string with "|" chars included as one would pass to --hex-string */
+static void
+print_hex_string(const char *str, const unsigned short int len)
+{
+       unsigned int i;
+       /* start hex block */
+       printf("\"|");
+       for (i=0; i < len; i++) {
+               /* see if we need to prepend a zero */
+               if ((unsigned char) str[i] <= 0x0F)
+                       printf("0%x", (unsigned char) str[i]);
+               else
+                       printf("%x", (unsigned char) str[i]);
+       }
+       /* close hex block */
+       printf("|\" ");
+}
+
+static void
+print_string(const char *str, const unsigned short int len)
+{
+       unsigned int i;
+       printf("\"");
+       for (i=0; i < len; i++) {
+               if ((unsigned char) str[i] == 0x22)  /* escape any embedded quotes */
+                       printf("%c", 0x5c);
+               printf("%c", (unsigned char) str[i]);
+       }
+       printf("\" ");  /* closing space and quote */
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_string_info *info =
+           (const struct ipt_string_info*) match->data;
+
+       if (is_hex_string(info->pattern, info->patlen)) {
+               printf("STRING match %s", (info->invert) ? "!" : "");
+               print_hex_string(info->pattern, info->patlen);
+       } else {
+               printf("STRING match %s", (info->invert) ? "!" : "");
+               print_string(info->pattern, info->patlen);
+       }
+       printf("ALGO name %s ", info->algo);
+       if (info->from_offset != 0)
+               printf("FROM %u ", info->from_offset);
+       if (info->to_offset != 0)
+               printf("TO %u ", info->to_offset);
+}
+
+
+/* Saves the union ipt_matchinfo in parseable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_string_info *info =
+           (const struct ipt_string_info*) match->data;
+
+       if (is_hex_string(info->pattern, info->patlen)) {
+               printf("--hex-string %s", (info->invert) ? "! ": "");
+               print_hex_string(info->pattern, info->patlen);
+       } else {
+               printf("--string %s", (info->invert) ? "! ": "");
+               print_string(info->pattern, info->patlen);
+       }
+       printf("--algo %s ", info->algo);
+       if (info->from_offset != 0)
+               printf("--from %u ", info->from_offset);
+       if (info->to_offset != 0)
+               printf("--to %u ", info->to_offset);
+}
+
+
+static struct iptables_match string = {
+    .name              = "string",
+    .version           = IPTABLES_VERSION,
+    .size              = IPT_ALIGN(sizeof(struct ipt_string_info)),
+    .userspacesize     = offsetof(struct ipt_string_info, config),
+    .help              = help,
+    .init              = init,
+    .parse             = parse,
+    .final_check       = final_check,
+    .print             = print,
+    .save              = save,
+    .extra_opts                = opts
+};
+
+
+void _init(void)
+{
+       register_match(&string);
+}
diff --git a/extensions/libipt_string.man b/extensions/libipt_string.man
new file mode 100644 (file)
index 0000000..3f3e5b7
--- /dev/null
@@ -0,0 +1,15 @@
+This modules matches a given string by using some pattern matching strategy. It requires a linux kernel >= 2.6.14.
+.TP
+.BI "--algo  " "bm|kmp"
+Select the pattern matching strategy. (bm = Boyer-Moore, kmp = Knuth-Pratt-Morris)
+.TP
+.BI "--from " "offset"
+Set the offset from which it starts looking for any matching. If not passed, default is 0.
+.TP
+.BI "--to " "offset"
+Set the offset from which it starts looking for any matching. If not passed, default is the packet size.
+.TP
+.BI "--string " "pattern"
+Matches the given pattern.
+.BI "--hex-string " "pattern"
+Matches the given pattern in hex notation.
diff --git a/extensions/libipt_tcp.c b/extensions/libipt_tcp.c
new file mode 100644 (file)
index 0000000..c712b92
--- /dev/null
@@ -0,0 +1,416 @@
+/* Shared library add-on to iptables to add TCP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"TCP v%s options:\n"
+" --tcp-flags [!] mask comp    match when TCP flags & mask == comp\n"
+"                              (Flags: SYN ACK FIN RST URG PSH ALL NONE)\n"
+"[!] --syn                     match when only SYN flag set\n"
+"                              (equivalent to --tcp-flags SYN,RST,ACK SYN)\n"
+" --source-port [!] port[:port]\n"
+" --sport ...\n"
+"                              match source port(s)\n"
+" --destination-port [!] port[:port]\n"
+" --dport ...\n"
+"                              match destination port(s)\n"
+" --tcp-option [!] number       match if TCP option set\n\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "source-port", 1, 0, '1' },
+       { "sport", 1, 0, '1' }, /* synonym */
+       { "destination-port", 1, 0, '2' },
+       { "dport", 1, 0, '2' }, /* synonym */
+       { "syn", 0, 0, '3' },
+       { "tcp-flags", 1, 0, '4' },
+       { "tcp-option", 1, 0, '5' },
+       {0}
+};
+
+static void
+parse_tcp_ports(const char *portstring, u_int16_t *ports)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(portstring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               ports[0] = ports[1] = parse_port(buffer, "tcp");
+       else {
+               *cp = '\0';
+               cp++;
+
+               ports[0] = buffer[0] ? parse_port(buffer, "tcp") : 0;
+               ports[1] = cp[0] ? parse_port(cp, "tcp") : 0xFFFF;
+
+               if (ports[0] > ports[1])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "invalid portrange (min > max)");
+       }
+       free(buffer);
+}
+
+struct tcp_flag_names {
+       const char *name;
+       unsigned int flag;
+};
+
+static struct tcp_flag_names tcp_flag_names[]
+= { { "FIN", 0x01 },
+    { "SYN", 0x02 },
+    { "RST", 0x04 },
+    { "PSH", 0x08 },
+    { "ACK", 0x10 },
+    { "URG", 0x20 },
+    { "ALL", 0x3F },
+    { "NONE", 0 },
+};
+
+static unsigned int
+parse_tcp_flag(const char *flags)
+{
+       unsigned int ret = 0;
+       char *ptr;
+       char *buffer;
+
+       buffer = strdup(flags);
+
+       for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
+               unsigned int i;
+               for (i = 0;
+                    i < sizeof(tcp_flag_names)/sizeof(struct tcp_flag_names);
+                    i++) {
+                       if (strcasecmp(tcp_flag_names[i].name, ptr) == 0) {
+                               ret |= tcp_flag_names[i].flag;
+                               break;
+                       }
+               }
+               if (i == sizeof(tcp_flag_names)/sizeof(struct tcp_flag_names))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unknown TCP flag `%s'", ptr);
+               }
+
+       free(buffer);
+       return ret;
+}
+
+static void
+parse_tcp_flags(struct ipt_tcp *tcpinfo,
+               const char *mask,
+               const char *cmp,
+               int invert)
+{
+       tcpinfo->flg_mask = parse_tcp_flag(mask);
+       tcpinfo->flg_cmp = parse_tcp_flag(cmp);
+
+       if (invert)
+               tcpinfo->invflags |= IPT_TCP_INV_FLAGS;
+}
+
+static void
+parse_tcp_option(const char *option, u_int8_t *result)
+{
+       unsigned int ret;
+
+       if (string_to_number(option, 1, 255, &ret) == -1)
+               exit_error(PARAMETER_PROBLEM, "Bad TCP option `%s'", option);
+
+       *result = (u_int8_t)ret;
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_tcp *tcpinfo = (struct ipt_tcp *)m->data;
+
+       tcpinfo->spts[1] = tcpinfo->dpts[1] = 0xFFFF;
+}
+
+#define TCP_SRC_PORTS 0x01
+#define TCP_DST_PORTS 0x02
+#define TCP_FLAGS 0x04
+#define TCP_OPTION     0x08
+
+/* Function which parses command options; returns true if it
+   ate an option. */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_tcp *tcpinfo = (struct ipt_tcp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & TCP_SRC_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--source-port' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_tcp_ports(argv[optind-1], tcpinfo->spts);
+               if (invert)
+                       tcpinfo->invflags |= IPT_TCP_INV_SRCPT;
+               *flags |= TCP_SRC_PORTS;
+               break;
+
+       case '2':
+               if (*flags & TCP_DST_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--destination-port' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_tcp_ports(argv[optind-1], tcpinfo->dpts);
+               if (invert)
+                       tcpinfo->invflags |= IPT_TCP_INV_DSTPT;
+               *flags |= TCP_DST_PORTS;
+               break;
+
+       case '3':
+               if (*flags & TCP_FLAGS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one of `--syn' or `--tcp-flags' "
+                                  " allowed");
+               parse_tcp_flags(tcpinfo, "SYN,RST,ACK,FIN", "SYN", invert);
+               *flags |= TCP_FLAGS;
+               break;
+
+       case '4':
+               if (*flags & TCP_FLAGS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one of `--syn' or `--tcp-flags' "
+                                  " allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (!argv[optind]
+                   || argv[optind][0] == '-' || argv[optind][0] == '!')
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--tcp-flags requires two args.");
+
+               parse_tcp_flags(tcpinfo, argv[optind-1], argv[optind],
+                               invert);
+               optind++;
+               *flags |= TCP_FLAGS;
+               break;
+
+       case '5':
+               if (*flags & TCP_OPTION)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--tcp-option' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_tcp_option(argv[optind-1], &tcpinfo->option);
+               if (invert)
+                       tcpinfo->invflags |= IPT_TCP_INV_OPTION;
+               *flags |= TCP_OPTION;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static char *
+port_to_service(int port)
+{
+       struct servent *service;
+
+       if ((service = getservbyport(htons(port), "tcp")))
+               return service->s_name;
+
+       return NULL;
+}
+
+static void
+print_port(u_int16_t port, int numeric)
+{
+       char *service;
+
+       if (numeric || (service = port_to_service(port)) == NULL)
+               printf("%u", port);
+       else
+               printf("%s", service);
+}
+
+static void
+print_ports(const char *name, u_int16_t min, u_int16_t max,
+           int invert, int numeric)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       print_port(min, numeric);
+               } else {
+                       printf("s:%s", inv);
+                       print_port(min, numeric);
+                       printf(":");
+                       print_port(max, numeric);
+               }
+               printf(" ");
+       }
+}
+
+static void
+print_option(u_int8_t option, int invert, int numeric)
+{
+       if (option || invert)
+               printf("option=%s%u ", invert ? "!" : "", option);
+}
+
+static void
+print_tcpf(u_int8_t flags)
+{
+       int have_flag = 0;
+
+       while (flags) {
+               unsigned int i;
+
+               for (i = 0; (flags & tcp_flag_names[i].flag) == 0; i++);
+
+               if (have_flag)
+                       printf(",");
+               printf("%s", tcp_flag_names[i].name);
+               have_flag = 1;
+
+               flags &= ~tcp_flag_names[i].flag;
+       }
+
+       if (!have_flag)
+               printf("NONE");
+}
+
+static void
+print_flags(u_int8_t mask, u_int8_t cmp, int invert, int numeric)
+{
+       if (mask || invert) {
+               printf("flags:%s", invert ? "!" : "");
+               if (numeric)
+                       printf("0x%02X/0x%02X ", mask, cmp);
+               else {
+                       print_tcpf(mask);
+                       printf("/");
+                       print_tcpf(cmp);
+                       printf(" ");
+               }
+       }
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match, int numeric)
+{
+       const struct ipt_tcp *tcp = (struct ipt_tcp *)match->data;
+
+       printf("tcp ");
+       print_ports("spt", tcp->spts[0], tcp->spts[1],
+                   tcp->invflags & IPT_TCP_INV_SRCPT,
+                   numeric);
+       print_ports("dpt", tcp->dpts[0], tcp->dpts[1],
+                   tcp->invflags & IPT_TCP_INV_DSTPT,
+                   numeric);
+       print_option(tcp->option,
+                    tcp->invflags & IPT_TCP_INV_OPTION,
+                    numeric);
+       print_flags(tcp->flg_mask, tcp->flg_cmp,
+                   tcp->invflags & IPT_TCP_INV_FLAGS,
+                   numeric);
+       if (tcp->invflags & ~IPT_TCP_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      tcp->invflags & ~IPT_TCP_INV_MASK);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_tcp *tcpinfo = (struct ipt_tcp *)match->data;
+
+       if (tcpinfo->spts[0] != 0
+           || tcpinfo->spts[1] != 0xFFFF) {
+               if (tcpinfo->invflags & IPT_TCP_INV_SRCPT)
+                       printf("! ");
+               if (tcpinfo->spts[0]
+                   != tcpinfo->spts[1])
+                       printf("--sport %u:%u ",
+                              tcpinfo->spts[0],
+                              tcpinfo->spts[1]);
+               else
+                       printf("--sport %u ",
+                              tcpinfo->spts[0]);
+       }
+
+       if (tcpinfo->dpts[0] != 0
+           || tcpinfo->dpts[1] != 0xFFFF) {
+               if (tcpinfo->invflags & IPT_TCP_INV_DSTPT)
+                       printf("! ");
+               if (tcpinfo->dpts[0]
+                   != tcpinfo->dpts[1])
+                       printf("--dport %u:%u ",
+                              tcpinfo->dpts[0],
+                              tcpinfo->dpts[1]);
+               else
+                       printf("--dport %u ",
+                              tcpinfo->dpts[0]);
+       }
+
+       if (tcpinfo->option
+           || (tcpinfo->invflags & IPT_TCP_INV_OPTION)) {
+               if (tcpinfo->invflags & IPT_TCP_INV_OPTION)
+                       printf("! ");
+               printf("--tcp-option %u ", tcpinfo->option);
+       }
+
+       if (tcpinfo->flg_mask
+           || (tcpinfo->invflags & IPT_TCP_INV_FLAGS)) {
+               if (tcpinfo->invflags & IPT_TCP_INV_FLAGS)
+                       printf("! ");
+               printf("--tcp-flags ");
+               if (tcpinfo->flg_mask != 0xFF) {
+                       print_tcpf(tcpinfo->flg_mask);
+               }
+               printf(" ");
+               print_tcpf(tcpinfo->flg_cmp);
+               printf(" ");
+       }
+}
+
+static struct iptables_match tcp = { 
+       .next           = NULL,
+       .name           = "tcp",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_tcp)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_tcp)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void
+_init(void)
+{
+       register_match(&tcp);
+}
diff --git a/extensions/libipt_tcp.man b/extensions/libipt_tcp.man
new file mode 100644 (file)
index 0000000..cfafc9e
--- /dev/null
@@ -0,0 +1,45 @@
+These extensions can be used if `--protocol tcp' is specified. It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification. This can either be a service
+name or a port number. An inclusive range can also be specified,
+using the format
+.IR port : port .
+If the first port is omitted, "0" is assumed; if the last is omitted,
+"65535" is assumed.
+If the second port greater then the first they will be swapped.
+The flag
+.B --sport
+is a convenient alias for this option.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification.  The flag
+.B --dport
+is a convenient alias for this option.
+.TP
+.BR "--tcp-flags " "[!] \fImask\fP \fIcomp\fP"
+Match when the TCP flags are as specified.  The first argument is the
+flags which we should examine, written as a comma-separated list, and
+the second argument is a comma-separated list of flags which must be
+set.  Flags are:
+.BR "SYN ACK FIN RST URG PSH ALL NONE" .
+Hence the command
+.nf
+ iptables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST SYN
+.fi
+will only match packets with the SYN flag set, and the ACK, FIN and
+RST flags unset.
+.TP
+.B "[!] --syn"
+Only match TCP packets with the SYN bit set and the ACK,RST and FIN bits
+cleared.  Such packets are used to request TCP connection initiation;
+for example, blocking such packets coming in an interface will prevent
+incoming TCP connections, but outgoing TCP connections will be
+unaffected.
+It is equivalent to \fB--tcp-flags SYN,RST,ACK,FIN SYN\fP.
+If the "!" flag precedes the "--syn", the sense of the
+option is inverted.
+.TP
+.BR "--tcp-option " "[!] \fInumber\fP"
+Match if TCP option set.
diff --git a/extensions/libipt_tcpmss.c b/extensions/libipt_tcpmss.c
new file mode 100644 (file)
index 0000000..9a399bb
--- /dev/null
@@ -0,0 +1,152 @@
+/* Shared library add-on to iptables to add tcp MSS matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_tcpmss.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"tcpmss match v%s options:\n"
+"[!] --mss value[:value]       Match TCP MSS range.\n"
+"                              (only valid for TCP SYN or SYN/ACK packets)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "mss", 1, 0, '1' },
+       {0}
+};
+
+static u_int16_t
+parse_tcp_mssvalue(const char *mssvalue)
+{
+       unsigned int mssvaluenum;
+
+       if (string_to_number(mssvalue, 0, 65535, &mssvaluenum) != -1)
+               return (u_int16_t)mssvaluenum;
+
+       exit_error(PARAMETER_PROBLEM,
+                  "Invalid mss `%s' specified", mssvalue);
+}
+
+static void
+parse_tcp_mssvalues(const char *mssvaluestring,
+                   u_int16_t *mss_min, u_int16_t *mss_max)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(mssvaluestring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               *mss_min = *mss_max = parse_tcp_mssvalue(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               *mss_min = buffer[0] ? parse_tcp_mssvalue(buffer) : 0;
+               *mss_max = cp[0] ? parse_tcp_mssvalue(cp) : 0xFFFF;
+       }
+       free(buffer);
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_tcpmss_match_info *mssinfo =
+               (struct ipt_tcpmss_match_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--mss' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_tcp_mssvalues(argv[optind-1],
+                                   &mssinfo->mss_min, &mssinfo->mss_max);
+               if (invert)
+                       mssinfo->invert = 1;
+               *flags = 1;
+               break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+print_tcpmss(u_int16_t mss_min, u_int16_t mss_max, int invert, int numeric)
+{
+       if (invert)
+               printf("! ");
+
+       if (mss_min == mss_max)
+               printf("%u ", mss_min);
+       else
+               printf("%u:%u ", mss_min, mss_max);
+}
+
+/* Final check; must have specified --mss. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "tcpmss match: You must specify `--mss'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_tcpmss_match_info *mssinfo =
+               (const struct ipt_tcpmss_match_info *)match->data;
+
+       printf("tcpmss match ");
+       print_tcpmss(mssinfo->mss_min, mssinfo->mss_max,
+                    mssinfo->invert, numeric);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_tcpmss_match_info *mssinfo =
+               (const struct ipt_tcpmss_match_info *)match->data;
+
+       printf("--mss ");
+       print_tcpmss(mssinfo->mss_min, mssinfo->mss_max,
+                    mssinfo->invert, 0);
+}
+
+static struct iptables_match tcpmss = {
+       .next           = NULL,
+       .name           = "tcpmss",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_tcpmss_match_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_tcpmss_match_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&tcpmss);
+}
diff --git a/extensions/libipt_tcpmss.man b/extensions/libipt_tcpmss.man
new file mode 100644 (file)
index 0000000..91fe322
--- /dev/null
@@ -0,0 +1,4 @@
+This matches the TCP MSS (maximum segment size) field of the TCP header.  You can only use this on TCP SYN or SYN/ACK packets, since the MSS is only negotiated during the TCP handshake at connection startup time.
+.TP
+.BI "[!] "--mss " value[:value]"
+Match a given TCP MSS value or range.
diff --git a/extensions/libipt_tos.c b/extensions/libipt_tos.c
new file mode 100644 (file)
index 0000000..f8b5cb4
--- /dev/null
@@ -0,0 +1,172 @@
+/* Shared library add-on to iptables to add TOS matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_tos.h>
+
+/* TOS names and values. */
+static
+struct TOS_value
+{
+       unsigned char TOS;
+       const char *name;
+} TOS_values[] = {
+       { IPTOS_LOWDELAY,    "Minimize-Delay" },
+       { IPTOS_THROUGHPUT,  "Maximize-Throughput" },
+       { IPTOS_RELIABILITY, "Maximize-Reliability" },
+       { IPTOS_MINCOST,     "Minimize-Cost" },
+       { IPTOS_NORMALSVC,   "Normal-Service" },
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       unsigned int i;
+
+       printf(
+"TOS match v%s options:\n"
+"[!] --tos value                 Match Type of Service field from one of the\n"
+"                                following numeric or descriptive values:\n",
+IPTABLES_VERSION);
+
+       for (i = 0; i < sizeof(TOS_values)/sizeof(struct TOS_value);i++)
+               printf("                                     %s %u (0x%02x)\n",
+                      TOS_values[i].name,
+                       TOS_values[i].TOS,
+                       TOS_values[i].TOS);
+       fputc('\n', stdout);
+}
+
+static struct option opts[] = {
+       { "tos", 1, 0, '1' },
+       {0}
+};
+
+static void
+parse_tos(const char *s, struct ipt_tos_info *info)
+{
+       unsigned int i;
+       unsigned int tos;
+
+       if (string_to_number(s, 0, 255, &tos) != -1) {
+               if (tos == IPTOS_LOWDELAY
+                   || tos == IPTOS_THROUGHPUT
+                   || tos == IPTOS_RELIABILITY
+                   || tos == IPTOS_MINCOST
+                   || tos == IPTOS_NORMALSVC) {
+                       info->tos = (u_int8_t )tos;
+                       return;
+               }
+       } else {
+               for (i = 0; i<sizeof(TOS_values)/sizeof(struct TOS_value); i++)
+                       if (strcasecmp(s,TOS_values[i].name) == 0) {
+                               info->tos = TOS_values[i].TOS;
+                               return;
+                       }
+       }
+       exit_error(PARAMETER_PROBLEM, "Bad TOS value `%s'", s);
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_tos_info *tosinfo = (struct ipt_tos_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               /* Ensure that `--tos' haven't been used yet. */
+               if (*flags == 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                       "tos match: only use --tos once!");
+
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_tos(argv[optind-1], tosinfo);
+               if (invert)
+                       tosinfo->invert = 1;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+print_tos(u_int8_t tos, int numeric)
+{
+       unsigned int i;
+
+       if (!numeric) {
+               for (i = 0; i<sizeof(TOS_values)/sizeof(struct TOS_value); i++)
+                       if (TOS_values[i].TOS == tos) {
+                               printf("%s ", TOS_values[i].name);
+                               return;
+                       }
+       }
+       printf("0x%02x ", tos);
+}
+
+/* Final check; must have specified --tos. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "TOS match: You must specify `--tos'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_tos_info *info = (const struct ipt_tos_info *)match->data;
+    
+       printf("TOS match ");
+       if (info->invert)
+               printf("!");
+       print_tos(info->tos, numeric);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_tos_info *info = (const struct ipt_tos_info *)match->data;
+    
+       if (info->invert)
+               printf("! ");
+       printf("--tos ");
+       print_tos(info->tos, 0);
+}
+
+static struct iptables_match tos = { 
+       .next           = NULL,
+       .name           = "tos",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_tos_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_tos_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&tos);
+}
diff --git a/extensions/libipt_tos.man b/extensions/libipt_tos.man
new file mode 100644 (file)
index 0000000..c612b29
--- /dev/null
@@ -0,0 +1,9 @@
+This module matches the 8 bits of Type of Service field in the IP
+header (ie. including the precedence bits).
+.TP
+.BI "--tos " "tos"
+The argument is either a standard name, (use
+.br
+ iptables -m tos -h
+.br
+to see the list), or a numeric value to match.
diff --git a/extensions/libipt_ttl.c b/extensions/libipt_ttl.c
new file mode 100644 (file)
index 0000000..89ea084
--- /dev/null
@@ -0,0 +1,172 @@
+/* Shared library add-on to iptables to add TTL matching support 
+ * (C) 2000 by Harald Welte <laforge@gnumonks.org>
+ *
+ * $Id: libipt_ttl.c 4544 2005-11-18 17:59:56Z /C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=kaber/emailAddress=kaber@netfilter.org $
+ *
+ * This program is released under the terms of GNU GPL */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <iptables.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_ttl.h>
+
+static void help(void) 
+{
+       printf(
+"TTL match v%s options:\n"
+"  --ttl-eq value      Match time to live value\n"
+"  --ttl-lt value      Match TTL < value\n"
+"  --ttl-gt value      Match TTL > value\n"
+, IPTABLES_VERSION);
+}
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+               const struct ipt_entry *entry, unsigned int *nfcache,
+               struct ipt_entry_match **match)
+{
+       struct ipt_ttl_info *info = (struct ipt_ttl_info *) (*match)->data;
+       unsigned int value;
+
+       check_inverse(optarg, &invert, &optind, 0);
+
+       switch (c) {
+               case '2':
+                       if (string_to_number(optarg, 0, 255, &value) == -1)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "ttl: Expected value between 0 and 255");
+
+                       if (invert)
+                               info->mode = IPT_TTL_NE;
+                       else
+                               info->mode = IPT_TTL_EQ;
+
+                       /* is 0 allowed? */
+                       info->ttl = value;
+                       break;
+               case '3':
+                       if (string_to_number(optarg, 0, 255, &value) == -1)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "ttl: Expected value between 0 and 255");
+
+                       if (invert) 
+                               exit_error(PARAMETER_PROBLEM,
+                                               "ttl: unexpected `!'");
+
+                       info->mode = IPT_TTL_LT;
+                       info->ttl = value;
+                       break;
+               case '4':
+                       if (string_to_number(optarg, 0, 255, &value) == -1)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "ttl: Expected value between 0 and 255");
+
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                               "ttl: unexpected `!'");
+
+                       info->mode = IPT_TTL_GT;
+                       info->ttl = value;
+                       break;
+               default:
+                       return 0;
+
+       }
+
+       if (*flags) 
+               exit_error(PARAMETER_PROBLEM, 
+                               "Can't specify TTL option twice");
+       *flags = 1;
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!flags) 
+               exit_error(PARAMETER_PROBLEM,
+                       "TTL match: You must specify one of "
+                       "`--ttl-eq', `--ttl-lt', `--ttl-gt");
+}
+
+static void print(const struct ipt_ip *ip, 
+               const struct ipt_entry_match *match,
+               int numeric)
+{
+       const struct ipt_ttl_info *info = 
+               (struct ipt_ttl_info *) match->data;
+
+       printf("TTL match ");
+       switch (info->mode) {
+               case IPT_TTL_EQ:
+                       printf("TTL == ");
+                       break;
+               case IPT_TTL_NE:
+                       printf("TTL != ");
+                       break;
+               case IPT_TTL_LT:
+                       printf("TTL < ");
+                       break;
+               case IPT_TTL_GT:
+                       printf("TTL > ");
+                       break;
+       }
+       printf("%u ", info->ttl);
+}
+
+static void save(const struct ipt_ip *ip, 
+               const struct ipt_entry_match *match)
+{
+       const struct ipt_ttl_info *info =
+               (struct ipt_ttl_info *) match->data;
+
+       switch (info->mode) {
+               case IPT_TTL_EQ:
+                       printf("--ttl-eq ");
+                       break;
+               case IPT_TTL_NE:
+                       printf("! --ttl-eq ");
+                       break;
+               case IPT_TTL_LT:
+                       printf("--ttl-lt ");
+                       break;
+               case IPT_TTL_GT:
+                       printf("--ttl-gt ");
+                       break;
+               default:
+                       /* error */
+                       break;
+       }
+       printf("%u ", info->ttl);
+}
+
+static struct option opts[] = {
+       { "ttl", 1, 0, '2' },
+       { "ttl-eq", 1, 0, '2'},
+       { "ttl-lt", 1, 0, '3'},
+       { "ttl-gt", 1, 0, '4'},
+       { 0 }
+};
+
+static struct iptables_match ttl = {
+       .next           = NULL,
+       .name           = "ttl",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_ttl_info)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_ttl_info)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+
+void _init(void) 
+{
+       register_match(&ttl);
+}
diff --git a/extensions/libipt_ttl.man b/extensions/libipt_ttl.man
new file mode 100644 (file)
index 0000000..f043c79
--- /dev/null
@@ -0,0 +1,10 @@
+This module matches the time to live field in the IP header.
+.TP
+.BI "--ttl-eq " "ttl"
+Matches the given TTL value.
+.TP
+.BI "--ttl-gt " "ttl"
+Matches if TTL is greater than the given TTL value.
+.TP
+.BI "--ttl-lt " "ttl"
+Matches if TTL is less than the given TTL value.
diff --git a/extensions/libipt_u32.c b/extensions/libipt_u32.c
new file mode 100644 (file)
index 0000000..cf9e63a
--- /dev/null
@@ -0,0 +1,263 @@
+/* Shared library add-on to iptables to add u32 matching,
+ * generalized matching on values found at packet offsets
+ *
+ * Detailed doc is in the kernel module source
+ * net/ipv4/netfilter/ipt_u32.c
+ *
+ * (C) 2002 by Don Cohen <don-netf@isis.cs3-inc.com>
+ * Released under the terms of GNU GPL v2
+ */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_u32.h>
+#include <errno.h>
+#include <ctype.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf( "u32 v%s options:\n"
+               " --u32 tests\n"
+               " tests := location = value | tests && location = value\n"
+               " value := range | value , range\n"
+               " range := number | number : number\n"
+               " location := number | location operator number\n"
+               " operator := & | << | >> | @\n"
+               ,IPTABLES_VERSION);
+}
+
+/* defined in /usr/include/getopt.h maybe in man getopt */
+static struct option opts[] = {
+       { "u32", 1, 0, '1' },
+       { 0 }
+};
+
+/* shared printing code */
+static void print_u32(struct ipt_u32 *data)
+{
+       unsigned int testind;
+
+       for (testind=0; testind < data->ntests; testind++) {
+               if (testind) printf("&&");
+               {
+                       unsigned int i;
+
+                       printf("0x%x", data->tests[testind].location[0].number);
+                       for (i = 1; i < data->tests[testind].nnums; i++) {
+                               switch (data->tests[testind].location[i].nextop) {
+                               case IPT_U32_AND: printf("&"); break;
+                               case IPT_U32_LEFTSH: printf("<<"); break;
+                               case IPT_U32_RIGHTSH: printf(">>"); break;
+                               case IPT_U32_AT: printf("@"); break;
+                               }
+                               printf("0x%x", data->tests[testind].location[i].number);
+                       }
+                       printf("=");
+                       for (i = 0; i < data->tests[testind].nvalues; i++) {
+                               if (i) printf(",");
+                               if (data->tests[testind].value[i].min
+                                   == data->tests[testind].value[i].max)
+                                       printf("0x%x", data->tests[testind].value[i].min);
+                               else printf("0x%x:0x%x", data->tests[testind].value[i].min,
+                                           data->tests[testind].value[i].max);
+                       }
+               }
+       }
+       printf(" ");
+}
+
+/* string_to_number is not quite what we need here ... */
+u_int32_t parse_number(char **s, int pos)
+{
+       u_int32_t number;
+       char *end;
+       errno = 0;
+
+       number = strtoul(*s, &end, 0);
+       if (end == *s)
+               exit_error(PARAMETER_PROBLEM, 
+                          "u32: at char %d expected number", pos);
+       if (errno)
+               exit_error(PARAMETER_PROBLEM, 
+                          "u32: at char %d error reading number", pos);
+       *s = end;
+       return number;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_match **match)
+{
+       struct ipt_u32 *data = (struct ipt_u32 *)(*match)->data;
+       char *arg = argv[optind-1]; /* the argument string */
+       char *start = arg;
+       int state=0, testind=0, locind=0, valind=0;
+
+       if (c != '1') return 0;
+       /* states: 0 = looking for numbers and operations, 1 = looking for ranges */
+       while (1) { /* read next operand/number or range */
+               while (isspace(*arg)) 
+                       arg++;  /* skip white space */
+               if (! *arg) { /* end of argument found */
+                       if (state == 0)
+                               exit_error(PARAMETER_PROBLEM, 
+                                          "u32: input ended in location spec");
+                       if (valind == 0)
+                               exit_error(PARAMETER_PROBLEM, 
+                                          "u32: test ended with no value spec");
+                       data->tests[testind].nnums = locind;
+                       data->tests[testind].nvalues = valind;
+                       testind++;
+                       data->ntests=testind;
+                       if (testind > U32MAXSIZE)
+                               exit_error(PARAMETER_PROBLEM, 
+                                          "u32: at char %d too many &&'s",
+                                          arg-start);
+                       /* debugging 
+                          print_u32(data);printf("\n");
+                          exit_error(PARAMETER_PROBLEM, "debugging output done"); */
+                       return 1;
+               }
+               if (state == 0) {
+                       /* reading location: read a number if nothing read yet,
+                          otherwise either op number or = to end location spec */       
+                       if (*arg == '=') {
+                               if (locind == 0)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "u32: at char %d location spec missing", arg-start);
+                               else {
+                                       arg++; 
+                                       state=1;
+                               }
+                       }
+                       else {
+                               if (locind) { /* need op before number */
+                                       if (*arg == '&') {
+                                               data->tests[testind].location[locind].nextop = IPT_U32_AND;
+                                       }
+                                       else if (*arg == '<') {
+                                               arg++;
+                                               if (*arg != '<')
+                                                       exit_error(PARAMETER_PROBLEM,
+                                                                  "u32: at char %d a second < expected", arg-start);
+                                               data->tests[testind].location[locind].nextop = IPT_U32_LEFTSH;
+                                       }
+                                       else if (*arg == '>') {
+                                               arg++;
+                                               if (*arg != '>')
+                                                       exit_error(PARAMETER_PROBLEM,
+                                                                  "u32: at char %d a second > expected", arg-start);
+                                               data->tests[testind].location[locind].nextop = IPT_U32_RIGHTSH;
+                                       }
+                                       else if (*arg == '@') {
+                                               data->tests[testind].location[locind].nextop = IPT_U32_AT;
+                                       }
+                                       else exit_error(PARAMETER_PROBLEM,
+                                                       "u32: at char %d operator expected", arg-start);
+                                       arg++;
+                               }
+                               /* now a number; string_to_number skips white space? */
+                               data->tests[testind].location[locind].number =
+                                       parse_number(&arg, arg-start);
+                               locind++;
+                               if (locind > U32MAXSIZE)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "u32: at char %d too many operators", arg-start);
+                       }
+               }
+               else {
+                       /* state 1 - reading values: read a range if nothing read yet,
+                          otherwise either ,range or && to end test spec */
+                       if (*arg == '&') {
+                               arg++;
+                               if (*arg != '&')
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "u32: at char %d a second & expected", arg-start);
+                               if (valind == 0)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "u32: at char %d value spec missing", arg-start);
+                               else {
+                                       data->tests[testind].nnums = locind;
+                                       data->tests[testind].nvalues = valind;
+                                       testind++;
+                                       if (testind > U32MAXSIZE)
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "u32: at char %d too many &&'s", arg-start);
+                                       arg++; state=0; locind=0; valind=0;
+                               }
+                       }
+                       else { /* read value range */
+                               if (valind) { /* need , before number */
+                                       if (*arg != ',')
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "u32: at char %d expected , or &&", arg-start);
+                                       arg++;
+                               }
+                               data->tests[testind].value[valind].min = parse_number(&arg, arg-start);
+                               while (isspace(*arg)) 
+                                       arg++;  /* another place white space could be */
+                               if (*arg==':') {
+                                       arg++;
+                                       data->tests[testind].value[valind].max
+                                               = parse_number(&arg, arg-start);
+                               }
+                               else data->tests[testind].value[valind].max
+                                            = data->tests[testind].value[valind].min;
+                               valind++;
+                               if (valind > U32MAXSIZE)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "u32: at char %d too many ,'s", arg-start);
+                       }
+               }
+       }
+}
+
+/* Final check; must specify something. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       printf("u32 ");
+       print_u32((struct ipt_u32 *)match->data);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       printf("--u32 ");
+       print_u32((struct ipt_u32 *)match->data);
+}
+
+struct iptables_match u32 = {
+       .next           = NULL,
+       .name           = "u32",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_u32)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_u32)),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void
+_init(void)
+{
+       register_match(&u32);
+}
diff --git a/extensions/libipt_u32.man b/extensions/libipt_u32.man
new file mode 100644 (file)
index 0000000..7028bd5
--- /dev/null
@@ -0,0 +1,8 @@
+U32 allows you to extract quantities of up to 4 bytes from a packet,
+AND them with specified masks, shift them by specified amounts and
+test whether the results are in any of a set of specified ranges.
+The specification of what to extract is general enough to skip over
+headers with lengths stored in the packet, as in IP or TCP header
+lengths.
+
+Details and examples are in the kernel module source.
diff --git a/extensions/libipt_udp.c b/extensions/libipt_udp.c
new file mode 100644 (file)
index 0000000..7f461d8
--- /dev/null
@@ -0,0 +1,230 @@
+/* Shared library add-on to iptables to add UDP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"UDP v%s options:\n"
+" --source-port [!] port[:port]\n"
+" --sport ...\n"
+"                              match source port(s)\n"
+" --destination-port [!] port[:port]\n"
+" --dport ...\n"
+"                              match destination port(s)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "source-port", 1, 0, '1' },
+       { "sport", 1, 0, '1' }, /* synonym */
+       { "destination-port", 1, 0, '2' },
+       { "dport", 1, 0, '2' }, /* synonym */
+       {0}
+};
+
+static void
+parse_udp_ports(const char *portstring, u_int16_t *ports)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(portstring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               ports[0] = ports[1] = parse_port(buffer, "udp");
+       else {
+               *cp = '\0';
+               cp++;
+
+               ports[0] = buffer[0] ? parse_port(buffer, "udp") : 0;
+               ports[1] = cp[0] ? parse_port(cp, "udp") : 0xFFFF;
+
+               if (ports[0] > ports[1])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "invalid portrange (min > max)");
+       }
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_udp *udpinfo = (struct ipt_udp *)m->data;
+
+       udpinfo->spts[1] = udpinfo->dpts[1] = 0xFFFF;
+}
+
+#define UDP_SRC_PORTS 0x01
+#define UDP_DST_PORTS 0x02
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_udp *udpinfo = (struct ipt_udp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & UDP_SRC_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--source-port' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_udp_ports(argv[optind-1], udpinfo->spts);
+               if (invert)
+                       udpinfo->invflags |= IPT_UDP_INV_SRCPT;
+               *flags |= UDP_SRC_PORTS;
+               break;
+
+       case '2':
+               if (*flags & UDP_DST_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--destination-port' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_udp_ports(argv[optind-1], udpinfo->dpts);
+               if (invert)
+                       udpinfo->invflags |= IPT_UDP_INV_DSTPT;
+               *flags |= UDP_DST_PORTS;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static char *
+port_to_service(int port)
+{
+       struct servent *service;
+
+       if ((service = getservbyport(htons(port), "udp")))
+               return service->s_name;
+
+       return NULL;
+}
+
+static void
+print_port(u_int16_t port, int numeric)
+{
+       char *service;
+
+       if (numeric || (service = port_to_service(port)) == NULL)
+               printf("%u", port);
+       else
+               printf("%s", service);
+}
+
+static void
+print_ports(const char *name, u_int16_t min, u_int16_t max,
+           int invert, int numeric)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       print_port(min, numeric);
+               } else {
+                       printf("s:%s", inv);
+                       print_port(min, numeric);
+                       printf(":");
+                       print_port(max, numeric);
+               }
+               printf(" ");
+       }
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match, int numeric)
+{
+       const struct ipt_udp *udp = (struct ipt_udp *)match->data;
+
+       printf("udp ");
+       print_ports("spt", udp->spts[0], udp->spts[1],
+                   udp->invflags & IPT_UDP_INV_SRCPT,
+                   numeric);
+       print_ports("dpt", udp->dpts[0], udp->dpts[1],
+                   udp->invflags & IPT_UDP_INV_DSTPT,
+                   numeric);
+       if (udp->invflags & ~IPT_UDP_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      udp->invflags & ~IPT_UDP_INV_MASK);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_udp *udpinfo = (struct ipt_udp *)match->data;
+
+       if (udpinfo->spts[0] != 0
+           || udpinfo->spts[1] != 0xFFFF) {
+               if (udpinfo->invflags & IPT_UDP_INV_SRCPT)
+                       printf("! ");
+               if (udpinfo->spts[0]
+                   != udpinfo->spts[1])
+                       printf("--sport %u:%u ",
+                              udpinfo->spts[0],
+                              udpinfo->spts[1]);
+               else
+                       printf("--sport %u ",
+                              udpinfo->spts[0]);
+       }
+
+       if (udpinfo->dpts[0] != 0
+           || udpinfo->dpts[1] != 0xFFFF) {
+               if (udpinfo->invflags & IPT_UDP_INV_DSTPT)
+                       printf("! ");
+               if (udpinfo->dpts[0]
+                   != udpinfo->dpts[1])
+                       printf("--dport %u:%u ",
+                              udpinfo->dpts[0],
+                              udpinfo->dpts[1]);
+               else
+                       printf("--dport %u ",
+                              udpinfo->dpts[0]);
+       }
+}
+
+static
+struct iptables_match udp = { 
+       .next           = NULL,
+       .name           = "udp",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_udp)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_udp)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
+};
+
+void
+_init(void)
+{
+       register_match(&udp);
+}
diff --git a/extensions/libipt_udp.man b/extensions/libipt_udp.man
new file mode 100644 (file)
index 0000000..1d5e590
--- /dev/null
@@ -0,0 +1,14 @@
+These extensions can be used if `--protocol udp' is specified.  It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification.
+See the description of the
+.B --source-port
+option of the TCP extension for details.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification.
+See the description of the
+.B --destination-port
+option of the TCP extension for details.
diff --git a/extensions/libipt_unclean.c b/extensions/libipt_unclean.c
new file mode 100644 (file)
index 0000000..7b9b3e4
--- /dev/null
@@ -0,0 +1,54 @@
+/* Shared library add-on to iptables for unclean. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"unclean v%s takes no options\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       {0}
+};
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       return 0;
+}
+
+/* Final check; must have specified --mac. */
+static void final_check(unsigned int flags)
+{
+}
+
+static
+struct iptables_match unclean = { 
+       .next           = NULL,
+       .name           = "unclean",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(0),
+       .userspacesize  = IPT_ALIGN(0),
+       .help           = &help,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = NULL,
+       .save           = NULL,
+       .extra_opts     = opts
+};
+
+void _init(void)
+{
+       register_match(&unclean);
+}
diff --git a/extensions/libipt_unclean.man b/extensions/libipt_unclean.man
new file mode 100644 (file)
index 0000000..3fecd55
--- /dev/null
@@ -0,0 +1,2 @@
+This module takes no options, but attempts to match packets which seem
+malformed or unusual.  This is regarded as experimental.
diff --git a/include/ip6tables.h b/include/ip6tables.h
new file mode 100644 (file)
index 0000000..8afe2ce
--- /dev/null
@@ -0,0 +1,181 @@
+#ifndef _IP6TABLES_USER_H
+#define _IP6TABLES_USER_H
+
+#include "iptables_common.h"
+#include "libiptc/libip6tc.h"
+
+#ifndef IP6T_LIB_DIR
+#define IP6T_LIB_DIR "/usr/local/lib/iptables"
+#endif
+
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP 132
+#endif
+#ifndef IPPROTO_DCCP
+#define IPPROTO_DCCP 33
+#endif
+#ifndef IPPROTO_UDPLITE
+#define IPPROTO_UDPLITE 136
+#endif
+
+#ifndef IP6T_SO_GET_REVISION_MATCH /* Old kernel source. */
+#define IP6T_SO_GET_REVISION_MATCH     68
+#define IP6T_SO_GET_REVISION_TARGET    69
+
+struct ip6t_get_revision
+{
+       char name[IP6T_FUNCTION_MAXNAMELEN-1];
+
+       u_int8_t revision;
+};
+#endif /* IP6T_SO_GET_REVISION_MATCH   Old kernel source */
+
+struct ip6tables_rule_match
+{
+       struct ip6tables_rule_match *next;
+
+       struct ip6tables_match *match;
+
+       /* Multiple matches of the same type: the ones before
+          the current one are completed from parsing point of view */  
+       unsigned int completed;
+};
+
+/* Include file for additions: new matches and targets. */
+struct ip6tables_match
+{
+       struct ip6tables_match *next;
+
+       ip6t_chainlabel name;
+
+       /* Revision of match (0 by default). */
+       u_int8_t revision;
+
+       const char *version;
+
+       /* Size of match data. */
+       size_t size;
+
+       /* Size of match data relevent for userspace comparison purposes */
+       size_t userspacesize;
+
+       /* Function which prints out usage message. */
+       void (*help)(void);
+
+       /* Initialize the match. */
+       void (*init)(struct ip6t_entry_match *m, unsigned int *nfcache);
+
+       /* Function which parses command options; returns true if it
+          ate an option */
+       int (*parse)(int c, char **argv, int invert, unsigned int *flags,
+                    const struct ip6t_entry *entry,
+                    unsigned int *nfcache,
+                    struct ip6t_entry_match **match);
+
+       /* Final check; exit if not ok. */
+       void (*final_check)(unsigned int flags);
+
+       /* Prints out the match iff non-NULL: put space at end */
+       void (*print)(const struct ip6t_ip6 *ip,
+                     const struct ip6t_entry_match *match, int numeric);
+
+       /* Saves the union ipt_matchinfo in parsable form to stdout. */
+       void (*save)(const struct ip6t_ip6 *ip,
+                    const struct ip6t_entry_match *match);
+
+       /* Pointer to list of extra command-line options */
+       const struct option *extra_opts;
+
+       /* Ignore these men behind the curtain: */
+       unsigned int option_offset;
+       struct ip6t_entry_match *m;
+       unsigned int mflags;
+#ifdef NO_SHARED_LIBS
+       unsigned int loaded; /* simulate loading so options are merged properly */
+#endif
+};
+
+struct ip6tables_target
+{
+       struct ip6tables_target *next;
+       
+       ip6t_chainlabel name;
+
+       const char *version;
+
+       /* Size of target data. */
+       size_t size;
+
+       /* Size of target data relevent for userspace comparison purposes */
+       size_t userspacesize;
+
+       /* Function which prints out usage message. */
+       void (*help)(void);
+
+       /* Initialize the target. */
+       void (*init)(struct ip6t_entry_target *t, unsigned int *nfcache);
+
+       /* Function which parses command options; returns true if it
+          ate an option */
+       int (*parse)(int c, char **argv, int invert, unsigned int *flags,
+                    const struct ip6t_entry *entry,
+                    struct ip6t_entry_target **target);
+       
+       /* Final check; exit if not ok. */
+       void (*final_check)(unsigned int flags);
+
+       /* Prints out the target iff non-NULL: put space at end */
+       void (*print)(const struct ip6t_ip6 *ip,
+                     const struct ip6t_entry_target *target, int numeric);
+
+       /* Saves the targinfo in parsable form to stdout. */
+       void (*save)(const struct ip6t_ip6 *ip,
+                    const struct ip6t_entry_target *target);
+
+       /* Pointer to list of extra command-line options */
+       struct option *extra_opts;
+
+       /* Ignore these men behind the curtain: */
+       unsigned int option_offset;
+       struct ip6t_entry_target *t;
+       unsigned int tflags;
+       unsigned int used;
+#ifdef NO_SHARED_LIBS
+       unsigned int loaded; /* simulate loading so options are merged properly */
+#endif
+};
+
+extern int line;
+
+/* Your shared library should call one of these. */
+extern void register_match6(struct ip6tables_match *me);
+extern void register_target6(struct ip6tables_target *me);
+
+extern int service_to_port(const char *name, const char *proto);
+extern u_int16_t parse_port(const char *port, const char *proto);
+extern int do_command6(int argc, char *argv[], char **table,
+                      ip6tc_handle_t *handle);
+/* Keeping track of external matches and targets: linked lists. */
+extern struct ip6tables_match *ip6tables_matches;
+extern struct ip6tables_target *ip6tables_targets;
+
+enum ip6t_tryload {
+       DONT_LOAD,
+       DURING_LOAD,
+       TRY_LOAD,
+       LOAD_MUST_SUCCEED
+};
+
+extern struct ip6tables_target *find_target(const char *name, enum ip6t_tryload);
+extern struct ip6tables_match *find_match(const char *name, enum ip6t_tryload, struct ip6tables_rule_match **match);
+
+extern void parse_interface(const char *arg, char *vianame, unsigned char *mask);
+
+extern int for_each_chain(int (*fn)(const ip6t_chainlabel, int, ip6tc_handle_t *), int verbose, int builtinstoo, ip6tc_handle_t *handle);
+extern int flush_entries(const ip6t_chainlabel chain, int verbose, ip6tc_handle_t *handle);
+extern int delete_chain(const ip6t_chainlabel chain, int verbose, ip6tc_handle_t *handle);
+extern int
+ip6tables_insmod(const char *modname, const char *modprobe, int quiet);
+extern int load_ip6tables_ko(const char *modprobe, int quiet);
+
+#endif /*_IP6TABLES_USER_H*/
diff --git a/include/iptables.h b/include/iptables.h
new file mode 100644 (file)
index 0000000..cd51428
--- /dev/null
@@ -0,0 +1,201 @@
+#ifndef _IPTABLES_USER_H
+#define _IPTABLES_USER_H
+
+#include "iptables_common.h"
+#include "libiptc/libiptc.h"
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/local/lib/iptables"
+#endif
+
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP 132
+#endif
+#ifndef IPPROTO_DCCP
+#define IPPROTO_DCCP 33
+#endif
+#ifndef IPPROTO_UDPLITE
+#define IPPROTO_UDPLITE        136
+#endif
+
+#ifndef IPT_SO_GET_REVISION_MATCH /* Old kernel source. */
+#define IPT_SO_GET_REVISION_MATCH      (IPT_BASE_CTL + 2)
+#define IPT_SO_GET_REVISION_TARGET     (IPT_BASE_CTL + 3)
+
+struct ipt_get_revision
+{
+       char name[IPT_FUNCTION_MAXNAMELEN-1];
+
+       u_int8_t revision;
+};
+#endif /* IPT_SO_GET_REVISION_MATCH   Old kernel source */
+
+struct iptables_rule_match
+{
+       struct iptables_rule_match *next;
+
+       struct iptables_match *match;
+
+       /* Multiple matches of the same type: the ones before
+          the current one are completed from parsing point of view */  
+       unsigned int completed;
+};
+
+/* Include file for additions: new matches and targets. */
+struct iptables_match
+{
+       struct iptables_match *next;
+
+       ipt_chainlabel name;
+
+       /* Revision of match (0 by default). */
+       u_int8_t revision;
+
+       const char *version;
+
+       /* Size of match data. */
+       size_t size;
+
+       /* Size of match data relevent for userspace comparison purposes */
+       size_t userspacesize;
+
+       /* Function which prints out usage message. */
+       void (*help)(void);
+
+       /* Initialize the match. */
+       void (*init)(struct ipt_entry_match *m, unsigned int *nfcache);
+
+       /* Function which parses command options; returns true if it
+           ate an option */
+       int (*parse)(int c, char **argv, int invert, unsigned int *flags,
+                    const struct ipt_entry *entry,
+                    unsigned int *nfcache,
+                    struct ipt_entry_match **match);
+
+       /* Final check; exit if not ok. */
+       void (*final_check)(unsigned int flags);
+
+       /* Prints out the match iff non-NULL: put space at end */
+       void (*print)(const struct ipt_ip *ip,
+                     const struct ipt_entry_match *match, int numeric);
+
+       /* Saves the match info in parsable form to stdout. */
+       void (*save)(const struct ipt_ip *ip,
+                    const struct ipt_entry_match *match);
+
+       /* Pointer to list of extra command-line options */
+       const struct option *extra_opts;
+
+       /* Ignore these men behind the curtain: */
+       unsigned int option_offset;
+       struct ipt_entry_match *m;
+       unsigned int mflags;
+#ifdef NO_SHARED_LIBS
+       unsigned int loaded; /* simulate loading so options are merged properly */
+#endif
+};
+
+struct iptables_target
+{
+       struct iptables_target *next;
+
+       ipt_chainlabel name;
+
+       /* Revision of target (0 by default). */
+       u_int8_t revision;
+
+       const char *version;
+
+       /* Size of target data. */
+       size_t size;
+
+       /* Size of target data relevent for userspace comparison purposes */
+       size_t userspacesize;
+
+       /* Function which prints out usage message. */
+       void (*help)(void);
+
+       /* Initialize the target. */
+       void (*init)(struct ipt_entry_target *t, unsigned int *nfcache);
+
+       /* Function which parses command options; returns true if it
+           ate an option */
+       int (*parse)(int c, char **argv, int invert, unsigned int *flags,
+                    const struct ipt_entry *entry,
+                    struct ipt_entry_target **target);
+
+       /* Final check; exit if not ok. */
+       void (*final_check)(unsigned int flags);
+
+       /* Prints out the target iff non-NULL: put space at end */
+       void (*print)(const struct ipt_ip *ip,
+                     const struct ipt_entry_target *target, int numeric);
+
+       /* Saves the targinfo in parsable form to stdout. */
+       void (*save)(const struct ipt_ip *ip,
+                    const struct ipt_entry_target *target);
+
+       /* Pointer to list of extra command-line options */
+       struct option *extra_opts;
+
+       /* Ignore these men behind the curtain: */
+       unsigned int option_offset;
+       struct ipt_entry_target *t;
+       unsigned int tflags;
+       unsigned int used;
+#ifdef NO_SHARED_LIBS
+       unsigned int loaded; /* simulate loading so options are merged properly */
+#endif
+};
+
+extern int line;
+
+/* Your shared library should call one of these. */
+extern void register_match(struct iptables_match *me);
+extern void register_target(struct iptables_target *me);
+
+extern int service_to_port(const char *name, const char *proto);
+extern u_int16_t parse_port(const char *port, const char *proto);
+extern struct in_addr *dotted_to_addr(const char *dotted);
+extern struct in_addr *dotted_to_mask(const char *dotted);
+extern char *addr_to_dotted(const struct in_addr *addrp);
+extern char *addr_to_anyname(const struct in_addr *addr);
+extern char *mask_to_dotted(const struct in_addr *mask);
+
+extern void parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
+                      struct in_addr *maskp, unsigned int *naddrs);
+extern u_int16_t parse_protocol(const char *s);
+extern void parse_interface(const char *arg, char *vianame, unsigned char *mask);
+
+extern int do_command(int argc, char *argv[], char **table,
+                     iptc_handle_t *handle);
+/* Keeping track of external matches and targets: linked lists.  */
+extern struct iptables_match *iptables_matches;
+extern struct iptables_target *iptables_targets;
+
+enum ipt_tryload {
+       DONT_LOAD,
+       DURING_LOAD,
+       TRY_LOAD,
+       LOAD_MUST_SUCCEED
+};
+
+extern struct iptables_target *find_target(const char *name, enum ipt_tryload);
+extern struct iptables_match *find_match(const char *name, enum ipt_tryload, struct iptables_rule_match **match);
+
+extern int delete_chain(const ipt_chainlabel chain, int verbose,
+                       iptc_handle_t *handle);
+extern int flush_entries(const ipt_chainlabel chain, int verbose, 
+                       iptc_handle_t *handle);
+extern int for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *),
+               int verbose, int builtinstoo, iptc_handle_t *handle);
+
+/* kernel revision handling */
+extern int kernel_version;
+extern void get_kernel_version(void);
+#define LINUX_VERSION(x,y,z)   (0x10000*(x) + 0x100*(y) + z)
+#define LINUX_VERSION_MAJOR(x) (((x)>>16) & 0xFF)
+#define LINUX_VERSION_MINOR(x) (((x)>> 8) & 0xFF)
+#define LINUX_VERSION_PATCH(x) ( (x)      & 0xFF)
+
+#endif /*_IPTABLES_USER_H*/
diff --git a/include/iptables_common.h b/include/iptables_common.h
new file mode 100644 (file)
index 0000000..3b29327
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef _IPTABLES_COMMON_H
+#define _IPTABLES_COMMON_H
+/* Shared definitions between ipv4 and ipv6. */
+
+enum exittype {
+       OTHER_PROBLEM = 1,
+       PARAMETER_PROBLEM,
+       VERSION_PROBLEM,
+       RESOURCE_PROBLEM
+};
+
+/* this is a special 64bit data type that is 8-byte aligned */
+#define aligned_u64 unsigned long long __attribute__((aligned(8)))
+
+extern void exit_printhelp() __attribute__((noreturn));
+extern void exit_tryhelp(int) __attribute__((noreturn));
+int check_inverse(const char option[], int *invert, int *optind, int argc);
+extern int string_to_number(const char *, 
+                           unsigned int, 
+                           unsigned int,
+                           unsigned int *);
+extern int string_to_number_l(const char *, 
+                           unsigned long int, 
+                           unsigned long int,
+                           unsigned long *);
+extern int string_to_number_ll(const char *, 
+                           unsigned long long int, 
+                           unsigned long long int,
+                           unsigned long long *);
+extern int
+iptables_insmod(const char *modname, const char *modprobe, int quiet);
+extern int load_iptables_ko(const char *modprobe, int quiet);
+void exit_error(enum exittype, char *, ...)__attribute__((noreturn,
+                                                         format(printf,2,3)));
+extern const char *program_name, *program_version;
+extern char *lib_dir;
+
+#define _init __attribute__((constructor)) my_init
+#ifdef NO_SHARED_LIBS
+# ifdef _INIT
+#  undef _init
+#  define _init _INIT
+# endif
+  extern void init_extensions(void);
+#endif
+
+#define __be32 u_int32_t
+#define __le32 u_int32_t
+#define __be16 u_int16_t
+#define __le16 u_int16_t
+
+#endif /*_IPTABLES_COMMON_H*/
diff --git a/include/libipq/ip_queue_64.h b/include/libipq/ip_queue_64.h
new file mode 100644 (file)
index 0000000..b0c3222
--- /dev/null
@@ -0,0 +1,62 @@
+/* Redefine everything for a 64-bit kernel on 32-bit userspace */
+
+/* Based on ip_queue.h by (C) 2000 James Morris, this code is GPL. */
+#ifndef _IP_QUEUE_H
+#define _IP_QUEUE_H
+
+#include <net/if.h>
+#include <sys/types.h>
+
+/* Messages sent from kernel */
+typedef struct ipq_packet_msg {
+       u_int64_t packet_id;    /* ID of queued packet */
+       u_int64_t mark;         /* Netfilter mark value */
+       int64_t timestamp_sec;  /* Packet arrival time (seconds) */
+       int64_t timestamp_usec; /* Packet arrvial time (+useconds) */
+       unsigned int hook;              /* Netfilter hook we rode in on */
+       char indev_name[IFNAMSIZ];      /* Name of incoming interface */
+       char outdev_name[IFNAMSIZ];     /* Name of outgoing interface */
+       unsigned short hw_protocol;     /* Hardware protocol (network order) */
+       unsigned short hw_type;         /* Hardware type */
+       unsigned char hw_addrlen;       /* Hardware address length */
+       unsigned char hw_addr[8];       /* Hardware address */
+       u_int64_t data_len;             /* Length of packet data */
+       unsigned char payload[0];       /* Optional packet data */
+} ipq_packet_msg_t;
+
+/* Messages sent from userspace */
+typedef struct ipq_mode_msg {
+       unsigned char value;            /* Requested mode */
+       u_int64_t range;                /* Optional range of packet requested */
+} ipq_mode_msg_t;
+
+typedef struct ipq_verdict_msg {
+       unsigned int value;             /* Verdict to hand to netfilter */
+       u_int64_t id;           /* Packet ID for this verdict */
+       u_int64_t data_len;             /* Length of replacement data */
+       unsigned char payload[0];       /* Optional replacement packet */
+} ipq_verdict_msg_t;
+
+typedef struct ipq_peer_msg {
+       union {
+               ipq_verdict_msg_t verdict;
+               ipq_mode_msg_t mode;
+       } msg;
+} ipq_peer_msg_t;
+
+/* Packet delivery modes */
+enum {
+       IPQ_COPY_NONE,          /* Initial mode, packets are dropped */
+       IPQ_COPY_META,          /* Copy metadata */
+       IPQ_COPY_PACKET         /* Copy metadata + packet (range) */
+};
+#define IPQ_COPY_MAX IPQ_COPY_PACKET
+
+/* Types of messages */
+#define IPQM_BASE      0x10    /* standard netlink messages below this */
+#define IPQM_MODE      (IPQM_BASE + 1)         /* Mode request from peer */
+#define IPQM_VERDICT   (IPQM_BASE + 2)         /* Verdict from peer */ 
+#define IPQM_PACKET    (IPQM_BASE + 3)         /* Packet from kernel */
+#define IPQM_MAX       (IPQM_BASE + 4)
+
+#endif /*_IP_QUEUE_H*/
diff --git a/include/libipq/libipq.h b/include/libipq/libipq.h
new file mode 100644 (file)
index 0000000..f60fc4b
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * libipq.h
+ *
+ * IPQ library for userspace.
+ *
+ * Author: James Morris <jmorris@intercode.com.au>
+ *
+ * Copyright (c) 2000-2001 Netfilter Core Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _LIBIPQ_H
+#define _LIBIPQ_H
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+
+#ifdef KERNEL_64_USERSPACE_32
+#include "ip_queue_64.h"
+typedef u_int64_t ipq_id_t;
+#else
+#include <linux/netfilter_ipv4/ip_queue.h>
+typedef unsigned long ipq_id_t;
+#endif
+
+#ifdef DEBUG_LIBIPQ
+#include <stdio.h>
+#define LDEBUG(x...) fprintf(stderr, ## x)
+#else
+#define LDEBUG(x...)
+#endif /* DEBUG_LIBIPQ */
+
+/* FIXME: glibc sucks */
+#ifndef MSG_TRUNC
+#define MSG_TRUNC 0x20
+#endif
+
+struct ipq_handle
+{
+       int fd;
+       u_int8_t blocking;
+       struct sockaddr_nl local;
+       struct sockaddr_nl peer;
+};
+
+struct ipq_handle *ipq_create_handle(u_int32_t flags, u_int32_t protocol);
+
+int ipq_destroy_handle(struct ipq_handle *h);
+
+ssize_t ipq_read(const struct ipq_handle *h,
+                unsigned char *buf, size_t len, int timeout);
+
+int ipq_set_mode(const struct ipq_handle *h, u_int8_t mode, size_t len);
+
+ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf);
+
+int ipq_message_type(const unsigned char *buf);
+
+int ipq_get_msgerr(const unsigned char *buf);
+
+int ipq_set_verdict(const struct ipq_handle *h,
+                    ipq_id_t id,
+                    unsigned int verdict,
+                    size_t data_len,
+                    unsigned char *buf);
+
+int ipq_ctl(const struct ipq_handle *h, int request, ...);
+
+char *ipq_errstr(void);
+void ipq_perror(const char *s);
+
+#endif /* _LIBIPQ_H */
+
diff --git a/include/libiptc/ipt_kernel_headers.h b/include/libiptc/ipt_kernel_headers.h
new file mode 100644 (file)
index 0000000..18861fe
--- /dev/null
@@ -0,0 +1,27 @@
+/* This is the userspace/kernel interface for Generic IP Chains,
+   required for libc6. */
+#ifndef _FWCHAINS_KERNEL_HEADERS_H
+#define _FWCHAINS_KERNEL_HEADERS_H
+
+#include <limits.h>
+
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <netinet/ip.h>
+#include <netinet/in.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <sys/types.h>
+#else /* libc5 */
+#include <sys/socket.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/icmp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/types.h>
+#include <linux/in6.h>
+#endif
+#endif
diff --git a/include/libiptc/libip6tc.h b/include/libiptc/libip6tc.h
new file mode 100644 (file)
index 0000000..7a247c4
--- /dev/null
@@ -0,0 +1,154 @@
+#ifndef _LIBIP6TC_H
+#define _LIBIP6TC_H
+/* Library which manipulates firewall rules. Version 0.2. */
+
+#include <libiptc/ipt_kernel_headers.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+#ifndef IP6T_MIN_ALIGN
+#define IP6T_MIN_ALIGN (__alignof__(struct ip6t_entry))
+#endif
+#define IP6T_ALIGN(s) (((s) + (IP6T_MIN_ALIGN-1)) & ~(IP6T_MIN_ALIGN-1))
+
+typedef char ip6t_chainlabel[32];
+
+#define IP6TC_LABEL_ACCEPT "ACCEPT"
+#define IP6TC_LABEL_DROP "DROP"
+#define IP6TC_LABEL_QUEUE   "QUEUE"
+#define IP6TC_LABEL_RETURN "RETURN"
+
+/* Transparent handle type. */
+typedef struct ip6tc_handle *ip6tc_handle_t;
+
+/* Does this chain exist? */
+int ip6tc_is_chain(const char *chain, const ip6tc_handle_t handle);
+
+/* Take a snapshot of the rules. Returns NULL on error. */
+ip6tc_handle_t ip6tc_init(const char *tablename);
+
+/* Cleanup after ip6tc_init(). */
+void ip6tc_free(ip6tc_handle_t *h);
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *ip6tc_first_chain(ip6tc_handle_t *handle);
+const char *ip6tc_next_chain(ip6tc_handle_t *handle);
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const struct ip6t_entry *ip6tc_first_rule(const char *chain,
+                                         ip6tc_handle_t *handle);
+
+/* Returns NULL when rules run out. */
+const struct ip6t_entry *ip6tc_next_rule(const struct ip6t_entry *prev,
+                                        ip6tc_handle_t *handle);
+
+/* Returns a pointer to the target name of this position. */
+const char *ip6tc_get_target(const struct ip6t_entry *e,
+                            ip6tc_handle_t *handle);
+
+/* Is this a built-in chain? */
+int ip6tc_builtin(const char *chain, const ip6tc_handle_t handle);
+
+/* Get the policy of a given built-in chain */
+const char *ip6tc_get_policy(const char *chain,
+                            struct ip6t_counters *counters,
+                            ip6tc_handle_t *handle);
+
+/* These functions return TRUE for OK or 0 and set errno. If errno ==
+   0, it means there was a version error (ie. upgrade libiptc). */
+/* Rule numbers start at 1 for the first rule. */
+
+/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
+int ip6tc_insert_entry(const ip6t_chainlabel chain,
+                      const struct ip6t_entry *e,
+                      unsigned int rulenum,
+                      ip6tc_handle_t *handle);
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int ip6tc_replace_entry(const ip6t_chainlabel chain,
+                       const struct ip6t_entry *e,
+                       unsigned int rulenum,
+                       ip6tc_handle_t *handle);
+
+/* Append entry `fw' to chain `chain'. Equivalent to insert with
+   rulenum = length of chain. */
+int ip6tc_append_entry(const ip6t_chainlabel chain,
+                      const struct ip6t_entry *e,
+                      ip6tc_handle_t *handle);
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int ip6tc_delete_entry(const ip6t_chainlabel chain,
+                      const struct ip6t_entry *origfw,
+                      unsigned char *matchmask,
+                      ip6tc_handle_t *handle);
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int ip6tc_delete_num_entry(const ip6t_chainlabel chain,
+                          unsigned int rulenum,
+                          ip6tc_handle_t *handle);
+
+/* Check the packet `fw' on chain `chain'. Returns the verdict, or
+   NULL and sets errno. */
+const char *ip6tc_check_packet(const ip6t_chainlabel chain,
+                              struct ip6t_entry *,
+                              ip6tc_handle_t *handle);
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int ip6tc_flush_entries(const ip6t_chainlabel chain,
+                       ip6tc_handle_t *handle);
+
+/* Zeroes the counters in a chain. */
+int ip6tc_zero_entries(const ip6t_chainlabel chain,
+                      ip6tc_handle_t *handle);
+
+/* Creates a new chain. */
+int ip6tc_create_chain(const ip6t_chainlabel chain,
+                      ip6tc_handle_t *handle);
+
+/* Deletes a chain. */
+int ip6tc_delete_chain(const ip6t_chainlabel chain,
+                      ip6tc_handle_t *handle);
+
+/* Renames a chain. */
+int ip6tc_rename_chain(const ip6t_chainlabel oldname,
+                      const ip6t_chainlabel newname,
+                      ip6tc_handle_t *handle);
+
+/* Sets the policy on a built-in chain. */
+int ip6tc_set_policy(const ip6t_chainlabel chain,
+                    const ip6t_chainlabel policy,
+                    struct ip6t_counters *counters,
+                    ip6tc_handle_t *handle);
+
+/* Get the number of references to this chain */
+int ip6tc_get_references(unsigned int *ref, const ip6t_chainlabel chain,
+                        ip6tc_handle_t *handle);
+
+/* read packet and byte counters for a specific rule */
+struct ip6t_counters *ip6tc_read_counter(const ip6t_chainlabel chain,
+                                       unsigned int rulenum,
+                                       ip6tc_handle_t *handle);
+
+/* zero packet and byte counters for a specific rule */
+int ip6tc_zero_counter(const ip6t_chainlabel chain,
+                      unsigned int rulenum,
+                      ip6tc_handle_t *handle);
+
+/* set packet and byte counters for a specific rule */
+int ip6tc_set_counter(const ip6t_chainlabel chain,
+                     unsigned int rulenum,
+                     struct ip6t_counters *counters,
+                     ip6tc_handle_t *handle);
+
+/* Makes the actual changes. */
+int ip6tc_commit(ip6tc_handle_t *handle);
+
+/* Get raw socket. */
+int ip6tc_get_raw_socket();
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *ip6tc_strerror(int err);
+
+/* Return prefix length, or -1 if not contiguous */
+int ipv6_prefix_length(const struct in6_addr *a);
+
+#endif /* _LIBIP6TC_H */
diff --git a/include/libiptc/libiptc.h b/include/libiptc/libiptc.h
new file mode 100644 (file)
index 0000000..50765d9
--- /dev/null
@@ -0,0 +1,166 @@
+#ifndef _LIBIPTC_H
+#define _LIBIPTC_H
+/* Library which manipulates filtering rules. */
+
+#include <libiptc/ipt_kernel_headers.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef IPT_MIN_ALIGN
+/* ipt_entry has pointers and u_int64_t's in it, so if you align to
+   it, you'll also align to any crazy matches and targets someone
+   might write */
+#define IPT_MIN_ALIGN (__alignof__(struct ipt_entry))
+#endif
+
+#define IPT_ALIGN(s) (((s) + ((IPT_MIN_ALIGN)-1)) & ~((IPT_MIN_ALIGN)-1))
+
+typedef char ipt_chainlabel[32];
+
+#define IPTC_LABEL_ACCEPT  "ACCEPT"
+#define IPTC_LABEL_DROP    "DROP"
+#define IPTC_LABEL_QUEUE   "QUEUE"
+#define IPTC_LABEL_RETURN  "RETURN"
+
+/* Transparent handle type. */
+typedef struct iptc_handle *iptc_handle_t;
+
+/* Does this chain exist? */
+int iptc_is_chain(const char *chain, const iptc_handle_t handle);
+
+/* Take a snapshot of the rules.  Returns NULL on error. */
+iptc_handle_t iptc_init(const char *tablename);
+
+/* Cleanup after iptc_init(). */
+void iptc_free(iptc_handle_t *h);
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *iptc_first_chain(iptc_handle_t *handle);
+const char *iptc_next_chain(iptc_handle_t *handle);
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const struct ipt_entry *iptc_first_rule(const char *chain,
+                                       iptc_handle_t *handle);
+
+/* Returns NULL when rules run out. */
+const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev,
+                                      iptc_handle_t *handle);
+
+/* Returns a pointer to the target name of this entry. */
+const char *iptc_get_target(const struct ipt_entry *e,
+                           iptc_handle_t *handle);
+
+/* Is this a built-in chain? */
+int iptc_builtin(const char *chain, const iptc_handle_t handle);
+
+/* Get the policy of a given built-in chain */
+const char *iptc_get_policy(const char *chain,
+                           struct ipt_counters *counter,
+                           iptc_handle_t *handle);
+
+/* These functions return TRUE for OK or 0 and set errno.  If errno ==
+   0, it means there was a version error (ie. upgrade libiptc). */
+/* Rule numbers start at 1 for the first rule. */
+
+/* Insert the entry `e' in chain `chain' into position `rulenum'. */
+int iptc_insert_entry(const ipt_chainlabel chain,
+                     const struct ipt_entry *e,
+                     unsigned int rulenum,
+                     iptc_handle_t *handle);
+
+/* Atomically replace rule `rulenum' in `chain' with `e'. */
+int iptc_replace_entry(const ipt_chainlabel chain,
+                      const struct ipt_entry *e,
+                      unsigned int rulenum,
+                      iptc_handle_t *handle);
+
+/* Append entry `e' to chain `chain'.  Equivalent to insert with
+   rulenum = length of chain. */
+int iptc_append_entry(const ipt_chainlabel chain,
+                     const struct ipt_entry *e,
+                     iptc_handle_t *handle);
+
+/* Delete the first rule in `chain' which matches `e', subject to
+   matchmask (array of length == origfw) */
+int iptc_delete_entry(const ipt_chainlabel chain,
+                     const struct ipt_entry *origfw,
+                     unsigned char *matchmask,
+                     iptc_handle_t *handle);
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int iptc_delete_num_entry(const ipt_chainlabel chain,
+                         unsigned int rulenum,
+                         iptc_handle_t *handle);
+
+/* Check the packet `e' on chain `chain'.  Returns the verdict, or
+   NULL and sets errno. */
+const char *iptc_check_packet(const ipt_chainlabel chain,
+                             struct ipt_entry *entry,
+                             iptc_handle_t *handle);
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int iptc_flush_entries(const ipt_chainlabel chain,
+                      iptc_handle_t *handle);
+
+/* Zeroes the counters in a chain. */
+int iptc_zero_entries(const ipt_chainlabel chain,
+                     iptc_handle_t *handle);
+
+/* Creates a new chain. */
+int iptc_create_chain(const ipt_chainlabel chain,
+                     iptc_handle_t *handle);
+
+/* Deletes a chain. */
+int iptc_delete_chain(const ipt_chainlabel chain,
+                     iptc_handle_t *handle);
+
+/* Renames a chain. */
+int iptc_rename_chain(const ipt_chainlabel oldname,
+                     const ipt_chainlabel newname,
+                     iptc_handle_t *handle);
+
+/* Sets the policy on a built-in chain. */
+int iptc_set_policy(const ipt_chainlabel chain,
+                   const ipt_chainlabel policy,
+                   struct ipt_counters *counters,
+                   iptc_handle_t *handle);
+
+/* Get the number of references to this chain */
+int iptc_get_references(unsigned int *ref,
+                       const ipt_chainlabel chain,
+                       iptc_handle_t *handle);
+
+/* read packet and byte counters for a specific rule */
+struct ipt_counters *iptc_read_counter(const ipt_chainlabel chain,
+                                      unsigned int rulenum,
+                                      iptc_handle_t *handle);
+
+/* zero packet and byte counters for a specific rule */
+int iptc_zero_counter(const ipt_chainlabel chain,
+                     unsigned int rulenum,
+                     iptc_handle_t *handle);
+
+/* set packet and byte counters for a specific rule */
+int iptc_set_counter(const ipt_chainlabel chain,
+                    unsigned int rulenum,
+                    struct ipt_counters *counters,
+                    iptc_handle_t *handle);
+
+/* Makes the actual changes. */
+int iptc_commit(iptc_handle_t *handle);
+
+/* Get raw socket. */
+int iptc_get_raw_socket();
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *iptc_strerror(int err);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _LIBIPTC_H */
diff --git a/include/libipulog/libipulog.h b/include/libipulog/libipulog.h
new file mode 100644 (file)
index 0000000..3f4cc2c
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _LIBIPULOG_H
+#define _LIBIPULOG_H
+
+/* libipulog.h,v 1.3 2001/05/21 19:15:16 laforge Exp */
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <net/if.h>
+#include <linux/netfilter_ipv4/ipt_ULOG.h>
+
+/* FIXME: glibc sucks */
+#ifndef MSG_TRUNC 
+#define MSG_TRUNC      0x20
+#endif
+
+struct ipulog_handle;
+
+u_int32_t ipulog_group2gmask(u_int32_t group);
+
+struct ipulog_handle *ipulog_create_handle(u_int32_t gmask);
+
+void ipulog_destroy_handle(struct ipulog_handle *h);
+
+ssize_t ipulog_read(struct ipulog_handle *h,
+                   unsigned char *buf, size_t len, int timeout);
+
+ulog_packet_msg_t *ipulog_get_packet(struct ipulog_handle *h,
+                                    const unsigned char *buf,
+                                    size_t len);
+
+void ipulog_perror(const char *s);
+
+#endif /* _LIBULOG_H */
diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h
new file mode 100644 (file)
index 0000000..3b452a6
--- /dev/null
@@ -0,0 +1,135 @@
+#ifndef _NF_CONNTRACK_COMMON_H
+#define _NF_CONNTRACK_COMMON_H
+/* Connection state tracking for netfilter.  This is separated from,
+   but required by, the NAT layer; it can also be used by an iptables
+   extension. */
+enum ip_conntrack_info
+{
+       /* Part of an established connection (either direction). */
+       IP_CT_ESTABLISHED,
+
+       /* Like NEW, but related to an existing connection, or ICMP error
+          (in either direction). */
+       IP_CT_RELATED,
+
+       /* Started a new connection to track (only
+           IP_CT_DIR_ORIGINAL); may be a retransmission. */
+       IP_CT_NEW,
+
+       /* >= this indicates reply direction */
+       IP_CT_IS_REPLY,
+
+       /* Number of distinct IP_CT types (no NEW in reply dirn). */
+       IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1
+};
+
+/* Bitset representing status of connection. */
+enum ip_conntrack_status {
+       /* It's an expected connection: bit 0 set.  This bit never changed */
+       IPS_EXPECTED_BIT = 0,
+       IPS_EXPECTED = (1 << IPS_EXPECTED_BIT),
+
+       /* We've seen packets both ways: bit 1 set.  Can be set, not unset. */
+       IPS_SEEN_REPLY_BIT = 1,
+       IPS_SEEN_REPLY = (1 << IPS_SEEN_REPLY_BIT),
+
+       /* Conntrack should never be early-expired. */
+       IPS_ASSURED_BIT = 2,
+       IPS_ASSURED = (1 << IPS_ASSURED_BIT),
+
+       /* Connection is confirmed: originating packet has left box */
+       IPS_CONFIRMED_BIT = 3,
+       IPS_CONFIRMED = (1 << IPS_CONFIRMED_BIT),
+
+       /* Connection needs src nat in orig dir.  This bit never changed. */
+       IPS_SRC_NAT_BIT = 4,
+       IPS_SRC_NAT = (1 << IPS_SRC_NAT_BIT),
+
+       /* Connection needs dst nat in orig dir.  This bit never changed. */
+       IPS_DST_NAT_BIT = 5,
+       IPS_DST_NAT = (1 << IPS_DST_NAT_BIT),
+
+       /* Both together. */
+       IPS_NAT_MASK = (IPS_DST_NAT | IPS_SRC_NAT),
+
+       /* Connection needs TCP sequence adjusted. */
+       IPS_SEQ_ADJUST_BIT = 6,
+       IPS_SEQ_ADJUST = (1 << IPS_SEQ_ADJUST_BIT),
+
+       /* NAT initialization bits. */
+       IPS_SRC_NAT_DONE_BIT = 7,
+       IPS_SRC_NAT_DONE = (1 << IPS_SRC_NAT_DONE_BIT),
+
+       IPS_DST_NAT_DONE_BIT = 8,
+       IPS_DST_NAT_DONE = (1 << IPS_DST_NAT_DONE_BIT),
+
+       /* Both together */
+       IPS_NAT_DONE_MASK = (IPS_DST_NAT_DONE | IPS_SRC_NAT_DONE),
+
+       /* Connection is dying (removed from lists), can not be unset. */
+       IPS_DYING_BIT = 9,
+       IPS_DYING = (1 << IPS_DYING_BIT),
+
+       /* Connection has fixed timeout. */
+       IPS_FIXED_TIMEOUT_BIT = 10,
+       IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT),
+};
+
+/* Connection tracking event bits */
+enum ip_conntrack_events
+{
+       /* New conntrack */
+       IPCT_NEW_BIT = 0,
+       IPCT_NEW = (1 << IPCT_NEW_BIT),
+
+       /* Expected connection */
+       IPCT_RELATED_BIT = 1,
+       IPCT_RELATED = (1 << IPCT_RELATED_BIT),
+
+       /* Destroyed conntrack */
+       IPCT_DESTROY_BIT = 2,
+       IPCT_DESTROY = (1 << IPCT_DESTROY_BIT),
+
+       /* Timer has been refreshed */
+       IPCT_REFRESH_BIT = 3,
+       IPCT_REFRESH = (1 << IPCT_REFRESH_BIT),
+
+       /* Status has changed */
+       IPCT_STATUS_BIT = 4,
+       IPCT_STATUS = (1 << IPCT_STATUS_BIT),
+
+       /* Update of protocol info */
+       IPCT_PROTOINFO_BIT = 5,
+       IPCT_PROTOINFO = (1 << IPCT_PROTOINFO_BIT),
+
+       /* Volatile protocol info */
+       IPCT_PROTOINFO_VOLATILE_BIT = 6,
+       IPCT_PROTOINFO_VOLATILE = (1 << IPCT_PROTOINFO_VOLATILE_BIT),
+
+       /* New helper for conntrack */
+       IPCT_HELPER_BIT = 7,
+       IPCT_HELPER = (1 << IPCT_HELPER_BIT),
+
+       /* Update of helper info */
+       IPCT_HELPINFO_BIT = 8,
+       IPCT_HELPINFO = (1 << IPCT_HELPINFO_BIT),
+
+       /* Volatile helper info */
+       IPCT_HELPINFO_VOLATILE_BIT = 9,
+       IPCT_HELPINFO_VOLATILE = (1 << IPCT_HELPINFO_VOLATILE_BIT),
+
+       /* NAT info */
+       IPCT_NATINFO_BIT = 10,
+       IPCT_NATINFO = (1 << IPCT_NATINFO_BIT),
+
+       /* Counter highest bit has been set */
+       IPCT_COUNTER_FILLING_BIT = 11,
+       IPCT_COUNTER_FILLING = (1 << IPCT_COUNTER_FILLING_BIT),
+};
+
+enum ip_conntrack_expect_events {
+       IPEXP_NEW_BIT = 0,
+       IPEXP_NEW = (1 << IPEXP_NEW_BIT),
+};
+
+#endif /* _NF_CONNTRACK_COMMON_H */
diff --git a/include/linux/netfilter/nf_conntrack_tuple.h b/include/linux/netfilter/nf_conntrack_tuple.h
new file mode 100644 (file)
index 0000000..cd5044e
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Definitions and Declarations for tuple.
+ *
+ * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
+ *     - generalize L3 protocol dependent part.
+ *
+ * Derived from include/linux/netfiter_ipv4/ip_conntrack_tuple.h
+ */
+
+#ifndef _NF_CONNTRACK_TUPLE_H
+#define _NF_CONNTRACK_TUPLE_H
+
+#include <linux/netfilter/nf_conntrack_tuple_common.h>
+
+/* A `tuple' is a structure containing the information to uniquely
+  identify a connection.  ie. if two packets have the same tuple, they
+  are in the same connection; if not, they are not.
+
+  We divide the structure along "manipulatable" and
+  "non-manipulatable" lines, for the benefit of the NAT code.
+*/
+
+#define NF_CT_TUPLE_L3SIZE     4
+
+/* The l3 protocol-specific manipulable parts of the tuple: always in
+   network order! */
+union nf_conntrack_address {
+       u_int32_t all[NF_CT_TUPLE_L3SIZE];
+       __be32 ip;
+       __be32 ip6[4];
+};
+
+/* The protocol-specific manipulable parts of the tuple: always in
+   network order! */
+union nf_conntrack_man_proto
+{
+       /* Add other protocols here. */
+       u_int16_t all;
+
+       struct {
+               __be16 port;
+       } tcp;
+       struct {
+               __be16 port;
+       } udp;
+       struct {
+               __be16 id;
+       } icmp;
+       struct {
+               __be16 port;
+       } sctp;
+       struct {
+               __be16 key;     /* GRE key is 32bit, PPtP only uses 16bit */
+       } gre;
+};
+
+/* The manipulable part of the tuple. */
+struct nf_conntrack_man
+{
+       union nf_conntrack_address u3;
+       union nf_conntrack_man_proto u;
+       /* Layer 3 protocol */
+       u_int16_t l3num;
+};
+
+/* This contains the information to distinguish a connection. */
+struct nf_conntrack_tuple
+{
+       struct nf_conntrack_man src;
+
+       /* These are the parts of the tuple which are fixed. */
+       struct {
+               union nf_conntrack_address u3;
+               union {
+                       /* Add other protocols here. */
+                       u_int16_t all;
+
+                       struct {
+                               __be16 port;
+                       } tcp;
+                       struct {
+                               __be16 port;
+                       } udp;
+                       struct {
+                               u_int8_t type, code;
+                       } icmp;
+                       struct {
+                               __be16 port;
+                       } sctp;
+                       struct {
+                               __be16 key;
+                       } gre;
+               } u;
+
+               /* The protocol. */
+               u_int8_t protonum;
+
+               /* The direction (for tuplehash) */
+               u_int8_t dir;
+       } dst;
+};
+
+#endif /* _NF_CONNTRACK_TUPLE_H */
diff --git a/include/linux/netfilter/nf_conntrack_tuple_common.h b/include/linux/netfilter/nf_conntrack_tuple_common.h
new file mode 100644 (file)
index 0000000..8e145f0
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _NF_CONNTRACK_TUPLE_COMMON_H
+#define _NF_CONNTRACK_TUPLE_COMMON_H
+
+enum ip_conntrack_dir
+{
+       IP_CT_DIR_ORIGINAL,
+       IP_CT_DIR_REPLY,
+       IP_CT_DIR_MAX
+};
+
+#define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL)
+
+#endif /* _NF_CONNTRACK_TUPLE_COMMON_H */
diff --git a/include/linux/netfilter/nf_nat.h b/include/linux/netfilter/nf_nat.h
new file mode 100644 (file)
index 0000000..5d3b5e0
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef _NF_NAT_H
+#define _NF_NAT_H
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter/nf_conntrack_tuple.h>
+
+#define NF_NAT_MAPPING_TYPE_MAX_NAMELEN 16
+
+enum nf_nat_manip_type
+{
+       IP_NAT_MANIP_SRC,
+       IP_NAT_MANIP_DST
+};
+
+/* SRC manip occurs POST_ROUTING or LOCAL_IN */
+#define HOOK2MANIP(hooknum) ((hooknum) != NF_IP_POST_ROUTING && (hooknum) != NF_IP_LOCAL_IN)
+
+#define IP_NAT_RANGE_MAP_IPS 1
+#define IP_NAT_RANGE_PROTO_SPECIFIED 2
+#define IP_NAT_RANGE_PROTO_RANDOM 4
+
+/* Single range specification. */
+struct nf_nat_range
+{
+       /* Set to OR of flags above. */
+       unsigned int flags;
+
+       /* Inclusive: network order. */
+       __be32 min_ip, max_ip;
+
+       /* Inclusive: network order */
+       union nf_conntrack_man_proto min, max;
+};
+
+/* For backwards compat: don't use in modern code. */
+struct nf_nat_multi_range_compat
+{
+       unsigned int rangesize; /* Must be 1. */
+
+       /* hangs off end. */
+       struct nf_nat_range range[1];
+};
+
+#define ip_nat_range nf_nat_range
+#define ip_nat_multi_range nf_nat_multi_range_compat
+#endif
diff --git a/include/linux/netfilter_ipv4/ipt_CLASSIFY.h b/include/linux/netfilter_ipv4/ipt_CLASSIFY.h
new file mode 100644 (file)
index 0000000..eab9bda
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _IPT_CLASSIFY_H
+#define _IPT_CLASSIFY_H
+
+struct ipt_classify_target_info {
+       u_int32_t priority;
+       u_int8_t add_mark;
+};
+
+#endif /*_IPT_CLASSIFY_H */
diff --git a/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h b/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h
new file mode 100644 (file)
index 0000000..6f76060
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _IPT_CLUSTERIP_H_target
+#define _IPT_CLUSTERIP_H_target
+
+enum clusterip_hashmode {
+    CLUSTERIP_HASHMODE_SIP = 0,
+    CLUSTERIP_HASHMODE_SIP_SPT,
+    CLUSTERIP_HASHMODE_SIP_SPT_DPT,
+};
+
+#define CLUSTERIP_HASHMODE_MAX CLUSTERIP_HASHMODE_SIP_SPT_DPT
+
+#define CLUSTERIP_MAX_NODES 16
+
+#define CLUSTERIP_FLAG_NEW 0x00000001
+
+struct clusterip_config;
+
+struct ipt_clusterip_tgt_info {
+
+       u_int32_t flags;
+       
+       /* only relevant for new ones */
+       u_int8_t clustermac[6];
+       u_int16_t num_total_nodes;
+       u_int16_t num_local_nodes;
+       u_int16_t local_nodes[CLUSTERIP_MAX_NODES];
+       enum clusterip_hashmode hash_mode;
+       u_int32_t hash_initval;
+       
+#ifdef KERNEL_64_USERSPACE_32
+       u_int64_t config;
+#else
+       struct clusterip_config *config;
+#endif
+};
+
+#endif /*_IPT_CLUSTERIP_H_target*/
diff --git a/include/linux/netfilter_ipv4/ipt_CONNMARK.h b/include/linux/netfilter_ipv4/ipt_CONNMARK.h
new file mode 100644 (file)
index 0000000..0148539
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef _IPT_CONNMARK_H_target
+#define _IPT_CONNMARK_H_target
+
+/* Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+enum {
+       IPT_CONNMARK_SET = 0,
+       IPT_CONNMARK_SAVE,
+       IPT_CONNMARK_RESTORE
+};
+
+struct ipt_connmark_target_info {
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long mark;
+       unsigned long long mask;
+#else
+       unsigned long mark;
+       unsigned long mask;
+#endif
+       u_int8_t mode;
+};
+
+#endif /*_IPT_CONNMARK_H_target*/
diff --git a/include/linux/netfilter_ipv4/ipt_DSCP.h b/include/linux/netfilter_ipv4/ipt_DSCP.h
new file mode 100644 (file)
index 0000000..678edee
--- /dev/null
@@ -0,0 +1,20 @@
+/* iptables module for setting the IPv4 DSCP field
+ *
+ * (C) 2002 Harald Welte <laforge@gnumonks.org>
+ * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh <mgm@paktronix.com>
+ * This software is distributed under GNU GPL v2, 1991
+ * 
+ * See RFC2474 for a description of the DSCP field within the IP Header.
+ *
+ * Id: ipt_DSCP.h,v 1.7 2002/03/14 12:03:13 laforge Exp
+*/
+#ifndef _IPT_DSCP_TARGET_H
+#define _IPT_DSCP_TARGET_H
+#include <linux/netfilter_ipv4/ipt_dscp.h>
+
+/* target info */
+struct ipt_DSCP_info {
+       u_int8_t dscp;
+};
+
+#endif /* _IPT_DSCP_TARGET_H */
diff --git a/include/linux/netfilter_ipv4/ipt_ECN.h b/include/linux/netfilter_ipv4/ipt_ECN.h
new file mode 100644 (file)
index 0000000..a711cd1
--- /dev/null
@@ -0,0 +1,31 @@
+/* Header file for iptables ipt_ECN target
+ *
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This software is distributed under GNU GPL v2, 1991
+ * 
+ * Id: ipt_ECN.h,v 1.3 2002/05/29 12:17:40 laforge Exp
+*/
+#ifndef _IPT_ECN_TARGET_H
+#define _IPT_ECN_TARGET_H
+#include <linux/netfilter_ipv4/ipt_DSCP.h>
+
+#define IPT_ECN_IP_MASK        (~IPT_DSCP_MASK)
+
+#define IPT_ECN_OP_SET_IP      0x01    /* set ECN bits of IPv4 header */
+#define IPT_ECN_OP_SET_ECE     0x10    /* set ECE bit of TCP header */
+#define IPT_ECN_OP_SET_CWR     0x20    /* set CWR bit of TCP header */
+
+#define IPT_ECN_OP_MASK                0xce
+
+struct ipt_ECN_info {
+       u_int8_t operation;     /* bitset of operations */
+       u_int8_t ip_ect;        /* ECT codepoint of IPv4 header, pre-shifted */
+       union {
+               struct {
+                       u_int8_t ece:1, cwr:1; /* TCP ECT bits */
+               } tcp;
+       } proto;
+};
+
+#endif /* _IPT_ECN_TARGET_H */
diff --git a/include/linux/netfilter_ipv4/ipt_FTOS.h b/include/linux/netfilter_ipv4/ipt_FTOS.h
new file mode 100644 (file)
index 0000000..3b04559
--- /dev/null
@@ -0,0 +1,16 @@
+/* Set TOS field in header to any value
+ *
+ * (C) 2000 by Matthew G. Marsh <mgm@paktronix.com>
+ *
+ * This software is distributed under GNU GPL v2, 1991
+ * 
+ * ipt_FTOS.h borrowed heavily from ipt_TOS.h  11/09/2000
+*/
+#ifndef _IPT_FTOS_H
+#define _IPT_FTOS_H
+
+struct ipt_FTOS_info {
+       u_int8_t ftos;
+};
+
+#endif /*_IPT_FTOS_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_MARK.h b/include/linux/netfilter_ipv4/ipt_MARK.h
new file mode 100644 (file)
index 0000000..adc1a26
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef _IPT_MARK_H_target
+#define _IPT_MARK_H_target
+
+struct ipt_mark_target_info {
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long mark;
+#else
+       unsigned long mark;
+#endif
+};
+
+enum {
+       IPT_MARK_SET=0,
+       IPT_MARK_AND,
+       IPT_MARK_OR,
+       IPT_MARK_COPYXID
+};
+
+struct ipt_mark_target_info_v1 {
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long mark;
+#else
+       unsigned long mark;
+#endif
+       u_int8_t mode;
+};
+
+#endif /*_IPT_MARK_H_target*/
diff --git a/include/linux/netfilter_ipv4/ipt_NFQUEUE.h b/include/linux/netfilter_ipv4/ipt_NFQUEUE.h
new file mode 100644 (file)
index 0000000..b5b2943
--- /dev/null
@@ -0,0 +1,16 @@
+/* iptables module for using NFQUEUE mechanism
+ *
+ * (C) 2005 Harald Welte <laforge@netfilter.org>
+ *
+ * This software is distributed under GNU GPL v2, 1991
+ * 
+*/
+#ifndef _IPT_NFQ_TARGET_H
+#define _IPT_NFQ_TARGET_H
+
+/* target info */
+struct ipt_NFQ_info {
+       u_int16_t queuenum;
+};
+
+#endif /* _IPT_DSCP_TARGET_H */
diff --git a/include/linux/netfilter_ipv4/ipt_SAME.h b/include/linux/netfilter_ipv4/ipt_SAME.h
new file mode 100644 (file)
index 0000000..89ba22f
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef _IPT_SAME_H
+#define _IPT_SAME_H
+
+#define IPT_SAME_MAX_RANGE     10
+
+#define IPT_SAME_NODST         0x01
+
+struct ipt_same_info
+{
+       unsigned char info;
+       u_int32_t rangesize;
+       u_int32_t ipnum;
+#ifdef KERNEL_64_USERSPACE_32
+       u_int64_t placeholder;
+#else
+       u_int32_t *iparray;
+#endif
+
+       /* hangs off end. */
+       struct ip_nat_range range[IPT_SAME_MAX_RANGE];
+};
+
+#endif /*_IPT_SAME_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_TCPMSS.h b/include/linux/netfilter_ipv4/ipt_TCPMSS.h
new file mode 100644 (file)
index 0000000..aadb395
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _IPT_TCPMSS_H
+#define _IPT_TCPMSS_H
+
+struct ipt_tcpmss_info {
+       u_int16_t mss;
+};
+
+#define IPT_TCPMSS_CLAMP_PMTU 0xffff
+
+#endif /*_IPT_TCPMSS_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_TTL.h b/include/linux/netfilter_ipv4/ipt_TTL.h
new file mode 100644 (file)
index 0000000..f669b8c
--- /dev/null
@@ -0,0 +1,19 @@
+/* TTL modification module for IP tables
+ * (C) 2000 by Harald Welte <laforge@gnumonks.org> */
+
+#ifndef _IPT_TTL_H
+#define _IPT_TTL_H
+
+enum {
+       IPT_TTL_SET = 0,
+       IPT_TTL_INC,
+       IPT_TTL_DEC
+};
+
+#define IPT_TTL_MAXMODE        IPT_TTL_DEC
+
+struct ipt_TTL_info {
+       u_int8_t        mode;
+       u_int8_t        ttl;
+};
+#endif
diff --git a/include/linux/netfilter_ipv4/ipt_ULOG.h b/include/linux/netfilter_ipv4/ipt_ULOG.h
new file mode 100644 (file)
index 0000000..f267ab8
--- /dev/null
@@ -0,0 +1,54 @@
+/* Header file for IP tables userspace logging, Version 1.8
+ *
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * 
+ * Distributed under the terms of GNU GPL */
+
+#ifndef _IPT_ULOG_H
+#define _IPT_ULOG_H
+
+#ifndef NETLINK_NFLOG
+#define NETLINK_NFLOG  5
+#endif
+
+#define ULOG_DEFAULT_NLGROUP   1
+#define ULOG_DEFAULT_QTHRESHOLD        1
+
+#define ULOG_MAC_LEN   80
+#define ULOG_PREFIX_LEN        32
+
+#define ULOG_MAX_QLEN  50
+/* Why 50? Well... there is a limit imposed by the slab cache 131000
+ * bytes. So the multipart netlink-message has to be < 131000 bytes.
+ * Assuming a standard ethernet-mtu of 1500, we could define this up
+ * to 80... but even 50 seems to be big enough. */
+
+/* private data structure for each rule with a ULOG target */
+struct ipt_ulog_info {
+       unsigned int nl_group;
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long copy_range;
+       unsigned long long qthreshold;
+#else
+       size_t copy_range;
+       size_t qthreshold;
+#endif
+       char prefix[ULOG_PREFIX_LEN];
+};
+
+/* Format of the ULOG packets passed through netlink */
+typedef struct ulog_packet_msg {
+       unsigned long mark;
+       long timestamp_sec;
+       long timestamp_usec;
+       unsigned int hook;
+       char indev_name[IFNAMSIZ];
+       char outdev_name[IFNAMSIZ];
+       size_t data_len;
+       char prefix[ULOG_PREFIX_LEN];
+       unsigned char mac_len;
+       unsigned char mac[ULOG_MAC_LEN];
+       unsigned char payload[0];
+} ulog_packet_msg_t;
+
+#endif /*_IPT_ULOG_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_addrtype.h b/include/linux/netfilter_ipv4/ipt_addrtype.h
new file mode 100644 (file)
index 0000000..166ed01
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _IPT_ADDRTYPE_H
+#define _IPT_ADDRTYPE_H
+
+struct ipt_addrtype_info {
+       u_int16_t       source;         /* source-type mask */
+       u_int16_t       dest;           /* dest-type mask */
+       u_int32_t       invert_source;
+       u_int32_t       invert_dest;
+};
+
+#endif
diff --git a/include/linux/netfilter_ipv4/ipt_ah.h b/include/linux/netfilter_ipv4/ipt_ah.h
new file mode 100644 (file)
index 0000000..7b9a2ac
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _IPT_AH_H
+#define _IPT_AH_H
+
+struct ipt_ah
+{
+       u_int32_t spis[2];                      /* Security Parameter Index */
+       u_int8_t  invflags;                     /* Inverse flags */
+};
+
+
+
+/* Values for "invflags" field in struct ipt_ah. */
+#define IPT_AH_INV_SPI         0x01    /* Invert the sense of spi. */
+#define IPT_AH_INV_MASK        0x01    /* All possible flags. */
+
+#endif /*_IPT_AH_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_comment.h b/include/linux/netfilter_ipv4/ipt_comment.h
new file mode 100644 (file)
index 0000000..85c1123
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _IPT_COMMENT_H
+#define _IPT_COMMENT_H
+
+#define IPT_MAX_COMMENT_LEN 256
+
+struct ipt_comment_info {
+       unsigned char comment[IPT_MAX_COMMENT_LEN];
+};
+
+#endif /* _IPT_COMMENT_H */
diff --git a/include/linux/netfilter_ipv4/ipt_connlimit.h b/include/linux/netfilter_ipv4/ipt_connlimit.h
new file mode 100644 (file)
index 0000000..d99193b
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _IPT_CONNLIMIT_H
+#define _IPT_CONNLIMIT_H
+
+struct ipt_connlimit_data;
+
+struct ipt_connlimit_info {
+       int limit;
+       int inverse;
+       u_int32_t mask;
+       struct ipt_connlimit_data *data;
+};
+#endif /* _IPT_CONNLIMIT_H */
diff --git a/include/linux/netfilter_ipv4/ipt_connmark.h b/include/linux/netfilter_ipv4/ipt_connmark.h
new file mode 100644 (file)
index 0000000..151e268
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef _IPT_CONNMARK_H
+#define _IPT_CONNMARK_H
+
+/* Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+struct ipt_connmark_info {
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long mark, mask;
+#else
+       unsigned long mark, mask;
+#endif
+       u_int8_t invert;
+};
+
+#endif /*_IPT_CONNMARK_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_conntrack.h b/include/linux/netfilter_ipv4/ipt_conntrack.h
new file mode 100644 (file)
index 0000000..c8661b8
--- /dev/null
@@ -0,0 +1,81 @@
+/* Header file for kernel module to match connection tracking information.
+ * GPL (C) 2001  Marc Boucher (marc@mbsi.ca).
+ */
+
+#ifndef _IPT_CONNTRACK_H
+#define _IPT_CONNTRACK_H
+
+#include <linux/netfilter/nf_conntrack_common.h>
+
+/* backwards compatibility crap. only exists in userspace - HW */
+#include <linux/version.h>
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c))
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)) || !defined IPS_EXPECTED
+#define IPS_EXPECTED   (1 << 0)
+#define IPS_SEEN_REPLY (1 << 1)
+#define IPS_ASSURED    (1 << 2)
+#define IP_CT_DIR_ORIGINAL     0
+#define IP_CT_DIR_REPLY                1
+#define IP_CT_DIR_MAX          2
+#endif
+
+#define IPT_CONNTRACK_STATE_BIT(ctinfo) (1 << ((ctinfo)%IP_CT_IS_REPLY+1))
+#define IPT_CONNTRACK_STATE_INVALID (1 << 0)
+
+#define IPT_CONNTRACK_STATE_SNAT (1 << (IP_CT_NUMBER + 1))
+#define IPT_CONNTRACK_STATE_DNAT (1 << (IP_CT_NUMBER + 2))
+#define IPT_CONNTRACK_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 3))
+
+/* flags, invflags: */
+#define IPT_CONNTRACK_STATE    0x01
+#define IPT_CONNTRACK_PROTO    0x02
+#define IPT_CONNTRACK_ORIGSRC  0x04
+#define IPT_CONNTRACK_ORIGDST  0x08
+#define IPT_CONNTRACK_REPLSRC  0x10
+#define IPT_CONNTRACK_REPLDST  0x20
+#define IPT_CONNTRACK_STATUS   0x40
+#define IPT_CONNTRACK_EXPIRES  0x80
+
+/* This is exposed to userspace, so remains frozen in time. */
+struct ip_conntrack_old_tuple
+{
+       struct {
+               u_int32_t ip;
+               union {
+                       u_int16_t all;
+               } u;
+       } src;
+
+       struct {
+               u_int32_t ip;
+               union {
+                       u_int16_t all;
+               } u;
+
+               /* The protocol. */
+               u_int16_t protonum;
+       } dst;
+};
+
+struct ipt_conntrack_info
+{
+       unsigned int statemask, statusmask;
+
+       struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX];
+       struct in_addr sipmsk[IP_CT_DIR_MAX], dipmsk[IP_CT_DIR_MAX];
+
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long expires_min, expires_max;
+#else
+       unsigned long expires_min, expires_max;
+#endif
+
+       /* Flags word */
+       u_int8_t flags;
+       /* Inverse flags */
+       u_int8_t invflags;
+};
+#endif /*_IPT_CONNTRACK_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_dscp.h b/include/linux/netfilter_ipv4/ipt_dscp.h
new file mode 100644 (file)
index 0000000..b6c59bd
--- /dev/null
@@ -0,0 +1,23 @@
+/* iptables module for matching the IPv4 DSCP field
+ *
+ * (C) 2002 Harald Welte <laforge@gnumonks.org>
+ * This software is distributed under GNU GPL v2, 1991
+ * 
+ * See RFC2474 for a description of the DSCP field within the IP Header.
+ *
+ * Id: ipt_dscp.h,v 1.3 2002/08/05 19:00:21 laforge Exp
+*/
+#ifndef _IPT_DSCP_H
+#define _IPT_DSCP_H
+
+#define IPT_DSCP_MASK  0xfc    /* 11111100 */
+#define IPT_DSCP_SHIFT 2
+#define IPT_DSCP_MAX   0x3f    /* 00111111 */
+
+/* match info */
+struct ipt_dscp_info {
+       u_int8_t dscp;
+       u_int8_t invert;
+};
+
+#endif /* _IPT_DSCP_H */
diff --git a/include/linux/netfilter_ipv4/ipt_dstlimit.h b/include/linux/netfilter_ipv4/ipt_dstlimit.h
new file mode 100644 (file)
index 0000000..1a88f6b
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _IPT_DSTLIMIT_H
+#define _IPT_DSTLIMIT_H
+
+/* timings are in milliseconds. */
+#define IPT_DSTLIMIT_SCALE 10000
+/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
+   seconds, or one every 59 hours. */
+
+/* details of this structure hidden by the implementation */
+struct ipt_dstlimit_htable;
+
+#define IPT_DSTLIMIT_HASH_DIP  0x0001
+#define IPT_DSTLIMIT_HASH_DPT  0x0002
+#define IPT_DSTLIMIT_HASH_SIP  0x0004
+
+struct dstlimit_cfg {
+       u_int32_t mode;   /* bitmask of IPT_DSTLIMIT_HASH_* */
+       u_int32_t avg;    /* Average secs between packets * scale */
+       u_int32_t burst;  /* Period multiplier for upper limit. */
+
+       /* user specified */
+       u_int32_t size;         /* how many buckets */
+       u_int32_t max;          /* max number of entries */
+       u_int32_t gc_interval;  /* gc interval */
+       u_int32_t expire;       /* when do entries expire? */
+};
+
+struct ipt_dstlimit_info {
+       char name [IFNAMSIZ];           /* name */
+       struct dstlimit_cfg cfg;
+       struct ipt_dstlimit_htable *hinfo;
+
+       /* Used internally by the kernel */
+       union {
+               void *ptr;
+               struct ipt_dstlimit_info *master;
+       } u;
+};
+#endif /*_IPT_DSTLIMIT_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_ecn.h b/include/linux/netfilter_ipv4/ipt_ecn.h
new file mode 100644 (file)
index 0000000..326c0c0
--- /dev/null
@@ -0,0 +1,33 @@
+/* iptables module for matching the ECN header in IPv4 and TCP header
+ *
+ * (C) 2002 Harald Welte <laforge@gnumonks.org>
+ *
+ * This software is distributed under GNU GPL v2, 1991
+ * 
+ * Id: ipt_ecn.h,v 1.4 2002/08/05 19:39:00 laforge Exp
+*/
+#ifndef _IPT_ECN_H
+#define _IPT_ECN_H
+#include <linux/netfilter_ipv4/ipt_dscp.h>
+
+#define IPT_ECN_IP_MASK        (~IPT_DSCP_MASK)
+
+#define IPT_ECN_OP_MATCH_IP    0x01
+#define IPT_ECN_OP_MATCH_ECE   0x10
+#define IPT_ECN_OP_MATCH_CWR   0x20
+
+#define IPT_ECN_OP_MATCH_MASK  0xce
+
+/* match info */
+struct ipt_ecn_info {
+       u_int8_t operation;
+       u_int8_t invert;
+       u_int8_t ip_ect;
+       union {
+               struct {
+                       u_int8_t ect;
+               } tcp;
+       } proto;
+};
+
+#endif /* _IPT_ECN_H */
diff --git a/include/linux/netfilter_ipv4/ipt_esp.h b/include/linux/netfilter_ipv4/ipt_esp.h
new file mode 100644 (file)
index 0000000..c782a83
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _IPT_ESP_H
+#define _IPT_ESP_H
+
+struct ipt_esp
+{
+       u_int32_t spis[2];                      /* Security Parameter Index */
+       u_int8_t  invflags;                     /* Inverse flags */
+};
+
+
+
+/* Values for "invflags" field in struct ipt_esp. */
+#define IPT_ESP_INV_SPI                0x01    /* Invert the sense of spi. */
+#define IPT_ESP_INV_MASK       0x01    /* All possible flags. */
+
+#endif /*_IPT_ESP_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_hashlimit.h b/include/linux/netfilter_ipv4/ipt_hashlimit.h
new file mode 100644 (file)
index 0000000..ac2cb64
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _IPT_HASHLIMIT_H
+#define _IPT_HASHLIMIT_H
+
+/* timings are in milliseconds. */
+#define IPT_HASHLIMIT_SCALE 10000
+/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
+   seconds, or one every 59 hours. */
+
+/* details of this structure hidden by the implementation */
+struct ipt_hashlimit_htable;
+
+#define IPT_HASHLIMIT_HASH_DIP 0x0001
+#define IPT_HASHLIMIT_HASH_DPT 0x0002
+#define IPT_HASHLIMIT_HASH_SIP 0x0004
+#define IPT_HASHLIMIT_HASH_SPT 0x0008
+
+struct hashlimit_cfg {
+       u_int32_t mode;   /* bitmask of IPT_HASHLIMIT_HASH_* */
+       u_int32_t avg;    /* Average secs between packets * scale */
+       u_int32_t burst;  /* Period multiplier for upper limit. */
+
+       /* user specified */
+       u_int32_t size;         /* how many buckets */
+       u_int32_t max;          /* max number of entries */
+       u_int32_t gc_interval;  /* gc interval */
+       u_int32_t expire;       /* when do entries expire? */
+};
+
+struct ipt_hashlimit_info {
+       char name [IFNAMSIZ];           /* name */
+       struct hashlimit_cfg cfg;
+       struct ipt_hashlimit_htable *hinfo;
+
+       /* Used internally by the kernel */
+       union {
+               void *ptr;
+               struct ipt_hashlimit_info *master;
+       } u;
+};
+#endif /*_IPT_HASHLIMIT_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_helper.h b/include/linux/netfilter_ipv4/ipt_helper.h
new file mode 100644 (file)
index 0000000..6f12ecb
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _IPT_HELPER_H
+#define _IPT_HELPER_H
+
+struct ipt_helper_info {
+       int invert;
+       char name[30];
+};
+#endif /* _IPT_HELPER_H */
diff --git a/include/linux/netfilter_ipv4/ipt_iprange.h b/include/linux/netfilter_ipv4/ipt_iprange.h
new file mode 100644 (file)
index 0000000..3ecb3bd
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef _IPT_IPRANGE_H
+#define _IPT_IPRANGE_H
+
+#define IPRANGE_SRC            0x01    /* Match source IP address */
+#define IPRANGE_DST            0x02    /* Match destination IP address */
+#define IPRANGE_SRC_INV                0x10    /* Negate the condition */
+#define IPRANGE_DST_INV                0x20    /* Negate the condition */
+
+struct ipt_iprange {
+       /* Inclusive: network order. */
+       u_int32_t min_ip, max_ip;
+};
+
+struct ipt_iprange_info
+{
+       struct ipt_iprange src;
+       struct ipt_iprange dst;
+
+       /* Flags from above */
+       u_int8_t flags;
+};
+
+#endif /* _IPT_IPRANGE_H */
diff --git a/include/linux/netfilter_ipv4/ipt_length.h b/include/linux/netfilter_ipv4/ipt_length.h
new file mode 100644 (file)
index 0000000..6e08852
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _IPT_LENGTH_H
+#define _IPT_LENGTH_H
+
+struct ipt_length_info {
+    u_int16_t  min, max;
+    u_int8_t   invert;
+};
+
+#endif /*_IPT_LENGTH_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_limit.h b/include/linux/netfilter_ipv4/ipt_limit.h
new file mode 100644 (file)
index 0000000..e2fb166
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef _IPT_RATE_H
+#define _IPT_RATE_H
+
+/* timings are in milliseconds. */
+#define IPT_LIMIT_SCALE 10000
+
+/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
+   seconds, or one every 59 hours. */
+struct ipt_rateinfo {
+       u_int32_t avg;    /* Average secs between packets * scale */
+       u_int32_t burst;  /* Period multiplier for upper limit. */
+
+#ifdef KERNEL_64_USERSPACE_32
+       u_int64_t prev;
+       u_int64_t placeholder;
+#else
+       /* Used internally by the kernel */
+       unsigned long prev;
+       /* Ugly, ugly fucker. */
+       struct ipt_rateinfo *master;
+#endif
+
+       u_int32_t credit;
+       u_int32_t credit_cap, cost;
+};
+#endif /*_IPT_RATE_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_mark.h b/include/linux/netfilter_ipv4/ipt_mark.h
new file mode 100644 (file)
index 0000000..b9e79fd
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _IPT_MARK_H
+#define _IPT_MARK_H
+
+struct ipt_mark_info {
+#ifdef KERNEL_64_USERSPACE_32
+    unsigned long long mark, mask;
+#else
+    unsigned long mark, mask;
+#endif
+    u_int8_t invert;
+};
+
+#endif /*_IPT_MARK_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_multiport.h b/include/linux/netfilter_ipv4/ipt_multiport.h
new file mode 100644 (file)
index 0000000..4b95d13
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef _IPT_MULTIPORT_H
+#define _IPT_MULTIPORT_H
+
+enum ipt_multiport_flags
+{
+       IPT_MULTIPORT_SOURCE,
+       IPT_MULTIPORT_DESTINATION,
+       IPT_MULTIPORT_EITHER
+};
+
+#define IPT_MULTI_PORTS        15
+
+/* Must fit inside union ipt_matchinfo: 16 bytes */
+struct ipt_multiport
+{
+       u_int8_t flags;                         /* Type of comparison */
+       u_int8_t count;                         /* Number of ports */
+       u_int16_t ports[IPT_MULTI_PORTS];       /* Ports */
+};
+
+struct ipt_multiport_v1
+{
+       u_int8_t flags;                         /* Type of comparison */
+       u_int8_t count;                         /* Number of ports */
+       u_int16_t ports[IPT_MULTI_PORTS];       /* Ports */
+       u_int8_t pflags[IPT_MULTI_PORTS];       /* Port flags */
+       u_int8_t invert;                        /* Invert flag */
+};
+#endif /*_IPT_MULTIPORT_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_physdev.h b/include/linux/netfilter_ipv4/ipt_physdev.h
new file mode 100644 (file)
index 0000000..7538c86
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef _IPT_PHYSDEV_H
+#define _IPT_PHYSDEV_H
+
+#ifdef __KERNEL__
+#include <linux/if.h>
+#endif
+
+#define IPT_PHYSDEV_OP_IN              0x01
+#define IPT_PHYSDEV_OP_OUT             0x02
+#define IPT_PHYSDEV_OP_BRIDGED         0x04
+#define IPT_PHYSDEV_OP_ISIN            0x08
+#define IPT_PHYSDEV_OP_ISOUT           0x10
+#define IPT_PHYSDEV_OP_MASK            (0x20 - 1)
+
+struct ipt_physdev_info {
+       char physindev[IFNAMSIZ];
+       char in_mask[IFNAMSIZ];
+       char physoutdev[IFNAMSIZ];
+       char out_mask[IFNAMSIZ];
+       u_int8_t invert;
+       u_int8_t bitmask;
+};
+
+#endif /*_IPT_PHYSDEV_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_pkttype.h b/include/linux/netfilter_ipv4/ipt_pkttype.h
new file mode 100644 (file)
index 0000000..c189e94
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _IPT_PKTTYPE_H
+#define _IPT_PKTTYPE_H
+
+struct ipt_pkttype_info {
+       int     pkttype;
+       int     invert;
+};
+
+#endif /*_IPT_PKTTYPE_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_policy.h b/include/linux/netfilter_ipv4/ipt_policy.h
new file mode 100644 (file)
index 0000000..74ca65c
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef _IPT_POLICY_H
+#define _IPT_POLICY_H
+
+#define IPT_POLICY_MAX_ELEM    4
+
+#ifndef __KERNEL__
+#include <netinet/in.h>
+#endif
+
+enum ipt_policy_flags
+{
+       IPT_POLICY_MATCH_IN     = 0x1,
+       IPT_POLICY_MATCH_OUT    = 0x2,
+       IPT_POLICY_MATCH_NONE   = 0x4,
+       IPT_POLICY_MATCH_STRICT = 0x8,
+};
+
+enum ipt_policy_modes
+{
+       IPT_POLICY_MODE_TRANSPORT,
+       IPT_POLICY_MODE_TUNNEL
+};
+
+struct ipt_policy_spec
+{
+       u_int8_t        saddr:1,
+                       daddr:1,
+                       proto:1,
+                       mode:1,
+                       spi:1,
+                       reqid:1;
+};
+
+union ipt_policy_addr
+{
+       struct in_addr  a4;
+       struct in6_addr a6;
+};
+
+struct ipt_policy_elem
+{
+       union ipt_policy_addr   saddr;
+       union ipt_policy_addr   smask;
+       union ipt_policy_addr   daddr;
+       union ipt_policy_addr   dmask;
+       u_int32_t               spi;
+       u_int32_t               reqid;
+       u_int8_t                proto;
+       u_int8_t                mode;
+
+       struct ipt_policy_spec  match;
+       struct ipt_policy_spec  invert;
+};
+
+struct ipt_policy_info
+{
+       struct ipt_policy_elem pol[IPT_POLICY_MAX_ELEM];
+       u_int16_t flags;
+       u_int16_t len;
+};
+
+#endif /* _IPT_POLICY_H */
diff --git a/include/linux/netfilter_ipv4/ipt_realm.h b/include/linux/netfilter_ipv4/ipt_realm.h
new file mode 100644 (file)
index 0000000..2357731
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _IPT_REALM_H
+#define _IPT_REALM_H
+
+struct ipt_realm_info {
+       u_int32_t id;
+       u_int32_t mask;
+       u_int8_t invert;
+};
+#endif /*_IPT_REALM_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_rpc.h b/include/linux/netfilter_ipv4/ipt_rpc.h
new file mode 100644 (file)
index 0000000..c204b7f
--- /dev/null
@@ -0,0 +1,35 @@
+/* RPC extension for IP netfilter matching, Version 2.2
+ * (C) 2000 by Marcelo Barbosa Lima <marcelo.lima@dcc.unicamp.br>
+ *     - original rpc tracking module
+ *     - "recent" connection handling for kernel 2.3+ netfilter
+ *
+ * (C) 2001 by Rusty Russell <rusty@rustcorp.com.au>
+ *     - upgraded conntrack modules to oldnat api - kernel 2.4.0+
+ *
+ * (C) 2002 by Ian (Larry) Latter <Ian.Latter@mq.edu.au>
+ *     - upgraded conntrack modules to newnat api - kernel 2.4.20+
+ *     - extended matching to support filtering on procedures
+ *
+ * ipt_rpc.h.c,v 2.2 2003/01/12 18:30:00
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version
+ *     2 of the License, or (at your option) any later version.
+ **
+ */
+
+#ifndef _IPT_RPC_H
+#define _IPT_RPC_H
+
+struct ipt_rpc_data;
+
+struct ipt_rpc_info {
+       int inverse;
+       int strict;
+       const char c_procs[1408];
+       int i_procs;
+       struct ipt_rpc_data *data;
+};
+
+#endif /* _IPT_RPC_H */
diff --git a/include/linux/netfilter_ipv4/ipt_sctp.h b/include/linux/netfilter_ipv4/ipt_sctp.h
new file mode 100644 (file)
index 0000000..e93a9ec
--- /dev/null
@@ -0,0 +1,107 @@
+#ifndef _IPT_SCTP_H_
+#define _IPT_SCTP_H_
+
+#define IPT_SCTP_SRC_PORTS             0x01
+#define IPT_SCTP_DEST_PORTS            0x02
+#define IPT_SCTP_CHUNK_TYPES           0x04
+
+#define IPT_SCTP_VALID_FLAGS           0x07
+
+#define ELEMCOUNT(x) (sizeof(x)/sizeof(x[0]))
+
+
+struct ipt_sctp_flag_info {
+       u_int8_t chunktype;
+       u_int8_t flag;
+       u_int8_t flag_mask;
+};
+
+#define IPT_NUM_SCTP_FLAGS     4
+
+struct ipt_sctp_info {
+       u_int16_t dpts[2];  /* Min, Max */
+       u_int16_t spts[2];  /* Min, Max */
+
+       u_int32_t chunkmap[256 / sizeof (u_int32_t)];  /* Bit mask of chunks to be matched according to RFC 2960 */
+
+#define SCTP_CHUNK_MATCH_ANY   0x01  /* Match if any of the chunk types are present */
+#define SCTP_CHUNK_MATCH_ALL   0x02  /* Match if all of the chunk types are present */
+#define SCTP_CHUNK_MATCH_ONLY  0x04  /* Match if these are the only chunk types present */
+
+       u_int32_t chunk_match_type;
+       struct ipt_sctp_flag_info flag_info[IPT_NUM_SCTP_FLAGS];
+       int flag_count;
+
+       u_int32_t flags;
+       u_int32_t invflags;
+};
+
+#define bytes(type) (sizeof(type) * 8)
+
+#define SCTP_CHUNKMAP_SET(chunkmap, type)              \
+       do {                                            \
+               chunkmap[type / bytes(u_int32_t)] |=    \
+                       1 << (type % bytes(u_int32_t)); \
+       } while (0)
+
+#define SCTP_CHUNKMAP_CLEAR(chunkmap, type)                    \
+       do {                                                    \
+               chunkmap[type / bytes(u_int32_t)] &=            \
+                       ~(1 << (type % bytes(u_int32_t)));      \
+       } while (0)
+
+#define SCTP_CHUNKMAP_IS_SET(chunkmap, type)                   \
+({                                                             \
+       (chunkmap[type / bytes (u_int32_t)] &                   \
+               (1 << (type % bytes (u_int32_t)))) ? 1: 0;      \
+})
+
+#define SCTP_CHUNKMAP_RESET(chunkmap)                          \
+       do {                                                    \
+               int i;                                          \
+               for (i = 0; i < ELEMCOUNT(chunkmap); i++)       \
+                       chunkmap[i] = 0;                        \
+       } while (0)
+
+#define SCTP_CHUNKMAP_SET_ALL(chunkmap)                        \
+       do {                                                    \
+               int i;                                          \
+               for (i = 0; i < ELEMCOUNT(chunkmap); i++)       \
+                       chunkmap[i] = ~0;                       \
+       } while (0)
+
+#define SCTP_CHUNKMAP_COPY(destmap, srcmap)                    \
+       do {                                                    \
+               int i;                                          \
+               for (i = 0; i < ELEMCOUNT(chunkmap); i++)       \
+                       destmap[i] = srcmap[i];                 \
+       } while (0)
+
+#define SCTP_CHUNKMAP_IS_CLEAR(chunkmap)               \
+({                                                     \
+       int i;                                          \
+       int flag = 1;                                   \
+       for (i = 0; i < ELEMCOUNT(chunkmap); i++) {     \
+               if (chunkmap[i]) {                      \
+                       flag = 0;                       \
+                       break;                          \
+               }                                       \
+       }                                               \
+        flag;                                          \
+})
+
+#define SCTP_CHUNKMAP_IS_ALL_SET(chunkmap)             \
+({                                                     \
+       int i;                                          \
+       int flag = 1;                                   \
+       for (i = 0; i < ELEMCOUNT(chunkmap); i++) {     \
+               if (chunkmap[i] != ~0) {                \
+                       flag = 0;                       \
+                               break;                  \
+               }                                       \
+       }                                               \
+        flag;                                          \
+})
+
+#endif /* _IPT_SCTP_H_ */
+
diff --git a/include/linux/netfilter_ipv4/ipt_tcpmss.h b/include/linux/netfilter_ipv4/ipt_tcpmss.h
new file mode 100644 (file)
index 0000000..e2b1439
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _IPT_TCPMSS_MATCH_H
+#define _IPT_TCPMSS_MATCH_H
+
+struct ipt_tcpmss_match_info {
+    u_int16_t mss_min, mss_max;
+    u_int8_t invert;
+};
+
+#endif /*_IPT_TCPMSS_MATCH_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_ttl.h b/include/linux/netfilter_ipv4/ipt_ttl.h
new file mode 100644 (file)
index 0000000..ee24fd8
--- /dev/null
@@ -0,0 +1,21 @@
+/* IP tables module for matching the value of the TTL
+ * (C) 2000 by Harald Welte <laforge@gnumonks.org> */
+
+#ifndef _IPT_TTL_H
+#define _IPT_TTL_H
+
+enum {
+       IPT_TTL_EQ = 0,         /* equals */
+       IPT_TTL_NE,             /* not equals */
+       IPT_TTL_LT,             /* less than */
+       IPT_TTL_GT,             /* greater than */
+};
+
+
+struct ipt_ttl_info {
+       u_int8_t        mode;
+       u_int8_t        ttl;
+};
+
+
+#endif
diff --git a/include/linux/netfilter_ipv6/ip6t_HL.h b/include/linux/netfilter_ipv6/ip6t_HL.h
new file mode 100644 (file)
index 0000000..afb7813
--- /dev/null
@@ -0,0 +1,22 @@
+/* Hop Limit modification module for ip6tables
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's TTL module */
+
+#ifndef _IP6T_HL_H
+#define _IP6T_HL_H
+
+enum {
+       IP6T_HL_SET = 0,
+       IP6T_HL_INC,
+       IP6T_HL_DEC
+};
+
+#define IP6T_HL_MAXMODE        IP6T_HL_DEC
+
+struct ip6t_HL_info {
+       u_int8_t        mode;
+       u_int8_t        hop_limit;
+};
+
+
+#endif
diff --git a/include/linux/netfilter_ipv6/ip6t_MARK.h b/include/linux/netfilter_ipv6/ip6t_MARK.h
new file mode 100644 (file)
index 0000000..06949b8
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _IP6T_MARK_H_target
+#define _IP6T_MARK_H_target
+
+struct ip6t_mark_target_info {
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long mark;
+#else
+       unsigned long mark;
+#endif
+};
+
+#endif /*_IPT_MARK_H_target*/
diff --git a/include/linux/netfilter_ipv6/ip6t_REJECT.h b/include/linux/netfilter_ipv6/ip6t_REJECT.h
new file mode 100644 (file)
index 0000000..7266402
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _IP6T_REJECT_H
+#define _IP6T_REJECT_H
+
+enum ip6t_reject_with {
+       IP6T_ICMP6_NO_ROUTE,
+       IP6T_ICMP6_ADM_PROHIBITED,
+       IP6T_ICMP6_ADDR_UNREACH,
+       IP6T_ICMP6_PORT_UNREACH,
+       IP6T_TCP_RESET
+};
+
+struct ip6t_reject_info {
+       enum ip6t_reject_with with;      /* reject type */
+};
+
+#endif /*_IP6T_REJECT_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_TCPMSS.h b/include/linux/netfilter_ipv6/ip6t_TCPMSS.h
new file mode 100644 (file)
index 0000000..412d1cb
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _IP6T_TCPMSS_H
+#define _IP6T_TCPMSS_H
+
+struct ip6t_tcpmss_info {
+       u_int16_t mss;
+};
+
+#define IP6T_TCPMSS_CLAMP_PMTU 0xffff
+
+#endif /*_IP6T_TCPMSS_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_ah.h b/include/linux/netfilter_ipv6/ip6t_ah.h
new file mode 100644 (file)
index 0000000..c4f0793
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef _IP6T_AH_H
+#define _IP6T_AH_H
+
+struct ip6t_ah
+{
+       u_int32_t spis[2];                      /* Security Parameter Index */
+       u_int32_t hdrlen;                       /* Header Length */
+       u_int8_t  hdrres;                       /* Test of the Reserved Filed */
+       u_int8_t  invflags;                     /* Inverse flags */
+};
+
+#define IP6T_AH_SPI 0x01
+#define IP6T_AH_LEN 0x02
+#define IP6T_AH_RES 0x04
+
+/* Values for "invflags" field in struct ip6t_ah. */
+#define IP6T_AH_INV_SPI                0x01    /* Invert the sense of spi. */
+#define IP6T_AH_INV_LEN                0x02    /* Invert the sense of length. */
+#define IP6T_AH_INV_MASK       0x03    /* All possible flags. */
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /*_IP6T_AH_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_esp.h b/include/linux/netfilter_ipv6/ip6t_esp.h
new file mode 100644 (file)
index 0000000..01142b9
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef _IP6T_ESP_H
+#define _IP6T_ESP_H
+
+struct ip6t_esp
+{
+       u_int32_t spis[2];                      /* Security Parameter Index */
+       u_int8_t  invflags;                     /* Inverse flags */
+};
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+/* Values for "invflags" field in struct ip6t_esp. */
+#define IP6T_ESP_INV_SPI               0x01    /* Invert the sense of spi. */
+#define IP6T_ESP_INV_MASK      0x01    /* All possible flags. */
+
+#endif /*_IP6T_ESP_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_frag.h b/include/linux/netfilter_ipv6/ip6t_frag.h
new file mode 100644 (file)
index 0000000..449a57e
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef _IP6T_FRAG_H
+#define _IP6T_FRAG_H
+
+struct ip6t_frag
+{
+       u_int32_t ids[2];                       /* Security Parameter Index */
+       u_int32_t hdrlen;                       /* Header Length */
+       u_int8_t  flags;                        /*  */
+       u_int8_t  invflags;                     /* Inverse flags */
+};
+
+#define IP6T_FRAG_IDS          0x01
+#define IP6T_FRAG_LEN          0x02
+#define IP6T_FRAG_RES          0x04
+#define IP6T_FRAG_FST          0x08
+#define IP6T_FRAG_MF           0x10
+#define IP6T_FRAG_NMF                  0x20
+
+/* Values for "invflags" field in struct ip6t_frag. */
+#define IP6T_FRAG_INV_IDS      0x01    /* Invert the sense of ids. */
+#define IP6T_FRAG_INV_LEN      0x02    /* Invert the sense of length. */
+#define IP6T_FRAG_INV_MASK     0x03    /* All possible flags. */
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /*_IP6T_FRAG_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_hl.h b/include/linux/netfilter_ipv6/ip6t_hl.h
new file mode 100644 (file)
index 0000000..5ef91b8
--- /dev/null
@@ -0,0 +1,22 @@
+/* ip6tables module for matching the Hop Limit value
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's ttl module */
+
+#ifndef _IP6T_HL_H
+#define _IP6T_HL_H
+
+enum {
+       IP6T_HL_EQ = 0,         /* equals */
+       IP6T_HL_NE,             /* not equals */
+       IP6T_HL_LT,             /* less than */
+       IP6T_HL_GT,             /* greater than */
+};
+
+
+struct ip6t_hl_info {
+       u_int8_t        mode;
+       u_int8_t        hop_limit;
+};
+
+
+#endif
diff --git a/include/linux/netfilter_ipv6/ip6t_length.h b/include/linux/netfilter_ipv6/ip6t_length.h
new file mode 100644 (file)
index 0000000..7fc09f9
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _IP6T_LENGTH_H
+#define _IP6T_LENGTH_H
+
+struct ip6t_length_info {
+       u_int16_t  min, max;
+       u_int8_t   invert;
+};
+
+#endif /*_IP6T_LENGTH_H*/
+       
diff --git a/include/linux/netfilter_ipv6/ip6t_limit.h b/include/linux/netfilter_ipv6/ip6t_limit.h
new file mode 100644 (file)
index 0000000..cd3e834
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef _IP6T_RATE_H
+#define _IP6T_RATE_H
+
+/* timings are in milliseconds. */
+#define IP6T_LIMIT_SCALE 10000
+
+/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
+   seconds, or one every 59 hours. */
+struct ip6t_rateinfo {
+       u_int32_t avg;    /* Average secs between packets * scale */
+       u_int32_t burst;  /* Period multiplier for upper limit. */
+
+#ifdef KERNEL_64_USERSPACE_32
+       u_int64_t prev;
+       u_int64_t placeholder;
+#else
+       /* Used internally by the kernel */
+       unsigned long prev;
+       /* Ugly, ugly fucker. */
+       struct ip6t_rateinfo *master;
+#endif
+       u_int32_t credit;
+       u_int32_t credit_cap, cost;
+};
+#endif /*_IPT_RATE_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_mark.h b/include/linux/netfilter_ipv6/ip6t_mark.h
new file mode 100644 (file)
index 0000000..7ede185
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _IP6T_MARK_H
+#define _IP6T_MARK_H
+
+struct ip6t_mark_info {
+#ifdef KERNEL_64_USERSPACE_32
+    unsigned long long mark, mask;
+#else
+    unsigned long mark, mask;
+#endif
+    u_int8_t invert;
+};
+
+#endif /*_IPT_MARK_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_multiport.h b/include/linux/netfilter_ipv6/ip6t_multiport.h
new file mode 100644 (file)
index 0000000..8c2cc9d
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef _IP6T_MULTIPORT_H
+#define _IP6T_MULTIPORT_H
+
+enum ip6t_multiport_flags
+{
+       IP6T_MULTIPORT_SOURCE,
+       IP6T_MULTIPORT_DESTINATION,
+       IP6T_MULTIPORT_EITHER
+};
+
+#define IP6T_MULTI_PORTS       15
+
+/* Must fit inside union xt_matchinfo: 16 bytes */
+struct ip6t_multiport
+{
+       u_int8_t flags;                         /* Type of comparison */
+       u_int8_t count;                         /* Number of ports */
+       u_int16_t ports[IP6T_MULTI_PORTS];      /* Ports */
+};
+
+struct ip6t_multiport_v1
+{
+       u_int8_t flags;                         /* Type of comparison */
+       u_int8_t count;                         /* Number of ports */
+       u_int16_t ports[IP6T_MULTI_PORTS];      /* Ports */
+       u_int8_t pflags[IP6T_MULTI_PORTS];      /* Port flags */
+       u_int8_t invert;                        /* Invert flag */
+};
+
+#endif /*_IP6T_MULTIPORT_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_owner.h b/include/linux/netfilter_ipv6/ip6t_owner.h
new file mode 100644 (file)
index 0000000..19937da
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _IP6T_OWNER_H
+#define _IP6T_OWNER_H
+
+/* match and invert flags */
+#define IP6T_OWNER_UID 0x01
+#define IP6T_OWNER_GID 0x02
+#define IP6T_OWNER_PID 0x04
+#define IP6T_OWNER_SID 0x08
+
+struct ip6t_owner_info {
+    uid_t uid;
+    gid_t gid;
+    pid_t pid;
+    pid_t sid;
+    u_int8_t match, invert;    /* flags */
+};
+
+#endif /*_IPT_OWNER_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_physdev.h b/include/linux/netfilter_ipv6/ip6t_physdev.h
new file mode 100644 (file)
index 0000000..c234731
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef _IP6T_PHYSDEV_H
+#define _IP6T_PHYSDEV_H
+
+#ifdef __KERNEL__
+#include <linux/if.h>
+#endif
+
+#define IP6T_PHYSDEV_OP_IN             0x01
+#define IP6T_PHYSDEV_OP_OUT            0x02
+#define IP6T_PHYSDEV_OP_BRIDGED                0x04
+#define IP6T_PHYSDEV_OP_ISIN           0x08
+#define IP6T_PHYSDEV_OP_ISOUT          0x10
+#define IP6T_PHYSDEV_OP_MASK           (0x20 - 1)
+
+struct ip6t_physdev_info {
+       char physindev[IFNAMSIZ];
+       char in_mask[IFNAMSIZ];
+       char physoutdev[IFNAMSIZ];
+       char out_mask[IFNAMSIZ];
+       u_int8_t invert;
+       u_int8_t bitmask;
+};
+
+#endif /*_IP6T_PHYSDEV_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_policy.h b/include/linux/netfilter_ipv6/ip6t_policy.h
new file mode 100644 (file)
index 0000000..671bd81
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef _IP6T_POLICY_H
+#define _IP6T_POLICY_H
+
+#define IP6T_POLICY_MAX_ELEM   4
+
+enum ip6t_policy_flags
+{
+       IP6T_POLICY_MATCH_IN            = 0x1,
+       IP6T_POLICY_MATCH_OUT           = 0x2,
+       IP6T_POLICY_MATCH_NONE          = 0x4,
+       IP6T_POLICY_MATCH_STRICT        = 0x8,
+};
+
+enum ip6t_policy_modes
+{
+       IP6T_POLICY_MODE_TRANSPORT,
+       IP6T_POLICY_MODE_TUNNEL
+};
+
+struct ip6t_policy_spec
+{
+       u_int8_t        saddr:1,
+                       daddr:1,
+                       proto:1,
+                       mode:1,
+                       spi:1,
+                       reqid:1;
+};
+
+union ip6t_policy_addr
+{
+       struct in_addr  a4;
+       struct in6_addr a6;
+};
+
+struct ip6t_policy_elem
+{
+       union ip6t_policy_addr  saddr;
+       union ip6t_policy_addr  smask;
+       union ip6t_policy_addr  daddr;
+       union ip6t_policy_addr  dmask;
+       u_int32_t               spi;
+       u_int32_t               reqid;
+       u_int8_t                proto;
+       u_int8_t                mode;
+
+       struct ip6t_policy_spec match;
+       struct ip6t_policy_spec invert;
+};
+
+struct ip6t_policy_info
+{
+       struct ip6t_policy_elem pol[IP6T_POLICY_MAX_ELEM];
+       u_int16_t flags;
+       u_int16_t len;
+};
+
+#endif /* _IP6T_POLICY_H */
diff --git a/ip6tables-restore.8 b/ip6tables-restore.8
new file mode 100644 (file)
index 0000000..43c1268
--- /dev/null
@@ -0,0 +1,51 @@
+.TH IP6TABLES-RESTORE 8 "Jan 30, 2002" "" ""
+.\"
+.\" Man page written by Harald Welte <laforge@gnumonks.org>
+.\" It is based on the iptables man page.
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ip6tables-restore \- Restore IPv6 Tables
+.SH SYNOPSIS
+.BR "ip6tables-restore " "[-c] [-n]"
+.br
+.SH DESCRIPTION
+.PP
+.B ip6tables-restore
+is used to restore IPv6 Tables from data specified on STDIN. Use 
+I/O redirection provided by your shell to read from a file
+.TP
+\fB\-c\fR, \fB\-\-counters\fR
+restore the values of all packet and byte counters
+.TP
+\fB\-n\fR, \fB\-\-noflush\fR 
+.TP
+don't flush the previous contents of the table. If not specified, 
+.B ip6tables-restore
+flushes (deletes) all previous contents of the respective IPv6 Table.
+.SH BUGS
+None known as of iptables-1.2.1 release
+.SH AUTHORS
+Harald Welte <laforge@gnumonks.org>
+.br
+Andras Kis-Szabo <kisza@sch.bme.hu>
+.SH SEE ALSO
+.BR ip6tables-save "(8), " ip6tables "(8) "
+.PP
+The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
+which details NAT, and the netfilter-hacking-HOWTO which details the
+internals.
diff --git a/ip6tables-restore.c b/ip6tables-restore.c
new file mode 100644 (file)
index 0000000..a607e18
--- /dev/null
@@ -0,0 +1,425 @@
+/* Code to restore the iptables state, from file by ip6tables-save. 
+ * Author:  Andras Kis-Szabo <kisza@sch.bme.hu>
+ *
+ * based on iptables-restore
+ * Authors:
+ *     Harald Welte <laforge@gnumonks.org>
+ *     Rusty Russell <rusty@linuxcare.com.au>
+ * This code is distributed under the terms of GNU GPL v2
+ *
+ * $Id: ip6tables-restore.c 6828 2007-05-10 15:00:39Z /C=EU/ST=EU/CN=Patrick McHardy/emailAddress=kaber@trash.net $
+ */
+
+#include <getopt.h>
+#include <sys/errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "ip6tables.h"
+#include "libiptc/libip6tc.h"
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...) 
+#endif
+
+static int binary = 0, counters = 0, verbose = 0, noflush = 0;
+
+/* Keeping track of external matches and targets.  */
+static struct option options[] = {
+       { "binary", 0, 0, 'b' },
+       { "counters", 0, 0, 'c' },
+       { "verbose", 0, 0, 'v' },
+       { "test", 0, 0, 't' },
+       { "help", 0, 0, 'h' },
+       { "noflush", 0, 0, 'n'},
+       { "modprobe", 1, 0, 'M'},
+       { 0 }
+};
+
+static void print_usage(const char *name, const char *version) __attribute__((noreturn));
+
+static void print_usage(const char *name, const char *version)
+{
+       fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
+                       "          [ --binary ]\n"
+                       "          [ --counters ]\n"
+                       "          [ --verbose ]\n"
+                       "          [ --test ]\n"
+                       "          [ --help ]\n"
+                       "          [ --noflush ]\n"
+                       "          [ --modprobe=<command>]\n", name);
+               
+       exit(1);
+}
+
+ip6tc_handle_t create_handle(const char *tablename, const char* modprobe)
+{
+       ip6tc_handle_t handle;
+
+       handle = ip6tc_init(tablename);
+
+       if (!handle) {
+               /* try to insmod the module if iptc_init failed */
+               ip6tables_insmod("ip6_tables", modprobe, 0);
+               handle = ip6tc_init(tablename);
+       }
+
+       if (!handle) {
+               exit_error(PARAMETER_PROBLEM, "%s: unable to initialize "
+                       "table '%s'\n", program_name, tablename);
+               exit(1);
+       }
+       return handle;
+}
+
+static int parse_counters(char *string, struct ip6t_counters *ctr)
+{
+       return (sscanf(string, "[%llu:%llu]", (unsigned long long *)&ctr->pcnt, (unsigned long long *)&ctr->bcnt) == 2);
+}
+
+/* global new argv and argc */
+static char *newargv[255];
+static int newargc;
+
+/* function adding one argument to newargv, updating newargc 
+ * returns true if argument added, false otherwise */
+static int add_argv(char *what) {
+       DEBUGP("add_argv: %s\n", what);
+       if (what && ((newargc + 1) < sizeof(newargv)/sizeof(char *))) {
+               newargv[newargc] = strdup(what);
+               newargc++;
+               return 1;
+       } else 
+               return 0;
+}
+
+static void free_argv(void) {
+       int i;
+
+       for (i = 0; i < newargc; i++)
+               free(newargv[i]);
+}
+
+int main(int argc, char *argv[])
+{
+       ip6tc_handle_t handle = NULL;
+       char buffer[10240];
+       int c;
+       char curtable[IP6T_TABLE_MAXNAMELEN + 1];
+       FILE *in;
+       const char *modprobe = 0;
+       int in_table = 0, testing = 0;
+
+       program_name = "ip6tables-restore";
+       program_version = IPTABLES_VERSION;
+       line = 0;
+
+       lib_dir = getenv("IP6TABLES_LIB_DIR");
+       if (!lib_dir)
+               lib_dir = IP6T_LIB_DIR;
+
+#ifdef NO_SHARED_LIBS
+       init_extensions();
+#endif
+
+       while ((c = getopt_long(argc, argv, "bcvthnM:", options, NULL)) != -1) {
+               switch (c) {
+                       case 'b':
+                               binary = 1;
+                               break;
+                       case 'c':
+                               counters = 1;
+                               break;
+                       case 'v':
+                               verbose = 1;
+                               break;
+                       case 't':
+                               testing = 1;
+                               break;
+                       case 'h':
+                               print_usage("ip6tables-restore",
+                                           IPTABLES_VERSION);
+                               break;
+                       case 'n':
+                               noflush = 1;
+                               break;
+                       case 'M':
+                               modprobe = optarg;
+                               break;
+               }
+       }
+       
+       if (optind == argc - 1) {
+               in = fopen(argv[optind], "r");
+               if (!in) {
+                       fprintf(stderr, "Can't open %s: %s\n", argv[optind],
+                               strerror(errno));
+                       exit(1);
+               }
+       }
+       else if (optind < argc) {
+               fprintf(stderr, "Unknown arguments found on commandline\n");
+               exit(1);
+       }
+       else in = stdin;
+       
+       /* Grab standard input. */
+       while (fgets(buffer, sizeof(buffer), in)) {
+               int ret = 0;
+
+               line++;
+               if (buffer[0] == '\n')
+                       continue;
+               else if (buffer[0] == '#') {
+                       if (verbose)
+                               fputs(buffer, stdout);
+                       continue;
+               } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
+                       if (!testing) {
+                               DEBUGP("Calling commit\n");
+                               ret = ip6tc_commit(&handle);
+                       } else {
+                               DEBUGP("Not calling commit, testing\n");
+                               ret = 1;
+                       }
+                       in_table = 0;
+               } else if ((buffer[0] == '*') && (!in_table)) {
+                       /* New table */
+                       char *table;
+
+                       table = strtok(buffer+1, " \t\n");
+                       DEBUGP("line %u, table '%s'\n", line, table);
+                       if (!table) {
+                               exit_error(PARAMETER_PROBLEM, 
+                                       "%s: line %u table name invalid\n",
+                                       program_name, line);
+                               exit(1);
+                       }
+                       strncpy(curtable, table, IP6T_TABLE_MAXNAMELEN);
+                       curtable[IP6T_TABLE_MAXNAMELEN] = '\0';
+
+                       if (handle)
+                               ip6tc_free(&handle);
+
+                       handle = create_handle(table, modprobe);
+                       if (noflush == 0) {
+                               DEBUGP("Cleaning all chains of table '%s'\n",
+                                       table);
+                               for_each_chain(flush_entries, verbose, 1, 
+                                               &handle);
+       
+                               DEBUGP("Deleting all user-defined chains "
+                                      "of table '%s'\n", table);
+                               for_each_chain(delete_chain, verbose, 0, 
+                                               &handle) ;
+                       }
+
+                       ret = 1;
+                       in_table = 1;
+
+               } else if ((buffer[0] == ':') && (in_table)) {
+                       /* New chain. */
+                       char *policy, *chain;
+
+                       chain = strtok(buffer+1, " \t\n");
+                       DEBUGP("line %u, chain '%s'\n", line, chain);
+                       if (!chain) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u chain name invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+
+                       if (ip6tc_builtin(chain, handle) <= 0) {
+                               if (noflush && ip6tc_is_chain(chain, handle)) {
+                                       DEBUGP("Flushing existing user defined chain '%s'\n", chain);
+                                       if (!ip6tc_flush_entries(chain, &handle))
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "error flushing chain "
+                                                          "'%s':%s\n", chain,
+                                                          strerror(errno));
+                               } else {
+                                       DEBUGP("Creating new chain '%s'\n", chain);
+                                       if (!ip6tc_create_chain(chain, &handle))
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "error creating chain "
+                                                          "'%s':%s\n", chain,
+                                                          strerror(errno));
+                               }
+                       }
+
+                       policy = strtok(NULL, " \t\n");
+                       DEBUGP("line %u, policy '%s'\n", line, policy);
+                       if (!policy) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u policy invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+
+                       if (strcmp(policy, "-") != 0) {
+                               struct ip6t_counters count;
+
+                               if (counters) {
+                                       char *ctrs;
+                                       ctrs = strtok(NULL, " \t\n");
+
+                                       if (!ctrs || !parse_counters(ctrs, &count))
+                                               exit_error(PARAMETER_PROBLEM,
+                                                         "invalid policy counters "
+                                                         "for chain '%s'\n", chain);
+
+                               } else {
+                                       memset(&count, 0, 
+                                              sizeof(struct ip6t_counters));
+                               }
+
+                               DEBUGP("Setting policy of chain %s to %s\n",
+                                       chain, policy);
+
+                               if (!ip6tc_set_policy(chain, policy, &count,
+                                                    &handle))
+                                       exit_error(OTHER_PROBLEM,
+                                               "Can't set policy `%s'"
+                                               " on `%s' line %u: %s\n",
+                                               chain, policy, line,
+                                               ip6tc_strerror(errno));
+                       }
+
+                       ret = 1;
+
+               } else if (in_table) {
+                       int a;
+                       char *ptr = buffer;
+                       char *pcnt = NULL;
+                       char *bcnt = NULL;
+                       char *parsestart;
+
+                       /* the parser */
+                       char *param_start, *curchar;
+                       int quote_open;
+
+                       /* reset the newargv */
+                       newargc = 0;
+
+                       if (buffer[0] == '[') {
+                               /* we have counters in our input */
+                               ptr = strchr(buffer, ']');
+                               if (!ptr)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need ]\n",
+                                                  line);
+
+                               pcnt = strtok(buffer+1, ":");
+                               if (!pcnt)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need :\n",
+                                                  line);
+
+                               bcnt = strtok(NULL, "]");
+                               if (!bcnt)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need ]\n",
+                                                  line);
+
+                               /* start command parsing after counter */
+                               parsestart = ptr + 1;
+                       } else {
+                               /* start command parsing at start of line */
+                               parsestart = buffer;
+                       }
+
+                       add_argv(argv[0]);
+                       add_argv("-t");
+                       add_argv((char *) &curtable);
+                       
+                       if (counters && pcnt && bcnt) {
+                               add_argv("--set-counters");
+                               add_argv((char *) pcnt);
+                               add_argv((char *) bcnt);
+                       }
+
+                       /* After fighting with strtok enough, here's now
+                        * a 'real' parser. According to Rusty I'm now no
+                        * longer a real hacker, but I can live with that */
+
+                       quote_open = 0;
+                       param_start = parsestart;
+                       
+                       for (curchar = parsestart; *curchar; curchar++) {
+                               if (*curchar == '"') {
+                                       /* quote_open cannot be true if there
+                                        * was no previous character.  Thus, 
+                                        * curchar-1 has to be within bounds */
+                                       if (quote_open && 
+                                           *(curchar-1) != '\\') {
+                                               quote_open = 0;
+                                               *curchar = ' ';
+                                       } else {
+                                               quote_open = 1;
+                                               param_start++;
+                                       }
+                               } 
+                               if (*curchar == ' '
+                                   || *curchar == '\t'
+                                   || * curchar == '\n') {
+                                       char param_buffer[1024];
+                                       int param_len = curchar-param_start;
+
+                                       if (quote_open)
+                                               continue;
+
+                                       if (!param_len) {
+                                               /* two spaces? */
+                                               param_start++;
+                                               continue;
+                                       }
+                                       
+                                       /* end of one parameter */
+                                       strncpy(param_buffer, param_start,
+                                               param_len);
+                                       *(param_buffer+param_len) = '\0';
+
+                                       /* check if table name specified */
+                                       if (!strncmp(param_buffer, "-t", 3)
+                                            || !strncmp(param_buffer, "--table", 8)) {
+                                               exit_error(PARAMETER_PROBLEM, 
+                                                  "Line %u seems to have a "
+                                                  "-t table option.\n", line);
+                                               exit(1);
+                                       }
+
+                                       add_argv(param_buffer);
+                                       param_start += param_len + 1;
+                               } else {
+                                       /* regular character, skip */
+                               }
+                       }
+
+                       DEBUGP("calling do_command6(%u, argv, &%s, handle):\n",
+                               newargc, curtable);
+
+                       for (a = 0; a < newargc; a++)
+                               DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+
+                       ret = do_command6(newargc, newargv, 
+                                        &newargv[2], &handle);
+
+                       free_argv();
+               }
+               if (!ret) {
+                       fprintf(stderr, "%s: line %u failed\n",
+                                       program_name, line);
+                       exit(1);
+               }
+       }
+       if (in_table) {
+               fprintf(stderr, "%s: COMMIT expected at line %u\n",
+                               program_name, line + 1);
+               exit(1);
+       }
+
+       return 0;
+}
diff --git a/ip6tables-save.8 b/ip6tables-save.8
new file mode 100644 (file)
index 0000000..c8b3e96
--- /dev/null
@@ -0,0 +1,50 @@
+.TH IP6TABLES-SAVE 8 "Jan 30, 2002" "" ""
+.\"
+.\" Man page written by Harald Welte <laforge@gnumonks.org>
+.\" It is based on the iptables man page.
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ip6tables-save \- Save IPv6 Tables
+.SH SYNOPSIS
+.BR "ip6tables-save " "[-c] [-t table]"
+.br
+.SH DESCRIPTION
+.PP
+.B ip6tables-save
+is used to dump the contents of an IPv6 Table in easily parseable format
+to STDOUT. Use I/O-redirection provided by your shell to write to a file.
+.TP
+\fB\-c\fR, \fB\-\-counters\fR
+include the current values of all packet and byte counters in the output
+.TP
+\fB\-t\fR, \fB\-\-table\fR \fBtablename\fR
+.TP
+restrict output to only one table. If not specified, output includes all
+available tables.
+.SH BUGS
+None known as of iptables-1.2.1 release
+.SH AUTHORS
+Harald Welte <laforge@gnumonks.org>
+.br
+Andras Kis-Szabo <kisza@sch.bme.hu>
+.SH SEE ALSO
+.BR ip6tables-restore "(8), " ip6tables "(8) "
+.PP
+The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
+which details NAT, and the netfilter-hacking-HOWTO which details the
+internals.
diff --git a/ip6tables-save.c b/ip6tables-save.c
new file mode 100644 (file)
index 0000000..479a92b
--- /dev/null
@@ -0,0 +1,359 @@
+/* Code to save the ip6tables state, in human readable-form. */
+/* Author:  Andras Kis-Szabo <kisza@sch.bme.hu>
+ * Original code: iptables-save
+ * Authors: Paul 'Rusty' Russel <rusty@linuxcare.com.au> and
+ *         Harald Welte <laforge@gnumonks.org>
+ * This code is distributed under the terms of GNU GPL v2
+ */
+#include <getopt.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <time.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include "libiptc/libip6tc.h"
+#include "ip6tables.h"
+
+static int binary = 0, counters = 0;
+
+static struct option options[] = {
+       { "binary", 0, 0, 'b' },
+       { "counters", 0, 0, 'c' },
+       { "dump", 0, 0, 'd' },
+       { "table", 1, 0, 't' },
+       { 0 }
+};
+
+
+/* This assumes that mask is contiguous, and byte-bounded. */
+static void
+print_iface(char letter, const char *iface, const unsigned char *mask,
+           int invert)
+{
+       unsigned int i;
+
+       if (mask[0] == 0)
+               return;
+
+       printf("-%c %s", letter, invert ? "! " : "");
+
+       for (i = 0; i < IFNAMSIZ; i++) {
+               if (mask[i] != 0) {
+                       if (iface[i] != '\0')
+                               printf("%c", iface[i]);
+               } else {
+                       /* we can access iface[i-1] here, because 
+                        * a few lines above we make sure that mask[0] != 0 */
+                       if (iface[i-1] != '\0')
+                               printf("+");
+                       break;
+               }
+       }
+
+       printf(" ");
+}
+
+/* These are hardcoded backups in ip6tables.c, so they are safe */
+struct pprot {
+       char *name;
+       u_int8_t num;
+};
+
+static const struct pprot chain_protos[] = {
+       { "tcp", IPPROTO_TCP },
+       { "udp", IPPROTO_UDP },
+       { "icmpv6", IPPROTO_ICMPV6 },
+       { "esp", IPPROTO_ESP },
+       { "ah", IPPROTO_AH },
+};
+
+/* The ip6tables looks up the /etc/protocols. */
+static void print_proto(u_int16_t proto, int invert)
+{
+       if (proto) {
+               unsigned int i;
+               const char *invertstr = invert ? "! " : "";
+
+                struct protoent *pent = getprotobynumber(proto);
+                if (pent) {
+                       printf("-p %s%s ",
+                              invertstr, pent->p_name);
+                       return;
+               }
+
+               for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+                       if (chain_protos[i].num == proto) {
+                               printf("-p %s%s ",
+                                      invertstr, chain_protos[i].name);
+                               return;
+                       }
+
+               printf("-p %s%u ", invertstr, proto);
+       }
+}
+
+static int print_match(const struct ip6t_entry_match *e,
+                       const struct ip6t_ip6 *ip)
+{
+       struct ip6tables_match *match
+               = find_match(e->u.user.name, TRY_LOAD, NULL);
+
+       if (match) {
+               printf("-m %s ", e->u.user.name);
+
+               /* some matches don't provide a save function */
+               if (match->save)
+                       match->save(ip, e);
+       } else {
+               if (e->u.match_size) {
+                       fprintf(stderr,
+                               "Can't find library for match `%s'\n",
+                               e->u.user.name);
+                       exit(1);
+               }
+       }
+       return 0;
+}
+
+/* print a given ip including mask if neccessary */
+static void print_ip(char *prefix, const struct in6_addr *ip, const struct in6_addr *mask, int invert)
+{
+       char buf[51];
+       int l = ipv6_prefix_length(mask);
+
+       if (l == 0 && !invert)
+               return;
+
+       printf("%s %s%s",
+               prefix,
+               invert ? "! " : "",
+               inet_ntop(AF_INET6, ip, buf, sizeof buf));
+
+       if (l == -1)
+               printf("/%s ", inet_ntop(AF_INET6, mask, buf, sizeof buf));
+       else
+               printf("/%d ", l);
+}
+
+/* We want this to be readable, so only print out neccessary fields.
+ * Because that's the kind of world I want to live in.  */
+static void print_rule(const struct ip6t_entry *e, 
+               ip6tc_handle_t *h, const char *chain, int counters)
+{
+       struct ip6t_entry_target *t;
+       const char *target_name;
+
+       /* print counters */
+       if (counters)
+               printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+
+       /* print chain name */
+       printf("-A %s ", chain);
+
+       /* Print IP part. */
+       print_ip("-s", &(e->ipv6.src), &(e->ipv6.smsk),
+                       e->ipv6.invflags & IP6T_INV_SRCIP);     
+
+       print_ip("-d", &(e->ipv6.dst), &(e->ipv6.dmsk),
+                       e->ipv6.invflags & IP6T_INV_DSTIP);
+
+       print_iface('i', e->ipv6.iniface, e->ipv6.iniface_mask,
+                   e->ipv6.invflags & IP6T_INV_VIA_IN);
+
+       print_iface('o', e->ipv6.outiface, e->ipv6.outiface_mask,
+                   e->ipv6.invflags & IP6T_INV_VIA_OUT);
+
+       print_proto(e->ipv6.proto, e->ipv6.invflags & IP6T_INV_PROTO);
+
+#if 0
+       /* not definied in ipv6
+        * FIXME: linux/netfilter_ipv6/ip6_tables: IP6T_INV_FRAG why definied? */
+       if (e->ipv6.flags & IPT_F_FRAG)
+               printf("%s-f ",
+                      e->ipv6.invflags & IP6T_INV_FRAG ? "! " : "");
+#endif
+
+       if (e->ipv6.flags & IP6T_F_TOS)
+               printf("%s-? %d ",
+                      e->ipv6.invflags & IP6T_INV_TOS ? "! " : "", 
+                      e->ipv6.tos);
+
+       /* Print matchinfo part */
+       if (e->target_offset) {
+               IP6T_MATCH_ITERATE(e, print_match, &e->ipv6);
+       }
+
+       /* Print target name */ 
+       target_name = ip6tc_get_target(e, h);
+       if (target_name && (*target_name != '\0'))
+               printf("-j %s ", target_name);
+
+       /* Print targinfo part */
+       t = ip6t_get_target((struct ip6t_entry *)e);
+       if (t->u.user.name[0]) {
+               struct ip6tables_target *target
+                       = find_target(t->u.user.name, TRY_LOAD);
+
+               if (!target) {
+                       fprintf(stderr, "Can't find library for target `%s'\n",
+                               t->u.user.name);
+                       exit(1);
+               }
+
+               if (target->save)
+                       target->save(&e->ipv6, t);
+               else {
+                       /* If the target size is greater than ip6t_entry_target
+                        * there is something to be saved, we just don't know
+                        * how to print it */
+                       if (t->u.target_size != 
+                           sizeof(struct ip6t_entry_target)) {
+                               fprintf(stderr, "Target `%s' is missing "
+                                               "save function\n",
+                                       t->u.user.name);
+                               exit(1);
+                       }
+               }
+       }
+       printf("\n");
+}
+
+/* Debugging prototype. */
+static int for_each_table(int (*func)(const char *tablename))
+{
+        int ret = 1;
+       FILE *procfile = NULL;
+       char tablename[IP6T_TABLE_MAXNAMELEN+1];
+
+       procfile = fopen("/proc/net/ip6_tables_names", "r");
+       if (!procfile)
+               return 0;
+
+       while (fgets(tablename, sizeof(tablename), procfile)) {
+               if (tablename[strlen(tablename) - 1] != '\n')
+                       exit_error(OTHER_PROBLEM, 
+                                  "Badly formed tablename `%s'\n",
+                                  tablename);
+               tablename[strlen(tablename) - 1] = '\0';
+               ret &= func(tablename);
+       }
+
+       return ret;
+}
+       
+
+static int do_output(const char *tablename)
+{
+       ip6tc_handle_t h;
+       const char *chain = NULL;
+
+       if (!tablename)
+               return for_each_table(&do_output);
+
+       h = ip6tc_init(tablename);
+       if (!h)
+               exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",
+                          ip6tc_strerror(errno));
+
+       if (!binary) {
+               time_t now = time(NULL);
+
+               printf("# Generated by ip6tables-save v%s on %s",
+                      IPTABLES_VERSION, ctime(&now));
+               printf("*%s\n", tablename);
+
+               /* Dump out chain names first, 
+                * thereby preventing dependency conflicts */
+               for (chain = ip6tc_first_chain(&h);
+                    chain;
+                    chain = ip6tc_next_chain(&h)) {
+
+                       printf(":%s ", chain);
+                       if (ip6tc_builtin(chain, h)) {
+                               struct ip6t_counters count;
+                               printf("%s ",
+                                      ip6tc_get_policy(chain, &count, &h));
+                               printf("[%llu:%llu]\n", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
+                       } else {
+                               printf("- [0:0]\n");
+                       }
+               }
+
+
+               for (chain = ip6tc_first_chain(&h);
+                    chain;
+                    chain = ip6tc_next_chain(&h)) {
+                       const struct ip6t_entry *e;
+
+                       /* Dump out rules */
+                       e = ip6tc_first_rule(chain, &h);
+                       while(e) {
+                               print_rule(e, &h, chain, counters);
+                               e = ip6tc_next_rule(e, &h);
+                       }
+               }
+
+               now = time(NULL);
+               printf("COMMIT\n");
+               printf("# Completed on %s", ctime(&now));
+       } else {
+               /* Binary, huh?  OK. */
+               exit_error(OTHER_PROBLEM, "Binary NYI\n");
+       }
+
+       ip6tc_free(&h);
+
+       return 1;
+}
+
+/* Format:
+ * :Chain name POLICY packets bytes
+ * rule
+ */
+int main(int argc, char *argv[])
+{
+       const char *tablename = NULL;
+       int c;
+
+       program_name = "ip6tables-save";
+       program_version = IPTABLES_VERSION;
+
+       lib_dir = getenv("IP6TABLES_LIB_DIR");
+       if (!lib_dir)
+               lib_dir = IP6T_LIB_DIR;
+
+#ifdef NO_SHARED_LIBS
+       init_extensions();
+#endif
+
+       while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
+               switch (c) {
+               case 'b':
+                       binary = 1;
+                       break;
+
+               case 'c':
+                       counters = 1;
+                       break;
+
+               case 't':
+                       /* Select specific table. */
+                       tablename = optarg;
+                       break;
+               case 'd':
+                       do_output(tablename);
+                       exit(0);
+               }
+       }
+
+       if (optind < argc) {
+               fprintf(stderr, "Unknown arguments found on commandline\n");
+               exit(1);
+       }
+
+       return !do_output(tablename);
+}
diff --git a/ip6tables-standalone.c b/ip6tables-standalone.c
new file mode 100644 (file)
index 0000000..36dde33
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ *                 Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ *                 Marc Boucher <marc+nf@mbsi.ca>
+ *                 James Morris <jmorris@intercode.com.au>
+ *                 Harald Welte <laforge@gnumonks.org>
+ *                 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * Based on the ipchains code by Paul Russell and Michael Neuling
+ *
+ *     iptables -- IP firewall administration for kernels with
+ *     firewall table (aimed for the 2.3 kernels)
+ *
+ *     See the accompanying manual page iptables(8) for information
+ *     about proper usage of this program.
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ip6tables.h>
+
+int
+main(int argc, char *argv[])
+{
+       int ret;
+       char *table = "filter";
+       ip6tc_handle_t handle = NULL;
+
+       program_name = "ip6tables";
+       program_version = IPTABLES_VERSION;
+
+       lib_dir = getenv("IP6TABLES_LIB_DIR");
+       if (!lib_dir)
+               lib_dir = IP6T_LIB_DIR;
+
+#ifdef NO_SHARED_LIBS
+       init_extensions();
+#endif
+
+       ret = do_command6(argc, argv, &table, &handle);
+       if (ret)
+               ret = ip6tc_commit(&handle);
+
+       if (!ret)
+               fprintf(stderr, "ip6tables: %s\n",
+                       ip6tc_strerror(errno));
+
+       exit(!ret);
+}
diff --git a/ip6tables.8 b/ip6tables.8
new file mode 100644 (file)
index 0000000..4e58c93
--- /dev/null
@@ -0,0 +1,1031 @@
+.TH IP6TABLES 8 "Jan 22, 2006" "" ""
+.\"
+.\" Man page written by Andras Kis-Szabo <kisza@sch.bme.hu>
+.\" It is based on iptables man page.
+.\"
+.\" iptables page by Herve Eychenne <rv@wallfire.org>
+.\" It is based on ipchains man page.
+.\"
+.\" ipchains page by Paul ``Rusty'' Russell March 1997
+.\" Based on the original ipfwadm man page by Jos Vos <jos@xos.nl>
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ip6tables \- IPv6 packet filter administration
+.SH SYNOPSIS
+.BR "ip6tables [-t table] -[AD] " "chain rule-specification [options]"
+.br
+.BR "ip6tables [-t table] -I " "chain [rulenum] rule-specification [options]"
+.br
+.BR "ip6tables [-t table] -R " "chain rulenum rule-specification [options]"
+.br
+.BR "ip6tables [-t table] -D " "chain rulenum [options]"
+.br
+.BR "ip6tables [-t table] -[LFZ] " "[chain] [options]"
+.br
+.BR "ip6tables [-t table] -N " "chain"
+.br
+.BR "ip6tables [-t table] -X " "[chain]"
+.br
+.BR "ip6tables [-t table] -P " "chain target [options]"
+.br
+.BR "ip6tables [-t table] -E " "old-chain-name new-chain-name"
+.SH DESCRIPTION
+.B Ip6tables
+is used to set up, maintain, and inspect the tables of IPv6 packet
+filter rules in the Linux kernel.  Several different tables
+may be defined.  Each table contains a number of built-in
+chains and may also contain user-defined chains.
+
+Each chain is a list of rules which can match a set of packets.  Each
+rule specifies what to do with a packet that matches.  This is called
+a `target', which may be a jump to a user-defined chain in the same
+table.
+
+.SH TARGETS
+A firewall rule specifies criteria for a packet, and a target.  If the
+packet does not match, the next rule in the chain is the examined; if
+it does match, then the next rule is specified by the value of the
+target, which can be the name of a user-defined chain or one of the
+special values 
+.IR ACCEPT ,
+.IR DROP ,
+.IR QUEUE ,
+or
+.IR RETURN .
+.PP
+.I ACCEPT 
+means to let the packet through.
+.I DROP
+means to drop the packet on the floor.
+.I QUEUE
+means to pass the packet to userspace.  (How the packet can be received
+by a userspace process differs by the particular queue handler.  2.4.x
+and 2.6.x kernels up to 2.6.13 include the 
+.B
+ip_queue
+queue handler.  Kernels 2.6.14 and later additionally include the 
+.B
+nfnetlink_queue
+queue handler.  Packets with a target of QUEUE will be sent to queue number '0'
+in this case. Please also see the
+.B
+NFQUEUE
+target as described later in this man page.)
+.I RETURN
+means stop traversing this chain and resume at the next rule in the
+previous (calling) chain.  If the end of a built-in chain is reached
+or a rule in a built-in chain with target
+.I RETURN
+is matched, the target specified by the chain policy determines the
+fate of the packet.
+.SH TABLES
+There are currently two independent tables (which tables are present
+at any time depends on the kernel configuration options and which
+modules are present), as nat table has not been implemented yet.
+.TP
+.BI "-t, --table " "table"
+This option specifies the packet matching table which the command
+should operate on.  If the kernel is configured with automatic module
+loading, an attempt will be made to load the appropriate module for
+that table if it is not already there.
+
+The tables are as follows:
+.RS
+.TP .4i
+.BR "filter" :
+This is the default table (if no -t option is passed).  It contains
+the built-in chains
+.B INPUT
+(for packets coming into the box itself),
+.B FORWARD
+(for packets being routed through the box), and
+.B OUTPUT
+(for locally-generated packets).
+.TP
+.BR "mangle" :
+This table is used for specialized packet alteration.  Until kernel
+2.4.17 it had two built-in chains:
+.B PREROUTING
+(for altering incoming packets before routing) and
+.B OUTPUT
+(for altering locally-generated packets before routing).
+Since kernel 2.4.18, three other built-in chains are also supported:
+.B INPUT
+(for packets coming into the box itself),
+.B FORWARD
+(for altering packets being routed through the box), and
+.B POSTROUTING
+(for altering packets as they are about to go out).
+.TP
+.BR "raw" :
+This table is used mainly for configuring exemptions from connection
+tracking in combination with the NOTRACK target.  It registers at the netfilter
+hooks with higher priority and is thus called before nf_conntrack, or any other
+IP6 tables.  It provides the following built-in chains:
+.B PREROUTING
+(for packets arriving via any network interface)
+.B OUTPUT
+(for packets generated by local processes)
+.RE
+.SH OPTIONS
+The options that are recognized by
+.B ip6tables
+can be divided into several different groups.
+.SS COMMANDS
+These options specify the specific action to perform.  Only one of them
+can be specified on the command line unless otherwise specified
+below.  For all the long versions of the command and option names, you
+need to use only enough letters to ensure that
+.B ip6tables
+can differentiate it from all other options.
+.TP
+.BI "-A, --append " "chain rule-specification"
+Append one or more rules to the end of the selected chain.
+When the source and/or destination names resolve to more than one
+address, a rule will be added for each possible address combination.
+.TP
+.BI "-D, --delete " "chain rule-specification"
+.ns
+.TP
+.BI "-D, --delete " "chain rulenum"
+Delete one or more rules from the selected chain.  There are two
+versions of this command: the rule can be specified as a number in the
+chain (starting at 1 for the first rule) or a rule to match.
+.TP
+.B "-I, --insert"
+Insert one or more rules in the selected chain as the given rule
+number.  So, if the rule number is 1, the rule or rules are inserted
+at the head of the chain.  This is also the default if no rule number
+is specified.
+.TP
+.BI "-R, --replace " "chain rulenum rule-specification"
+Replace a rule in the selected chain.  If the source and/or
+destination names resolve to multiple addresses, the command will
+fail.  Rules are numbered starting at 1.
+.TP
+.BR "-L, --list " "[\fIchain\fP]"
+List all rules in the selected chain.  If no chain is selected, all
+chains are listed.  As every other iptables command, it applies to the
+specified table (filter is the default), so mangle rules get listed by
+.nf
+ ip6tables -t mangle -n -L
+.fi
+Please note that it is often used with the
+.B -n
+option, in order to avoid long reverse DNS lookups.
+It is legal to specify the
+.B -Z
+(zero) option as well, in which case the chain(s) will be atomically
+listed and zeroed.  The exact output is affected by the other
+arguments given. The exact rules are suppressed until you use
+.nf
+ ip6tables -L -v
+.fi
+.TP
+.BR "-F, --flush " "[\fIchain\fP]"
+Flush the selected chain (all the chains in the table if none is given).
+This is equivalent to deleting all the rules one by one.
+.TP
+.BR "-Z, --zero " "[\fIchain\fP]"
+Zero the packet and byte counters in all chains.  It is legal to
+specify the
+.B "-L, --list"
+(list) option as well, to see the counters immediately before they are
+cleared. (See above.)
+.TP
+.BI "-N, --new-chain " "chain"
+Create a new user-defined chain by the given name.  There must be no
+target of that name already.
+.TP
+.BR "-X, --delete-chain " "[\fIchain\fP]"
+Delete the optional user-defined chain specified.  There must be no references
+to the chain.  If there are, you must delete or replace the referring
+rules before the chain can be deleted.  If no argument is given, it
+will attempt to delete every non-builtin chain in the table.
+.TP
+.BI "-P, --policy " "chain target"
+Set the policy for the chain to the given target.  See the section
+.B TARGETS
+for the legal targets.  Only built-in (non-user-defined) chains can have
+policies, and neither built-in nor user-defined chains can be policy
+targets.
+.TP
+.BI "-E, --rename-chain " "old-chain new-chain"
+Rename the user specified chain to the user supplied name.  This is
+cosmetic, and has no effect on the structure of the table.
+.TP
+.B -h
+Help.
+Give a (currently very brief) description of the command syntax.
+.SS PARAMETERS
+The following parameters make up a rule specification (as used in the
+add, delete, insert, replace and append commands).
+.TP
+.BR "-p, --protocol " "[!] \fIprotocol\fP"
+The protocol of the rule or of the packet to check.
+The specified protocol can be one of
+.IR tcp ,
+.IR udp ,
+.IR icmpv6 ,
+.IR esp ,
+.IR all ,
+or it can be a numeric value, representing one of these protocols or a
+different one. A protocol name from /etc/protocols is also allowed.
+But IPv6 extension headers except
+.IR esp
+are not allowed.
+.IR esp ,
+and
+.IR ipv6-nonext
+can be used with Kernel version 2.6.11 or later.
+A "!" argument before the protocol inverts the
+test.  The number zero is equivalent to
+.IR all .
+Protocol
+.I all
+will match with all protocols and is taken as default when this
+option is omitted.
+.TP
+.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+Source specification.
+.I Address
+can be either a hostname (please note that specifying
+any name to be resolved with a remote query such as DNS is a really bad idea),
+a network IPv6 address (with /mask), or a plain IPv6 address.
+(the network name isn't supported now).
+The
+.I mask
+can be either a network mask or a plain number,
+specifying the number of 1's at the left side of the network mask.
+Thus, a mask of
+.I 64
+is equivalent to
+.IR ffff:ffff:ffff:ffff:0000:0000:0000:0000 .
+A "!" argument before the address specification inverts the sense of
+the address. The flag
+.B --src
+is an alias for this option.
+.TP
+.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]"
+Destination specification. 
+See the description of the
+.B -s
+(source) flag for a detailed description of the syntax.  The flag
+.B --dst
+is an alias for this option.
+.TP
+.BI "-j, --jump " "target"
+This specifies the target of the rule; i.e., what to do if the packet
+matches it.  The target can be a user-defined chain (other than the
+one this rule is in), one of the special builtin targets which decide
+the fate of the packet immediately, or an extension (see
+.B EXTENSIONS
+below).  If this
+option is omitted in a rule, then matching the rule will have no
+effect on the packet's fate, but the counters on the rule will be
+incremented.
+.TP
+.BR "-i, --in-interface " "[!] \fIname\fP"
+Name of an interface via which a packet is going to be received (only for
+packets entering the 
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains).  When the "!" argument is used before the interface name, the
+sense is inverted.  If the interface name ends in a "+", then any
+interface which begins with this name will match.  If this option is
+omitted, any interface name will match.
+.TP
+.BR "-o, --out-interface " "[!] \fIname\fP"
+Name of an interface via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD 
+and
+.B OUTPUT
+chains).  When the "!" argument is used before the interface name, the
+sense is inverted.  If the interface name ends in a "+", then any
+interface which begins with this name will match.  If this option is
+omitted, any interface name will match.
+.TP
+.\" Currently not supported (header-based)
+.\" 
+.\" .B "[!] " "-f, --fragment"
+.\" This means that the rule only refers to second and further fragments
+.\" of fragmented packets.  Since there is no way to tell the source or
+.\" destination ports of such a packet (or ICMP type), such a packet will
+.\" not match any rules which specify them.  When the "!" argument
+.\" precedes the "-f" flag, the rule will only match head fragments, or
+.\" unfragmented packets.
+.\" .TP
+.B "-c, --set-counters " "PKTS BYTES"
+This enables the administrator to initialize the packet and byte
+counters of a rule (during
+.B INSERT,
+.B APPEND,
+.B REPLACE
+operations).
+.SS "OTHER OPTIONS"
+The following additional options can be specified:
+.TP
+.B "-v, --verbose"
+Verbose output.  This option makes the list command show the interface
+name, the rule options (if any), and the TOS masks.  The packet and
+byte counters are also listed, with the suffix 'K', 'M' or 'G' for
+1000, 1,000,000 and 1,000,000,000 multipliers respectively (but see
+the
+.B -x
+flag to change this).
+For appending, insertion, deletion and replacement, this causes
+detailed information on the rule or rules to be printed.
+.TP
+.B "-n, --numeric"
+Numeric output.
+IP addresses and port numbers will be printed in numeric format.
+By default, the program will try to display them as host names,
+network names, or services (whenever applicable).
+.TP
+.B "-x, --exact"
+Expand numbers.
+Display the exact value of the packet and byte counters,
+instead of only the rounded number in K's (multiples of 1000)
+M's (multiples of 1000K) or G's (multiples of 1000M).  This option is
+only relevant for the 
+.B -L
+command.
+.TP
+.B "--line-numbers"
+When listing rules, add line numbers to the beginning of each rule,
+corresponding to that rule's position in the chain.
+.TP
+.B "--modprobe=command"
+When adding or inserting rules into a chain, use
+.B command
+to load any necessary modules (targets, match extensions, etc).
+.SH MATCH EXTENSIONS
+ip6tables can use extended packet matching modules.  These are loaded
+in two ways: implicitly, when
+.B -p
+or
+.B --protocol
+is specified, or with the
+.B -m
+or
+.B --match
+options, followed by the matching module name; after these, various
+extra command line options become available, depending on the specific
+module.  You can specify multiple extended match modules in one line,
+and you can use the
+.B -h
+or
+.B --help
+options after the module has been specified to receive help specific
+to that module.
+
+The following are included in the base package, and most of these can
+be preceded by a
+.B !
+to invert the sense of the match.
+.\" @MATCH@
+.SS ah
+This module matches the parameters in Authentication header of IPsec packets.
+.TP
+.BR "--ahspi " "[!] \fIspi\fP[:\fIspi\fP]"
+Matches SPI.
+.TP
+.BR "--ahlen " "[!] \fIlength"
+Total length of this header in octets.
+.TP
+.BI "--ahres"
+Matches if the reserved field is filled with zero.
+.SS condition
+This matches if a specific /proc filename is '0' or '1'.
+.TP
+.BR "--condition " "[!] \fIfilename"
+Match on boolean value stored in /proc/net/ip6t_condition/filename file
+.SS dst
+This module matches the parameters in Destination Options header
+.TP
+.BR "--dst-len " "[!] \fIlength"
+Total length of this header in octets.
+.TP
+.BR "--dst-opts " "\fItype\fP[:\fIlength\fP][,\fItype\fP[:\fIlength\fP]...]"
+numeric type of option and the length of the option data in octets.
+.SS esp
+This module matches the SPIs in ESP header of IPsec packets.
+.TP
+.BR "--espspi " "[!] \fIspi\fP[:\fIspi\fP]"
+.SS eui64
+This module matches the EUI-64 part of a stateless autoconfigured IPv6 address.
+It compares the EUI-64 derived from the source MAC address in Ehternet frame
+with the lower 64 bits of the IPv6 source address. But "Universal/Local"
+bit is not compared. This module doesn't match other link layer frame, and
+is only valid in the
+.BR PREROUTING ,
+.BR INPUT
+and
+.BR FORWARD
+chains.
+.SS frag
+This module matches the parameters in Fragment header.
+.TP
+.BR "--fragid " "[!] \fIid\fP[:\fIid\fP]"
+Matches the given Identification or range of it.
+.TP
+.BR "--fraglen " "[!] \fIlength\fP"
+This option cannot be used with kernel version 2.6.10 or later. The length of
+Fragment header is static and this option doesn't make sense.
+.TP
+.BR "--fragres "
+Matches if the reserved fields are filled with zero.
+.TP
+.BR "--fragfirst "
+Matches on the first fragment.
+.TP
+.BR "[--fragmore]"
+Matches if there are more fragments.
+.TP
+.BR "[--fraglast]"
+Matches if this is the last fragement.
+.SS fuzzy
+This module matches a rate limit based on a fuzzy logic controller [FLC]
+.TP
+.BI "--lower-limit " "number"
+Specifies the lower limit (in packets per second).
+.TP
+.BI "--upper-limit " "number"
+Specifies the upper limit (in packets per second).
+.SS hbh
+This module matches the parameters in Hop-by-Hop Options header
+.TP
+.BR "--hbh-len " "[!] \fIlength\fP"
+Total length of this header in octets.
+.TP
+.BR "--hbh-opts " "\fItype\fP[:\fIlength\fP][,\fItype\fP[:\fIlength\fP]...]"
+numeric type of option and the length of the option data in octets.
+.SS hl
+This module matches the Hop Limit field in the IPv6 header.
+.TP
+.BR "--hl-eq " "[!] \fIvalue\fP"
+Matches if Hop Limit equals \fIvalue\fP.
+.TP
+.BI "--hl-lt " "value"
+Matches if Hop Limit is less than \fIvalue\fP.
+.TP
+.BI "--hl-gt " "value"
+Matches if Hop Limit is greater than \fIvalue\fP.
+.SS icmpv6
+This extension is loaded if `--protocol ipv6-icmp' or `--protocol icmpv6' is
+specified. It provides the following option:
+.TP
+.BR "--icmpv6-type " "[!] \fItype\fP[/\fIcode\fP]|\fItypename\fP"
+This allows specification of the ICMPv6 type, which can be a numeric
+ICMPv6
+.IR type ,
+.IR type
+and
+.IR code ,
+or one of the ICMPv6 type names shown by the command
+.nf
+ ip6tables -p ipv6-icmp -h
+.fi
+.SS ipv6header
+This module matches IPv6 extension headers and/or upper layer header.
+.TP
+.BR "--header " "[!] \fIheader\fP[,\fIheader\fP...]"
+Matches the packet which EXACTLY includes all specified headers. The headers
+encapsulated with ESP header are out of scope.
+.IR header
+can be
+.IR hop | hop-by-hop
+(Hop-by-Hop Options header),
+.IR dst
+(Destination Options header),
+.IR route
+(Routing header),
+.IR frag
+(Fragment header),
+.IR auth
+(Authentication header),
+.IR esp
+(Encapsulating Security Payload header),
+.IR none
+(No Next header) which matches 59 in the 'Next Header field' of IPv6 header or any IPv6 extension headers, or
+.IR proto
+which matches any upper layer protocol header. A protocol name from /etc/protocols and numeric value also allowed. The number 255 is equivalent to
+.IR proto .
+.TP
+.BR "[--soft]"
+Matches if the packet includes all specified headers with
+.BR --header ,
+AT LEAST.
+.SS length
+This module matches the length of the IPv6 payload in octets, or range of it.
+IPv6 header itself isn't counted.
+.TP
+.BR "--length " "[!] \fIlength\fP[:\fIlength\fP]"
+.SS limit
+This module matches at a limited rate using a token bucket filter.
+A rule using this extension will match until this limit is reached
+(unless the `!' flag is used).  It can be used in combination with the
+.B LOG
+target to give limited logging, for example.
+.TP
+.BI "--limit " "rate"
+Maximum average matching rate: specified as a number, with an optional
+`/second', `/minute', `/hour', or `/day' suffix; the default is
+3/hour.
+.TP
+.BI "--limit-burst " "number"
+Maximum initial number of packets to match: this number gets
+recharged by one every time the limit specified above is not reached,
+up to this number; the default is 5.
+.SS mac
+.TP
+.BR "--mac-source " "[!] \fIaddress\fP"
+Match source MAC address.  It must be of the form XX:XX:XX:XX:XX:XX.
+Note that this only makes sense for packets coming from an Ethernet device
+and entering the
+.BR PREROUTING ,
+.B FORWARD
+or
+.B INPUT
+chains.
+.SS mark
+This module matches the netfilter mark field associated with a packet
+(which can be set using the
+.B MARK
+target below).
+.TP
+.BR "--mark " "\fIvalue\fP[/\fImask\fP]"
+Matches packets with the given unsigned mark value (if a \fImask\fP is
+specified, this is logically ANDed with the \fImask\fP before the
+comparison).
+.SS multiport
+This module matches a set of source or destination ports.  Up to 15
+ports can be specified.  A port range (port:port) counts as two
+ports, but range isn't supported now. It can only be used in conjunction
+with
+.B "-p tcp"
+or
+.BR "-p udp" .
+.TP
+.BR "--source-ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if the source port is one of the given ports.  The flag
+.B --sports
+is a convenient alias for this option.
+.TP
+.BR "--destination-ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if the destination port is one of the given ports.  The flag
+.B --dports
+is a convenient alias for this option.
+.TP
+.BR "--ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if the both the source and destination ports are equal to each
+other and to one of the given ports.
+.SS nth
+This module matches every `n'th packet
+.TP
+.BI "--every " "value"
+Match every `value' packet
+.TP
+.BI "[" "--counter " "num" "]"
+Use internal counter number `num'.  Default is `0'.
+.TP
+.BI "[" "--start " "num" "]"
+Initialize the counter at the number `num' insetad of `0'.  Most between `0'
+and `value'-1.
+.TP
+.BI "[" "--packet " "num" "]"
+Match on `num' packet.  Most be between `0' and `value'-1.
+.SS owner
+This module attempts to match various characteristics of the packet
+creator, for locally-generated packets.  It is only valid in the
+.B OUTPUT
+chain, and even this some packets (such as ICMPv6 ping responses) may
+have no owner, and hence never match.  This is regarded as experimental.
+.TP
+.BI "--uid-owner " "userid"
+Matches if the packet was created by a process with the given
+effective user id.
+.TP
+.BI "--gid-owner " "groupid"
+Matches if the packet was created by a process with the given
+effective group id.
+.TP
+.BI "--pid-owner " "processid"
+Matches if the packet was created by a process with the given
+process id.
+.TP
+.BI "--sid-owner " "sessionid"
+Matches if the packet was created by a process in the given session
+group.
+.TP
+.B NOTE: pid, sid and command matching are broken on SMP
+.SS physdev
+This module matches on the bridge port input and output devices enslaved
+to a bridge device. This module is a part of the infrastructure that enables
+a transparent bridging IP firewall and is only useful for kernel versions
+above version 2.5.44.
+.TP
+.BR --physdev-in " [!] \fIname\fP"
+Name of a bridge port via which a packet is received (only for
+packets entering the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains). If the interface name ends in a "+", then any
+interface which begins with this name will match. If the packet didn't arrive
+through a bridge device, this packet won't match this option, unless '!' is used.
+.TP
+.BR --physdev-out " [!] \fIname\fP"
+Name of a bridge port via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD ,
+.B OUTPUT
+and
+.B POSTROUTING
+chains).  If the interface name ends in a "+", then any
+interface which begins with this name will match. Note that in the
+.BR nat " and " mangle
+.B OUTPUT
+chains one cannot match on the bridge output port, however one can in the
+.B "filter OUTPUT"
+chain. If the packet won't leave by a bridge device or it is yet unknown what
+the output device will be, then the packet won't match this option, unless
+'!' is used.
+.TP
+.RB "[!] " --physdev-is-in
+Matches if the packet has entered through a bridge interface.
+.TP
+.RB "[!] " --physdev-is-out
+Matches if the packet will leave through a bridge interface.
+.TP
+.RB "[!] " --physdev-is-bridged
+Matches if the packet is being bridged and therefore is not being routed.
+This is only useful in the FORWARD and POSTROUTING chains.
+.SS policy
+This modules matches the policy used by IPsec for handling a packet.
+.TP
+.BI "--dir " "in|out"
+Used to select whether to match the policy used for decapsulation or the
+policy that will be used for encapsulation.
+.B in
+is valid in the
+.B PREROUTING, INPUT and FORWARD
+chains,
+.B out
+is valid in the
+.B POSTROUTING, OUTPUT and FORWARD
+chains.
+.TP
+.BI "--pol " "none|ipsec"
+Matches if the packet is subject to IPsec processing.
+.TP
+.BI "--strict"
+Selects whether to match the exact policy or match if any rule of
+the policy matches the given policy.
+.TP
+.BI "--reqid " "id"
+Matches the reqid of the policy rule. The reqid can be specified with
+.B setkey(8)
+using
+.B unique:id
+as level.
+.TP
+.BI "--spi " "spi"
+Matches the SPI of the SA.
+.TP
+.BI "--proto " "ah|esp|ipcomp"
+Matches the encapsulation protocol.
+.TP
+.BI "--mode " "tunnel|transport"
+Matches the encapsulation mode.
+.TP
+.BI "--tunnel-src " "addr[/mask]"
+Matches the source end-point address of a tunnel mode SA.
+Only valid with --mode tunnel.
+.TP
+.BI "--tunnel-dst " "addr[/mask]"
+Matches the destination end-point address of a tunnel mode SA.
+Only valid with --mode tunnel.
+.TP
+.BI "--next"
+Start the next element in the policy specification. Can only be used with
+--strict
+.SS random
+This module randomly matches a certain percentage of all packets.
+.TP
+.BI "--average " "percent"
+Matches the given percentage.  If omitted, a probability of 50% is set. 
+.SS rt
+Match on IPv6 routing header
+.TP
+.BR "--rt-type" " [!] \fItype\fP"
+Match the type (numeric).
+.TP
+.BR "--rt-segsleft" " [!] \fInum\fP[:\fInum\fP]"
+Match the `segments left' field (range).
+.TP
+.BR "--rt-len" " [!] \fIlength\fP"
+Match the length of this header.
+.TP
+.BR "--rt-0-res"
+Match the reserved field, too (type=0)
+.TP
+.BR "--rt-0-addrs" " \fIADDR\fP[,\fIADDR\fP...]"
+Match type=0 addresses (list).
+.TP
+.BR "--rt-0-not-strict"
+List of type=0 addresses is not a strict list.
+.SS tcp
+These extensions are loaded if `--protocol tcp' is specified. It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification. This can either be a service
+name or a port number. An inclusive range can also be specified,
+using the format
+.IR port : port .
+If the first port is omitted, "0" is assumed; if the last is omitted,
+"65535" is assumed.
+If the second port greater then the first they will be swapped.
+The flag
+.B --sport
+is a convenient alias for this option.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification.  The flag
+.B --dport
+is a convenient alias for this option.
+.TP
+.BR "--tcp-flags " "[!] \fImask\fP \fIcomp\fP"
+Match when the TCP flags are as specified.  The first argument is the
+flags which we should examine, written as a comma-separated list, and
+the second argument is a comma-separated list of flags which must be
+set.  Flags are: 
+.BR "SYN ACK FIN RST URG PSH ALL NONE" .
+Hence the command
+.nf
+ ip6tables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST SYN
+.fi
+will only match packets with the SYN flag set, and the ACK, FIN and
+RST flags unset.
+.TP
+.B "[!] --syn"
+Only match TCP packets with the SYN bit set and the ACK and RST bits
+cleared.  Such packets are used to request TCP connection initiation;
+for example, blocking such packets coming in an interface will prevent
+incoming TCP connections, but outgoing TCP connections will be
+unaffected.
+It is equivalent to \fB--tcp-flags SYN,RST,ACK SYN\fP.
+If the "!" flag precedes the "--syn", the sense of the
+option is inverted.
+.TP
+.BR "--tcp-option " "[!] \fInumber\fP"
+Match if TCP option set.
+.SS udp
+These extensions are loaded if `--protocol udp' is specified.  It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification.
+See the description of the
+.B --source-port
+option of the TCP extension for details.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification.
+See the description of the
+.B --destination-port
+option of the TCP extension for details.
+.SH TARGET EXTENSIONS
+ip6tables can use extended target modules: the following are included
+in the standard distribution.
+.\" @TARGET@
+.SS HL
+This is used to modify the Hop Limit field in IPv6 header. The Hop Limit field
+is similar to what is known as TTL value in IPv4.  Setting or incrementing the
+Hop Limit field can potentially be very dangerous, so it should be avoided at
+any cost. This target is only valid in
+.B mangle
+table.
+.TP
+.B Don't ever set or increment the value on packets that leave your local network!
+.TP
+.BI "--hl-set " "value"
+Set the Hop Limit to `value'.
+.TP
+.BI "--hl-dec " "value"
+Decrement the Hop Limit `value' times.
+.TP
+.BI "--hl-inc " "value"
+Increment the Hop Limit `value' times.
+.SS LOG
+Turn on kernel logging of matching packets.  When this option is set
+for a rule, the Linux kernel will print some information on all
+matching packets (like most IPv6 IPv6-header fields) via the kernel log
+(where it can be read with
+.I dmesg
+or 
+.IR syslogd (8)).
+This is a "non-terminating target", i.e. rule traversal continues at
+the next rule.  So if you want to LOG the packets you refuse, use two
+separate rules with the same matching criteria, first using target LOG
+then DROP (or REJECT).
+.TP
+.BI "--log-level " "level"
+Level of logging (numeric or see \fIsyslog.conf\fP(5)).
+.TP
+.BI "--log-prefix " "prefix"
+Prefix log messages with the specified prefix; up to 29 letters long,
+and useful for distinguishing messages in the logs.
+.TP
+.B --log-tcp-sequence
+Log TCP sequence numbers. This is a security risk if the log is
+readable by users.
+.TP
+.B --log-tcp-options
+Log options from the TCP packet header.
+.TP
+.B --log-ip-options
+Log options from the IPv6 packet header.
+.TP
+.B --log-uid
+Log the userid of the process which generated the packet.
+.SS MARK
+This is used to set the netfilter mark value associated with the
+packet.  It is only valid in the
+.B mangle
+table.
+.TP
+.BI "--set-mark " "mark"
+.SS NFQUEUE
+This target is an extension of the QUEUE target. As opposed to QUEUE, it allows
+you to put a packet into any specific queue, identified by its 16-bit queue
+number.  
+.TP
+.BR "--queue-num " "\fIvalue"
+This specifies the QUEUE number to use. Valud queue numbers are 0 to 65535. The default value is 0.
+.TP
+It can only be used with Kernel versions 2.6.14 or later, since it requires
+the
+.B
+nfnetlink_queue
+kernel support.
+.SS REJECT
+This is used to send back an error packet in response to the matched
+packet: otherwise it is equivalent to 
+.B DROP
+so it is a terminating TARGET, ending rule traversal.
+This target is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains.  The following option controls the nature of the error packet
+returned:
+.TP
+.BI "--reject-with " "type"
+The type given can be
+.nf
+.B " icmp6-no-route"
+.B " no-route"
+.B " icmp6-adm-prohibited"
+.B " adm-prohibited"
+.B " icmp6-addr-unreachable"
+.B " addr-unreach"
+.B " icmp6-port-unreachable"
+.B " port-unreach"
+.fi
+which return the appropriate ICMPv6 error message (\fBport-unreach\fP is
+the default). Finally, the option
+.B tcp-reset
+can be used on rules which only match the TCP protocol: this causes a
+TCP RST packet to be sent back.  This is mainly useful for blocking 
+.I ident
+(113/tcp) probes which frequently occur when sending mail to broken mail
+hosts (which won't accept your mail otherwise).
+.B tcp-reset
+can only be used with kernel versions 2.6.14 or latter.
+
+.SS ROUTE
+This is used to explicitly override the core network stack's routing decision.
+.B mangle
+table.
+.TP
+.BI "--oif " "ifname"
+Route the packet through `ifname' network interface
+.TP
+.BI "--gw " "IPv6_address"
+Route the packet via this gateway
+.TP
+.BI "--continue "
+Behave like a non-terminating target and continue traversing the rules. Not valid in combination with `--tee'
+.TP
+.BI "--tee "
+Make a copy of the packet, and route that copy to the given destination. For the original, uncopied packet, behave like a non-terminating target and continue traversing the rules.  Not valid in combination with `--continue'
+.SS TRACE
+This target has no options.  It just turns on 
+.B packet tracing
+for all packets that match this rule.
+.SH DIAGNOSTICS
+Various error messages are printed to standard error.  The exit code
+is 0 for correct functioning.  Errors which appear to be caused by
+invalid or abused command line parameters cause an exit code of 2, and
+other errors cause an exit code of 1.
+.SH BUGS
+Bugs?  What's this? ;-)
+Well... the counters are not reliable on sparc64.
+.SH COMPATIBILITY WITH IPCHAINS
+This 
+.B ip6tables
+is very similar to ipchains by Rusty Russell.  The main difference is
+that the chains 
+.B INPUT
+and
+.B OUTPUT
+are only traversed for packets coming into the local host and
+originating from the local host respectively.  Hence every packet only
+passes through one of the three chains (except loopback traffic, which
+involves both INPUT and OUTPUT chains); previously a forwarded packet
+would pass through all three.
+.PP
+The other main difference is that 
+.B -i
+refers to the input interface;
+.B -o
+refers to the output interface, and both are available for packets
+entering the
+.B FORWARD
+chain.
+.\" .PP The various forms of NAT have been separated out; 
+.\" .B iptables 
+.\" is a pure packet filter when using the default `filter' table, with
+.\" optional extension modules.  This should simplify much of the previous
+.\" confusion over the combination of IP masquerading and packet filtering
+.\" seen previously.  So the following options are handled differently:
+.\" .br
+.\" -j MASQ
+.\" .br
+.\" -M -S
+.\" .br
+.\" -M -L
+.\" .br
+There are several other changes in ip6tables.
+.SH SEE ALSO
+.BR ip6tables-save (8),
+.BR ip6tables-restore(8),
+.BR iptables (8),
+.BR iptables-save (8),
+.BR iptables-restore (8),
+.BR libipq (3).
+.P
+The packet-filtering-HOWTO details iptables usage for
+packet filtering, the NAT-HOWTO details NAT,
+the netfilter-extensions-HOWTO details the extensions that are
+not in the standard distribution,
+and the netfilter-hacking-HOWTO details the netfilter internals.
+.br
+See
+.BR "http://www.netfilter.org/" .
+.SH AUTHORS
+Rusty Russell wrote iptables, in early consultation with Michael
+Neuling.
+.PP
+Marc Boucher made Rusty abandon ipnatctl by lobbying for a generic packet
+selection framework in iptables, then wrote the mangle table, the owner match,
+the mark stuff, and ran around doing cool stuff everywhere.
+.PP
+James Morris wrote the TOS target, and tos match.
+.PP
+Jozsef Kadlecsik wrote the REJECT target.
+.PP
+Harald Welte wrote the ULOG and NFQUEUE target, the new libiptc, aswell as TTL match+target and libipulog.
+.PP
+The Netfilter Core Team is: Marc Boucher, Martin Josefsson, Jozsef Kadlecsik,
+James Morris, Harald Welte and Rusty Russell.
+.PP
+ip6tables man page created by Andras Kis-Szabo, based on
+iptables man page written by Herve Eychenne <rv@wallfire.org>.
+.\" .. and did I mention that we are incredibly cool people?
+.\" .. sexy, too ..
+.\" .. witty, charming, powerful ..
+.\" .. and most of all, modest ..
diff --git a/ip6tables.8.in b/ip6tables.8.in
new file mode 100644 (file)
index 0000000..8528b8e
--- /dev/null
@@ -0,0 +1,492 @@
+.TH IP6TABLES 8 "Jan 22, 2006" "" ""
+.\"
+.\" Man page written by Andras Kis-Szabo <kisza@sch.bme.hu>
+.\" It is based on iptables man page.
+.\"
+.\" iptables page by Herve Eychenne <rv@wallfire.org>
+.\" It is based on ipchains man page.
+.\"
+.\" ipchains page by Paul ``Rusty'' Russell March 1997
+.\" Based on the original ipfwadm man page by Jos Vos <jos@xos.nl>
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ip6tables \- IPv6 packet filter administration
+.SH SYNOPSIS
+.BR "ip6tables [-t table] -[AD] " "chain rule-specification [options]"
+.br
+.BR "ip6tables [-t table] -I " "chain [rulenum] rule-specification [options]"
+.br
+.BR "ip6tables [-t table] -R " "chain rulenum rule-specification [options]"
+.br
+.BR "ip6tables [-t table] -D " "chain rulenum [options]"
+.br
+.BR "ip6tables [-t table] -[LFZ] " "[chain] [options]"
+.br
+.BR "ip6tables [-t table] -N " "chain"
+.br
+.BR "ip6tables [-t table] -X " "[chain]"
+.br
+.BR "ip6tables [-t table] -P " "chain target [options]"
+.br
+.BR "ip6tables [-t table] -E " "old-chain-name new-chain-name"
+.SH DESCRIPTION
+.B Ip6tables
+is used to set up, maintain, and inspect the tables of IPv6 packet
+filter rules in the Linux kernel.  Several different tables
+may be defined.  Each table contains a number of built-in
+chains and may also contain user-defined chains.
+
+Each chain is a list of rules which can match a set of packets.  Each
+rule specifies what to do with a packet that matches.  This is called
+a `target', which may be a jump to a user-defined chain in the same
+table.
+
+.SH TARGETS
+A firewall rule specifies criteria for a packet, and a target.  If the
+packet does not match, the next rule in the chain is the examined; if
+it does match, then the next rule is specified by the value of the
+target, which can be the name of a user-defined chain or one of the
+special values 
+.IR ACCEPT ,
+.IR DROP ,
+.IR QUEUE ,
+or
+.IR RETURN .
+.PP
+.I ACCEPT 
+means to let the packet through.
+.I DROP
+means to drop the packet on the floor.
+.I QUEUE
+means to pass the packet to userspace.  (How the packet can be received
+by a userspace process differs by the particular queue handler.  2.4.x
+and 2.6.x kernels up to 2.6.13 include the 
+.B
+ip_queue
+queue handler.  Kernels 2.6.14 and later additionally include the 
+.B
+nfnetlink_queue
+queue handler.  Packets with a target of QUEUE will be sent to queue number '0'
+in this case. Please also see the
+.B
+NFQUEUE
+target as described later in this man page.)
+.I RETURN
+means stop traversing this chain and resume at the next rule in the
+previous (calling) chain.  If the end of a built-in chain is reached
+or a rule in a built-in chain with target
+.I RETURN
+is matched, the target specified by the chain policy determines the
+fate of the packet.
+.SH TABLES
+There are currently two independent tables (which tables are present
+at any time depends on the kernel configuration options and which
+modules are present), as nat table has not been implemented yet.
+.TP
+.BI "-t, --table " "table"
+This option specifies the packet matching table which the command
+should operate on.  If the kernel is configured with automatic module
+loading, an attempt will be made to load the appropriate module for
+that table if it is not already there.
+
+The tables are as follows:
+.RS
+.TP .4i
+.BR "filter" :
+This is the default table (if no -t option is passed).  It contains
+the built-in chains
+.B INPUT
+(for packets coming into the box itself),
+.B FORWARD
+(for packets being routed through the box), and
+.B OUTPUT
+(for locally-generated packets).
+.TP
+.BR "mangle" :
+This table is used for specialized packet alteration.  Until kernel
+2.4.17 it had two built-in chains:
+.B PREROUTING
+(for altering incoming packets before routing) and
+.B OUTPUT
+(for altering locally-generated packets before routing).
+Since kernel 2.4.18, three other built-in chains are also supported:
+.B INPUT
+(for packets coming into the box itself),
+.B FORWARD
+(for altering packets being routed through the box), and
+.B POSTROUTING
+(for altering packets as they are about to go out).
+.TP
+.BR "raw" :
+This table is used mainly for configuring exemptions from connection
+tracking in combination with the NOTRACK target.  It registers at the netfilter
+hooks with higher priority and is thus called before nf_conntrack, or any other
+IP6 tables.  It provides the following built-in chains:
+.B PREROUTING
+(for packets arriving via any network interface)
+.B OUTPUT
+(for packets generated by local processes)
+.RE
+.SH OPTIONS
+The options that are recognized by
+.B ip6tables
+can be divided into several different groups.
+.SS COMMANDS
+These options specify the specific action to perform.  Only one of them
+can be specified on the command line unless otherwise specified
+below.  For all the long versions of the command and option names, you
+need to use only enough letters to ensure that
+.B ip6tables
+can differentiate it from all other options.
+.TP
+.BI "-A, --append " "chain rule-specification"
+Append one or more rules to the end of the selected chain.
+When the source and/or destination names resolve to more than one
+address, a rule will be added for each possible address combination.
+.TP
+.BI "-D, --delete " "chain rule-specification"
+.ns
+.TP
+.BI "-D, --delete " "chain rulenum"
+Delete one or more rules from the selected chain.  There are two
+versions of this command: the rule can be specified as a number in the
+chain (starting at 1 for the first rule) or a rule to match.
+.TP
+.B "-I, --insert"
+Insert one or more rules in the selected chain as the given rule
+number.  So, if the rule number is 1, the rule or rules are inserted
+at the head of the chain.  This is also the default if no rule number
+is specified.
+.TP
+.BI "-R, --replace " "chain rulenum rule-specification"
+Replace a rule in the selected chain.  If the source and/or
+destination names resolve to multiple addresses, the command will
+fail.  Rules are numbered starting at 1.
+.TP
+.BR "-L, --list " "[\fIchain\fP]"
+List all rules in the selected chain.  If no chain is selected, all
+chains are listed.  As every other iptables command, it applies to the
+specified table (filter is the default), so mangle rules get listed by
+.nf
+ ip6tables -t mangle -n -L
+.fi
+Please note that it is often used with the
+.B -n
+option, in order to avoid long reverse DNS lookups.
+It is legal to specify the
+.B -Z
+(zero) option as well, in which case the chain(s) will be atomically
+listed and zeroed.  The exact output is affected by the other
+arguments given. The exact rules are suppressed until you use
+.nf
+ ip6tables -L -v
+.fi
+.TP
+.BR "-F, --flush " "[\fIchain\fP]"
+Flush the selected chain (all the chains in the table if none is given).
+This is equivalent to deleting all the rules one by one.
+.TP
+.BR "-Z, --zero " "[\fIchain\fP]"
+Zero the packet and byte counters in all chains.  It is legal to
+specify the
+.B "-L, --list"
+(list) option as well, to see the counters immediately before they are
+cleared. (See above.)
+.TP
+.BI "-N, --new-chain " "chain"
+Create a new user-defined chain by the given name.  There must be no
+target of that name already.
+.TP
+.BR "-X, --delete-chain " "[\fIchain\fP]"
+Delete the optional user-defined chain specified.  There must be no references
+to the chain.  If there are, you must delete or replace the referring
+rules before the chain can be deleted.  If no argument is given, it
+will attempt to delete every non-builtin chain in the table.
+.TP
+.BI "-P, --policy " "chain target"
+Set the policy for the chain to the given target.  See the section
+.B TARGETS
+for the legal targets.  Only built-in (non-user-defined) chains can have
+policies, and neither built-in nor user-defined chains can be policy
+targets.
+.TP
+.BI "-E, --rename-chain " "old-chain new-chain"
+Rename the user specified chain to the user supplied name.  This is
+cosmetic, and has no effect on the structure of the table.
+.TP
+.B -h
+Help.
+Give a (currently very brief) description of the command syntax.
+.SS PARAMETERS
+The following parameters make up a rule specification (as used in the
+add, delete, insert, replace and append commands).
+.TP
+.BR "-p, --protocol " "[!] \fIprotocol\fP"
+The protocol of the rule or of the packet to check.
+The specified protocol can be one of
+.IR tcp ,
+.IR udp ,
+.IR icmpv6 ,
+.IR esp ,
+.IR all ,
+or it can be a numeric value, representing one of these protocols or a
+different one. A protocol name from /etc/protocols is also allowed.
+But IPv6 extension headers except
+.IR esp
+are not allowed.
+.IR esp ,
+and
+.IR ipv6-nonext
+can be used with Kernel version 2.6.11 or later.
+A "!" argument before the protocol inverts the
+test.  The number zero is equivalent to
+.IR all .
+Protocol
+.I all
+will match with all protocols and is taken as default when this
+option is omitted.
+.TP
+.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+Source specification.
+.I Address
+can be either a hostname (please note that specifying
+any name to be resolved with a remote query such as DNS is a really bad idea),
+a network IPv6 address (with /mask), or a plain IPv6 address.
+(the network name isn't supported now).
+The
+.I mask
+can be either a network mask or a plain number,
+specifying the number of 1's at the left side of the network mask.
+Thus, a mask of
+.I 64
+is equivalent to
+.IR ffff:ffff:ffff:ffff:0000:0000:0000:0000 .
+A "!" argument before the address specification inverts the sense of
+the address. The flag
+.B --src
+is an alias for this option.
+.TP
+.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]"
+Destination specification. 
+See the description of the
+.B -s
+(source) flag for a detailed description of the syntax.  The flag
+.B --dst
+is an alias for this option.
+.TP
+.BI "-j, --jump " "target"
+This specifies the target of the rule; i.e., what to do if the packet
+matches it.  The target can be a user-defined chain (other than the
+one this rule is in), one of the special builtin targets which decide
+the fate of the packet immediately, or an extension (see
+.B EXTENSIONS
+below).  If this
+option is omitted in a rule, then matching the rule will have no
+effect on the packet's fate, but the counters on the rule will be
+incremented.
+.TP
+.BR "-i, --in-interface " "[!] \fIname\fP"
+Name of an interface via which a packet is going to be received (only for
+packets entering the 
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains).  When the "!" argument is used before the interface name, the
+sense is inverted.  If the interface name ends in a "+", then any
+interface which begins with this name will match.  If this option is
+omitted, any interface name will match.
+.TP
+.BR "-o, --out-interface " "[!] \fIname\fP"
+Name of an interface via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD 
+and
+.B OUTPUT
+chains).  When the "!" argument is used before the interface name, the
+sense is inverted.  If the interface name ends in a "+", then any
+interface which begins with this name will match.  If this option is
+omitted, any interface name will match.
+.TP
+.\" Currently not supported (header-based)
+.\" 
+.\" .B "[!] " "-f, --fragment"
+.\" This means that the rule only refers to second and further fragments
+.\" of fragmented packets.  Since there is no way to tell the source or
+.\" destination ports of such a packet (or ICMP type), such a packet will
+.\" not match any rules which specify them.  When the "!" argument
+.\" precedes the "-f" flag, the rule will only match head fragments, or
+.\" unfragmented packets.
+.\" .TP
+.B "-c, --set-counters " "PKTS BYTES"
+This enables the administrator to initialize the packet and byte
+counters of a rule (during
+.B INSERT,
+.B APPEND,
+.B REPLACE
+operations).
+.SS "OTHER OPTIONS"
+The following additional options can be specified:
+.TP
+.B "-v, --verbose"
+Verbose output.  This option makes the list command show the interface
+name, the rule options (if any), and the TOS masks.  The packet and
+byte counters are also listed, with the suffix 'K', 'M' or 'G' for
+1000, 1,000,000 and 1,000,000,000 multipliers respectively (but see
+the
+.B -x
+flag to change this).
+For appending, insertion, deletion and replacement, this causes
+detailed information on the rule or rules to be printed.
+.TP
+.B "-n, --numeric"
+Numeric output.
+IP addresses and port numbers will be printed in numeric format.
+By default, the program will try to display them as host names,
+network names, or services (whenever applicable).
+.TP
+.B "-x, --exact"
+Expand numbers.
+Display the exact value of the packet and byte counters,
+instead of only the rounded number in K's (multiples of 1000)
+M's (multiples of 1000K) or G's (multiples of 1000M).  This option is
+only relevant for the 
+.B -L
+command.
+.TP
+.B "--line-numbers"
+When listing rules, add line numbers to the beginning of each rule,
+corresponding to that rule's position in the chain.
+.TP
+.B "--modprobe=command"
+When adding or inserting rules into a chain, use
+.B command
+to load any necessary modules (targets, match extensions, etc).
+.SH MATCH EXTENSIONS
+ip6tables can use extended packet matching modules.  These are loaded
+in two ways: implicitly, when
+.B -p
+or
+.B --protocol
+is specified, or with the
+.B -m
+or
+.B --match
+options, followed by the matching module name; after these, various
+extra command line options become available, depending on the specific
+module.  You can specify multiple extended match modules in one line,
+and you can use the
+.B -h
+or
+.B --help
+options after the module has been specified to receive help specific
+to that module.
+
+The following are included in the base package, and most of these can
+be preceded by a
+.B !
+to invert the sense of the match.
+.\" @MATCH@
+.SH TARGET EXTENSIONS
+ip6tables can use extended target modules: the following are included
+in the standard distribution.
+.\" @TARGET@
+.SH DIAGNOSTICS
+Various error messages are printed to standard error.  The exit code
+is 0 for correct functioning.  Errors which appear to be caused by
+invalid or abused command line parameters cause an exit code of 2, and
+other errors cause an exit code of 1.
+.SH BUGS
+Bugs?  What's this? ;-)
+Well... the counters are not reliable on sparc64.
+.SH COMPATIBILITY WITH IPCHAINS
+This 
+.B ip6tables
+is very similar to ipchains by Rusty Russell.  The main difference is
+that the chains 
+.B INPUT
+and
+.B OUTPUT
+are only traversed for packets coming into the local host and
+originating from the local host respectively.  Hence every packet only
+passes through one of the three chains (except loopback traffic, which
+involves both INPUT and OUTPUT chains); previously a forwarded packet
+would pass through all three.
+.PP
+The other main difference is that 
+.B -i
+refers to the input interface;
+.B -o
+refers to the output interface, and both are available for packets
+entering the
+.B FORWARD
+chain.
+.\" .PP The various forms of NAT have been separated out; 
+.\" .B iptables 
+.\" is a pure packet filter when using the default `filter' table, with
+.\" optional extension modules.  This should simplify much of the previous
+.\" confusion over the combination of IP masquerading and packet filtering
+.\" seen previously.  So the following options are handled differently:
+.\" .br
+.\" -j MASQ
+.\" .br
+.\" -M -S
+.\" .br
+.\" -M -L
+.\" .br
+There are several other changes in ip6tables.
+.SH SEE ALSO
+.BR ip6tables-save (8),
+.BR ip6tables-restore(8),
+.BR iptables (8),
+.BR iptables-save (8),
+.BR iptables-restore (8),
+.BR libipq (3).
+.P
+The packet-filtering-HOWTO details iptables usage for
+packet filtering, the NAT-HOWTO details NAT,
+the netfilter-extensions-HOWTO details the extensions that are
+not in the standard distribution,
+and the netfilter-hacking-HOWTO details the netfilter internals.
+.br
+See
+.BR "http://www.netfilter.org/" .
+.SH AUTHORS
+Rusty Russell wrote iptables, in early consultation with Michael
+Neuling.
+.PP
+Marc Boucher made Rusty abandon ipnatctl by lobbying for a generic packet
+selection framework in iptables, then wrote the mangle table, the owner match,
+the mark stuff, and ran around doing cool stuff everywhere.
+.PP
+James Morris wrote the TOS target, and tos match.
+.PP
+Jozsef Kadlecsik wrote the REJECT target.
+.PP
+Harald Welte wrote the ULOG and NFQUEUE target, the new libiptc, aswell as TTL match+target and libipulog.
+.PP
+The Netfilter Core Team is: Marc Boucher, Martin Josefsson, Yasuyuki Kozakai,
+Jozsef Kadlecsik, Patrick McHardy, James Morris, Pablo Neira Ayuso,
+Harald Welte and Rusty Russell.
+.PP
+ip6tables man page created by Andras Kis-Szabo, based on
+iptables man page written by Herve Eychenne <rv@wallfire.org>.
+.\" .. and did I mention that we are incredibly cool people?
+.\" .. sexy, too ..
+.\" .. witty, charming, powerful ..
+.\" .. and most of all, modest ..
diff --git a/ip6tables.c b/ip6tables.c
new file mode 100644 (file)
index 0000000..09fb07c
--- /dev/null
@@ -0,0 +1,2525 @@
+/* Code to take an ip6tables-style command line and do it. */
+
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ *                 Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ *                 Marc Boucher <marc+nf@mbsi.ca>
+ *                 James Morris <jmorris@intercode.com.au>
+ *                 Harald Welte <laforge@gnumonks.org>
+ *                 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <ip6tables.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+
+#define FMT_NUMERIC    0x0001
+#define FMT_NOCOUNTS   0x0002
+#define FMT_KILOMEGAGIGA 0x0004
+#define FMT_OPTIONS    0x0008
+#define FMT_NOTABLE    0x0010
+#define FMT_NOTARGET   0x0020
+#define FMT_VIA                0x0040
+#define FMT_NONEWLINE  0x0080
+#define FMT_LINENUMBERS 0x0100
+
+#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
+                       | FMT_NUMERIC | FMT_NOTABLE)
+#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
+
+
+#define CMD_NONE               0x0000U
+#define CMD_INSERT             0x0001U
+#define CMD_DELETE             0x0002U
+#define CMD_DELETE_NUM         0x0004U
+#define CMD_REPLACE            0x0008U
+#define CMD_APPEND             0x0010U
+#define CMD_LIST               0x0020U
+#define CMD_FLUSH              0x0040U
+#define CMD_ZERO               0x0080U
+#define CMD_NEW_CHAIN          0x0100U
+#define CMD_DELETE_CHAIN       0x0200U
+#define CMD_SET_POLICY         0x0400U
+#define CMD_RENAME_CHAIN       0x0800U
+#define NUMBER_OF_CMD  13
+static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+                                'N', 'X', 'P', 'E' };
+
+#define OPTION_OFFSET 256
+
+#define OPT_NONE       0x00000U
+#define OPT_NUMERIC    0x00001U
+#define OPT_SOURCE     0x00002U
+#define OPT_DESTINATION        0x00004U
+#define OPT_PROTOCOL   0x00008U
+#define OPT_JUMP       0x00010U
+#define OPT_VERBOSE    0x00020U
+#define OPT_EXPANDED   0x00040U
+#define OPT_VIANAMEIN  0x00080U
+#define OPT_VIANAMEOUT 0x00100U
+#define OPT_LINENUMBERS 0x00200U
+#define OPT_COUNTERS   0x00400U
+#define NUMBER_OF_OPT  11
+static const char optflags[NUMBER_OF_OPT]
+= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '0', 'c'};
+
+static struct option original_opts[] = {
+       { "append", 1, 0, 'A' },
+       { "delete", 1, 0,  'D' },
+       { "insert", 1, 0,  'I' },
+       { "replace", 1, 0,  'R' },
+       { "list", 2, 0,  'L' },
+       { "flush", 2, 0,  'F' },
+       { "zero", 2, 0,  'Z' },
+       { "new-chain", 1, 0,  'N' },
+       { "delete-chain", 2, 0,  'X' },
+       { "rename-chain", 1, 0,  'E' },
+       { "policy", 1, 0,  'P' },
+       { "source", 1, 0, 's' },
+       { "destination", 1, 0,  'd' },
+       { "src", 1, 0,  's' }, /* synonym */
+       { "dst", 1, 0,  'd' }, /* synonym */
+       { "protocol", 1, 0,  'p' },
+       { "in-interface", 1, 0, 'i' },
+       { "jump", 1, 0, 'j' },
+       { "table", 1, 0, 't' },
+       { "match", 1, 0, 'm' },
+       { "numeric", 0, 0, 'n' },
+       { "out-interface", 1, 0, 'o' },
+       { "verbose", 0, 0, 'v' },
+       { "exact", 0, 0, 'x' },
+       { "version", 0, 0, 'V' },
+       { "help", 2, 0, 'h' },
+       { "line-numbers", 0, 0, '0' },
+       { "modprobe", 1, 0, 'M' },
+       { "set-counters", 1, 0, 'c' },
+       { 0 }
+};
+
+/* we need this for ip6tables-restore. ip6tables-restore.c sets line to the
+ * current line of the input file, in order to give a more precise error
+ * message. ip6tables itself doesn't need this, so it is initialized to the
+ * magic number of -1 */
+int line = -1;
+
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
+/* Table of legal combinations of commands and options.  If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ *  +  compulsory
+ *  x  illegal
+ *     optional
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+       /*     -n  -s  -d  -p  -j  -v  -x  -i  -o  --line -c */
+/*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' '},
+/*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x'},
+/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' '},
+/*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' '},
+/*LIST*/      {' ','x','x','x','x',' ',' ','x','x',' ','x'},
+/*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x','x'}
+};
+
+static int inverse_for_options[NUMBER_OF_OPT] =
+{
+/* -n */ 0,
+/* -s */ IP6T_INV_SRCIP,
+/* -d */ IP6T_INV_DSTIP,
+/* -p */ IP6T_INV_PROTO,
+/* -j */ 0,
+/* -v */ 0,
+/* -x */ 0,
+/* -i */ IP6T_INV_VIA_IN,
+/* -o */ IP6T_INV_VIA_OUT,
+/*--line*/ 0,
+/* -c */ 0,
+};
+
+const char *program_version;
+const char *program_name;
+char *lib_dir;
+
+/* the path to command to load kernel module */
+const char *modprobe = NULL;
+
+/* Keeping track of external matches and targets: linked lists.  */
+struct ip6tables_match *ip6tables_matches = NULL;
+struct ip6tables_target *ip6tables_targets = NULL;
+
+/* Extra debugging from libiptc */
+extern void dump_entries6(const ip6tc_handle_t handle);
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+   /etc/protocols */
+struct pprot {
+       char *name;
+       u_int8_t num;
+};
+
+/* Primitive headers... */
+/* defined in netinet/in.h */
+#if 0
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+#endif
+#ifndef IPPROTO_MH
+#define IPPROTO_MH 135
+#endif
+
+static const struct pprot chain_protos[] = {
+       { "tcp", IPPROTO_TCP },
+       { "udp", IPPROTO_UDP },
+       { "udplite", IPPROTO_UDPLITE },
+       { "icmpv6", IPPROTO_ICMPV6 },
+       { "ipv6-icmp", IPPROTO_ICMPV6 },
+       { "esp", IPPROTO_ESP },
+       { "ah", IPPROTO_AH },
+       { "ipv6-mh", IPPROTO_MH },
+       { "mh", IPPROTO_MH },
+       { "all", 0 },
+};
+
+static char *
+proto_to_name(u_int8_t proto, int nolookup)
+{
+       unsigned int i;
+
+       if (proto && !nolookup) {
+               struct protoent *pent = getprotobynumber(proto);
+               if (pent)
+                       return pent->p_name;
+       }
+
+       for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+               if (chain_protos[i].num == proto)
+                       return chain_protos[i].name;
+
+       return NULL;
+}
+
+int
+service_to_port(const char *name, const char *proto)
+{
+       struct servent *service;
+
+       if ((service = getservbyname(name, proto)) != NULL)
+               return ntohs((unsigned short) service->s_port);
+
+       return -1;
+}
+
+u_int16_t
+parse_port(const char *port, const char *proto)
+{
+       unsigned int portnum;
+
+       if ((string_to_number(port, 0, 65535, &portnum)) != -1 ||
+           (portnum = service_to_port(port, proto)) != -1)
+               return (u_int16_t)portnum;
+
+       exit_error(PARAMETER_PROBLEM,
+                  "invalid port/service `%s' specified", port);
+}
+
+static void
+in6addrcpy(struct in6_addr *dst, struct in6_addr *src)
+{
+       memcpy(dst, src, sizeof(struct in6_addr));
+       /* dst->s6_addr = src->s6_addr; */
+}
+
+static void free_opts(int reset_offset)
+{
+       if (opts != original_opts) {
+               free(opts);
+               opts = original_opts;
+               if (reset_offset)
+                       global_option_offset = 0;
+       }
+}
+
+void
+exit_error(enum exittype status, char *msg, ...)
+{
+       va_list args;
+
+       va_start(args, msg);
+       fprintf(stderr, "%s v%s: ", program_name, program_version);
+       vfprintf(stderr, msg, args);
+       va_end(args);
+       fprintf(stderr, "\n");
+       if (status == PARAMETER_PROBLEM)
+               exit_tryhelp(status);
+       if (status == VERSION_PROBLEM)
+               fprintf(stderr,
+                       "Perhaps ip6tables or your kernel needs to be upgraded.\n");
+       /* On error paths, make sure that we don't leak memory */
+       free_opts(1);
+       exit(status);
+}
+
+void
+exit_tryhelp(int status)
+{
+       if (line != -1)
+               fprintf(stderr, "Error occurred at line: %d\n", line);
+       fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+                       program_name, program_name );
+       free_opts(1);
+       exit(status);
+}
+
+void
+exit_printhelp(struct ip6tables_rule_match *matches)
+{
+       struct ip6tables_rule_match *matchp = NULL;
+       struct ip6tables_target *t = NULL;
+
+       printf("%s v%s\n\n"
+"Usage: %s -[AD] chain rule-specification [options]\n"
+"       %s -[RI] chain rulenum rule-specification [options]\n"
+"       %s -D chain rulenum [options]\n"
+"       %s -[LFZ] [chain] [options]\n"
+"       %s -[NX] chain\n"
+"       %s -E old-chain-name new-chain-name\n"
+"       %s -P chain target [options]\n"
+"       %s -h (print this help information)\n\n",
+              program_name, program_version, program_name, program_name,
+              program_name, program_name, program_name, program_name,
+              program_name, program_name);
+
+       printf(
+"Commands:\n"
+"Either long or short options are allowed.\n"
+"  --append  -A chain          Append to chain\n"
+"  --delete  -D chain          Delete matching rule from chain\n"
+"  --delete  -D chain rulenum\n"
+"                              Delete rule rulenum (1 = first) from chain\n"
+"  --insert  -I chain [rulenum]\n"
+"                              Insert in chain as rulenum (default 1=first)\n"
+"  --replace -R chain rulenum\n"
+"                              Replace rule rulenum (1 = first) in chain\n"
+"  --list    -L [chain]                List the rules in a chain or all chains\n"
+"  --flush   -F [chain]                Delete all rules in  chain or all chains\n"
+"  --zero    -Z [chain]                Zero counters in chain or all chains\n"
+"  --new     -N chain          Create a new user-defined chain\n"
+"  --delete-chain\n"
+"            -X [chain]                Delete a user-defined chain\n"
+"  --policy  -P chain target\n"
+"                              Change policy on chain to target\n"
+"  --rename-chain\n"
+"            -E old-chain new-chain\n"
+"                              Change chain name, (moving any references)\n"
+
+"Options:\n"
+"  --proto     -p [!] proto    protocol: by number or name, eg. `tcp'\n"
+"  --source    -s [!] address[/mask]\n"
+"                              source specification\n"
+"  --destination -d [!] address[/mask]\n"
+"                              destination specification\n"
+"  --in-interface -i [!] input name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --jump      -j target\n"
+"                              target for rule (may load target extension)\n"
+"  --match     -m match\n"
+"                              extended match (may load extension)\n"
+"  --numeric   -n              numeric output of addresses and ports\n"
+"  --out-interface -o [!] output name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --table     -t table        table to manipulate (default: `filter')\n"
+"  --verbose   -v              verbose mode\n"
+"  --line-numbers              print line numbers when listing\n"
+"  --exact     -x              expand numbers (display exact values)\n"
+/*"[!] --fragment      -f              match second or further fragments only\n"*/
+"  --modprobe=<command>                try to insert modules using this command\n"
+"  --set-counters PKTS BYTES   set the counter during insert/append\n"
+"[!] --version -V              print package version.\n");
+
+       /* Print out any special helps. A user might like to be able to add a --help 
+          to the commandline, and see expected results. So we call help for all 
+          specified matches & targets */
+       for (t = ip6tables_targets; t; t = t->next) {
+               if (t->used) {
+                       printf("\n");
+                       t->help();
+               }
+       }
+       for (matchp = matches; matchp; matchp = matchp->next) {
+               printf("\n");
+               matchp->match->help();
+       }
+       exit(0);
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+       int i, j, legal = 0;
+
+       /* Check that commands are valid with options.  Complicated by the
+        * fact that if an option is legal with *any* command given, it is
+        * legal overall (ie. -z and -l).
+        */
+       for (i = 0; i < NUMBER_OF_OPT; i++) {
+               legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+               for (j = 0; j < NUMBER_OF_CMD; j++) {
+                       if (!(command & (1<<j)))
+                               continue;
+
+                       if (!(options & (1<<i))) {
+                               if (commands_v_options[j][i] == '+')
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "You need to supply the `-%c' "
+                                                  "option for this command\n",
+                                                  optflags[i]);
+                       } else {
+                               if (commands_v_options[j][i] != 'x')
+                                       legal = 1;
+                               else if (legal == 0)
+                                       legal = -1;
+                       }
+               }
+               if (legal == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Illegal option `-%c' with this command\n",
+                                  optflags[i]);
+       }
+}
+
+static char
+opt2char(int option)
+{
+       const char *ptr;
+       for (ptr = optflags; option > 1; option >>= 1, ptr++);
+
+       return *ptr;
+}
+
+static char
+cmd2char(int option)
+{
+       const char *ptr;
+       for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
+
+       return *ptr;
+}
+
+static void
+add_command(unsigned int *cmd, const int newcmd, const int othercmds,
+           int invert)
+{
+       if (invert)
+               exit_error(PARAMETER_PROBLEM, "unexpected ! flag");
+       if (*cmd & (~othercmds))
+               exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
+                          cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
+       *cmd |= newcmd;
+}
+
+int
+check_inverse(const char option[], int *invert, int *optind, int argc)
+{
+       if (option && strcmp(option, "!") == 0) {
+               if (*invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Multiple `!' flags not allowed");
+               *invert = TRUE;
+               if (optind) {
+                       *optind = *optind+1;
+                       if (argc && *optind > argc)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "no argument following `!'");
+               }
+
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static void *
+fw_calloc(size_t count, size_t size)
+{
+       void *p;
+
+       if ((p = calloc(count, size)) == NULL) {
+               perror("ip6tables: calloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static void *
+fw_malloc(size_t size)
+{
+       void *p;
+
+       if ((p = malloc(size)) == NULL) {
+               perror("ip6tables: malloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static char *
+addr_to_numeric(const struct in6_addr *addrp)
+{
+       /* 0000:0000:0000:0000:0000:000.000.000.000
+        * 0000:0000:0000:0000:0000:0000:0000:0000 */
+       static char buf[50+1];
+       return (char *)inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
+}
+
+static struct in6_addr *
+numeric_to_addr(const char *num)
+{
+       static struct in6_addr ap;
+       int err;
+       if ((err=inet_pton(AF_INET6, num, &ap)) == 1)
+               return &ap;
+#ifdef DEBUG
+       fprintf(stderr, "\nnumeric2addr: %d\n", err);
+#endif
+       return (struct in6_addr *)NULL;
+}
+
+
+static struct in6_addr *
+host_to_addr(const char *name, unsigned int *naddr)
+{
+       struct addrinfo hints;
+        struct addrinfo *res;
+        static struct in6_addr *addr;
+       int err;
+
+       memset(&hints, 0, sizeof(hints));
+        hints.ai_flags=AI_CANONNAME;
+        hints.ai_family=AF_INET6;
+        hints.ai_socktype=SOCK_RAW;
+        hints.ai_protocol=41;
+        hints.ai_next=NULL;
+
+       *naddr = 0;
+        if ( (err=getaddrinfo(name, NULL, &hints, &res)) != 0 ){
+#ifdef DEBUG
+                fprintf(stderr,"Name2IP: %s\n",gai_strerror(err)); 
+#endif
+                return (struct in6_addr *) NULL;
+        } else {
+               if (res->ai_family != AF_INET6 ||
+                   res->ai_addrlen != sizeof(struct sockaddr_in6))
+                       return (struct in6_addr *) NULL;
+
+#ifdef DEBUG
+                fprintf(stderr, "resolved: len=%d  %s ", res->ai_addrlen, 
+                    addr_to_numeric(&(((struct sockaddr_in6 *)res->ai_addr)->sin6_addr)));
+#endif
+               /* Get the first element of the address-chain */
+               addr = fw_calloc(1, sizeof(struct in6_addr));
+               in6addrcpy(addr, (struct in6_addr *)
+                       &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr);
+               freeaddrinfo(res);
+               *naddr = 1;
+               return addr;
+       }
+
+       return (struct in6_addr *) NULL;
+}
+
+static char *
+addr_to_host(const struct in6_addr *addr)
+{
+       struct sockaddr_in6 saddr;
+       int err;
+       static char hostname[NI_MAXHOST];
+
+       memset(&saddr, 0, sizeof(struct sockaddr_in6));
+       in6addrcpy(&(saddr.sin6_addr),(struct in6_addr *)addr);
+       saddr.sin6_family = AF_INET6;
+
+        if ( (err=getnameinfo((struct sockaddr *)&saddr,
+                              sizeof(struct sockaddr_in6),
+                              hostname, sizeof(hostname)-1,
+                              NULL, 0, 0)) != 0 ){
+#ifdef DEBUG
+                fprintf(stderr,"IP2Name: %s\n",gai_strerror(err)); 
+#endif
+                return (char *) NULL;
+        } else {
+#ifdef DEBUG
+               fprintf (stderr, "\naddr2host: %s\n", hostname);
+#endif
+
+               return hostname;
+       }
+
+       return (char *) NULL;
+}
+
+static char *
+mask_to_numeric(const struct in6_addr *addrp)
+{
+       static char buf[50+2];
+       int l = ipv6_prefix_length(addrp);
+       if (l == -1) {
+               strcpy(buf, "/");
+               strcat(buf, addr_to_numeric(addrp));
+               return buf;
+       }
+       sprintf(buf, "/%d", l);
+       return buf;
+}
+
+static struct in6_addr *
+network_to_addr(const char *name)
+{
+       /*      abort();*/
+       /* TODO: not implemented yet, but the exception breaks the
+        *       name resolvation */
+       return (struct in6_addr *)NULL;
+}
+
+static char *
+addr_to_anyname(const struct in6_addr *addr)
+{
+       char *name;
+
+       if ((name = addr_to_host(addr)) != NULL)
+               return name;
+
+       return addr_to_numeric(addr);
+}
+
+/*
+ *     All functions starting with "parse" should succeed, otherwise
+ *     the program fails.
+ *     Most routines return pointers to static data that may change
+ *     between calls to the same or other routines with a few exceptions:
+ *     "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
+ *     return global static data.
+*/
+
+static struct in6_addr *
+parse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+       struct in6_addr *addrp, *addrptmp;
+
+       if ((addrptmp = numeric_to_addr(name)) != NULL ||
+           (addrptmp = network_to_addr(name)) != NULL) {
+               addrp = fw_malloc(sizeof(struct in6_addr));
+               in6addrcpy(addrp, addrptmp);
+               *naddrs = 1;
+               return addrp;
+       }
+       if ((addrp = host_to_addr(name, naddrs)) != NULL)
+               return addrp;
+
+       exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static struct in6_addr *
+parse_mask(char *mask)
+{
+       static struct in6_addr maskaddr;
+       struct in6_addr *addrp;
+       unsigned int bits;
+
+       if (mask == NULL) {
+               /* no mask at all defaults to 128 bits */
+               memset(&maskaddr, 0xff, sizeof maskaddr);
+               return &maskaddr;
+       }
+       if ((addrp = numeric_to_addr(mask)) != NULL)
+               return addrp;
+       if (string_to_number(mask, 0, 128, &bits) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "invalid mask `%s' specified", mask);
+       if (bits != 0) {
+               char *p = (char *)&maskaddr;
+               memset(p, 0xff, bits / 8);
+               memset(p + (bits / 8) + 1, 0, (128 - bits) / 8);
+               p[bits / 8] = 0xff << (8 - (bits & 7));
+               return &maskaddr;
+       }
+
+       memset(&maskaddr, 0, sizeof maskaddr);
+       return &maskaddr;
+}
+
+void
+parse_hostnetworkmask(const char *name, struct in6_addr **addrpp,
+                     struct in6_addr *maskp, unsigned int *naddrs)
+{
+       struct in6_addr *addrp;
+       char buf[256];
+       char *p;
+       int i, j, n;
+
+       strncpy(buf, name, sizeof(buf) - 1);
+       buf[sizeof(buf) - 1] = '\0';
+       if ((p = strrchr(buf, '/')) != NULL) {
+               *p = '\0';
+               addrp = parse_mask(p + 1);
+       } else
+               addrp = parse_mask(NULL);
+       in6addrcpy(maskp, addrp);
+
+       /* if a null mask is given, the name is ignored, like in "any/0" */
+       if (!memcmp(maskp, &in6addr_any, sizeof(in6addr_any)))
+               strcpy(buf, "::");
+
+       addrp = *addrpp = parse_hostnetwork(buf, naddrs);
+       n = *naddrs;
+       for (i = 0, j = 0; i < n; i++) {
+               int k;
+               for (k = 0; k < 4; k++)
+                       addrp[j].s6_addr32[k] &= maskp->s6_addr32[k];
+               j++;
+               for (k = 0; k < j - 1; k++) {
+                       if (IN6_ARE_ADDR_EQUAL(&addrp[k], &addrp[j - 1])) {
+                               (*naddrs)--;
+                               j--;
+                               break;
+                       }
+               }
+       }
+}
+
+struct ip6tables_match *
+find_match(const char *match_name, enum ip6t_tryload tryload, struct ip6tables_rule_match **matches)
+{
+       struct ip6tables_match *ptr;
+       const char *icmp6 = "icmp6";
+       const char *name;
+  
+       /* This is ugly as hell. Nonetheless, there is no way of changing
+        * this without hurting backwards compatibility */
+       if ( (strcmp(match_name,"icmpv6") == 0) ||
+            (strcmp(match_name,"ipv6-icmp") == 0) ||
+            (strcmp(match_name,"icmp6") == 0) )
+               name = icmp6;
+       else
+               name = match_name;
+       for (ptr = ip6tables_matches; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0) {
+                       struct ip6tables_match *clone;
+                       
+                       /* First match of this type: */
+                       if (ptr->m == NULL)
+                               break;
+
+                       /* Second and subsequent clones */
+                       clone = fw_malloc(sizeof(struct ip6tables_match));
+                       memcpy(clone, ptr, sizeof(struct ip6tables_match));
+                       clone->mflags = 0;
+                       /* This is a clone: */
+                       clone->next = clone;
+
+                       ptr = clone;
+                       break;
+               }
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) {
+               char path[strlen(lib_dir) + sizeof("/libip6t_.so")
+                        + strlen(name)];
+               sprintf(path, "%s/libip6t_%s.so", lib_dir, name);
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified target as match. */
+                       ptr = find_match(name, DONT_LOAD, NULL);
+
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load match `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load match `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find match `%s'\n", name);
+       }
+#endif
+
+       if (ptr && matches) {
+               struct ip6tables_rule_match **i;
+               struct ip6tables_rule_match *newentry;
+
+               newentry = fw_malloc(sizeof(struct ip6tables_rule_match));
+
+               for (i = matches; *i; i = &(*i)->next) {
+                       if (strcmp(name, (*i)->match->name) == 0)
+                               (*i)->completed = 1;
+               }
+               newentry->match = ptr;
+               newentry->completed = 0;
+               newentry->next = NULL;
+               *i = newentry;
+       }
+
+       return ptr;
+}
+
+/* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
+static struct ip6tables_match *
+find_proto(const char *pname, enum ip6t_tryload tryload, int nolookup, struct ip6tables_rule_match **matches)
+{
+       unsigned int proto;
+
+       if (string_to_number(pname, 0, 255, &proto) != -1) {
+               char *protoname = proto_to_name(proto, nolookup);
+
+               if (protoname)
+                       return find_match(protoname, tryload, matches);
+       } else
+               return find_match(pname, tryload, matches);
+
+       return NULL;
+}
+
+u_int16_t
+parse_protocol(const char *s)
+{
+       unsigned int proto;
+
+       if (string_to_number(s, 0, 255, &proto) == -1) {
+               struct protoent *pent;
+
+               /* first deal with the special case of 'all' to prevent
+                * people from being able to redefine 'all' in nsswitch
+                * and/or provoke expensive [not working] ldap/nis/... 
+                * lookups */
+               if (!strcmp(s, "all"))
+                       return 0;
+
+               if ((pent = getprotobyname(s)))
+                       proto = pent->p_proto;
+               else {
+                       unsigned int i;
+                       for (i = 0;
+                            i < sizeof(chain_protos)/sizeof(struct pprot);
+                            i++) {
+                               if (strcmp(s, chain_protos[i].name) == 0) {
+                                       proto = chain_protos[i].num;
+                                       break;
+                               }
+                       }
+                       if (i == sizeof(chain_protos)/sizeof(struct pprot))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unknown protocol `%s' specified",
+                                          s);
+               }
+       }
+
+       return (u_int16_t)proto;
+}
+
+/* These are invalid numbers as upper layer protocol */
+static int is_exthdr(u_int16_t proto)
+{
+       return (proto == IPPROTO_ROUTING ||
+               proto == IPPROTO_FRAGMENT ||
+               proto == IPPROTO_AH ||
+               proto == IPPROTO_DSTOPTS);
+}
+
+void parse_interface(const char *arg, char *vianame, unsigned char *mask)
+{
+       int vialen = strlen(arg);
+       unsigned int i;
+
+       memset(mask, 0, IFNAMSIZ);
+       memset(vianame, 0, IFNAMSIZ);
+
+       if (vialen + 1 > IFNAMSIZ)
+               exit_error(PARAMETER_PROBLEM,
+                          "interface name `%s' must be shorter than IFNAMSIZ"
+                          " (%i)", arg, IFNAMSIZ-1);
+
+       strcpy(vianame, arg);
+       if ((vialen == 0) || (vialen == 1 && vianame[0] == '+'))
+               memset(mask, 0, IFNAMSIZ);
+       else if (vianame[vialen - 1] == '+') {
+               memset(mask, 0xFF, vialen - 1);
+               memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
+               /* Don't remove `+' here! -HW */
+       } else {
+               /* Include nul-terminator in match */
+               memset(mask, 0xFF, vialen + 1);
+               memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
+               for (i = 0; vianame[i]; i++) {
+                       if (vianame[i] == ':' ||
+                           vianame[i] == '!' ||
+                           vianame[i] == '*') {
+                               printf("Warning: weird character in interface"
+                                      " `%s' (No aliases, :, ! or *).\n",
+                                      vianame);
+                               break;
+                       }
+               }
+       }
+}
+
+/* Can't be zero. */
+static int
+parse_rulenumber(const char *rule)
+{
+       unsigned int rulenum;
+
+       if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid rule number `%s'", rule);
+
+       return rulenum;
+}
+
+static const char *
+parse_target(const char *targetname)
+{
+       const char *ptr;
+
+       if (strlen(targetname) < 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name (too short)");
+
+       if (strlen(targetname)+1 > sizeof(ip6t_chainlabel))
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name `%s' (%u chars max)",
+                          targetname, (unsigned int)sizeof(ip6t_chainlabel)-1);
+
+       for (ptr = targetname; *ptr; ptr++)
+               if (isspace(*ptr))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid target name `%s'", targetname);
+       return targetname;
+}
+
+int
+string_to_number_ll(const char *s, unsigned long long min, unsigned long long max,
+                unsigned long long *ret)
+{
+       unsigned long long number;
+       char *end;
+
+       /* Handle hex, octal, etc. */
+       errno = 0;
+       number = strtoull(s, &end, 0);
+       if (*end == '\0' && end != s) {
+               /* we parsed a number, let's see if we want this */
+               if (errno != ERANGE && min <= number && (!max || number <= max)) {
+                       *ret = number;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+int
+string_to_number_l(const char *s, unsigned long min, unsigned long max,
+                unsigned long *ret)
+{
+       int result;
+       unsigned long long number;
+
+       result = string_to_number_ll(s, min, max, &number);
+       *ret = (unsigned long)number;
+
+       return result;
+}
+
+int string_to_number(const char *s, unsigned int min, unsigned int max,
+               unsigned int *ret)
+{
+       int result;
+       unsigned long number;
+
+       result = string_to_number_l(s, min, max, &number);
+       *ret = (unsigned int)number;
+
+       return result;
+}
+
+static void
+set_option(unsigned int *options, unsigned int option, u_int8_t *invflg,
+          int invert)
+{
+       if (*options & option)
+               exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
+                          opt2char(option));
+       *options |= option;
+
+       if (invert) {
+               unsigned int i;
+               for (i = 0; 1 << i != option; i++);
+
+               if (!inverse_for_options[i])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "cannot have ! before -%c",
+                                  opt2char(option));
+               *invflg |= inverse_for_options[i];
+       }
+}
+
+struct ip6tables_target *
+find_target(const char *name, enum ip6t_tryload tryload)
+{
+       struct ip6tables_target *ptr;
+
+       /* Standard target? */
+       if (strcmp(name, "") == 0
+           || strcmp(name, IP6TC_LABEL_ACCEPT) == 0
+           || strcmp(name, IP6TC_LABEL_DROP) == 0
+           || strcmp(name, IP6TC_LABEL_QUEUE) == 0
+           || strcmp(name, IP6TC_LABEL_RETURN) == 0)
+               name = "standard";
+
+       for (ptr = ip6tables_targets; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0)
+                       break;
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) {
+               char path[strlen(lib_dir) + sizeof("/libip6t_.so")
+                        + strlen(name)];
+               sprintf(path, "%s/libip6t_%s.so", lib_dir, name);
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified match as a target. */
+                       ptr = find_target(name, DONT_LOAD);
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load target `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load target `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find target `%s'\n", name);
+       }
+#endif
+
+       if (ptr)
+               ptr->used = 1;
+
+       return ptr;
+}
+
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+             unsigned int *option_offset)
+{
+       unsigned int num_old, num_new, i;
+       struct option *merge;
+
+       for (num_old = 0; oldopts[num_old].name; num_old++);
+       for (num_new = 0; newopts[num_new].name; num_new++);
+
+       global_option_offset += OPTION_OFFSET;
+       *option_offset = global_option_offset;
+
+       merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+       memcpy(merge, oldopts, num_old * sizeof(struct option));
+       free_opts(0); /* Release previous options merged if any */
+       for (i = 0; i < num_new; i++) {
+               merge[num_old + i] = newopts[i];
+               merge[num_old + i].val += *option_offset;
+       }
+       memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+       return merge;
+}
+
+static int compatible_revision(const char *name, u_int8_t revision, int opt)
+{
+       struct ip6t_get_revision rev;
+       socklen_t s = sizeof(rev);
+       int max_rev, sockfd;
+
+       sockfd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0) {
+               fprintf(stderr, "Could not open socket to kernel: %s\n",
+                       strerror(errno));
+               exit(1);
+       }
+
+       strcpy(rev.name, name);
+       rev.revision = revision;
+
+       load_ip6tables_ko(modprobe, 1);
+
+       max_rev = getsockopt(sockfd, IPPROTO_IPV6, opt, &rev, &s);
+       if (max_rev < 0) {
+               /* Definitely don't support this? */
+               if (errno == EPROTONOSUPPORT) {
+                       close(sockfd);
+                       return 0;
+               } else if (errno == ENOPROTOOPT) {
+                       close(sockfd);
+                       /* Assume only revision 0 support (old kernel) */
+                       return (revision == 0);
+               } else {
+                       fprintf(stderr, "getsockopt failed strangely: %s\n",
+                               strerror(errno));
+                       exit(1);
+               }
+       }
+       close(sockfd);
+       return 1;
+}
+
+static int compatible_match_revision(const char *name, u_int8_t revision)
+{
+       return compatible_revision(name, revision, IP6T_SO_GET_REVISION_MATCH);
+}
+
+void
+register_match6(struct ip6tables_match *me)
+{
+       struct ip6tables_match **i, *old;
+
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       /* Revision field stole a char from name. */
+       if (strlen(me->name) >= IP6T_FUNCTION_MAXNAMELEN-1) {
+               fprintf(stderr, "%s: target `%s' has invalid name\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       old = find_match(me->name, DURING_LOAD, NULL);
+       if (old) {
+               if (old->revision == me->revision) {
+                       fprintf(stderr,
+                               "%s: match `%s' already registered.\n",
+                               program_name, me->name);
+                       exit(1);
+               }
+
+               /* Now we have two (or more) options, check compatibility. */
+               if (compatible_match_revision(old->name, old->revision)
+                   && old->revision > me->revision)
+                       return;
+
+               /* Replace if compatible. */
+               if (!compatible_match_revision(me->name, me->revision))
+                       return;
+
+               /* Delete old one. */
+               for (i = &ip6tables_matches; *i!=old; i = &(*i)->next);
+               *i = old->next;
+       }
+       
+       if (me->size != IP6T_ALIGN(me->size)) {
+               fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
+                       program_name, me->name, (unsigned int)me->size);
+               exit(1);
+       }
+
+       /* Append to list. */
+       for (i = &ip6tables_matches; *i; i = &(*i)->next);
+       me->next = NULL;
+       *i = me;
+
+       me->m = NULL;
+       me->mflags = 0;
+}
+
+void
+register_target6(struct ip6tables_target *me)
+{
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       if (find_target(me->name, DURING_LOAD)) {
+               fprintf(stderr, "%s: target `%s' already registered.\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       if (me->size != IP6T_ALIGN(me->size)) {
+               fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
+                       program_name, me->name, (unsigned int)me->size);
+               exit(1);
+       }
+
+       /* Prepend to list. */
+       me->next = ip6tables_targets;
+       ip6tables_targets = me;
+       me->t = NULL;
+       me->tflags = 0;
+}
+
+static void
+print_num(u_int64_t number, unsigned int format)
+{
+       if (format & FMT_KILOMEGAGIGA) {
+               if (number > 99999) {
+                       number = (number + 500) / 1000;
+                       if (number > 9999) {
+                               number = (number + 500) / 1000;
+                               if (number > 9999) {
+                                       number = (number + 500) / 1000;
+                                       if (number > 9999) {
+                                               number = (number + 500) / 1000;
+                                               printf(FMT("%4lluT ","%lluT "), (unsigned long long)number);
+                                       }
+                                       else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number);
+                               }
+                               else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number);
+                       } else
+                               printf(FMT("%4lluK ","%lluK "), (unsigned long long)number);
+               } else
+                       printf(FMT("%5llu ","%llu "), (unsigned long long)number);
+       } else
+               printf(FMT("%8llu ","%llu "), (unsigned long long)number);
+}
+
+
+static void
+print_header(unsigned int format, const char *chain, ip6tc_handle_t *handle)
+{
+       struct ip6t_counters counters;
+       const char *pol = ip6tc_get_policy(chain, &counters, handle);
+       printf("Chain %s", chain);
+       if (pol) {
+               printf(" (policy %s", pol);
+               if (!(format & FMT_NOCOUNTS)) {
+                       fputc(' ', stdout);
+                       print_num(counters.pcnt, (format|FMT_NOTABLE));
+                       fputs("packets, ", stdout);
+                       print_num(counters.bcnt, (format|FMT_NOTABLE));
+                       fputs("bytes", stdout);
+               }
+               printf(")\n");
+       } else {
+               unsigned int refs;
+               if (!ip6tc_get_references(&refs, chain, handle))
+                       printf(" (ERROR obtaining refs)\n");
+               else
+                       printf(" (%u references)\n", refs);
+       }
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4s ", "%s "), "num");
+       if (!(format & FMT_NOCOUNTS)) {
+               if (format & FMT_KILOMEGAGIGA) {
+                       printf(FMT("%5s ","%s "), "pkts");
+                       printf(FMT("%5s ","%s "), "bytes");
+               } else {
+                       printf(FMT("%8s ","%s "), "pkts");
+                       printf(FMT("%10s ","%s "), "bytes");
+               }
+       }
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ","%s "), "target");
+       fputs(" prot ", stdout);
+       if (format & FMT_OPTIONS)
+               fputs("opt", stdout);
+       if (format & FMT_VIA) {
+               printf(FMT(" %-6s ","%s "), "in");
+               printf(FMT("%-6s ","%s "), "out");
+       }
+       printf(FMT(" %-19s ","%s "), "source");
+       printf(FMT(" %-19s "," %s "), "destination");
+       printf("\n");
+}
+
+
+static int
+print_match(const struct ip6t_entry_match *m,
+           const struct ip6t_ip6 *ip,
+           int numeric)
+{
+       struct ip6tables_match *match = find_match(m->u.user.name, TRY_LOAD, NULL);
+
+       if (match) {
+               if (match->print)
+                       match->print(ip, m, numeric);
+               else
+                       printf("%s ", match->name);
+       } else {
+               if (m->u.user.name[0])
+                       printf("UNKNOWN match `%s' ", m->u.user.name);
+       }
+       /* Don't stop iterating. */
+       return 0;
+}
+
+/* e is called `fw' here for hysterical raisins */
+static void
+print_firewall(const struct ip6t_entry *fw,
+              const char *targname,
+              unsigned int num,
+              unsigned int format,
+              const ip6tc_handle_t handle)
+{
+       struct ip6tables_target *target = NULL;
+       const struct ip6t_entry_target *t;
+       u_int8_t flags;
+       char buf[BUFSIZ];
+
+       if (!ip6tc_is_chain(targname, handle))
+               target = find_target(targname, TRY_LOAD);
+       else
+               target = find_target(IP6T_STANDARD_TARGET, LOAD_MUST_SUCCEED);
+
+       t = ip6t_get_target((struct ip6t_entry *)fw);
+       flags = fw->ipv6.flags;
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4u ", "%u "), num+1);
+
+       if (!(format & FMT_NOCOUNTS)) {
+               print_num(fw->counters.pcnt, format);
+               print_num(fw->counters.bcnt, format);
+       }
+
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ", "%s "), targname);
+
+       fputc(fw->ipv6.invflags & IP6T_INV_PROTO ? '!' : ' ', stdout);
+       {
+               char *pname = proto_to_name(fw->ipv6.proto, format&FMT_NUMERIC);
+               if (pname)
+                       printf(FMT("%-5s", "%s "), pname);
+               else
+                       printf(FMT("%-5hu", "%hu "), fw->ipv6.proto);
+       }
+
+       if (format & FMT_OPTIONS) {
+               if (format & FMT_NOTABLE)
+                       fputs("opt ", stdout);
+               fputc(' ', stdout); /* Invert flag of FRAG */
+               fputc(' ', stdout); /* -f */
+               fputc(' ', stdout);
+       }
+
+       if (format & FMT_VIA) {
+               char iface[IFNAMSIZ+2];
+
+               if (fw->ipv6.invflags & IP6T_INV_VIA_IN) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ipv6.iniface[0] != '\0') {
+                       strcat(iface, fw->ipv6.iniface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT(" %-6s ","in %s "), iface);
+
+               if (fw->ipv6.invflags & IP6T_INV_VIA_OUT) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ipv6.outiface[0] != '\0') {
+                       strcat(iface, fw->ipv6.outiface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT("%-6s ","out %s "), iface);
+       }
+
+       fputc(fw->ipv6.invflags & IP6T_INV_SRCIP ? '!' : ' ', stdout);
+       if (!memcmp(&fw->ipv6.smsk, &in6addr_any, sizeof in6addr_any) 
+           && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s ","%s "), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_numeric(&(fw->ipv6.src)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ipv6.src)));
+               strcat(buf, mask_to_numeric(&(fw->ipv6.smsk)));
+               printf(FMT("%-19s ","%s "), buf);
+       }
+
+       fputc(fw->ipv6.invflags & IP6T_INV_DSTIP ? '!' : ' ', stdout);
+       if (!memcmp(&fw->ipv6.dmsk, &in6addr_any, sizeof in6addr_any)
+           && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s","-> %s"), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_numeric(&(fw->ipv6.dst)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ipv6.dst)));
+               strcat(buf, mask_to_numeric(&(fw->ipv6.dmsk)));
+               printf(FMT("%-19s","-> %s"), buf);
+       }
+
+       if (format & FMT_NOTABLE)
+               fputs("  ", stdout);
+
+       IP6T_MATCH_ITERATE(fw, print_match, &fw->ipv6, format & FMT_NUMERIC);
+
+       if (target) {
+               if (target->print)
+                       /* Print the target information. */
+                       target->print(&fw->ipv6, t, format & FMT_NUMERIC);
+       } else if (t->u.target_size != sizeof(*t))
+               printf("[%u bytes of unknown target data] ",
+                      (unsigned int)(t->u.target_size - sizeof(*t)));
+
+       if (!(format & FMT_NONEWLINE))
+               fputc('\n', stdout);
+}
+
+static void
+print_firewall_line(const struct ip6t_entry *fw,
+                   const ip6tc_handle_t h)
+{
+       struct ip6t_entry_target *t;
+
+       t = ip6t_get_target((struct ip6t_entry *)fw);
+       print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h);
+}
+
+static int
+append_entry(const ip6t_chainlabel chain,
+            struct ip6t_entry *fw,
+            unsigned int nsaddrs,
+            const struct in6_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in6_addr daddrs[],
+            int verbose,
+            ip6tc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ipv6.src = saddrs[i];
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ipv6.dst = daddrs[j];
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= ip6tc_append_entry(chain, fw, handle);
+               }
+       }
+
+       return ret;
+}
+
+static int
+replace_entry(const ip6t_chainlabel chain,
+             struct ip6t_entry *fw,
+             unsigned int rulenum,
+             const struct in6_addr *saddr,
+             const struct in6_addr *daddr,
+             int verbose,
+             ip6tc_handle_t *handle)
+{
+       fw->ipv6.src = *saddr;
+       fw->ipv6.dst = *daddr;
+
+       if (verbose)
+               print_firewall_line(fw, *handle);
+       return ip6tc_replace_entry(chain, fw, rulenum, handle);
+}
+
+static int
+insert_entry(const ip6t_chainlabel chain,
+            struct ip6t_entry *fw,
+            unsigned int rulenum,
+            unsigned int nsaddrs,
+            const struct in6_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in6_addr daddrs[],
+            int verbose,
+            ip6tc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ipv6.src = saddrs[i];
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ipv6.dst = daddrs[j];
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= ip6tc_insert_entry(chain, fw, rulenum, handle);
+               }
+       }
+
+       return ret;
+}
+
+static unsigned char *
+make_delete_mask(struct ip6t_entry *fw, struct ip6tables_rule_match *matches)
+{
+       /* Establish mask for comparison */
+       unsigned int size;
+       struct ip6tables_rule_match *matchp;
+       unsigned char *mask, *mptr;
+
+       size = sizeof(struct ip6t_entry);
+       for (matchp = matches; matchp; matchp = matchp->next)
+               size += IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + matchp->match->size;
+
+       mask = fw_calloc(1, size
+                        + IP6T_ALIGN(sizeof(struct ip6t_entry_target))
+                        + ip6tables_targets->size);
+
+       memset(mask, 0xFF, sizeof(struct ip6t_entry));
+       mptr = mask + sizeof(struct ip6t_entry);
+
+       for (matchp = matches; matchp; matchp = matchp->next) {
+               memset(mptr, 0xFF,
+                      IP6T_ALIGN(sizeof(struct ip6t_entry_match))
+                      + matchp->match->userspacesize);
+               mptr += IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + matchp->match->size;
+       }
+
+       memset(mptr, 0xFF, 
+              IP6T_ALIGN(sizeof(struct ip6t_entry_target))
+              + ip6tables_targets->userspacesize);
+
+       return mask;
+}
+
+static int
+delete_entry(const ip6t_chainlabel chain,
+            struct ip6t_entry *fw,
+            unsigned int nsaddrs,
+            const struct in6_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in6_addr daddrs[],
+            int verbose,
+            ip6tc_handle_t *handle,
+            struct ip6tables_rule_match *matches)
+{
+       unsigned int i, j;
+       int ret = 1;
+       unsigned char *mask;
+
+       mask = make_delete_mask(fw, matches);
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ipv6.src = saddrs[i];
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ipv6.dst = daddrs[j];
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= ip6tc_delete_entry(chain, fw, mask, handle);
+               }
+       }
+       free(mask);
+
+       return ret;
+}
+
+int
+for_each_chain(int (*fn)(const ip6t_chainlabel, int, ip6tc_handle_t *),
+              int verbose, int builtinstoo, ip6tc_handle_t *handle)
+{
+        int ret = 1;
+       const char *chain;
+       char *chains;
+       unsigned int i, chaincount = 0;
+
+       chain = ip6tc_first_chain(handle);
+       while (chain) {
+               chaincount++;
+               chain = ip6tc_next_chain(handle);
+        }
+
+       chains = fw_malloc(sizeof(ip6t_chainlabel) * chaincount);
+       i = 0;
+       chain = ip6tc_first_chain(handle);
+       while (chain) {
+               strcpy(chains + i*sizeof(ip6t_chainlabel), chain);
+               i++;
+               chain = ip6tc_next_chain(handle);
+        }
+
+       for (i = 0; i < chaincount; i++) {
+               if (!builtinstoo
+                   && ip6tc_builtin(chains + i*sizeof(ip6t_chainlabel),
+                                   *handle) == 1)
+                       continue;
+               ret &= fn(chains + i*sizeof(ip6t_chainlabel), verbose, handle);
+       }
+
+       free(chains);
+        return ret;
+}
+
+int
+flush_entries(const ip6t_chainlabel chain, int verbose,
+             ip6tc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(flush_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Flushing chain `%s'\n", chain);
+       return ip6tc_flush_entries(chain, handle);
+}
+
+static int
+zero_entries(const ip6t_chainlabel chain, int verbose,
+            ip6tc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(zero_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Zeroing chain `%s'\n", chain);
+       return ip6tc_zero_entries(chain, handle);
+}
+
+int
+delete_chain(const ip6t_chainlabel chain, int verbose,
+            ip6tc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(delete_chain, verbose, 0, handle);
+
+       if (verbose)
+               fprintf(stdout, "Deleting chain `%s'\n", chain);
+       return ip6tc_delete_chain(chain, handle);
+}
+
+static int
+list_entries(const ip6t_chainlabel chain, int verbose, int numeric,
+            int expanded, int linenumbers, ip6tc_handle_t *handle)
+{
+       int found = 0;
+       unsigned int format;
+       const char *this;
+
+       format = FMT_OPTIONS;
+       if (!verbose)
+               format |= FMT_NOCOUNTS;
+       else
+               format |= FMT_VIA;
+
+       if (numeric)
+               format |= FMT_NUMERIC;
+
+       if (!expanded)
+               format |= FMT_KILOMEGAGIGA;
+
+       if (linenumbers)
+               format |= FMT_LINENUMBERS;
+
+       for (this = ip6tc_first_chain(handle);
+            this;
+            this = ip6tc_next_chain(handle)) {
+               const struct ip6t_entry *i;
+               unsigned int num;
+
+               if (chain && strcmp(chain, this) != 0)
+                       continue;
+
+               if (found) printf("\n");
+
+               print_header(format, this, handle);
+               i = ip6tc_first_rule(this, handle);
+
+               num = 0;
+               while (i) {
+                       print_firewall(i,
+                                      ip6tc_get_target(i, handle),
+                                      num++,
+                                      format,
+                                      *handle);
+                       i = ip6tc_next_rule(i, handle);
+               }
+               found = 1;
+       }
+
+       errno = ENOENT;
+       return found;
+}
+
+static char *get_modprobe(void)
+{
+       int procfile;
+       char *ret;
+
+#define PROCFILE_BUFSIZ 1024
+       procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+       if (procfile < 0)
+               return NULL;
+
+       ret = malloc(PROCFILE_BUFSIZ);
+       if (ret) {
+               memset(ret, 0, PROCFILE_BUFSIZ);
+               switch (read(procfile, ret, PROCFILE_BUFSIZ)) {
+               case -1: goto fail;
+               case PROCFILE_BUFSIZ: goto fail; /* Partial read.  Wierd */
+               }
+               if (ret[strlen(ret)-1]=='\n') 
+                       ret[strlen(ret)-1]=0;
+               close(procfile);
+               return ret;
+       }
+ fail:
+       free(ret);
+       close(procfile);
+       return NULL;
+}
+
+int ip6tables_insmod(const char *modname, const char *modprobe, int quiet)
+{
+       char *buf = NULL;
+       char *argv[4];
+       int status;
+
+       /* If they don't explicitly set it, read out of kernel */
+       if (!modprobe) {
+               buf = get_modprobe();
+               if (!buf)
+                       return -1;
+               modprobe = buf;
+       }
+
+       switch (fork()) {
+       case 0:
+               argv[0] = (char *)modprobe;
+               argv[1] = (char *)modname;
+               if (quiet) {
+                       argv[2] = "-q";
+                       argv[3] = NULL;
+               } else {
+                       argv[2] = NULL;
+                       argv[3] = NULL;
+               }
+               execv(argv[0], argv);
+
+               /* not usually reached */
+               exit(1);
+       case -1:
+               return -1;
+
+       default: /* parent */
+               wait(&status);
+       }
+
+       free(buf);
+       if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+               return 0;
+       return -1;
+}
+
+int load_ip6tables_ko(const char *modprobe, int quiet)
+{
+       static int loaded = 0;
+       static int ret = -1;
+
+       if (!loaded) {
+               ret = ip6tables_insmod("ip6_tables", modprobe, quiet);
+               loaded = (ret == 0);
+       }
+
+       return ret;
+}
+
+static struct ip6t_entry *
+generate_entry(const struct ip6t_entry *fw,
+              struct ip6tables_rule_match *matches,
+              struct ip6t_entry_target *target)
+{
+       unsigned int size;
+       struct ip6tables_rule_match *matchp;
+       struct ip6t_entry *e;
+
+       size = sizeof(struct ip6t_entry);
+       for (matchp = matches; matchp; matchp = matchp->next)
+               size += matchp->match->m->u.match_size;
+
+       e = fw_malloc(size + target->u.target_size);
+       *e = *fw;
+       e->target_offset = size;
+       e->next_offset = size + target->u.target_size;
+
+       size = 0;
+       for (matchp = matches; matchp; matchp = matchp->next) {
+               memcpy(e->elems + size, matchp->match->m, matchp->match->m->u.match_size);
+               size += matchp->match->m->u.match_size;
+       }
+       memcpy(e->elems + size, target, target->u.target_size);
+
+       return e;
+}
+
+void clear_rule_matches(struct ip6tables_rule_match **matches)
+{
+       struct ip6tables_rule_match *matchp, *tmp;
+
+       for (matchp = *matches; matchp;) {
+               tmp = matchp->next;
+               if (matchp->match->m) {
+                       free(matchp->match->m);
+                       matchp->match->m = NULL;
+               }
+               if (matchp->match == matchp->match->next) {
+                       free(matchp->match);
+                       matchp->match = NULL;
+               }
+               free(matchp);
+               matchp = tmp;
+       }
+
+       *matches = NULL;
+}
+
+static void set_revision(char *name, u_int8_t revision)
+{
+       /* Old kernel sources don't have ".revision" field,
+          but we stole a byte from name. */
+       name[IP6T_FUNCTION_MAXNAMELEN - 2] = '\0';
+       name[IP6T_FUNCTION_MAXNAMELEN - 1] = revision;
+}
+
+int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
+{
+       struct ip6t_entry fw, *e = NULL;
+       int invert = 0;
+       unsigned int nsaddrs = 0, ndaddrs = 0;
+       struct in6_addr *saddrs = NULL, *daddrs = NULL;
+
+       int c, verbose = 0;
+       const char *chain = NULL;
+       const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
+       const char *policy = NULL, *newname = NULL;
+       unsigned int rulenum = 0, options = 0, command = 0;
+       const char *pcnt = NULL, *bcnt = NULL;
+       int ret = 1;
+       struct ip6tables_match *m;
+       struct ip6tables_rule_match *matches = NULL;
+       struct ip6tables_rule_match *matchp;
+       struct ip6tables_target *target = NULL;
+       struct ip6tables_target *t;
+       const char *jumpto = "";
+       char *protocol = NULL;
+       int proto_used = 0;
+
+       memset(&fw, 0, sizeof(fw));
+
+       /* re-set optind to 0 in case do_command gets called
+        * a second time */
+       optind = 0;
+
+       /* clear mflags in case do_command gets called a second time
+        * (we clear the global list of all matches for security)*/
+       for (m = ip6tables_matches; m; m = m->next)
+               m->mflags = 0;
+
+       for (t = ip6tables_targets; t; t = t->next) {
+               t->tflags = 0;
+               t->used = 0;
+       }
+
+       /* Suppress error messages: we may add new options if we
+           demand-load a protocol. */
+       opterr = 0;
+
+       while ((c = getopt_long(argc, argv,
+          "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvnt:m:xc:",
+                                          opts, NULL)) != -1) {
+               switch (c) {
+                       /*
+                        * Command selection
+                        */
+               case 'A':
+                       add_command(&command, CMD_APPEND, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'D':
+                       add_command(&command, CMD_DELETE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!') {
+                               rulenum = parse_rulenumber(argv[optind++]);
+                               command = CMD_DELETE_NUM;
+                       }
+                       break;
+
+               case 'R':
+                       add_command(&command, CMD_REPLACE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a rule number",
+                                          cmd2char(CMD_REPLACE));
+                       break;
+
+               case 'I':
+                       add_command(&command, CMD_INSERT, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else rulenum = 1;
+                       break;
+
+               case 'L':
+                       add_command(&command, CMD_LIST, CMD_ZERO,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'F':
+                       add_command(&command, CMD_FLUSH, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'Z':
+                       add_command(&command, CMD_ZERO, CMD_LIST,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                               && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'N':
+                       if (optarg && (*optarg == '-' || *optarg == '!'))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name not allowed to start "
+                                          "with `%c'\n", *optarg);
+                       if (find_target(optarg, TRY_LOAD))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name may not clash "
+                                          "with target name\n");
+                       add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'X':
+                       add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'E':
+                       add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               newname = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires old-chain-name and "
+                                          "new-chain-name",
+                                           cmd2char(CMD_RENAME_CHAIN));
+                       break;
+
+               case 'P':
+                       add_command(&command, CMD_SET_POLICY, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               policy = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a chain and a policy",
+                                          cmd2char(CMD_SET_POLICY));
+                       break;
+
+               case 'h':
+                       if (!optarg)
+                               optarg = argv[optind];
+
+                       /* ip6tables -p icmp -h */
+                       if (!matches && protocol)
+                               find_match(protocol, TRY_LOAD, &matches);
+
+                       exit_printhelp(matches);
+
+                       /*
+                        * Option selection
+                        */
+               case 'p':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_PROTOCOL, &fw.ipv6.invflags,
+                                  invert);
+
+                       /* Canonicalize into lower case */
+                       for (protocol = argv[optind-1]; *protocol; protocol++)
+                               *protocol = tolower(*protocol);
+
+                       protocol = argv[optind-1];
+                       fw.ipv6.proto = parse_protocol(protocol);
+                       fw.ipv6.flags |= IP6T_F_PROTO;
+
+                       if (fw.ipv6.proto == 0
+                           && (fw.ipv6.invflags & IP6T_INV_PROTO))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "rule would never match protocol");
+                       
+                       if (is_exthdr(fw.ipv6.proto)
+                           && (fw.ipv6.invflags & IP6T_INV_PROTO) == 0)
+                               printf("Warning: never matched protocol: %s. "
+                                      "use extension match instead.\n",
+                                      protocol);
+                       break;
+
+               case 's':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_SOURCE, &fw.ipv6.invflags,
+                                  invert);
+                       shostnetworkmask = argv[optind-1];
+                       break;
+
+               case 'd':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_DESTINATION, &fw.ipv6.invflags,
+                                  invert);
+                       dhostnetworkmask = argv[optind-1];
+                       break;
+
+               case 'j':
+                       set_option(&options, OPT_JUMP, &fw.ipv6.invflags,
+                                  invert);
+                       jumpto = parse_target(optarg);
+                       /* TRY_LOAD (may be chain name) */
+                       target = find_target(jumpto, TRY_LOAD);
+
+                       if (target) {
+                               size_t size;
+
+                               size = IP6T_ALIGN(sizeof(struct ip6t_entry_target))
+                                       + target->size;
+
+                               target->t = fw_calloc(1, size);
+                               target->t->u.target_size = size;
+                               strcpy(target->t->u.user.name, jumpto);
+                               if (target->init != NULL)
+                                       target->init(target->t, &fw.nfcache);
+                               opts = merge_options(opts, target->extra_opts, &target->option_offset);
+                       }
+                       break;
+
+
+               case 'i':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEIN, &fw.ipv6.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ipv6.iniface,
+                                       fw.ipv6.iniface_mask);
+                       break;
+
+               case 'o':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEOUT, &fw.ipv6.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ipv6.outiface,
+                                       fw.ipv6.outiface_mask);
+                       break;
+
+               case 'v':
+                       if (!verbose)
+                               set_option(&options, OPT_VERBOSE,
+                                          &fw.ipv6.invflags, invert);
+                       verbose++;
+                       break;
+
+               case 'm': {
+                       size_t size;
+
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --match");
+
+                       m = find_match(optarg, LOAD_MUST_SUCCEED, &matches);
+                       size = IP6T_ALIGN(sizeof(struct ip6t_entry_match))
+                                        + m->size;
+                       m->m = fw_calloc(1, size);
+                       m->m->u.match_size = size;
+                       strcpy(m->m->u.user.name, m->name);
+                       set_revision(m->m->u.user.name, m->revision);
+                       if (m->init != NULL)
+                               m->init(m->m, &fw.nfcache);
+                       if (m != m->next)
+                               /* Merge options for non-cloned matches */
+                               opts = merge_options(opts, m->extra_opts, &m->option_offset);
+               }
+               break;
+
+               case 'n':
+                       set_option(&options, OPT_NUMERIC, &fw.ipv6.invflags,
+                                  invert);
+                       break;
+
+               case 't':
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --table");
+                       *table = argv[optind-1];
+                       break;
+
+               case 'x':
+                       set_option(&options, OPT_EXPANDED, &fw.ipv6.invflags,
+                                  invert);
+                       break;
+
+               case 'V':
+                       if (invert)
+                               printf("Not %s ;-)\n", program_version);
+                       else
+                               printf("%s v%s\n",
+                                      program_name, program_version);
+                       exit(0);
+
+               case '0':
+                       set_option(&options, OPT_LINENUMBERS, &fw.ipv6.invflags,
+                                  invert);
+                       break;
+
+               case 'M':
+                       modprobe = optarg;
+                       break;
+
+               case 'c':
+
+                       set_option(&options, OPT_COUNTERS, &fw.ipv6.invflags,
+                                  invert);
+                       pcnt = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               bcnt = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c requires packet and byte counter",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(pcnt, "%llu", (unsigned long long *)&fw.counters.pcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c packet counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(bcnt, "%llu", (unsigned long long *)&fw.counters.bcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c byte counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+                       
+                       break;
+
+
+               case 1: /* non option */
+                       if (optarg[0] == '!' && optarg[1] == '\0') {
+                               if (invert)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "multiple consecutive ! not"
+                                                  " allowed");
+                               invert = TRUE;
+                               optarg[0] = '\0';
+                               continue;
+                       }
+                       printf("Bad argument `%s'\n", optarg);
+                       exit_tryhelp(2);
+
+               default:
+                       if (!target
+                           || !(target->parse(c - target->option_offset,
+                                              argv, invert,
+                                              &target->tflags,
+                                              &fw, &target->t))) {
+                               for (matchp = matches; matchp; matchp = matchp->next) {
+                                       if (matchp->completed) 
+                                               continue;
+                                       if (matchp->match->parse(c - matchp->match->option_offset,
+                                                    argv, invert,
+                                                    &matchp->match->mflags,
+                                                    &fw,
+                                                    &fw.nfcache,
+                                                    &matchp->match->m))
+                                               break;
+                               }
+                               m = matchp ? matchp->match : NULL;
+
+                               /* If you listen carefully, you can
+                                  actually hear this code suck. */
+
+                               /* some explanations (after four different bugs
+                                * in 3 different releases): If we encounter a
+                                * parameter, that has not been parsed yet,
+                                * it's not an option of an explicitly loaded
+                                * match or a target.  However, we support
+                                * implicit loading of the protocol match
+                                * extension.  '-p tcp' means 'l4 proto 6' and
+                                * at the same time 'load tcp protocol match on
+                                * demand if we specify --dport'.
+                                *
+                                * To make this work, we need to make sure:
+                                * - the parameter has not been parsed by
+                                *   a match (m above)
+                                * - a protocol has been specified
+                                * - the protocol extension has not been
+                                *   loaded yet, or is loaded and unused
+                                *   [think of ip6tables-restore!]
+                                * - the protocol extension can be successively
+                                *   loaded
+                                */
+                               if (m == NULL
+                                   && protocol
+                                   && (!find_proto(protocol, DONT_LOAD,
+                                                  options&OPT_NUMERIC, NULL) 
+                                       || (find_proto(protocol, DONT_LOAD,
+                                                       options&OPT_NUMERIC, NULL)
+                                           && (proto_used == 0))
+                                      )
+                                   && (m = find_proto(protocol, TRY_LOAD,
+                                                      options&OPT_NUMERIC, &matches))) {
+                                       /* Try loading protocol */
+                                       size_t size;
+                                       
+                                       proto_used = 1;
+
+                                       size = IP6T_ALIGN(sizeof(struct ip6t_entry_match))
+                                                        + m->size;
+
+                                       m->m = fw_calloc(1, size);
+                                       m->m->u.match_size = size;
+                                       strcpy(m->m->u.user.name, m->name);
+                                       set_revision(m->m->u.user.name,
+                                                    m->revision);
+                                       if (m->init != NULL)
+                                               m->init(m->m, &fw.nfcache);
+
+                                       opts = merge_options(opts,
+                                           m->extra_opts, &m->option_offset);
+
+                                       optind--;
+                                       continue;
+                               }
+
+                               if (!m)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Unknown arg `%s'",
+                                                  argv[optind-1]);
+                       }
+               }
+               invert = FALSE;
+       }
+
+       for (matchp = matches; matchp; matchp = matchp->next)
+               matchp->match->final_check(matchp->match->mflags);
+
+       if (target)
+               target->final_check(target->tflags);
+
+       /* Fix me: must put inverse options checking here --MN */
+
+       if (optind < argc)
+               exit_error(PARAMETER_PROBLEM,
+                          "unknown arguments found on commandline");
+       if (!command)
+               exit_error(PARAMETER_PROBLEM, "no command specified");
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "nothing appropriate following !");
+
+       if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
+               if (!(options & OPT_DESTINATION))
+                       dhostnetworkmask = "::0/0";
+               if (!(options & OPT_SOURCE))
+                       shostnetworkmask = "::0/0";
+       }
+
+       if (shostnetworkmask)
+               parse_hostnetworkmask(shostnetworkmask, &saddrs,
+                                     &(fw.ipv6.smsk), &nsaddrs);
+
+       if (dhostnetworkmask)
+               parse_hostnetworkmask(dhostnetworkmask, &daddrs,
+                                     &(fw.ipv6.dmsk), &ndaddrs);
+
+       if ((nsaddrs > 1 || ndaddrs > 1) &&
+           (fw.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP)))
+               exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+                          " source or destination IP addresses");
+
+       if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
+               exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
+                          "specify a unique address");
+
+       generic_opt_check(command, options);
+
+       if (chain && strlen(chain) > IP6T_FUNCTION_MAXNAMELEN)
+               exit_error(PARAMETER_PROBLEM,
+                          "chain name `%s' too long (must be under %i chars)",
+                          chain, IP6T_FUNCTION_MAXNAMELEN);
+
+       /* only allocate handle if we weren't called with a handle */
+       if (!*handle)
+               *handle = ip6tc_init(*table);
+
+       /* try to insmod the module if iptc_init failed */
+       if (!*handle && load_ip6tables_ko(modprobe, 0) != -1)
+               *handle = ip6tc_init(*table);
+
+       if (!*handle)
+               exit_error(VERSION_PROBLEM,
+                       "can't initialize ip6tables table `%s': %s",
+                       *table, ip6tc_strerror(errno));
+
+       if (command == CMD_APPEND
+           || command == CMD_DELETE
+           || command == CMD_INSERT
+           || command == CMD_REPLACE) {
+               if (strcmp(chain, "PREROUTING") == 0
+                   || strcmp(chain, "INPUT") == 0) {
+                       /* -o not valid with incoming packets. */
+                       if (options & OPT_VIANAMEOUT)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEOUT),
+                                          chain);
+               }
+
+               if (strcmp(chain, "POSTROUTING") == 0
+                   || strcmp(chain, "OUTPUT") == 0) {
+                       /* -i not valid with outgoing packets */
+                       if (options & OPT_VIANAMEIN)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEIN),
+                                          chain);
+               }
+
+               if (target && ip6tc_is_chain(jumpto, *handle)) {
+                       printf("Warning: using chain %s, not extension\n",
+                              jumpto);
+
+                       if (target->t)
+                               free(target->t);
+
+                       target = NULL;
+               }
+
+               /* If they didn't specify a target, or it's a chain
+                  name, use standard. */
+               if (!target
+                   && (strlen(jumpto) == 0
+                       || ip6tc_is_chain(jumpto, *handle))) {
+                       size_t size;
+
+                       target = find_target(IP6T_STANDARD_TARGET,
+                                            LOAD_MUST_SUCCEED);
+
+                       size = sizeof(struct ip6t_entry_target)
+                               + target->size;
+                       target->t = fw_calloc(1, size);
+                       target->t->u.target_size = size;
+                       strcpy(target->t->u.user.name, jumpto);
+                       if (target->init != NULL)
+                               target->init(target->t, &fw.nfcache);
+               }
+
+               if (!target) {
+                       /* it is no chain, and we can't load a plugin.
+                        * We cannot know if the plugin is corrupt, non
+                        * existant OR if the user just misspelled a
+                        * chain. */
+                       find_target(jumpto, LOAD_MUST_SUCCEED);
+               } else {
+                       e = generate_entry(&fw, matches, target->t);
+                       free(target->t);
+               }
+       }
+
+       switch (command) {
+       case CMD_APPEND:
+               ret = append_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_DELETE:
+               ret = delete_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle, matches);
+               break;
+       case CMD_DELETE_NUM:
+               ret = ip6tc_delete_num_entry(chain, rulenum - 1, handle);
+               break;
+       case CMD_REPLACE:
+               ret = replace_entry(chain, e, rulenum - 1,
+                                   saddrs, daddrs, options&OPT_VERBOSE,
+                                   handle);
+               break;
+       case CMD_INSERT:
+               ret = insert_entry(chain, e, rulenum - 1,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_LIST:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               break;
+       case CMD_FLUSH:
+               ret = flush_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_ZERO:
+               ret = zero_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_LIST|CMD_ZERO:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               if (ret)
+                       ret = zero_entries(chain,
+                                          options&OPT_VERBOSE, handle);
+               break;
+       case CMD_NEW_CHAIN:
+               ret = ip6tc_create_chain(chain, handle);
+               break;
+       case CMD_DELETE_CHAIN:
+               ret = delete_chain(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_RENAME_CHAIN:
+               ret = ip6tc_rename_chain(chain, newname,        handle);
+               break;
+       case CMD_SET_POLICY:
+               ret = ip6tc_set_policy(chain, policy, NULL, handle);
+               break;
+       default:
+               /* We should never reach this... */
+               exit_tryhelp(2);
+       }
+
+       if (verbose > 1)
+               dump_entries6(*handle);
+
+       clear_rule_matches(&matches);
+
+       if (e != NULL) {
+               free(e);
+               e = NULL;
+       }
+
+       for (c = 0; c < nsaddrs; c++)
+               free(&saddrs[c]);
+
+       for (c = 0; c < ndaddrs; c++)
+               free(&daddrs[c]);
+
+       free_opts(1);
+
+       return ret;
+}
diff --git a/ipset/ChangeLog b/ipset/ChangeLog
new file mode 100644 (file)
index 0000000..ee0154e
--- /dev/null
@@ -0,0 +1,118 @@
+2.3.0
+ - jiffies rollover bug in iptree type fixed (reported by Lukasz Nierycho
+   and others)
+ - endiannes bug in iptree type fixed (spotted by Jan Engelhardt)
+ - iptreemap type added (submitted by Sven Wegener)  
+ - 2.6.22/23 compatibility fixes (Jeremy Jacque)
+ - typo fixes in ipset (Neville D)
+ - separator changed to ':' from '%' (old one still supported) in ipset
+
+2.2.9a
+ - use correct type (socklen_t) for getsockopt (H. Nakano)
+ - incorrect return codes fixed (Tomasz Lemiech, Alexey Bortnikov)
+ - kernel header dependency removed (asm/bitops.h)
+ - ipset now tries to load in the ip_set kernel module if the protocol
+   is not available
+
+2.2.9
+ - 'ipset -N' did not generate proper return code
+ - 'limit' module parameter added to the kernel modules of the
+   iphash, ipporthash, nethash and iptree type of sets so that
+   the maximal number of elements can now be limited
+ - zero valued entries (port 0 or IP address 0.0.0.0) were
+   detected as members of the hash/tree kind of sets
+   (reported by Andrew Kraslavsky)
+ - list and save operations used the external identifier
+   of the sets for the bindings instead of the internal one
+   (reported by Amin Azez)
+
+2.2.8
+ - Nasty off-by-one bug fixed in iptree type of sets
+   (bug reported by Pablo Sole)
+
+2.2.7
+ All patches were submitted by Jones Desougi
+ - missing or confusing error message fixes for ipporthash
+ - minor correction in debugging in nethash
+ - copy-paste bug in kernel set types at memory allocation
+   checking fixed
+ - unified memory allocations in ipset
+
+2.2.6
+ - memory allocation in iptree is changed to GFP_ATOMIC because
+   we hold a lock (bug reported by Radek Hladik)
+ - compatibility fix: __nocast is not defined in all 2.6 branches
+   (problem reported by Ming-Ching Tiew)
+ - manpage corrections
+
+2.2.5
+ - garbage collector of iptree type of sets is fixed: flushing
+   sets/removing kernel module could corrupt the timer
+ - new ipporthash type added
+ - manpage fixes and corrections
+
+2.2.4
+ - half-fixed memory allocation bug in iphash and nethash finally
+   completely fixed (bug reported by Nikolai Malykh)
+ - restrictions to enter zero-valued entries into all non-hash type sets
+   were removed
+ - Too strict check on the set size of ipmap type was corrected 
+
+2.2.3
+ - memory allocation bug in iphash and nethash in connection with the SET
+   target was fixed (bug reported by Nikolai Malykh)
+ - lockhelp.h was removed from the 2.6.13 kernel tree, ip_set.c is
+   updated accordingly (Cardoso Didier, Samir Bellabes)
+ - manpage is updated to clearly state the command order in restore mode
+
+2.2.2
+ - Jiffies rollover bug in ip_set_iptree reported and fixed by Rob Nielsen
+ - Compiler warning in the non-SMP case fixed (Marcus Sundberg)
+ - slab cache names shrunk in order to be compatible with 2.4.* (Marcus
+   Sundberg)
+
+2.2.1
+ - Magic number in ip_set_nethash.h was mistyped (bug reported by Rob
+   Carlson)
+ - ipset can now test IP addresses in nethash type of sets (i.e. addresses
+   in netblocks added to the set)
+
+2.2.0
+ - Locking bug in ip_set_nethash.c (Clifford Wolf and Rob Carlson)
+ - Makefile contained an unnecessary variable in IPSET_LIB_DIR (Clifford
+   Wolf)
+ - Safety checkings of restore in ipset was incomplete (Robin H. Johnson)
+ - More careful resizing by avoiding locking completely
+ - stdin stored internally in a temporary file, so we can feed 'ipset -R'
+   from a pipe
+ - iptree maptype added
+
+2.1
+ - Lock debugging used with debugless lock definiton (Piotr Chytla and
+   others).
+ - Bindings were not properly filled out at listing (kernel)
+ - When listing sets from kernel, id was not added to the set structure
+   (ipset)
+ - nethash maptype added
+ - ipset manpage corrections (macipmap)
+
+2.0.1
+ - Missing -fPIC in Makefile (Robert Iakobashvili)
+ - Cut'n'paste bug at saving macipmap types (Vincent Bernat).
+ - Bug in printing/saving SET targets reported and fixed by Michal
+   Pokrywka
+
+2.0
+ - Chaining of sets are changed: child sets replaced by bindings
+ - Kernel-userspace communication reorganized to minimize the number
+   of syscalls
+ - Save and restore functionality implemented
+ - iphash type reworked: clashing resolved by double-hashing and by
+   dynamically growing the set
+
+1.0
+ - Renamed to ipset
+ - Rewritten to support child pools
+ - portmap, iphash pool support added
+ - too much other mods here and there to list...
+
diff --git a/ipset/ChangeLog.ippool b/ipset/ChangeLog.ippool
new file mode 100644 (file)
index 0000000..669c304
--- /dev/null
@@ -0,0 +1,59 @@
+Original changelog as ippool:
+
+0.3.2b
+- Fixed missing kfree(pool)                              (Martin Josefsson)
+
+0.3.2a
+- Added libipt_pool.c and libipt_POOL.c                          (Martin Josefsson)
+
+
+0.3.2
+- Passes pointers to skb's around instead of ip's in the  (Martin Josefsson)
+  kernel modules.
+- Added a new pooltype, macipmap, which matches ip's      (Martin Josefsson)
+  against macaddresses.
+- Cleaned up a lot of typedefs.                           (Martin Josefsson)
+- Fixed an unlocking of the wrong lock.                   (Martin Josefsson)
+- Fixed a refcount bug when allocated memory was too      (Martin Josefsson)
+  small.
+- Fixed a free() of unallocated memory.                   (Martin Josefsson)
+- Switched from kmalloc/kfree to vmalloc/vfree for        (Martin Josefsson)
+  pool-listings/additions.
+
+
+0.3.1
+- Changed the API between userspace modules and base.     (Joakim Axelsson)
+  Moved the memberdata pointer to module self.
+  As a result of this Protocolversion is increased to 4.
+- Fixed problems with crashing null-pooltype              (Joakim Axelsson)
+- Fixed problems with compiling warnings                  (Joakim Axelsson)
+  in null pooltype.
+
+       
+0.3.0:
+- Changed the listing to use getsockopt.                   (Joakim Axelsson)
+  /proc is left for debuging purpose.
+  This is a mayor change. 
+  Protocolversion is increased to 3 
+- Added support for --quiet                                (Joakim Axelsson)
+- Added support for --sorted                               (Joakim Axelsson)
+- Added support for --numeric                              (Joakim Axelsson)
+- Added support for --exact                                (Joakim Axelsson)
+- Added -Z (Zero) which zero's the counter                 (Joakim Axelsson)
+  on one or all pools.
+- Added support for --debug that prints all debug-messages (Joakim Axelsosn)
+  in userspace. Need to be compiled with 
+  IP_POOL_DEBUG tho.
+- Added null pooltype. For demostration and                (Joakim Axelsson)
+  pooltype skeleton mostly                                 
+- Fixed bug with possibly renaming to an already           (Joakim Axelsson) 
+  existing pool.
+- Change error to OTHER_PROBLEM on add and del IP.         (Joakim Axelsson)
+
+0.2.1-0.2.3
+- Better handling of references                            (Patrick Schaaf)
+- Various bugfixes                                         (Patrick Schaaf)
+- Cleaning up the code in kernelspace                      (Patrick Schaaf)
+
+0.2.0:
+- Rewrote the entrie system. Modulized it.                 (Joakim Axelsson)
diff --git a/ipset/Makefile b/ipset/Makefile
new file mode 100644 (file)
index 0000000..4d4a805
--- /dev/null
@@ -0,0 +1,51 @@
+IPSET_VERSION:=2.3.0
+
+IPSET_LIB_DIR:=$(LIBDIR)/ipset
+
+IPSET_CFLAGS:=# -g -DIPSET_DEBUG #-pg # -DIPTC_DEBUG
+SETTYPES:=ipmap portmap macipmap iphash nethash iptree iptreemap ipporthash
+
+# for building on fedora 8
+ifneq "$(shell ld --help | grep build-id)" ""
+#not needed# CFLAGS += -Wl,--build-id
+LD += --build-id
+endif
+
+ifneq ($(wildcard $(KERNEL_DIR)/include/linux/netfilter_ipv4/ip_set.h),)
+
+EXTRAS+=ipset/ipset
+EXTRAS+=$(foreach T, $(SETTYPES),ipset/libipset_$(T).so)
+EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/ipset $(DESTDIR)$(MANDIR)/man8/ipset.8
+EXTRA_INSTALLS+=$(foreach T, $(SETTYPES), $(DESTDIR)$(LIBDIR)/ipset/libipset_$(T).so)
+
+#Dependencies
+ipset/libipset.d: $(foreach T, $(SETTYPES),ipset/ipset_$(T).c)
+       @-$(CC) -M -MG $(CFLAGS) $(IPSET_CFLAGS) $^ | sed -e 's@^.*\.o:@$*.d $*.a($*.o):@' > $@
+
+#The ipset(8) self
+ipset/ipset.o: ipset/ipset.c
+       $(CC) $(CFLAGS) $(IPSET_CFLAGS) -DIPSET_VERSION=\"$(IPSET_VERSION)\" -DIPSET_LIB_DIR=\"$(IPSET_LIB_DIR)\" -c -o $@ $<
+
+ipset/ipset: ipset/ipset.o
+       $(CC) $(CFLAGS) $(IPSET_CFLAGS) -ldl -rdynamic -o $@ $^
+
+#Pooltypes
+ipset/ipset_%.o: ipset/ipset_%.c
+       $(CC) $(SH_CFLAGS) $(IPSET_CFLAGS) -o $@ -c $<
+
+ipset/libipset_%.so: ipset/ipset_%.o
+       $(CC) -shared -o $@ $<
+
+$(DESTDIR)$(LIBDIR)/ipset/libipset_%.so: ipset/libipset_%.so
+       @[ -d $(DESTDIR)$(LIBDIR)/ipset ] || mkdir -p $(DESTDIR)$(LIBDIR)/ipset
+       cp $< $@
+
+$(DESTDIR)$(BINDIR)/ipset: ipset/ipset
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+$(DESTDIR)$(MANDIR)/man8/ipset.8: ipset/ipset.8
+       @[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8
+       cp $< $@
+
+endif
diff --git a/ipset/TODO b/ipset/TODO
new file mode 100644 (file)
index 0000000..9d46233
--- /dev/null
@@ -0,0 +1,2 @@
+- rewrite kernel-userspace communication from sockopt to netlink
+- IPv6 support
diff --git a/ipset/ipset.8 b/ipset/ipset.8
new file mode 100644 (file)
index 0000000..d2c8100
--- /dev/null
@@ -0,0 +1,468 @@
+.TH IPSET 8 "Feb 05, 2004" "" ""
+.\"
+.\" Man page written by Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ipset \- administration tool for IP sets
+.SH SYNOPSIS
+.BR "ipset -N " "set type-specification [options]"
+.br
+.BR "ipset -[XFLSHh] " "[set] [options]"
+.br
+.BR "ipset -[EW] " "from-set to-set"
+.br
+.BR "ipset -[ADU] " "set entry"
+.br
+.BR "ipset -B " "set entry -b binding"
+.br
+.BR "ipset -T " "set entry [-b binding]"
+.br
+.BR "ipset -R "
+.SH DESCRIPTION
+.B ipset
+is used to set up, maintain and inspect so called IP sets in the Linux
+kernel. Depending on the type, an IP set may store IP addresses, (TCP/UDP)
+port numbers or additional informations besides IP addresses: the word IP 
+means a general term here. See the set type definitions below.
+.P
+Any entry in a set can be bound to another set, which forms a relationship
+between a set element and the set it is bound to. In order to define a
+binding it is not required that the entry be already added to the set. 
+The sets may have a default binding, which is valid for every set element 
+for which there is no binding defined at all.
+.P
+IP set bindings pointing to sets and iptables matches and targets 
+referring to sets creates references, which protects the given sets in 
+the kernel. A set cannot be removed (destroyed) while there is a single
+reference pointing to it.
+.SH OPTIONS
+The options that are recognized by
+.B ipset
+can be divided into several different groups.
+.SS COMMANDS
+These options specify the specific action to perform.  Only one of them
+can be specified on the command line unless otherwise specified
+below.  For all the long versions of the command and option names, you
+need to use only enough letters to ensure that
+.B ipset
+can differentiate it from all other options.
+.TP
+.BI "-N, --create " "\fIsetname\fP type type-specific-options"
+Create a set identified with setname and specified type. 
+Type-specific options must be supplied.
+.TP
+.BI "-X, --destroy " "[\fIsetname\fP]"
+Destroy the specified set, or all sets if none or the keyword
+.B
+:all:
+is specified.
+Before destroying the set, all bindings belonging to the 
+set elements and the default binding of the set are removed.
+
+If the set has got references, nothing is done.
+.TP
+.BI "-F, --flush " "[\fIsetname\fP]"
+Delete all entries from the specified set, or flush
+all sets if none or the keyword
+.B
+:all:
+is given. Bindings are not affected by the flush operation.
+.TP
+.BI "-E, --rename " "\fIfrom-setname\fP \fIto-setname\fP"
+Rename a set. Set identified by to-setname must not exist.
+.TP
+.BI "-W, --swap " "\fIfrom-setname\fP \fIto-setname\fP"
+Swap two sets as they referenced in the Linux kernel.
+.B
+iptables
+rules or
+.B
+ipset
+bindings pointing to the content of from-setname will point to 
+the content of to-setname and vice versa. Both sets must exist.
+.TP
+.BI "-L, --list " "[\fIsetname\fP]"
+List the entries and bindings for the specified set, or for
+all sets if none or the keyword
+.B
+:all:
+is given. The
+.B "-n, --numeric"
+option can be used to suppress name lookups and generate numeric
+output. When the
+.B "-s, --sorted"
+option is given, the entries are listed sorted (if the given set
+type supports the operation).
+.TP
+.BI "-S, --save " "[\fIsetname\fP]"
+Save the given set, or all sets if none or the keyword
+.B
+:all:
+is specified to stdout in a format that --restore can read.
+.TP
+.BI "-R, --restore "
+Restore a saved session generated by --save. The saved session
+can be fed from stdin.
+
+When generating a session file please note that the supported commands
+(create set, add element, bind) must appear in a strict order: first create
+the set, then add all elements. Then create the next set, add all its elements
+and so on. Finally you can list all binding commands. Also, it is a restore
+operation, so the sets being restored must not exist.
+.TP
+.BI "-A, --add " "\fIsetname\fP \fIIP\fP"
+Add an IP to a set.
+.TP
+.BI "-D, --del " "\fIsetname\fP \fIIP\fP"
+Delete an IP from a set. 
+.TP
+.BI "-T, --test " "\fIsetname\fP \fIIP
+Test wether an IP is in a set or not. Exit status number is zero
+if the tested IP is in the set and nonzero if it is missing from 
+the set.
+.TP
+.BI "-T, --test " "\fIsetname\fP \fIIP\fP \fI--binding\fP \fIto-setname\fP"
+Test wether the IP belonging to the set points to the specified binding. 
+Exit status number is zero if the binding points to the specified set, 
+otherwise it is nonzero. The keyword
+.B
+:default:
+can be used to test the default binding of the set.
+.TP
+.BI "-B, --bind " "\fIsetname\fP \fIIP\fP \fI--binding\fP \fIto-setname\fP"
+Bind the IP in setname to to-setname.
+.TP
+.BI "-U, --unbind " "\fIsetname\fP \fIIP\fP"
+Delete the binding belonging to IP in set setname. 
+.TP
+.BI "-H, --help " "[settype]"
+Print help and settype specific help if settype specified.
+.P
+At the
+.B
+-B, -U
+and
+.B 
+-T
+commands you can use the token
+.B
+:default:
+to bind, unbind or test the default binding of a set instead
+of an IP. At the
+.B
+-U
+command you can use the token
+.B
+:all:
+to destroy the bindings of all elements of a set.
+.SS "OTHER OPTIONS"
+The following additional options can be specified:
+.TP
+.B "-b, --binding setname"
+The option specifies the value of the binding for the
+.B "-B"
+binding command, for which it is a mandatory option.
+You can use it in the
+.B "-T"
+test command as well to test bindings.
+.TP
+.B "-s, --sorted"
+Sorted output. When listing sets, entries are listed sorted.
+.TP
+.B "-n, --numeric"
+Numeric output. When listing sets, bindings, IP addresses and 
+port numbers will be printed in numeric format. By default the 
+program will try to display them as host names, network names 
+or services (whenever applicable), which can trigger
+.B
+slow
+DNS 
+lookups.
+.TP
+.B "-q, --quiet"
+Suppress any output to stdout and stderr. ipset will still return
+possible errors.
+.SH SET TYPES
+ipset supports the following set types:
+.SS ipmap
+The ipmap set type uses a memory range, where each bit represents
+one IP address. An ipmap set can store up to 65536 (B-class network)
+IP addresses. The ipmap set type is very fast and memory cheap, great
+for use when one want to match certain IPs in a range. Using the
+.B "--netmask"
+option with a CIDR netmask value between 0-32 when creating an ipmap
+set, you will be able to store and match network addresses: i.e an
+IP address will be in the set if the value resulted by masking the address
+with the specified netmask can be found in the set.
+.P
+Options to use when creating an ipmap set:
+.TP
+.BR "--from " from-IP
+.TP
+.BR "--to " to-IP
+Create an ipmap set from the specified range.
+.TP
+.BR "--network " IP/mask
+Create an ipmap set from the specified network.
+.TP
+.BR "--netmask " CIDR-netmask
+When the optional
+.B "--netmask"
+parameter specified, network addresses will be 
+stored in the set instead of IP addresses, and the from-IP parameter
+must be a network address.
+.SS macipmap
+The macipmap set type uses a memory range, where each 8 bytes
+represents one IP and a MAC addresses. A macipmap set type can store
+up to 65536 (B-class network) IP addresses with MAC.
+When adding an entry to a macipmap set, you must specify the entry as
+.I IP:MAC.
+When deleting or testing macipmap entries, the
+.I :MAC
+part is not mandatory. (The old "%" separation token instead of ":", i.e
+IP%MAC is accepted as well.)
+.P
+Options to use when creating an macipmap set:
+.TP
+.BR "--from " from-IP
+.TP
+.BR "--to " to-IP
+Create a macipmap set from the specified range.
+.TP
+.BR "--network " IP/mask
+Create a macipmap set from the specified network.
+.TP
+.BR "--matchunset"
+When the optional
+.B "--matchunset"
+parameter specified, IP addresses which could be stored 
+in the set but not set yet, will always match.
+.P
+Please note, the 
+.I
+set
+and
+.I
+SET
+netfilter kernel modules
+.B
+always
+use the source MAC address from the packet to match, add or delete
+entries from a macipmap type of set.
+.SS portmap
+The portmap set type uses a memory range, where each bit represents
+one port. A portmap set type can store up to 65536 ports.
+The portmap set type is very fast and memory cheap.
+.P
+Options to use when creating an portmap set:
+.TP
+.BR "--from " from-port
+.TP
+.BR "--to " to-port
+Create a portmap set from the specified range.
+.SS iphash
+The iphash set type uses a hash to store IP addresses.
+In order to avoid clashes in the hash double-hashing, and as a last
+resort, dynamic growing of the hash performed. The iphash set type is
+great to store random addresses. By supplyig the
+.B "--netmask"
+option with a CIDR netmask value between 0-32 at creating the set,
+you will be able to store and match network addresses instead: i.e 
+an IP address will be in the set if the value of the address
+masked with the specified netmask can be found in the set.
+.P
+Options to use when creating an iphash set:
+.TP
+.BR "--hashsize " hashsize
+The initial hash size (default 1024)
+.TP
+.BR "--probes " probes
+How many times try to resolve clashing at adding an IP to the hash 
+by double-hashing (default 8).
+.TP
+.BR "--resize " percent
+Increase the hash size by this many percent (default 50) when adding
+an IP to the hash could not be performed after
+.B
+probes
+number of double-hashing. 
+.TP
+.BR "--netmask " CIDR-netmask
+When the optional
+.B "--netmask"
+parameter specified, network addresses will be 
+stored in the set instead of IP addresses.
+.P
+The iphash type of sets can store up to 65535 entries. If a set is full,
+no new entries can be added to it.
+.P
+Sets created by zero valued resize parameter won't be resized at all.
+The lookup time in an iphash type of set approximately linearly grows with
+the value of the 
+.B
+probes
+parameter. At the same time higher 
+.B
+probes
+values result a better utilized hash while smaller values 
+produce a larger, sparse hash.
+.SS nethash
+The nethash set type uses a hash to store different size of
+network addresses. The
+.I
+IP
+"address" used in the ipset commands must be in the form
+.I
+IP-address/cidr-size
+where the CIDR block size must be in the inclusive range of 1-31.
+In order to avoid clashes in the hash 
+double-hashing, and as a last resort, dynamic growing of the hash performed.
+.P
+Options to use when creating an nethash set:
+.TP
+.BR "--hashsize " hashsize
+The initial hash size (default 1024)
+.TP
+.BR "--probes " probes
+How many times try to resolve clashing at adding an IP to the hash 
+by double-hashing (default 4).
+.TP
+.BR "--resize " percent
+Increase the hash size by this many percent (default 50) when adding
+an IP to the hash could not be performed after
+.P
+The nethash type of sets can store up to 65535 entries. If a set is full,
+no new entries can be added to it.
+.P
+An IP address will be in a nethash type of set if it is in any of the
+netblocks added to the set and the matching always start from the smallest
+size of netblock (most specific netmask) to the biggest ones (least
+specific netmasks). When adding/deleting IP addresses
+to a nethash set by the
+.I
+SET
+netfilter kernel module, it will be added/deleted by the smallest
+netblock size which can be found in the set.
+.P
+The lookup time in a nethash type of set is approximately linearly 
+grows with the times of the
+.B
+probes
+parameter and the number of different mask parameters in the hash.
+Otherwise the same speed and memory efficiency comments applies here 
+as at the iphash type.
+.SS ipporthash
+The ipporthash set type uses a hash to store IP address and port pairs.
+In order to avoid clashes in the hash double-hashing, and as a last
+resort, dynamic growing of the hash performed. An ipporthash set can 
+store up to 65536 (B-class network) IP addresses with all possible port
+values. When adding, deleting and testing values in an ipporthash type of
+set, the entries must be specified as
+.B
+"IP:port".
+(Old "IP%port" format accepted as well.)
+.P
+The ipporthash types of sets evaluates two src/dst parameters of the 
+.I
+set
+match and 
+.I
+SET
+target. 
+.P
+Options to use when creating an ipporthash set:
+.TP
+.BR "--from " from-IP
+.TP
+.BR "--to " to-IP
+Create an ipporthash set from the specified range.
+.TP
+.BR "--network " IP/mask
+Create an ipporthash set from the specified network.
+.TP
+.BR "--hashsize " hashsize
+The initial hash size (default 1024)
+.TP
+.BR "--probes " probes
+How many times try to resolve clashing at adding an IP to the hash 
+by double-hashing (default 8).
+.TP
+.BR "--resize " percent
+Increase the hash size by this many percent (default 50) when adding
+an IP to the hash could not be performed after
+.B
+probes
+number of double-hashing.
+.P
+The same resizing, speed and memory efficiency comments applies here 
+as at the iphash type.
+.SS iptree
+The iptree set type uses a tree to store IP addresses, optionally 
+with timeout values.
+.P
+Options to use when creating an iptree set:
+.TP
+.BR "--timeout " value
+The timeout value for the entries in seconds (default 0)
+.P
+If a set was created with a nonzero valued 
+.B "--timeout"
+parameter then one may add IP addresses to the set with a specific 
+timeout value using the syntax 
+.I IP:timeout-value.
+Similarly to the hash types, the iptree type of sets can store up to 65535
+entries.
+.SS iptreemap
+The iptreemap set type uses a tree to store IP addresses or networks, 
+where the last octet of an IP address are stored in a bitmap.
+As input entry, you can add IP addresses, CIDR blocks or network ranges
+to the set. Network ranges can be specified in the format
+.I IP1:IP2
+.P
+Options to use when creating an iptreemap set:
+.TP
+.BR "--gc " value
+How often the garbage collection should be called, in seconds (default 300)
+.SH GENERAL RESTRICTIONS
+Setnames starting with colon (:) cannot be defined. Zero valued set 
+entries cannot be used with hash type of sets.
+.SH COMMENTS
+If you want to store same size subnets from a given network
+(say /24 blocks from a /8 network), use the ipmap set type.
+If you want to store random same size networks (say random /24 blocks), 
+use the iphash set type. If you have got random size of netblocks, 
+use nethash.
+.SH DIAGNOSTICS
+Various error messages are printed to standard error.  The exit code
+is 0 for correct functioning.  Errors which appear to be caused by
+invalid or abused command line parameters cause an exit code of 2, and
+other errors cause an exit code of 1.
+.SH BUGS
+Bugs? No, just funny features. :-)
+OK, just kidding...
+.SH SEE ALSO
+.BR iptables (8),
+.SH AUTHORS
+Jozsef Kadlecsik wrote ipset, which is based on ippool by
+Joakim Axelsson, Patrick Schaaf and Martin Josefsson.
+.P
+Sven Wegener wrote the iptreemap type.
+.\" .. and did I mention that we are incredibly cool people?
+.\" .. sexy, too ..
+.\" .. witty, charming, powerful ..
+.\" .. and most of all, modest ..
diff --git a/ipset/ipset.c b/ipset/ipset.c
new file mode 100644 (file)
index 0000000..9d7f78b
--- /dev/null
@@ -0,0 +1,2301 @@
+/* Copyright 2000-2002 Joakim Axelsson (gozem@linux.nu)
+ *                     Patrick Schaaf (bof@bof.de)
+ * Copyright 2003-2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <stdarg.h>
+#include <netdb.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+/* #include <asm/bitops.h> */
+
+#include "ipset.h"
+
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+
+char program_name[] = "ipset";
+char program_version[] = IPSET_VERSION;
+
+/* The list of loaded set types */
+static struct settype *all_settypes = NULL;
+
+/* Array of sets */
+struct set **set_list = NULL;
+ip_set_id_t max_sets = 0;
+
+/* Suppress output to stdout and stderr? */
+static int option_quiet = 0;
+
+/* Data for restore mode */
+static int restore = 0;
+void *restore_data = NULL;
+struct ip_set_restore *restore_set = NULL;
+size_t restore_offset = 0;
+socklen_t restore_size;
+unsigned line = 0;
+
+#define TEMPFILE_PATTERN       "/ipsetXXXXXX"
+
+#ifdef IPSET_DEBUG
+int option_debug = 0;
+#endif
+
+#define OPTION_OFFSET 256
+static unsigned int global_option_offset = 0;
+
+/* Most of these command parsing functions are borrowed from iptables.c */
+
+static const char cmdflags[] = { ' ',                  /* CMD_NONE */ 
+       'N', 'X', 'F', 'E', 'W', 'L', 'S', 'R', 
+       'A', 'D', 'T', 'B', 'U', 'H', 'V',
+};
+
+/* Options */
+#define OPT_NONE               0x0000U
+#define OPT_NUMERIC            0x0001U         /* -n */
+#define OPT_SORTED             0x0002U         /* -s */
+#define OPT_QUIET              0x0004U         /* -q */
+#define OPT_DEBUG              0x0008U         /* -z */
+#define OPT_BINDING            0x0010U         /* -b */
+#define NUMBER_OF_OPT 5
+static const char optflags[] =
+    { 'n', 's', 'q', 'z', 'b' };
+
+static struct option opts_long[] = {
+       /* set operations */
+       {"create",  1, 0, 'N'},
+       {"destroy", 2, 0, 'X'},
+       {"flush",   2, 0, 'F'},
+       {"rename",  1, 0, 'E'},
+       {"swap",    1, 0, 'W'},
+       {"list",    2, 0, 'L'},
+
+       {"save",    2, 0, 'S'},
+       {"restore", 0, 0, 'R'},
+
+       /* ip in set operations */
+       {"add",     1, 0, 'A'},
+       {"del",     1, 0, 'D'},
+       {"test",    1, 0, 'T'},
+       
+       /* binding operations */
+       {"bind",    1, 0, 'B'},
+       {"unbind",  1, 0, 'U'},
+       
+       /* free options */
+       {"numeric", 0, 0, 'n'},
+       {"sorted",  0, 0, 's'},
+       {"quiet",   0, 0, 'q'},
+       {"binding", 1, 0, 'b'},
+
+#ifdef IPSET_DEBUG
+       /* debug (if compiled with it) */
+       {"debug",   0, 0, 'z'},
+#endif
+
+       /* version and help */
+       {"version", 0, 0, 'V'},
+       {"help",    2, 0, 'H'},
+
+       /* end */
+       {0}
+};
+
+static char opts_short[] =
+    "-N:X::F::E:W:L::S::RA:D:T:B:U:nsqzb:Vh::H::";
+
+/* Table of legal combinations of commands and options. If any of the
+ * given commands make an option legal, that option is legal.
+ * Key:
+ *  +  compulsory
+ *  x  illegal
+ *     optional
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = {
+       /*            -n   -s   -q   -z   -b  */
+        /*CREATE*/  {'x', 'x', ' ', ' ', 'x'},
+        /*DESTROY*/ {'x', 'x', ' ', ' ', 'x'},
+        /*FLUSH*/   {'x', 'x', ' ', ' ', 'x'},
+        /*RENAME*/  {'x', 'x', ' ', ' ', 'x'},
+        /*SWAP*/    {'x', 'x', ' ', ' ', 'x'},
+        /*LIST*/    {' ', ' ', 'x', ' ', 'x'},
+        /*SAVE*/    {'x', 'x', ' ', ' ', 'x'},
+        /*RESTORE*/ {'x', 'x', ' ', ' ', 'x'},
+        /*ADD*/     {'x', 'x', ' ', ' ', 'x'},
+        /*DEL*/     {'x', 'x', ' ', ' ', 'x'},
+        /*TEST*/    {'x', 'x', ' ', ' ', ' '},
+        /*BIND*/    {'x', 'x', ' ', ' ', '+'},
+        /*UNBIND*/  {'x', 'x', ' ', ' ', 'x'},
+        /*HELP*/    {'x', 'x', 'x', ' ', 'x'},
+        /*VERSION*/ {'x', 'x', 'x', ' ', 'x'},
+};
+
+/* Main parser function */
+int parse_commandline(int argc, char *argv[]);
+
+void exit_tryhelp(int status)
+{
+       fprintf(stderr,
+               "Try `%s -H' or '%s --help' for more information.\n",
+               program_name, program_name);
+       exit(status);
+}
+
+void exit_error(enum exittype status, char *msg, ...)
+{
+       va_list args;
+
+       if (!option_quiet) {
+               va_start(args, msg);
+               fprintf(stderr, "%s v%s: ", program_name, program_version);
+               vfprintf(stderr, msg, args);
+               va_end(args);
+               fprintf(stderr, "\n");
+               if (line)
+                       fprintf(stderr, "Restore failed at line %u:\n", line);
+               if (status == PARAMETER_PROBLEM)
+                       exit_tryhelp(status);
+               if (status == VERSION_PROBLEM)
+                       fprintf(stderr,
+                               "Perhaps %s or your kernel needs to be upgraded.\n",
+                               program_name);
+       }
+
+       exit(status);
+}
+
+void ipset_printf(char *msg, ...)
+{
+       va_list args;
+
+       if (!option_quiet) {
+               va_start(args, msg);
+               vfprintf(stdout, msg, args);
+               va_end(args);
+               fprintf(stdout, "\n");
+       }
+}
+
+static void generic_opt_check(int command, int options)
+{
+       int i, j, legal = 0;
+
+       /* Check that commands are valid with options.  Complicated by the
+        * fact that if an option is legal with *any* command given, it is
+        * legal overall (ie. -z and -l).
+        */
+       for (i = 0; i < NUMBER_OF_OPT; i++) {
+               legal = 0;      /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+               for (j = 1; j <= NUMBER_OF_CMD; j++) {
+                       if (command != j)
+                               continue;
+
+                       if (!(options & (1 << i))) {
+                               if (commands_v_options[j-1][i] == '+')
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "You need to supply the `-%c' "
+                                                  "option for this command\n",
+                                                  optflags[i]);
+                       } else {
+                               if (commands_v_options[j-1][i] != 'x')
+                                       legal = 1;
+                               else if (legal == 0)
+                                       legal = -1;
+                       }
+               }
+               if (legal == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Illegal option `-%c' with this command\n",
+                                  optflags[i]);
+       }
+}
+
+static char opt2char(int option)
+{
+       const char *ptr;
+       for (ptr = optflags; option > 1; option >>= 1, ptr++);
+
+       return *ptr;
+}
+
+static char cmd2char(int option)
+{
+       if (option <= CMD_NONE || option > NUMBER_OF_CMD)
+               return ' '; 
+
+       return cmdflags[option];
+}
+
+/* From iptables.c ... */
+static char *get_modprobe(void)
+{
+       int procfile;
+       char *ret;
+
+#define PROCFILE_BUFSIZ        1024
+       procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+       if (procfile < 0)
+               return NULL;
+
+       ret = (char *) malloc(PROCFILE_BUFSIZ);
+       if (ret) {
+               memset(ret, 0, PROCFILE_BUFSIZ);
+               switch (read(procfile, ret, PROCFILE_BUFSIZ)) {
+               case -1: goto fail;
+               case PROCFILE_BUFSIZ: goto fail; /* Partial read.  Wierd */
+               }
+               if (ret[strlen(ret)-1]=='\n') 
+                       ret[strlen(ret)-1]=0;
+               close(procfile);
+               return ret;
+       }
+ fail:
+       free(ret);
+       close(procfile);
+       return NULL;
+}
+
+static int ipset_insmod(const char *modname, const char *modprobe)
+{
+       char *buf = NULL;
+       char *argv[3];
+       struct stat junk;
+       int status;
+       
+       if (!stat(modprobe, &junk)) {
+               /* Try to read out of the kernel */
+               buf = get_modprobe();
+               if (!buf)
+                       return -1;
+               modprobe = buf;
+       }
+       
+       switch (fork()) {
+       case 0:
+               argv[0] = (char *)modprobe;
+               argv[1] = (char *)modname;
+               argv[2] = NULL;
+               execv(argv[0], argv);
+               
+               /* Should not reach */
+               exit(1);
+       case -1:
+               return -1;
+       
+       default: /* parent */
+               wait(&status);
+       }
+       
+       free(buf);
+       
+       if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+               return 0;
+       return -1;
+}
+
+static int kernel_getsocket(void)
+{
+       int sockfd = -1;
+
+       sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0)
+               exit_error(OTHER_PROBLEM,
+                          "You need to be root to perform this command.");
+
+       return sockfd;
+}
+
+static void kernel_error(unsigned cmd, int err)
+{
+       unsigned int i;
+       struct translate_error {
+               int err;
+               unsigned cmd;
+               char *message;
+       } table[] =
+       { /* Generic error codes */
+         { EPERM, 0, "Missing capability" },
+         { EBADF, 0, "Invalid socket option" },
+         { EINVAL, 0, "Size mismatch for expected socket data" },
+         { ENOMEM, 0, "Not enough memory" },
+         { EFAULT, 0, "Failed to copy data" },
+         { EPROTO, 0, "ipset kernel/userspace version mismatch" },
+         { EBADMSG, 0, "Unknown command" },
+         /* Per command error codes */
+         /* Reserved ones for add/del/test to handle internally: 
+          *    EEXIST
+          */
+         { ENOENT, CMD_CREATE, "Unknown set type" },
+         { ENOENT, 0, "Unknown set" },
+         { EAGAIN, 0, "Sets are busy, try again later" },
+         { ERANGE, CMD_CREATE, "No free slot remained to add a new set" },
+         { ERANGE, 0, "IP/port is outside of the set" },
+         { ENOEXEC, CMD_CREATE, "Invalid parameters to create a set" },
+         { ENOEXEC, CMD_SWAP, "Sets with different types cannot be swapped" },
+         { EEXIST, CMD_CREATE, "Set already exists" },
+         { EEXIST, CMD_RENAME, "Set with new name already exists" },
+         { EBUSY, 0, "Set is in use, operation not permitted" },
+         };
+       for (i = 0; i < sizeof(table)/sizeof(struct translate_error); i++) {
+               if ((table[i].cmd == cmd || table[i].cmd == 0)
+                   && table[i].err == err)
+                       exit_error(err == EPROTO ? VERSION_PROBLEM
+                                                : OTHER_PROBLEM, 
+                                  table[i].message);
+       }
+       exit_error(OTHER_PROBLEM, "Error from kernel: %s", strerror(err));
+}
+
+static inline int wrapped_getsockopt(void *data, socklen_t *size)
+{
+       int res;
+       int sockfd = kernel_getsocket();
+
+       /* Send! */
+       res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+       if (res != 0 
+           && errno == ENOPROTOOPT 
+           && ipset_insmod("ip_set", "/sbin/modprobe") == 0)
+               res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+       DP("res=%d errno=%d", res, errno);
+       
+       return res;
+}
+
+static inline int wrapped_setsockopt(void *data, socklen_t size)
+{
+       int res;
+       int sockfd = kernel_getsocket();
+
+       /* Send! */
+       res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+       if (res != 0 
+           && errno == ENOPROTOOPT 
+           && ipset_insmod("ip_set", "/sbin/modprobe") == 0)
+               res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+       DP("res=%d errno=%d", res, errno);
+       
+       return res;
+}
+
+static void kernel_getfrom(unsigned cmd, void *data, socklen_t * size)
+{
+       int res = wrapped_getsockopt(data, size);
+
+       if (res != 0)
+               kernel_error(cmd, errno);
+}
+
+static int kernel_sendto_handleerrno(unsigned cmd, unsigned op,
+                                    void *data, socklen_t size)
+{
+       int res = wrapped_setsockopt(data, size);
+
+       if (res != 0) {
+               if (errno == EEXIST)
+                       return -1;
+               else
+                       kernel_error(cmd, errno);
+       }
+
+       return 0; /* all ok */
+}
+
+static void kernel_sendto(unsigned cmd, void *data, size_t size)
+{
+       int res = wrapped_setsockopt(data, size);
+
+       if (res != 0)
+               kernel_error(cmd, errno);
+}
+
+static int kernel_getfrom_handleerrno(unsigned cmd, void *data, size_t * size)
+{
+       int res = wrapped_getsockopt(data, size);
+
+       if (res != 0) {
+               if (errno == EAGAIN)
+                       return -1;
+               else
+                       kernel_error(cmd, errno);
+       }
+
+       return 0; /* all ok */
+}
+
+static void check_protocolversion(void)
+{
+       struct ip_set_req_version req_version;
+       socklen_t size = sizeof(struct ip_set_req_version);
+       int sockfd = kernel_getsocket();
+       int res;
+
+       req_version.op = IP_SET_OP_VERSION;
+       res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req_version, &size);
+
+       if (res != 0) {
+               ipset_printf("I'm of protocol version %u.\n"
+                            "Kernel module is not loaded in, "
+                            "cannot verify kernel version.",
+                            IP_SET_PROTOCOL_VERSION);
+               return;
+       }
+       if (req_version.version != IP_SET_PROTOCOL_VERSION)
+               exit_error(OTHER_PROBLEM,
+                          "Kernel ipset code is of protocol version %u."
+                          "I'm of protocol version %u.\n"
+                          "Please upgrade your kernel and/or ipset(8) utillity.",
+                          req_version.version, IP_SET_PROTOCOL_VERSION);
+}
+
+static void set_command(unsigned *cmd, const int newcmd)
+{
+       if (*cmd != CMD_NONE)
+               exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
+                          cmd2char(*cmd), cmd2char(newcmd));
+       *cmd = newcmd;
+}
+
+static void add_option(unsigned int *options, unsigned int option)
+{
+       if (*options & option)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiple -%c flags not allowed",
+                          opt2char(option));
+       *options |= option;
+}
+
+void *ipset_malloc(size_t size)
+{
+       void *p;
+
+       if (size == 0)
+               return NULL;
+
+       if ((p = malloc(size)) == NULL) {
+               perror("ipset: not enough memory");
+               exit(1);
+       }
+       return p;
+}
+
+char *ipset_strdup(const char *s)
+{
+       char *p;
+
+       if ((p = strdup(s)) == NULL) {
+               perror("ipset: not enough memory");
+               exit(1);
+       }
+       return p;
+}
+
+void ipset_free(void **data)
+{
+       if (*data == NULL)
+               return;
+
+       free(*data);
+       *data = NULL;
+}
+
+static struct option *merge_options(struct option *oldopts,
+                                   const struct option *newopts,
+                                   unsigned int *option_offset)
+{
+       unsigned int num_old, num_new, i;
+       struct option *merge;
+
+       for (num_old = 0; oldopts[num_old].name; num_old++);
+       for (num_new = 0; newopts[num_new].name; num_new++);
+
+       global_option_offset += OPTION_OFFSET;
+       *option_offset = global_option_offset;
+
+       merge = ipset_malloc(sizeof(struct option) * (num_new + num_old + 1));
+       memcpy(merge, oldopts, num_old * sizeof(struct option));
+       for (i = 0; i < num_new; i++) {
+               merge[num_old + i] = newopts[i];
+               merge[num_old + i].val += *option_offset;
+       }
+       memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+       return merge;
+}
+
+static char *ip_tohost(const struct in_addr *addr)
+{
+       struct hostent *host;
+
+       if ((host = gethostbyaddr((char *) addr,
+                                 sizeof(struct in_addr),
+                                 AF_INET)) != NULL) {
+               DP("%s", host->h_name);
+               return (char *) host->h_name;
+       }
+
+       return (char *) NULL;
+}
+
+static char *ip_tonetwork(const struct in_addr *addr)
+{
+       struct netent *net;
+
+       if ((net = getnetbyaddr((long) ntohl(addr->s_addr), 
+                               AF_INET)) != NULL) {
+               DP("%s", net->n_name);
+               return (char *) net->n_name;
+       }
+
+       return (char *) NULL;
+}
+
+/* Return a string representation of an IP address.
+ * Please notice that a pointer to static char* area is returned.
+ */
+char *ip_tostring(ip_set_ip_t ip, unsigned options)
+{
+       struct in_addr addr;
+       addr.s_addr = htonl(ip);
+
+       if (!(options & OPT_NUMERIC)) {
+               char *name;
+               if ((name = ip_tohost(&addr)) != NULL ||
+                   (name = ip_tonetwork(&addr)) != NULL)
+                       return name;
+       }
+       
+       return inet_ntoa(addr);
+}
+
+char *binding_ip_tostring(struct set *set, ip_set_ip_t ip, unsigned options)
+{
+       return ip_tostring(ip, options);
+}
+char *ip_tostring_numeric(ip_set_ip_t ip)
+{
+       return ip_tostring(ip, OPT_NUMERIC);
+}
+
+/* Fills the 'ip' with the parsed ip or host in host byte order */
+void parse_ip(const char *str, ip_set_ip_t * ip)
+{
+       struct hostent *host;
+       struct in_addr addr;
+
+       DP("%s", str);
+       
+       if (inet_aton(str, &addr) != 0) {
+               *ip = ntohl(addr.s_addr);       /* We want host byte order */
+               return;
+       }
+
+       host = gethostbyname(str);
+       if (host != NULL) {
+               if (host->h_addrtype != AF_INET ||
+                   host->h_length != sizeof(struct in_addr))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "host/network `%s' not an internet name",
+                                  str);
+               if (host->h_addr_list[1] != 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "host/network `%s' resolves to serveral ip-addresses. "
+                                  "Please specify one.", str);
+
+               *ip = ntohl(((struct in_addr *) host->h_addr_list[0])->s_addr);
+               return;
+       }
+
+       exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", str);
+}
+
+/* Fills 'mask' with the parsed mask in host byte order */
+void parse_mask(const char *str, ip_set_ip_t * mask)
+{
+       struct in_addr addr;
+       unsigned int bits;
+
+       DP("%s", str);
+
+       if (str == NULL) {
+               /* no mask at all defaults to 32 bits */
+               *mask = 0xFFFFFFFF;
+               return;
+       }
+       if (strchr(str, '.') && inet_aton(str, &addr) != 0) {
+               *mask = ntohl(addr.s_addr);     /* We want host byte order */
+               return;
+       }
+       if (sscanf(str, "%d", &bits) != 1 || bits < 0 || bits > 32)
+               exit_error(PARAMETER_PROBLEM,
+                          "invalid mask `%s' specified", str);
+
+       DP("bits: %d", bits);
+
+       *mask = bits != 0 ? 0xFFFFFFFF << (32 - bits) : 0L;
+}
+
+/* Combines parse_ip and parse_mask */
+void
+parse_ipandmask(const char *str, ip_set_ip_t * ip, ip_set_ip_t * mask)
+{
+       char buf[256];
+       char *p;
+
+       strncpy(buf, str, sizeof(buf) - 1);
+       buf[255] = '\0';
+
+       if ((p = strrchr(buf, '/')) != NULL) {
+               *p = '\0';
+               parse_mask(p + 1, mask);
+       } else
+               parse_mask(NULL, mask);
+
+       /* if a null mask is given, the name is ignored, like in "any/0" */
+       if (*mask == 0U)
+               *ip = 0U;
+       else
+               parse_ip(buf, ip);
+
+       DP("%s ip: %08X (%s) mask: %08X",
+          str, *ip, ip_tostring_numeric(*ip), *mask);
+
+       /* Apply the netmask */
+       *ip &= *mask;
+
+       DP("%s ip: %08X (%s) mask: %08X",
+          str, *ip, ip_tostring_numeric(*ip), *mask);
+}
+
+/* Return a string representation of a port
+ * Please notice that a pointer to static char* area is returned
+ * and we assume TCP protocol.
+ */
+char *port_tostring(ip_set_ip_t port, unsigned options)
+{
+       struct servent *service;
+       static char name[] = "65535";
+       
+       if (!(options & OPT_NUMERIC)) {
+               if ((service = getservbyport(htons(port), "tcp")))
+                       return service->s_name;
+       }
+       sprintf(name, "%u", port);
+       return name;
+}
+
+int
+string_to_number(const char *str, unsigned int min, unsigned int max,
+                ip_set_ip_t *port)
+{
+       long number;
+       char *end;
+
+       /* Handle hex, octal, etc. */
+       errno = 0;
+       number = strtol(str, &end, 0);
+       if (*end == '\0' && end != str) {
+               /* we parsed a number, let's see if we want this */
+               if (errno != ERANGE && min <= number && number <= max) {
+                       *port = number;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static int
+string_to_port(const char *str, ip_set_ip_t *port)
+{
+       struct servent *service;
+
+       if ((service = getservbyname(str, "tcp")) != NULL) {
+               *port = ntohs((unsigned short) service->s_port);
+               return 0;
+       }
+       
+       return -1;
+}
+
+/* Fills the 'ip' with the parsed port in host byte order */
+void parse_port(const char *str, ip_set_ip_t *port)
+{      
+       if ((string_to_number(str, 0, 65535, port) != 0)
+             && (string_to_port(str, port) != 0))
+               exit_error(PARAMETER_PROBLEM, 
+                          "Invalid TCP port `%s' specified", str);     
+}
+
+/* 
+ * Settype functions
+ */
+static struct settype *settype_find(const char *typename)
+{
+       struct settype *runner = all_settypes;
+
+       DP("%s", typename);
+
+       while (runner != NULL) {
+               if (strncmp(runner->typename, typename, 
+                           IP_SET_MAXNAMELEN) == 0)
+                       return runner;
+
+               runner = runner->next;
+       }
+
+       return NULL;            /* not found */
+}
+
+static struct settype *settype_load(const char *typename)
+{
+       char path[sizeof(IPSET_LIB_DIR) + sizeof(IPSET_LIB_NAME) +
+                 strlen(typename)];
+       struct settype *settype;
+
+       /* do some search in list */
+       settype = settype_find(typename);
+       if (settype != NULL)
+               return settype; /* found */
+
+       /* Else we have to load it */
+       sprintf(path, IPSET_LIB_DIR IPSET_LIB_NAME, typename);
+
+       if (dlopen(path, RTLD_NOW)) {
+               /* Found library. */
+
+               settype = settype_find(typename);
+
+               if (settype != NULL)
+                       return settype;
+       }
+
+       /* Can't load the settype */
+       exit_error(PARAMETER_PROBLEM,
+                  "Couldn't load settype `%s':%s\n",
+                  typename, dlerror());
+
+       return NULL;            /* Never executed, but keep compilers happy */
+}
+
+static char *check_set_name(char *setname)
+{
+       if (strlen(setname) > IP_SET_MAXNAMELEN - 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Setname '%s' too long, max %d characters.",
+                          setname, IP_SET_MAXNAMELEN - 1);
+
+       return setname;
+}
+
+static struct settype *check_set_typename(const char *typename)
+{
+       if (strlen(typename) > IP_SET_MAXNAMELEN - 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Typename '%s' too long, max %d characters.",
+                          typename, IP_SET_MAXNAMELEN - 1);
+
+       return settype_load(typename);
+}
+
+#define MAX(a,b)       ((a) > (b) ? (a) : (b))
+
+/* Register a new set type */
+void settype_register(struct settype *settype)
+{
+       struct settype *chk;
+       size_t size;
+
+       DP("%s", settype->typename);
+
+       /* Check if this typename already exists */
+       chk = settype_find(settype->typename);
+
+       if (chk != NULL)
+               exit_error(OTHER_PROBLEM,
+                          "Set type '%s' already registered!\n",
+                          settype->typename);
+
+       /* Check version */
+       if (settype->protocol_version != IP_SET_PROTOCOL_VERSION)
+               exit_error(OTHER_PROBLEM,
+                          "Set type %s is of wrong protocol version %u!"
+                          " I'm of version %u.\n", settype->typename,
+                          settype->protocol_version,
+                          IP_SET_PROTOCOL_VERSION);
+
+       /* Initialize internal data */
+       settype->header = ipset_malloc(settype->header_size);
+       size = MAX(settype->create_size, settype->adt_size);
+       settype->data = ipset_malloc(size);
+
+       /* Insert first */
+       settype->next = all_settypes;
+       all_settypes = settype;
+
+       DP("%s registered", settype->typename);
+}
+
+/* Find set functions */
+static struct set *set_find_byid(ip_set_id_t id)
+{
+       struct set *set = NULL;
+       ip_set_id_t i;
+       
+       for (i = 0; i < max_sets; i++)
+               if (set_list[i] && set_list[i]->id == id) {
+                       set = set_list[i];
+                       break;
+               }
+                       
+       if (set == NULL)
+               exit_error(PARAMETER_PROBLEM,
+                          "Set identified by id %u is not found", id);
+       return set;
+}
+
+static struct set *set_find_byname(const char *name)
+{
+       struct set *set = NULL;
+       ip_set_id_t i;
+       
+       for (i = 0; i < max_sets; i++)
+               if (set_list[i]
+                   && strncmp(set_list[i]->name, name,
+                              IP_SET_MAXNAMELEN) == 0) {
+                       set = set_list[i];
+                       break;
+               }
+       if (set == NULL)
+               exit_error(PARAMETER_PROBLEM,
+                          "Set %s is not found", name);
+       return set;
+}
+
+static ip_set_id_t set_find_free_index(const char *name)
+{
+       ip_set_id_t i, index = IP_SET_INVALID_ID;
+
+       for (i = 0; i < max_sets; i++) {
+               if (index == IP_SET_INVALID_ID
+                   && set_list[i] == NULL)
+                       index = i;
+               if (set_list[i] != NULL
+                   && strncmp(set_list[i]->name, name,
+                              IP_SET_MAXNAMELEN) == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Set %s is already defined, cannot be restored",
+                                  name);
+       }
+                       
+       if (index == IP_SET_INVALID_ID)         
+               exit_error(PARAMETER_PROBLEM,
+                          "Set %s cannot be restored, "
+                          "max number of set %u reached",
+                          name, max_sets);
+
+       return index;
+}
+
+/* 
+ * Send create set order to kernel
+ */
+static void set_create(const char *name, struct settype *settype)
+{
+       struct ip_set_req_create req_create;
+       size_t size;
+       void *data;
+
+       DP("%s %s", name, settype->typename);
+
+       req_create.op = IP_SET_OP_CREATE;
+       req_create.version = IP_SET_PROTOCOL_VERSION;
+       strcpy(req_create.name, name);
+       strcpy(req_create.typename, settype->typename);
+
+       /* Final checks */
+       settype->create_final(settype->data, settype->flags);
+
+       /* Alloc memory for the data to send */
+       size = sizeof(struct ip_set_req_create) + settype->create_size;
+       data = ipset_malloc(size);
+
+       /* Add up ip_set_req_create and the settype data */
+       memcpy(data, &req_create, sizeof(struct ip_set_req_create));
+       memcpy(data + sizeof(struct ip_set_req_create),
+              settype->data, settype->create_size);
+
+       kernel_sendto(CMD_CREATE, data, size);
+       free(data);
+}
+
+static void set_restore_create(const char *name, struct settype *settype)
+{
+       struct set *set;
+       
+       DP("%s %s %u %u %u %u", name, settype->typename,
+          restore_offset, sizeof(struct ip_set_restore),
+          settype->create_size, restore_size);
+
+       /* Sanity checking */
+       if (restore_offset
+           + sizeof(struct ip_set_restore)
+           + settype->create_size > restore_size)
+               exit_error(PARAMETER_PROBLEM,
+                          "Giving up, restore file is screwed up!");
+                          
+       /* Final checks */
+       settype->create_final(settype->data, settype->flags);
+
+       /* Fill out restore_data */
+       restore_set = (struct ip_set_restore *) 
+                       (restore_data + restore_offset);
+       strcpy(restore_set->name, name);
+       strcpy(restore_set->typename, settype->typename);
+       restore_set->index = set_find_free_index(name);
+       restore_set->header_size = settype->create_size;
+       restore_set->members_size = 0;
+
+       DP("name %s, restore index %u", restore_set->name, restore_set->index);
+       /* Add settype data */
+       
+       memcpy(restore_data + restore_offset + sizeof(struct ip_set_restore),
+              settype->data, settype->create_size);
+
+       restore_offset += sizeof(struct ip_set_restore)
+                         + settype->create_size;       
+       
+       /* Add set to set_list */
+       set = ipset_malloc(sizeof(struct set));
+       strcpy(set->name, name);
+       set->settype = settype;
+       set->index = restore_set->index;
+       set_list[restore_set->index] = set;
+}
+
+/*
+ * Send destroy/flush order to kernel for one or all sets
+ */
+static void set_destroy(const char *name, unsigned op, unsigned cmd)
+{
+       struct ip_set_req_std req;
+
+       DP("%s %s", cmd == CMD_DESTROY ? "destroy" : "flush", name);
+
+       req.op = op;
+       req.version = IP_SET_PROTOCOL_VERSION;
+       strcpy(req.name, name);
+
+       kernel_sendto(cmd, &req, sizeof(struct ip_set_req_std));
+}
+
+/*
+ * Send rename/swap order to kernel
+ */
+static void set_rename(const char *name, const char *newname,
+                      unsigned op, unsigned cmd)
+{
+       struct ip_set_req_create req;
+
+       DP("%s %s %s", cmd == CMD_RENAME ? "rename" : "swap",
+                      name, newname);
+
+       req.op = op;
+       req.version = IP_SET_PROTOCOL_VERSION;
+       strcpy(req.name, name);
+       strcpy(req.typename, newname);
+
+       kernel_sendto(cmd, &req,
+                     sizeof(struct ip_set_req_create));
+}
+
+/*
+ * Send MAX_SETS, LIST_SIZE and/or SAVE_SIZE orders to kernel
+ */
+static size_t load_set_list(const char name[IP_SET_MAXNAMELEN],
+                           ip_set_id_t *index,
+                           unsigned op, unsigned cmd)
+{
+       void *data = NULL;
+       struct ip_set_req_max_sets req_max_sets;
+       struct ip_set_name_list *name_list;
+       struct set *set;
+       ip_set_id_t i;
+       socklen_t size, req_size;
+       int repeated = 0, res = 0;
+
+       DP("%s %s", cmd == CMD_MAX_SETS ? "MAX_SETS"
+                   : cmd == CMD_LIST_SIZE ? "LIST_SIZE"
+                   : "SAVE_SIZE",
+                   name);
+       
+tryagain:
+       if (set_list) {
+               for (i = 0; i < max_sets; i++)
+                       if (set_list[i])
+                               free(set_list[i]);
+               free(set_list);
+               set_list = NULL;
+       }
+       /* Get max_sets */
+       req_max_sets.op = IP_SET_OP_MAX_SETS;
+       req_max_sets.version = IP_SET_PROTOCOL_VERSION;
+       strcpy(req_max_sets.set.name, name);
+       size = sizeof(req_max_sets);
+       kernel_getfrom(CMD_MAX_SETS, &req_max_sets, &size);
+
+       DP("got MAX_SETS: sets %d, max_sets %d",
+          req_max_sets.sets, req_max_sets.max_sets);
+
+       max_sets = req_max_sets.max_sets;
+       set_list = ipset_malloc(max_sets * sizeof(struct set *));
+       memset(set_list, 0, max_sets * sizeof(struct set *));
+       *index = req_max_sets.set.index;
+
+       if (req_max_sets.sets == 0)
+               /* No sets in kernel */
+               return 0;
+
+       /* Get setnames */
+       size = req_size = sizeof(struct ip_set_req_setnames) 
+                         + req_max_sets.sets * sizeof(struct ip_set_name_list);
+       data = ipset_malloc(size);
+       ((struct ip_set_req_setnames *) data)->op = op;
+       ((struct ip_set_req_setnames *) data)->index = *index;
+
+       res = kernel_getfrom_handleerrno(cmd, data, &size);
+
+       if (res != 0 || size != req_size) {
+               free(data);
+               if (repeated++ < LIST_TRIES)
+                       goto tryagain;
+               exit_error(OTHER_PROBLEM,
+                          "Tried to get sets from kernel %d times"
+                          " and failed. Please try again when the load on"
+                          " the sets has gone down.", LIST_TRIES);
+       }
+               
+       /* Load in setnames */
+       size = sizeof(struct ip_set_req_setnames);                      
+       while (size + sizeof(struct ip_set_name_list) <= req_size) {
+               name_list = (struct ip_set_name_list *)
+                       (data + size);
+               set = ipset_malloc(sizeof(struct set));
+               strcpy(set->name, name_list->name);
+               set->index = name_list->index;
+               set->id = name_list->id;
+               set->settype = settype_load(name_list->typename);
+               set_list[name_list->index] = set;
+               DP("loaded %s, type %s, index %u",
+                  set->name, set->settype->typename, set->index);
+               size += sizeof(struct ip_set_name_list);
+       }
+       /* Size to get set members, bindings */
+       size = ((struct ip_set_req_setnames *)data)->size;
+       free(data);
+       
+       return size;
+}
+
+/*
+ * Save operation
+ */
+static size_t save_bindings(void *data, size_t offset, size_t len)
+{
+       struct ip_set_hash_save *hash =
+               (struct ip_set_hash_save *) (data + offset);
+       struct set *set;
+
+       DP("offset %u, len %u", offset, len);
+       if (offset + sizeof(struct ip_set_hash_save) > len)
+               exit_error(OTHER_PROBLEM,
+                          "Save operation failed, try again later.");
+
+       set = set_find_byid(hash->id);
+       if (!(set && set_list[hash->binding]))
+               exit_error(OTHER_PROBLEM,
+                          "Save binding failed, try again later.");
+       printf("-B %s %s -b %s\n",
+               set->name,
+               set->settype->bindip_tostring(set, hash->ip, OPT_NUMERIC),
+               set_list[hash->binding]->name);
+
+       return sizeof(struct ip_set_hash_save);
+}              
+
+static size_t save_set(void *data, int *bindings,
+                      size_t offset, size_t len)
+{
+       struct ip_set_save *set_save =
+               (struct ip_set_save *) (data + offset);
+       struct set *set;
+       struct settype *settype;
+       size_t used;
+       
+       DP("offset %u (%u/%u/%u), len %u", offset,
+          sizeof(struct ip_set_save), 
+          set_save->header_size, set_save->members_size, 
+          len);
+       if (offset + sizeof(struct ip_set_save) > len
+           || offset + sizeof(struct ip_set_save)
+              + set_save->header_size + set_save->members_size > len)
+               exit_error(OTHER_PROBLEM,
+                          "Save operation failed, try again later.");
+
+       DP("index: %u", set_save->index);
+       if (set_save->index == IP_SET_INVALID_ID) {
+               /* Marker */
+               *bindings = 1;
+               return sizeof(struct ip_set_save);
+       }
+       set = set_list[set_save->index];
+       if (!set)
+               exit_error(OTHER_PROBLEM,
+                          "Save set failed, try again later.");
+       settype = set->settype;
+
+       /* Init set header */
+       used = sizeof(struct ip_set_save);
+       settype->initheader(set, data + offset + used);
+
+       /* Print create set */
+       settype->saveheader(set, OPT_NUMERIC);
+
+       /* Print add IPs */
+       used += set_save->header_size;
+       settype->saveips(set, data + offset + used,
+                        set_save->members_size, OPT_NUMERIC);
+
+       return (used + set_save->members_size);
+}
+
+static size_t save_default_bindings(void *data, int *bindings)
+{
+       struct ip_set_save *set_save = (struct ip_set_save *) data;
+       struct set *set;
+       
+       if (set_save->index == IP_SET_INVALID_ID) {
+               /* Marker */
+               *bindings = 1;
+               return sizeof(struct ip_set_save);
+       }
+
+       set = set_list[set_save->index];
+       DP("%s, binding %u", set->name, set_save->binding);
+       if (set_save->binding != IP_SET_INVALID_ID) {
+               if (!set_list[set_save->binding])
+                       exit_error(OTHER_PROBLEM,
+                                  "Save set failed, try again later.");
+
+               printf("-B %s %s -b %s\n",
+                       set->name, IPSET_TOKEN_DEFAULT, 
+                       set_list[set_save->binding]->name);
+       }
+       return (sizeof(struct ip_set_save)
+               + set_save->header_size
+               + set_save->members_size);
+}
+
+static int try_save_sets(const char name[IP_SET_MAXNAMELEN])
+{
+       void *data = NULL;
+       socklen_t size, req_size = 0;
+       ip_set_id_t index;
+       int res = 0, bindings = 0;
+       time_t now = time(NULL);
+
+       /* Load set_list from kernel */
+       size = load_set_list(name, &index,
+                            IP_SET_OP_SAVE_SIZE, CMD_SAVE);
+       
+       if (size) {
+               /* Get sets, bindings and print them */
+               /* Take into account marker */
+               req_size = (size += sizeof(struct ip_set_save));
+               data = ipset_malloc(size);
+               ((struct ip_set_req_list *) data)->op = IP_SET_OP_SAVE;
+               ((struct ip_set_req_list *) data)->index = index;
+               res = kernel_getfrom_handleerrno(CMD_SAVE, data, &size);
+
+               if (res != 0 || size != req_size) {
+                       free(data);
+                       return -EAGAIN;
+               }
+       }
+
+       printf("# Generated by ipset %s on %s", IPSET_VERSION, ctime(&now));
+       size = 0;
+       while (size < req_size) {
+               DP("size: %u, req_size: %u", size, req_size);
+               if (bindings)
+                       size += save_bindings(data, size, req_size);
+               else
+                       size += save_set(data, &bindings, size, req_size);
+       }
+       /* Re-read data to save default bindings */
+       bindings = 0;
+       size = 0;
+       while (size < req_size && bindings == 0)
+               size += save_default_bindings(data + size, &bindings);
+
+       printf("COMMIT\n");
+       now = time(NULL);
+       printf("# Completed on %s", ctime(&now));
+       ipset_free(&data);
+       return res;
+}
+
+/*
+ * Performs a save to stdout
+ */
+static void set_save(const char name[IP_SET_MAXNAMELEN])
+{
+       int i;
+
+       DP("%s", name);
+       for (i = 0; i < LIST_TRIES; i++)
+               if (try_save_sets(name) == 0)
+                       return;
+
+       if (errno == EAGAIN)
+               exit_error(OTHER_PROBLEM,
+                          "Tried to save sets from kernel %d times"
+                          " and failed. Please try again when the load on"
+                          " the sets has gone down.", LIST_TRIES);
+       else
+               kernel_error(CMD_SAVE, errno);
+}
+
+/*
+ * Restore operation
+ */
+
+/* global new argv and argc */
+static char *newargv[255];
+static int newargc = 0;
+
+/* Build faked argv from parsed line */
+static void build_argv(int line, char *buffer) {
+       char *ptr;
+       int i;
+
+       /* Reset */     
+       for (i = 1; i < newargc; i++)
+               free(newargv[i]);
+       newargc = 1;
+
+       ptr = strtok(buffer, " \t\n");
+       newargv[newargc++] = ipset_strdup(ptr);
+       while ((ptr = strtok(NULL, " \t\n")) != NULL) {
+               if ((newargc + 1) < sizeof(newargv)/sizeof(char *))
+                       newargv[newargc++] = ipset_strdup(ptr);
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Line %d is too long to restore\n", line);
+       }
+}
+
+static FILE *create_tempfile(void)
+{
+       char buffer[1024];      
+       char *tmpdir = NULL;
+       char *filename;
+       int fd;
+       FILE *file;
+       
+       if (!(tmpdir = getenv("TMPDIR")) && !(tmpdir = getenv("TMP")))
+               tmpdir = "/tmp";
+       filename = ipset_malloc(strlen(tmpdir) + strlen(TEMPFILE_PATTERN) + 1);
+       strcpy(filename, tmpdir);
+       strcat(filename, TEMPFILE_PATTERN);
+       
+       (void) umask(077);      /* Create with restrictive permissions */
+       fd = mkstemp(filename);
+       if (fd == -1)
+               exit_error(OTHER_PROBLEM, "Could not create temporary file.");
+       if (!(file = fdopen(fd, "r+")))
+               exit_error(OTHER_PROBLEM, "Could not open temporary file.");
+       if (unlink(filename) == -1)
+               exit_error(OTHER_PROBLEM, "Could not unlink temporary file.");
+       free(filename);
+
+       while (fgets(buffer, sizeof(buffer), stdin)) {
+               fputs(buffer, file);
+       }
+       fseek(file, 0L, SEEK_SET);
+
+       return file;
+}
+
+/*
+ * Performs a restore from a file
+ */
+static void set_restore(char *argv0)
+{
+       char buffer[1024];      
+       char *ptr, *name = NULL;
+       char cmd = ' ';
+       int line = 0, first_pass, i, bindings = 0;
+       struct settype *settype = NULL;
+       struct ip_set_req_setnames *header;
+       ip_set_id_t index;
+       FILE *in;
+       int res;
+       
+       /* Create and store stdin in temporary file */
+       in = create_tempfile();
+       
+       /* Load existing sets from kernel */
+       load_set_list(IPSET_TOKEN_ALL, &index,
+                     IP_SET_OP_LIST_SIZE, CMD_RESTORE);
+       
+       restore_size = sizeof(struct ip_set_req_setnames)/* header */
+                      + sizeof(struct ip_set_restore);  /* marker */
+       DP("restore_size: %u", restore_size);
+       /* First pass: calculate required amount of data */
+       while (fgets(buffer, sizeof(buffer), in)) {
+               line++;
+
+               if (buffer[0] == '\n')
+                       continue;
+               else if (buffer[0] == '#')
+                       continue;
+               else if (strcmp(buffer, "COMMIT\n") == 0) {
+                       /* Enable restore mode */
+                       restore = 1;
+                       break;
+               }
+                       
+               /* -N, -A or -B */
+               ptr = strtok(buffer, " \t\n");
+               DP("ptr: %s", ptr);
+               if (ptr == NULL
+                   || ptr[0] != '-'
+                   || !(ptr[1] == 'N'
+                        || ptr[1] == 'A'
+                        || ptr[1] == 'B')
+                   || ptr[2] != '\0') {
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Line %u does not start as a valid restore command\n",
+                                  line);
+               }
+               cmd = ptr[1];           
+               /* setname */
+               ptr = strtok(NULL, " \t\n");
+               DP("setname: %s", ptr);
+               if (ptr == NULL)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Missing set name in line %u\n",
+                                  line);
+               DP("cmd %c", cmd);
+               switch (cmd) {
+               case 'N': {
+                       name = check_set_name(ptr);
+                       /* settype */
+                       ptr = strtok(NULL, " \t\n");
+                       if (ptr == NULL)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Missing settype in line %u\n",
+                                          line);
+                       if (bindings)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Invalid line %u: create must precede bindings\n",
+                                          line);
+                       settype = check_set_typename(ptr);
+                       restore_size += sizeof(struct ip_set_restore)
+                                       + settype->create_size;
+                       DP("restore_size (N): %u", restore_size);
+                       break; 
+               }
+               case 'A': {
+                       if (name == NULL
+                           || strncmp(name, ptr, sizeof(name)) != 0)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Add IP to set %s in line %u without "
+                                          "preceding corresponding create set line\n",
+                                          ptr, line);
+                       if (bindings)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Invalid line %u: adding entries must precede bindings\n",
+                                          line);
+                       restore_size += settype->adt_size;
+                       DP("restore_size (A): %u", restore_size);
+                       break;
+               }
+               case 'B': {
+                       bindings = 1;
+                       restore_size += sizeof(struct ip_set_hash_save);
+                       DP("restore_size (B): %u", restore_size);
+                       break;
+               }
+               default: {
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unrecognized restore command in line %u\n",
+                                  line);
+               }
+               } /* end of switch */
+       }                       
+       /* Sanity checking */
+       if (!restore)
+               exit_error(PARAMETER_PROBLEM,
+                          "Missing COMMIT line\n");
+       DP("restore_size: %u", restore_size);
+       restore_data = ipset_malloc(restore_size);
+       header = (struct ip_set_req_setnames *) restore_data;
+       header->op = IP_SET_OP_RESTORE; 
+       header->size = restore_size; 
+       restore_offset = sizeof(struct ip_set_req_setnames);
+
+       /* Rewind to scan the file again */
+       fseek(in, 0L, SEEK_SET);
+       first_pass = line;
+       line = 0;
+       
+       /* Initialize newargv/newargc */
+       newargv[newargc++] = ipset_strdup(argv0);
+       
+       /* Second pass: build up restore request */
+       while (fgets(buffer, sizeof(buffer), in)) {             
+               line++;
+
+               if (buffer[0] == '\n')
+                       continue;
+               else if (buffer[0] == '#')
+                       continue;
+               else if (strcmp(buffer, "COMMIT\n") == 0)
+                       goto do_restore;
+               DP("restoring: %s", buffer);
+               /* Build faked argv, argc */
+               build_argv(line, buffer);
+               for (i = 0; i < newargc; i++)
+                       DP("argv[%u]: %s", i, newargv[i]);
+               
+               /* Parse line */
+               parse_commandline(newargc, newargv);
+       }
+       exit_error(PARAMETER_PROBLEM,
+                  "Broken restore file\n");
+   do_restore:
+       if (bindings == 0
+           && restore_size == 
+              (restore_offset + sizeof(struct ip_set_restore))) {
+               /* No bindings */
+               struct ip_set_restore *marker = 
+                       (struct ip_set_restore *) (restore_data + restore_offset);
+
+               DP("restore marker");
+               marker->index = IP_SET_INVALID_ID;
+               marker->header_size = marker->members_size = 0;
+               restore_offset += sizeof(struct ip_set_restore);
+       }
+       if (restore_size != restore_offset)
+               exit_error(PARAMETER_PROBLEM,
+                          "Giving up, restore file is screwed up!");
+       res = kernel_getfrom_handleerrno(CMD_RESTORE, restore_data, &restore_size);
+
+       if (res != 0) {
+               if (restore_size != sizeof(struct ip_set_req_setnames))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Communication with kernel failed (%u %u)!",
+                                  restore_size, sizeof(struct ip_set_req_setnames));
+               /* Check errors  */
+               header = (struct ip_set_req_setnames *) restore_data;
+               if (header->size != 0) 
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Committing restoring failed at line %u!",
+                                  header->size);
+       }
+}
+
+/*
+ * Send ADT_GET order to kernel for a set
+ */
+static struct set *set_adt_get(const char *name)
+{
+       struct ip_set_req_adt_get req_adt_get;
+       struct set *set;
+       socklen_t size;
+
+       DP("%s", name);
+
+       req_adt_get.op = IP_SET_OP_ADT_GET;
+       req_adt_get.version = IP_SET_PROTOCOL_VERSION;
+       strcpy(req_adt_get.set.name, name);
+       size = sizeof(struct ip_set_req_adt_get);
+
+       kernel_getfrom(CMD_ADT_GET, (void *) &req_adt_get, &size);
+
+       set = ipset_malloc(sizeof(struct set));
+       strcpy(set->name, name);
+       set->index = req_adt_get.set.index;     
+       set->settype = settype_load(req_adt_get.typename);
+
+       return set;
+}      
+
+/*
+ * Send add/del/test order to kernel for a set
+ */
+static int set_adtip(struct set *set, const char *adt, 
+                    unsigned op, unsigned cmd)
+{
+       struct ip_set_req_adt *req_adt;
+       size_t size;
+       void *data;
+       int res = 0;
+
+       DP("%s -> %s", set->name, adt);
+
+       /* Alloc memory for the data to send */
+       size = sizeof(struct ip_set_req_adt) + set->settype->adt_size ;
+       DP("alloc size %i", size);
+       data = ipset_malloc(size);
+
+       /* Fill out the request */
+       req_adt = (struct ip_set_req_adt *) data;
+       req_adt->op = op;
+       req_adt->index = set->index;
+       memcpy(data + sizeof(struct ip_set_req_adt),
+              set->settype->data, set->settype->adt_size);
+       
+       if (kernel_sendto_handleerrno(cmd, op, data, size) == -1)
+               switch (op) {
+               case IP_SET_OP_ADD_IP:
+                       exit_error(OTHER_PROBLEM, "%s is already in set %s.",
+                                  adt, set->name);
+                       break;
+               case IP_SET_OP_DEL_IP:
+                       exit_error(OTHER_PROBLEM, "%s is not in set %s.",
+                                  adt, set->name);
+                       break;
+               case IP_SET_OP_TEST_IP:
+                       ipset_printf("%s is in set %s.", adt, set->name);
+                       res = 0;
+                       break;
+               default:
+                       break;
+               }
+       else
+               switch (op) {
+               case IP_SET_OP_TEST_IP:
+                       ipset_printf("%s is NOT in set %s.", adt, set->name);
+                       res = 1;
+                       break;
+               default:
+                       break;
+               }
+       free(data);
+
+       return res;
+}
+
+static void set_restore_add(struct set *set, const char *adt)
+{
+       DP("%s %s", set->name, adt);
+       /* Sanity checking */
+       if (restore_offset + set->settype->adt_size > restore_size)
+               exit_error(PARAMETER_PROBLEM,
+                          "Giving up, restore file is screwed up!");
+                          
+       memcpy(restore_data + restore_offset,
+              set->settype->data, set->settype->adt_size);
+       restore_set->members_size += set->settype->adt_size;
+       restore_offset += set->settype->adt_size;
+}
+
+/*
+ * Send bind/unbind/test binding order to kernel for a set
+ */
+static int set_bind(struct set *set, const char *adt,
+                   const char *binding,
+                   unsigned op, unsigned cmd)
+{
+       struct ip_set_req_bind *req_bind;
+       size_t size;
+       void *data;
+       int res = 0;
+
+       /* set may be null: '-U :all: :all:|:default:' */
+       DP("(%s, %s) -> %s", set ? set->name : IPSET_TOKEN_ALL, adt, binding);
+
+       /* Ugly */
+       if (strcmp(set->settype->typename, "iptreemap") == 0)
+               exit_error(PARAMETER_PROBLEM,
+                       "iptreemap type of sets cannot be used at binding operations\n");
+       /* Alloc memory for the data to send */
+       size = sizeof(struct ip_set_req_bind);
+       if (op != IP_SET_OP_UNBIND_SET && adt[0] == ':')
+               /* Set default binding */
+               size += IP_SET_MAXNAMELEN;
+       else if (!(op == IP_SET_OP_UNBIND_SET && set == NULL))
+               size += set->settype->adt_size;
+       DP("alloc size %i", size);
+       data = ipset_malloc(size);
+
+       /* Fill out the request */
+       req_bind = (struct ip_set_req_bind *) data;
+       req_bind->op = op;
+       req_bind->index = set ? set->index : IP_SET_INVALID_ID;
+       if (adt[0] == ':') {
+               /* ':default:' and ':all:' */
+               strncpy(req_bind->binding, adt, IP_SET_MAXNAMELEN);
+               if (op != IP_SET_OP_UNBIND_SET && adt[0] == ':')
+                       strncpy(data + sizeof(struct ip_set_req_bind),
+                               binding, IP_SET_MAXNAMELEN);
+       } else {
+               strncpy(req_bind->binding, binding, IP_SET_MAXNAMELEN);
+               memcpy(data + sizeof(struct ip_set_req_bind),
+                      set->settype->data, set->settype->adt_size);
+       }
+
+       if (op == IP_SET_OP_TEST_BIND_SET) {
+               if (kernel_sendto_handleerrno(cmd, op, data, size) == -1) {
+                       ipset_printf("%s in set %s is bound to %s.",
+                                    adt, set->name, binding);
+                       res = 0;
+               } else {
+                       ipset_printf("%s in set %s is NOT bound to %s.",
+                                    adt, set->name, binding);
+                       res = 1;
+               }
+       } else  
+               kernel_sendto(cmd, data, size);
+       free(data);
+
+       return res;
+}
+
+static void set_restore_bind(struct set *set,
+                            const char *adt,
+                            const char *binding)
+{
+       struct ip_set_hash_save *hash_restore;
+
+       if (restore == 1) {
+               /* Marker */
+               struct ip_set_restore *marker = 
+                       (struct ip_set_restore *) (restore_data + restore_offset);
+
+               DP("restore marker");
+               if (restore_offset + sizeof(struct ip_set_restore) 
+                   > restore_size)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Giving up, restore file is screwed up!");
+               marker->index = IP_SET_INVALID_ID;
+               marker->header_size = marker->members_size = 0;
+               restore_offset += sizeof(struct ip_set_restore);
+               restore = 2;
+       }
+       /* Sanity checking */
+       if (restore_offset + sizeof(struct ip_set_hash_save) > restore_size)
+               exit_error(PARAMETER_PROBLEM,
+                          "Giving up, restore file is screwed up!");
+
+       hash_restore = (struct ip_set_hash_save *) (restore_data + restore_offset);
+       DP("%s -> %s", adt, binding);
+       if (strcmp(adt, IPSET_TOKEN_DEFAULT) == 0)
+               hash_restore->ip = 0;
+       else
+               set->settype->bindip_parse(adt, &hash_restore->ip);
+       hash_restore->id = set->index;                     
+       hash_restore->binding = (set_find_byname(binding))->index;      
+       DP("id %u, ip %u, binding %u",
+          hash_restore->id, hash_restore->ip, hash_restore->binding);
+       restore_offset += sizeof(struct ip_set_hash_save);
+}
+
+/*
+ * Print operation
+ */
+
+static void print_bindings(struct set *set,
+                          void *data, size_t size, unsigned options,
+                          char * (*printip)(struct set *set, 
+                                            ip_set_ip_t ip, unsigned options))
+{
+       size_t offset = 0;
+       struct ip_set_hash_list *hash;
+
+       while (offset < size) {
+               hash = (struct ip_set_hash_list *) (data + offset);
+               printf("%s -> %s\n", 
+                       printip(set, hash->ip, options),
+                       set_list[hash->binding]->name);
+               offset += sizeof(struct ip_set_hash_list);
+       }
+}
+
+/* Help function to set_list() */
+static size_t print_set(void *data, unsigned options)
+{
+       struct ip_set_list *setlist = (struct ip_set_list *) data;
+       struct set *set = set_list[setlist->index];
+       struct settype *settype = set->settype;
+       size_t offset;
+
+       /* Pretty print the set */
+       printf("Name: %s\n", set->name);
+       printf("Type: %s\n", settype->typename);
+       printf("References: %d\n", setlist->ref);
+       printf("Default binding: %s\n",
+              setlist->binding == IP_SET_INVALID_ID ? ""
+              : set_list[setlist->binding]->name);
+
+       /* Init header */
+       offset = sizeof(struct ip_set_list);
+       settype->initheader(set, data + offset);
+
+       /* Pretty print the type header */
+       printf("Header:");
+       settype->printheader(set, options);
+
+       /* Pretty print all IPs */
+       printf("Members:\n");
+       offset += setlist->header_size;
+       if (options & OPT_SORTED)
+               settype->printips_sorted(set, data + offset,
+                                        setlist->members_size, options);
+       else
+               settype->printips(set, data + offset,
+                                 setlist->members_size, options);
+
+       /* Print bindings */
+       printf("Bindings:\n");
+       offset += setlist->members_size;
+       print_bindings(set,
+                      data + offset, setlist->bindings_size, options,
+                      settype->bindip_tostring);
+
+       printf("\n");           /* One newline between sets */
+       
+       return (offset + setlist->bindings_size);
+}
+
+static int try_list_sets(const char name[IP_SET_MAXNAMELEN],
+                        unsigned options)
+{
+       void *data = NULL;
+       ip_set_id_t index;
+       socklen_t size, req_size;
+       int res = 0;
+
+       DP("%s", name);
+       /* Load set_list from kernel */
+       size = req_size = load_set_list(name, &index,
+                                       IP_SET_OP_LIST_SIZE, CMD_LIST);
+
+       if (size) {
+               /* Get sets and print them */
+               data = ipset_malloc(size);
+               ((struct ip_set_req_list *) data)->op = IP_SET_OP_LIST;
+               ((struct ip_set_req_list *) data)->index = index;
+               res = kernel_getfrom_handleerrno(CMD_LIST, data, &size);
+               DP("get_lists getsockopt() res=%d errno=%d", res, errno);
+
+               if (res != 0 || size != req_size) {
+                       free(data);
+                       return -EAGAIN;
+               }
+               size = 0;
+       }
+       while (size != req_size)
+               size += print_set(data + size, options);
+
+       ipset_free(&data);
+       return res;
+}
+
+/* Print a set or all sets
+ * All sets: name = NULL
+ */
+static void list_sets(const char name[IP_SET_MAXNAMELEN], unsigned options)
+{
+       int i;
+
+       DP("%s", name);
+       for (i = 0; i < LIST_TRIES; i++)
+               if (try_list_sets(name, options) == 0)
+                       return;
+
+       if (errno == EAGAIN)
+               exit_error(OTHER_PROBLEM,
+                          "Tried to list sets from kernel %d times"
+                          " and failed. Please try again when the load on"
+                          " the sets has gone down.", LIST_TRIES);
+       else
+               kernel_error(CMD_LIST, errno);
+}
+
+/* Prints help
+ * If settype is non null help for that type is printed as well
+ */
+static void set_help(const struct settype *settype)
+{
+#ifdef IPSET_DEBUG
+       char debughelp[] =
+              "  --debug      -z   Enable debugging\n\n";
+#else
+       char debughelp[] = "\n";
+#endif
+
+       printf("%s v%s\n\n"
+              "Usage: %s -N new-set settype [options]\n"
+              "       %s -[XFLSH] [set] [options]\n"
+              "       %s -[EW] from-set to-set\n"
+              "       %s -[ADTU] set IP\n"
+              "       %s -B set IP option\n"
+              "       %s -R\n"
+              "       %s -h (print this help information)\n\n",
+              program_name, program_version, 
+              program_name, program_name, program_name,
+              program_name, program_name, program_name,
+              program_name);
+
+       printf("Commands:\n"
+              "Either long or short options are allowed.\n"
+              "  --create  -N setname settype <options>\n"
+              "                    Create a new set\n"
+              "  --destroy -X [setname]\n"
+              "                    Destroy a set or all sets\n"
+              "  --flush   -F [setname]\n"
+              "                    Flush a set or all sets\n"
+              "  --rename  -E from-set to-set\n"
+              "                    Rename from-set to to-set\n"
+              "  --swap    -W from-set to-set\n"
+              "                    Swap the content of two existing sets\n"
+              "  --list    -L [setname] [options]\n"
+              "                    List the IPs in a set or all sets\n"
+              "  --save    -S [setname]\n"
+              "                    Save the set or all sets to stdout\n"
+              "  --restore -R [option]\n"
+              "                    Restores a saved state\n"
+              "  --add     -A setname IP\n"
+              "                    Add an IP to a set\n"
+              "  --del     -D setname IP\n"
+              "                    Deletes an IP from a set\n"
+              "  --test    -T setname IP \n"
+              "                    Tests if an IP exists in a set.\n"
+              "  --bind    -B setname IP|:default: -b bind-setname\n"
+              "                    Bind the IP in setname to bind-setname.\n"
+              "  --unbind  -U setname IP|:all:|:default:\n"
+              "                    Delete binding belonging to IP,\n"
+              "                    all bindings or default binding of setname.\n"
+              "  --unbind  -U :all: :all:|:default:\n"
+              "                    Delete all bindings or all default bindings.\n"
+              "  --help    -H [settype]\n"
+              "                    Prints this help, and settype specific help\n"
+              "  --version -V\n"
+              "                    Prints version information\n\n"
+              "Options:\n"
+              "  --sorted     -s   Numeric sort of the IPs in -L\n"
+              "  --numeric    -n   Numeric output of addresses in a -L\n"
+              "  --quiet      -q   Suppress any output to stdout and stderr.\n"
+              "  --binding    -b   Specifies the binding for -B\n");
+       printf(debughelp);
+
+       if (settype != NULL) {
+               printf("Type '%s' specific:\n", settype->typename);
+               settype->usage();
+       }
+}
+
+static int find_cmd(const char option)
+{
+       int i;
+       
+       for (i = 1; i <= NUMBER_OF_CMD; i++)
+               if (cmdflags[i] == option)
+                       return i;
+                       
+       return CMD_NONE;
+}
+
+static int parse_adt_cmdline(unsigned command,
+                            const char *name,
+                            char *adt,
+                            struct set **set,
+                            struct settype **settype)
+{
+       int res = 0;
+
+       /* -U :all: :all:|:default: */
+       if (command == CMD_UNBIND) {
+               if (strcmp(name, IPSET_TOKEN_ALL) == 0) {
+                       if (strcmp(adt, IPSET_TOKEN_DEFAULT) == 0
+                           || strcmp(adt, IPSET_TOKEN_ALL) == 0) {
+                               *set = NULL;
+                               *settype = NULL;
+                               return 1;
+                       } else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-U %s requires %s or %s as binding name",
+                                          IPSET_TOKEN_ALL,
+                                          IPSET_TOKEN_DEFAULT,
+                                          IPSET_TOKEN_ALL);
+               }
+       }
+       *set = restore ? set_find_byname(name)
+                      : set_adt_get(name);
+                                       
+       /* Reset space for adt data */
+       *settype = (*set)->settype;
+       memset((*settype)->data, 0, (*settype)->adt_size);
+
+       if ((command == CMD_TEST
+            || command == CMD_BIND
+            || command == CMD_UNBIND)
+           && (strcmp(adt, IPSET_TOKEN_DEFAULT) == 0
+               || strcmp(adt, IPSET_TOKEN_ALL) == 0))
+               res = 1;
+       else
+               res = (*settype)->adt_parser(
+                               command,
+                               adt,
+                               (*settype)->data);
+
+       return res;
+}
+
+/* Main worker function */
+int parse_commandline(int argc, char *argv[])
+{
+       int res = 0;
+       unsigned command = CMD_NONE;
+       unsigned options = 0;
+       int c;
+       
+       char *name = NULL;              /* All except -H, -R */
+       char *newname = NULL;           /* -E, -W */
+       char *adt = NULL;               /* -A, -D, -T, -B, -U */
+       char *binding = NULL;           /* -B */
+       struct set *set = NULL;         /* -A, -D, -T, -B, -U */
+       struct settype *settype = NULL; /* -N, -H */
+       char all_sets[] = IPSET_TOKEN_ALL;
+       
+       struct option *opts = opts_long;
+
+       /* Suppress error messages: we may add new options if we
+          demand-load a protocol. */
+       opterr = 0;
+       /* Reset optind to 0 for restore */
+       optind = 0;
+       
+       while ((c = getopt_long(argc, argv, opts_short, opts, NULL)) != -1) {
+
+               DP("commandline parsed: opt %c (%s)", c, argv[optind]);
+
+               switch (c) {
+                       /*
+                        * Command selection
+                        */
+               case 'h':
+               case 'H':{      /* Help: -H [typename [options]] */
+                               check_protocolversion();
+                               set_command(&command, CMD_HELP);
+                               
+                               if (optarg)
+                                       settype = check_set_typename(optarg);
+                               else if (optind < argc
+                                        && argv[optind][0] != '-')
+                                       settype = check_set_typename(argv[optind++]);
+                               
+                               break;
+                       }
+
+               case 'V':{      /* Version */
+                               printf("%s v%s Protocol version %u.\n",
+                                      program_name, program_version,
+                                      IP_SET_PROTOCOL_VERSION);
+                               check_protocolversion();
+                               exit(0);
+                       }
+
+               case 'N':{      /* Create: -N name typename options */
+                               set_command(&command, CMD_CREATE);
+
+                               name = check_set_name(optarg);
+                               
+                               /* Protect reserved names (binding) */
+                               if (name[0] == ':')
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "setname might not start with colon",
+                                                  cmd2char(CMD_CREATE));
+                               
+                               if (optind < argc
+                                   && argv[optind][0] != '-')
+                                       settype = check_set_typename(argv[optind++]);
+                               else
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "-%c requires setname and settype",
+                                                  cmd2char(CMD_CREATE));
+
+                               DP("merge options");
+                               /* Merge the create options */
+                               opts = merge_options(opts,
+                                            settype->create_opts,
+                                            &settype->option_offset);
+
+                               /* Reset space for create data */
+                               memset(settype->data, 0, settype->create_size);
+
+                               /* Zero the flags */
+                               settype->flags = 0;
+
+                               DP("call create_init");
+                               /* Call the settype create_init */
+                               settype->create_init(settype->data);
+
+                               break;
+                       }
+
+               case 'X':       /* Destroy */
+               case 'F':       /* Flush */
+               case 'L':       /* List */
+               case 'S':{      /* Save */
+                               set_command(&command, find_cmd(c));
+
+                               if (optarg)
+                                       name = check_set_name(optarg);
+                               else if (optind < argc
+                                          && argv[optind][0] != '-')
+                                       name = check_set_name(argv[optind++]);
+                               else
+                                       name = all_sets;
+
+                               break;
+                       }
+
+               case 'R':{      /* Restore */
+                               set_command(&command, find_cmd(c));
+
+                               break;
+                       }
+
+               case 'E':       /* Rename */
+               case 'W':{      /* Swap */
+                               set_command(&command, find_cmd(c));
+                               name = check_set_name(optarg);
+
+                               if (optind < argc
+                                   && argv[optind][0] != '-')
+                                       newname = check_set_name(argv[optind++]);
+                               else
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "-%c requires a setname "
+                                                  "and the new name for that set",
+                                                  cmd2char(CMD_RENAME));
+
+                               break;
+                       }
+
+               case 'A':       /* Add IP */
+               case 'D':       /* Del IP */
+               case 'T':       /* Test IP */
+               case 'B':       /* Bind IP */
+               case 'U':{      /* Unbind IP */
+                               set_command(&command, find_cmd(c));
+
+                               name = check_set_name(optarg);
+
+                               /* IP */
+                               if (optind < argc
+                                   && argv[optind][0] != '-')
+                                       adt = argv[optind++];
+                               else
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "-%c requires setname and IP",
+                                                  c);
+
+                               res = parse_adt_cmdline(command, name, adt,
+                                                       &set, &settype);
+
+                               if (!res)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Unknown arg `%s'",
+                                                  argv[optind - 1]);
+
+                               res = 0;
+                               break;
+                       }
+
+                       /* options */
+
+               case 'n':
+                       add_option(&options, OPT_NUMERIC);
+                       break;
+
+               case 's':
+                       add_option(&options, OPT_SORTED);
+                       break;
+
+               case 'q':
+                       add_option(&options, OPT_QUIET);
+                       option_quiet = 1;
+                       break;
+
+#ifdef IPSET_DEBUG
+               case 'z':       /* debug */
+                       add_option(&options, OPT_DEBUG);
+                       option_debug = 1;
+                       break;
+#endif
+
+               case 'b':
+                       add_option(&options, OPT_BINDING);
+                       binding = check_set_name(optarg);
+                       break;
+
+               case 1: /* non option */
+                       printf("Bad argument `%s'\n", optarg);
+                       exit_tryhelp(2);
+                       break;  /*always good */
+
+               default:{
+                               DP("default");
+
+                               switch (command) {
+                               case CMD_CREATE:
+                                       res = settype->create_parse(
+                                                       c - settype->option_offset,
+                                                       argv,
+                                                       settype->data,
+                                                       &settype->flags);
+                                       break;
+
+                               default:
+                                       res = 0;        /* failed */
+                               }       /* switch (command) */
+
+
+                               if (!res)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Unknown arg `%s'",
+                                                  argv[optind - 1]);
+                               
+                               res = 0;
+                       }
+
+                       DP("next arg");
+               }       /* switch */
+
+       }       /* while( getopt_long() ) */
+
+
+       if (optind < argc)
+               exit_error(PARAMETER_PROBLEM,
+                          "unknown arguments found on commandline");
+       if (command == CMD_NONE)
+               exit_error(PARAMETER_PROBLEM, "no command specified");
+
+       /* Check options */
+       generic_opt_check(command, options);
+
+       DP("cmd: %c", cmd2char(command));
+
+       switch (command) {
+       case CMD_CREATE:
+               DP("CMD_CREATE");
+               if (restore)
+                       set_restore_create(name, settype);
+               else
+                       set_create(name, settype);
+               break;
+
+       case CMD_DESTROY:
+               set_destroy(name, IP_SET_OP_DESTROY, CMD_DESTROY);
+               break;
+
+       case CMD_FLUSH:
+               set_destroy(name, IP_SET_OP_FLUSH, CMD_FLUSH);
+               break;
+
+       case CMD_RENAME:
+               set_rename(name, newname, IP_SET_OP_RENAME, CMD_RENAME);
+               break;
+
+       case CMD_SWAP:
+               set_rename(name, newname, IP_SET_OP_SWAP, CMD_SWAP);
+               break;
+
+       case CMD_LIST:
+               list_sets(name, options);
+               break;
+
+       case CMD_SAVE:
+               set_save(name);
+               break;
+
+       case CMD_RESTORE:
+               set_restore(argv[0]);
+               break;
+
+       case CMD_ADD:
+               if (restore)
+                       set_restore_add(set, adt);
+               else
+                       set_adtip(set, adt, IP_SET_OP_ADD_IP, CMD_ADD);
+               break;
+
+       case CMD_DEL:
+               set_adtip(set, adt, IP_SET_OP_DEL_IP, CMD_DEL);
+               break;
+
+       case CMD_TEST:
+               if (binding)
+                       res = set_bind(set, adt, binding, 
+                                      IP_SET_OP_TEST_BIND_SET, CMD_TEST);
+               else
+                       res = set_adtip(set, adt, 
+                                       IP_SET_OP_TEST_IP, CMD_TEST);
+               break;
+
+       case CMD_BIND:
+               if (restore)
+                       set_restore_bind(set, adt, binding);
+               else
+                       set_bind(set, adt, binding,
+                                IP_SET_OP_BIND_SET, CMD_BIND);
+               break;
+
+       case CMD_UNBIND:
+               set_bind(set, adt, "", IP_SET_OP_UNBIND_SET, CMD_UNBIND);
+               break;
+
+       case CMD_HELP:
+               set_help(settype);
+               break;
+
+       default:
+               /* Will never happen */
+               break; /* Keep the compiler happy */
+
+       }       /* switch( command ) */
+
+       return res;
+}
+
+
+int main(int argc, char *argv[])
+{      
+       return parse_commandline(argc, argv);
+
+}
diff --git a/ipset/ipset.h b/ipset/ipset.h
new file mode 100644 (file)
index 0000000..4ac2da9
--- /dev/null
@@ -0,0 +1,192 @@
+#ifndef __IPSET_H
+#define __IPSET_H
+
+/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu)
+ *                     Patrick Schaaf (bof@bof.de)
+ *                     Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <getopt.h>
+#include <sys/types.h>
+#include <netdb.h>
+
+#include <linux/netfilter_ipv4/ip_set.h>
+
+#define IPSET_LIB_NAME "/libipset_%s.so"
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+
+#define LIST_TRIES 5
+
+#ifdef IPSET_DEBUG
+extern int option_debug;
+#define DP(format, args...) if (option_debug)                  \
+       do {                                                    \
+               fprintf(stderr, "%s: %s (DBG): ", __FILE__, __FUNCTION__);\
+               fprintf(stderr, format "\n" , ## args);                 \
+       } while (0)
+#else
+#define DP(format, args...)
+#endif
+
+/* Commands */
+enum set_commands {
+       CMD_NONE,
+       CMD_CREATE,             /* -N */
+       CMD_DESTROY,            /* -X */
+       CMD_FLUSH,              /* -F */
+       CMD_RENAME,             /* -E */
+       CMD_SWAP,               /* -W */
+       CMD_LIST,               /* -L */
+       CMD_SAVE,               /* -S */
+       CMD_RESTORE,            /* -R */
+       CMD_ADD,                /* -A */
+       CMD_DEL,                /* -D */
+       CMD_TEST,               /* -T */
+       CMD_BIND,               /* -B */
+       CMD_UNBIND,             /* -U */
+       CMD_HELP,               /* -H */
+       CMD_VERSION,            /* -V */
+       NUMBER_OF_CMD = CMD_VERSION,
+       /* Internal commands */
+       CMD_MAX_SETS,
+       CMD_LIST_SIZE,
+       CMD_SAVE_SIZE,
+       CMD_ADT_GET,
+};
+
+enum exittype {
+       OTHER_PROBLEM = 1,
+       PARAMETER_PROBLEM,
+       VERSION_PROBLEM
+};
+
+/* The view of an ipset in userspace */
+struct set {
+       char name[IP_SET_MAXNAMELEN];           /* Name of the set */
+       ip_set_id_t id;                         /* Unique set id */
+       ip_set_id_t index;                      /* Array index */
+       unsigned ref;                           /* References in kernel */
+       struct settype *settype;                /* Pointer to set type functions */
+};
+
+struct settype {
+       struct settype *next;
+
+       char typename[IP_SET_MAXNAMELEN];
+
+       int protocol_version;
+
+       /*
+        * Create set
+        */
+
+       /* Size of create data. Will be sent to kernel */
+       size_t create_size;
+
+       /* Initialize the create. */
+       void (*create_init) (void *data);
+
+       /* Function which parses command options; returns true if it ate an option */
+       int (*create_parse) (int c, char *argv[], void *data,
+                            unsigned *flags);
+
+       /* Final check; exit if not ok. */
+       void (*create_final) (void *data, unsigned int flags);
+
+       /* Pointer to list of extra command-line options for create */
+       struct option *create_opts;
+
+       /*
+        * Add/del/test IP
+        */
+
+       /* Size of data. Will be sent to kernel */
+       size_t adt_size;
+
+       /* Function which parses command options */
+       ip_set_ip_t (*adt_parser) (unsigned cmd, const char *optarg, void *data);
+
+       /*
+        * Printing
+        */
+
+       /* Size of header. */
+       size_t header_size;
+
+       /* Initialize the type-header */
+       void (*initheader) (struct set *set, const void *data);
+
+       /* Pretty print the type-header */
+       void (*printheader) (struct set *set, unsigned options);
+
+       /* Pretty print all IPs */
+       void (*printips) (struct set *set, void *data, size_t len, unsigned options);
+
+       /* Pretty print all IPs sorted */
+       void (*printips_sorted) (struct set *set, void *data, size_t len, unsigned options);
+
+       /* Print save arguments for creating the set */
+       void (*saveheader) (struct set *set, unsigned options);
+
+       /* Print save for all IPs */
+       void (*saveips) (struct set *set, void *data, size_t len, unsigned options);
+
+       /* Conver a single IP (binding) to string */
+       char * (*bindip_tostring)(struct set *set, ip_set_ip_t ip, unsigned options);
+       
+       /* Parse an IP at restoring bindings. FIXME */
+       void (*bindip_parse) (const char *str, ip_set_ip_t * ip);
+
+       /* Print usage */
+       void (*usage) (void);
+
+       /* Internal data */
+       void *header;
+       void *data;
+       unsigned int option_offset;
+       unsigned int flags;
+};
+
+extern void settype_register(struct settype *settype);
+
+/* extern void unregister_settype(set_type_t *set_type); */
+
+extern void exit_error(enum exittype status, char *msg, ...);
+
+extern char *binding_ip_tostring(struct set *set,
+                                ip_set_ip_t ip, unsigned options);
+extern char *ip_tostring(ip_set_ip_t ip, unsigned options);
+extern char *ip_tostring_numeric(ip_set_ip_t ip);
+extern void parse_ip(const char *str, ip_set_ip_t * ip);
+extern void parse_mask(const char *str, ip_set_ip_t * mask);
+extern void parse_ipandmask(const char *str, ip_set_ip_t * ip,
+                           ip_set_ip_t * mask);
+extern char *port_tostring(ip_set_ip_t port, unsigned options);
+extern void parse_port(const char *str, ip_set_ip_t * port);
+extern int string_to_number(const char *str, unsigned int min, unsigned int max,
+                           ip_set_ip_t *port);
+
+extern void *ipset_malloc(size_t size);
+extern char *ipset_strdup(const char *);
+extern void ipset_free(void **data);
+
+#define BITSPERBYTE    (8*sizeof(char))
+#define ID2BYTE(id)    ((id)/BITSPERBYTE)
+#define ID2MASK(id)    (1 << ((id)%BITSPERBYTE))
+#define test_bit(id, heap)     ((((char *)(heap))[ID2BYTE(id)] & ID2MASK(id)) != 0)
+
+#endif /* __IPSET_H */
diff --git a/ipset/ipset_iphash.c b/ipset/ipset_iphash.c
new file mode 100644 (file)
index 0000000..c8b400c
--- /dev/null
@@ -0,0 +1,297 @@
+/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <asm/types.h>
+#include <limits.h>
+
+#include <linux/netfilter_ipv4/ip_set_iphash.h>
+#include <linux/netfilter_ipv4/ip_set_jhash.h>
+
+#include "ipset.h"
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_HASHSIZE    0x01U
+#define OPT_CREATE_PROBES      0x02U
+#define OPT_CREATE_RESIZE      0x04U
+#define OPT_CREATE_NETMASK     0x08U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       struct ip_set_req_iphash_create *mydata =
+           (struct ip_set_req_iphash_create *) data;
+
+       DP("create INIT");
+
+       /* Default create parameters */ 
+       mydata->hashsize = 1024;
+       mydata->probes = 8;
+       mydata->resize = 50;
+       
+       mydata->netmask = 0xFFFFFFFF;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_iphash_create *mydata =
+           (struct ip_set_req_iphash_create *) data;
+       unsigned int bits;
+       ip_set_ip_t value;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+
+               if (string_to_number(optarg, 1, UINT_MAX - 1, &mydata->hashsize))
+                       exit_error(PARAMETER_PROBLEM, "Invalid hashsize `%s' specified", optarg);
+
+               *flags |= OPT_CREATE_HASHSIZE;
+
+               DP("--hashsize %u", mydata->hashsize);
+               
+               break;
+
+       case '2':
+
+               if (string_to_number(optarg, 1, 65535, &value))
+                       exit_error(PARAMETER_PROBLEM, "Invalid probes `%s' specified", optarg);
+
+               mydata->probes = value;
+               *flags |= OPT_CREATE_PROBES;
+
+               DP("--probes %u", mydata->probes);
+               
+               break;
+
+       case '3':
+
+               if (string_to_number(optarg, 0, 65535, &value))
+                       exit_error(PARAMETER_PROBLEM, "Invalid resize `%s' specified", optarg);
+
+               mydata->resize = value;
+               *flags |= OPT_CREATE_RESIZE;
+
+               DP("--resize %u", mydata->resize);
+               
+               break;
+
+       case '4':
+
+               if (string_to_number(optarg, 0, 32, &bits))
+                       exit_error(PARAMETER_PROBLEM, 
+                                 "Invalid netmask `%s' specified", optarg);
+               
+               if (bits != 0)
+                       mydata->netmask = 0xFFFFFFFF << (32 - bits);
+
+               *flags |= OPT_CREATE_NETMASK;
+
+               DP("--netmask %x", mydata->netmask);
+               
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+#ifdef IPSET_DEBUG
+       struct ip_set_req_iphash_create *mydata =
+           (struct ip_set_req_iphash_create *) data;
+
+       DP("hashsize %u probes %u resize %u",
+          mydata->hashsize, mydata->probes, mydata->resize);
+#endif
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"hashsize", 1, 0, '1'},
+       {"probes", 1, 0, '2'},
+       {"resize", 1, 0, '3'},
+       {"netmask", 1, 0, '4'},
+       {0}
+};
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_iphash *mydata =
+           (struct ip_set_req_iphash *) data;
+
+       parse_ip(optarg, &mydata->ip);
+       if (!mydata->ip)
+               exit_error(PARAMETER_PROBLEM,
+                          "Zero valued IP address `%s' specified", optarg);
+
+       return mydata->ip;      
+};
+
+/*
+ * Print and save
+ */
+
+void initheader(struct set *set, const void *data)
+{
+       struct ip_set_req_iphash_create *header =
+           (struct ip_set_req_iphash_create *) data;
+       struct ip_set_iphash *map =
+               (struct ip_set_iphash *) set->settype->header;
+
+       memset(map, 0, sizeof(struct ip_set_iphash));
+       map->hashsize = header->hashsize;
+       map->probes = header->probes;
+       map->resize = header->resize;
+       map->netmask = header->netmask;
+}
+
+unsigned int
+mask_to_bits(ip_set_ip_t mask)
+{
+       unsigned int bits = 32;
+       ip_set_ip_t maskaddr;
+       
+       if (mask == 0xFFFFFFFF)
+               return bits;
+       
+       maskaddr = 0xFFFFFFFE;
+       while (--bits >= 0 && maskaddr != mask)
+               maskaddr <<= 1;
+       
+       return bits;
+}
+       
+void printheader(struct set *set, unsigned options)
+{
+       struct ip_set_iphash *mysetdata =
+           (struct ip_set_iphash *) set->settype->header;
+
+       printf(" hashsize: %u", mysetdata->hashsize);
+       printf(" probes: %u", mysetdata->probes);
+       printf(" resize: %u", mysetdata->resize);
+       if (mysetdata->netmask == 0xFFFFFFFF)
+               printf("\n");
+       else
+               printf(" netmask: %d\n", mask_to_bits(mysetdata->netmask));
+}
+
+void printips(struct set *set, void *data, size_t len, unsigned options)
+{
+       size_t offset = 0;
+       ip_set_ip_t *ip;
+
+       while (offset < len) {
+               ip = data + offset;
+               if (*ip)
+                       printf("%s\n", ip_tostring(*ip, options));
+               offset += sizeof(ip_set_ip_t);
+       }
+}
+
+void saveheader(struct set *set, unsigned options)
+{
+       struct ip_set_iphash *mysetdata =
+           (struct ip_set_iphash *) set->settype->header;
+
+       printf("-N %s %s --hashsize %u --probes %u --resize %u",
+              set->name, set->settype->typename,
+              mysetdata->hashsize, mysetdata->probes, mysetdata->resize);
+       if (mysetdata->netmask == 0xFFFFFFFF)
+               printf("\n");
+       else
+               printf(" --netmask %d\n", mask_to_bits(mysetdata->netmask));
+}
+
+/* Print save for an IP */
+void saveips(struct set *set, void *data, size_t len, unsigned options)
+{
+       size_t offset = 0;
+       ip_set_ip_t *ip;
+
+       while (offset < len) {
+               ip = data + offset;
+               if (*ip)
+                       printf("-A %s %s\n", set->name, 
+                              ip_tostring(*ip, options));
+               offset += sizeof(ip_set_ip_t);
+       }
+}
+
+void usage(void)
+{
+       printf
+           ("-N set iphash [--hashsize hashsize] [--probes probes ]\n"
+            "              [--resize resize] [--netmask CIDR-netmask]\n"
+            "-A set IP\n"
+            "-D set IP\n"
+            "-T set IP\n");
+}
+
+static struct settype settype_iphash = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_iphash_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .adt_size = sizeof(struct ip_set_req_iphash),
+       .adt_parser = &adt_parser,
+
+       /* Printing */
+       .header_size = sizeof(struct ip_set_iphash),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printips,          /* We only have the unsorted version */
+       .printips_sorted = &printips,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+       
+       /* Bindings */
+       .bindip_tostring = &binding_ip_tostring,
+       .bindip_parse = &parse_ip,
+       
+       .usage = &usage,
+};
+
+void _init(void)
+{
+       settype_register(&settype_iphash);
+
+}
diff --git a/ipset/ipset_ipmap.c b/ipset/ipset_ipmap.c
new file mode 100644 (file)
index 0000000..df8efbf
--- /dev/null
@@ -0,0 +1,360 @@
+/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu)
+ *                     Patrick Schaaf (bof@bof.de)
+ *                     Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+/* #include <asm/bitops.h> */
+
+#include <linux/netfilter_ipv4/ip_set_ipmap.h>
+#include "ipset.h"
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_FROM    0x01U
+#define OPT_CREATE_TO      0x02U
+#define OPT_CREATE_NETWORK 0x04U
+#define OPT_CREATE_NETMASK 0x08U
+
+#define OPT_ADDDEL_IP      0x01U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       struct ip_set_req_ipmap_create *mydata =
+           (struct ip_set_req_ipmap_create *) data;
+
+       DP("create INIT");
+       mydata->netmask = 0xFFFFFFFF;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_ipmap_create *mydata =
+           (struct ip_set_req_ipmap_create *) data;
+       unsigned int bits;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+               parse_ip(optarg, &mydata->from);
+
+               *flags |= OPT_CREATE_FROM;
+
+               DP("--from %x (%s)", mydata->from,
+                  ip_tostring_numeric(mydata->from));
+
+               break;
+
+       case '2':
+               parse_ip(optarg, &mydata->to);
+
+               *flags |= OPT_CREATE_TO;
+
+               DP("--to %x (%s)", mydata->to,
+                  ip_tostring_numeric(mydata->to));
+
+               break;
+
+       case '3':
+               parse_ipandmask(optarg, &mydata->from, &mydata->to);
+
+               /* Make to the last of from + mask */
+               if (mydata->to)
+                       mydata->to = mydata->from | ~(mydata->to);
+               else {
+                       mydata->from = 0x00000000;
+                       mydata->to = 0xFFFFFFFF;
+               }
+               *flags |= OPT_CREATE_NETWORK;
+
+               DP("--network from %x (%s)", 
+                  mydata->from, ip_tostring_numeric(mydata->from));
+               DP("--network to %x (%s)", 
+                  mydata->to, ip_tostring_numeric(mydata->to));
+
+               break;
+
+       case '4':
+               if (string_to_number(optarg, 0, 32, &bits))
+                       exit_error(PARAMETER_PROBLEM, 
+                                 "Invalid netmask `%s' specified", optarg);
+               
+               if (bits != 0)
+                       mydata->netmask = 0xFFFFFFFF << (32 - bits);
+
+               *flags |= OPT_CREATE_NETMASK;
+
+               DP("--netmask %x", mydata->netmask);
+               
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+#define ERRSTRLEN      256
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+       struct ip_set_req_ipmap_create *mydata =
+           (struct ip_set_req_ipmap_create *) data;
+       ip_set_ip_t range;
+       char errstr[ERRSTRLEN];
+
+       if (flags == 0)
+               exit_error(PARAMETER_PROBLEM,
+                          "Need to specify --from and --to, or --network\n");
+
+       if (flags & OPT_CREATE_NETWORK) {
+               /* --network */
+               if ((flags & OPT_CREATE_FROM) || (flags & OPT_CREATE_TO))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --from or --to with --network\n");
+       } else {
+               /* --from --to */
+               if ((flags & OPT_CREATE_FROM) == 0
+                   || (flags & OPT_CREATE_TO) == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need to specify both --from and --to\n");
+       }
+
+       DP("from : %x to: %x diff: %x", 
+          mydata->from, mydata->to,
+          mydata->to - mydata->from);
+
+       if (mydata->from > mydata->to)
+               exit_error(PARAMETER_PROBLEM,
+                          "From can't be lower than to.\n");
+
+       if (flags & OPT_CREATE_NETMASK) {
+               unsigned int mask_bits, netmask_bits;
+               ip_set_ip_t mask;
+               
+               if ((mydata->from & mydata->netmask) != mydata->from)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "%s is not a network address according to netmask %d\n",
+                                  ip_tostring_numeric(mydata->from),
+                                  mask_to_bits(mydata->netmask));
+               
+               mask = range_to_mask(mydata->from, mydata->to, &mask_bits);
+               if (!mask
+                   && (mydata->from || mydata->to != 0xFFFFFFFF)) {
+                       strncpy(errstr, ip_tostring_numeric(mydata->from),
+                               ERRSTRLEN-2);
+                       errstr[ERRSTRLEN-1] = '\0';
+                       exit_error(PARAMETER_PROBLEM,
+                                  "%s-%s is not a full network (%x)\n",
+                                  errstr,
+                                  ip_tostring_numeric(mydata->to), mask);
+               }
+               netmask_bits = mask_to_bits(mydata->netmask);
+               
+               if (netmask_bits <= mask_bits) {
+                       strncpy(errstr, ip_tostring_numeric(mydata->from),
+                               ERRSTRLEN-2);
+                       errstr[ERRSTRLEN-1] = '\0';
+                       exit_error(PARAMETER_PROBLEM,
+                                  "%d netmask specifies larger or equal netblock than %s-%s (%d)\n",
+                                  netmask_bits,
+                                  errstr,
+                                  ip_tostring_numeric(mydata->to),
+                                  mask_bits);
+               }
+               range = (1<<(netmask_bits - mask_bits)) - 1;
+       } else {
+               range = mydata->to - mydata->from;
+       }
+       if (range > MAX_RANGE)
+               exit_error(PARAMETER_PROBLEM,
+                          "Range too large. Max is %d IPs in range\n",
+                          MAX_RANGE+1);
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"from", 1, 0, '1'},
+       {"to", 1, 0, '2'},
+       {"network", 1, 0, '3'},
+       {"netmask", 1, 0, '4'},
+       {0}
+};
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_ipmap *mydata =
+           (struct ip_set_req_ipmap *) data;
+
+       DP("ipmap: %p %p", optarg, data);
+
+       parse_ip(optarg, &mydata->ip);
+       DP("%s", ip_tostring_numeric(mydata->ip));
+
+       return 1;       
+}
+
+/*
+ * Print and save
+ */
+
+void initheader(struct set *set, const void *data)
+{
+       struct ip_set_req_ipmap_create *header =
+           (struct ip_set_req_ipmap_create *) data;
+       struct ip_set_ipmap *map =
+               (struct ip_set_ipmap *) set->settype->header;
+               
+       memset(map, 0, sizeof(struct ip_set_ipmap));
+       map->first_ip = header->from;
+       map->last_ip = header->to;
+       map->netmask = header->netmask;
+
+       if (map->netmask == 0xFFFFFFFF) {
+               map->hosts = 1;
+               map->sizeid = map->last_ip - map->first_ip + 1;
+       } else {
+               unsigned int mask_bits, netmask_bits;
+               ip_set_ip_t mask;
+       
+               mask = range_to_mask(header->from, header->to, &mask_bits);
+               netmask_bits = mask_to_bits(header->netmask);
+
+               DP("bits: %i %i", mask_bits, netmask_bits);
+               map->hosts = 2 << (32 - netmask_bits - 1);
+               map->sizeid = 2 << (netmask_bits - mask_bits - 1);
+       }
+
+       DP("%i %i", map->hosts, map->sizeid );
+}
+
+void printheader(struct set *set, unsigned options)
+{
+       struct ip_set_ipmap *mysetdata =
+           (struct ip_set_ipmap *) set->settype->header;
+
+       printf(" from: %s", ip_tostring(mysetdata->first_ip, options));
+       printf(" to: %s", ip_tostring(mysetdata->last_ip, options));
+       if (mysetdata->netmask == 0xFFFFFFFF)
+               printf("\n");
+       else
+               printf(" netmask: %d\n", mask_to_bits(mysetdata->netmask));
+}
+
+void printips_sorted(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_ipmap *mysetdata =
+           (struct ip_set_ipmap *) set->settype->header;
+       ip_set_ip_t id;
+
+       for (id = 0; id < mysetdata->sizeid; id++)
+               if (test_bit(id, data))
+                       printf("%s\n",
+                              ip_tostring(mysetdata->first_ip
+                                          + id * mysetdata->hosts,
+                                          options));
+}
+
+void saveheader(struct set *set, unsigned options)
+{
+       struct ip_set_ipmap *mysetdata =
+           (struct ip_set_ipmap *) set->settype->header;
+
+       printf("-N %s %s --from %s",
+              set->name, set->settype->typename,
+              ip_tostring(mysetdata->first_ip, options));
+       printf(" --to %s",
+              ip_tostring(mysetdata->last_ip, options));
+       if (mysetdata->netmask == 0xFFFFFFFF)
+               printf("\n");
+       else
+               printf(" --netmask %d\n",
+                      mask_to_bits(mysetdata->netmask));
+}
+
+void saveips(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_ipmap *mysetdata =
+           (struct ip_set_ipmap *) set->settype->header;
+       ip_set_ip_t id;
+
+       DP("%s", set->name);
+       for (id = 0; id < mysetdata->sizeid; id++)
+               if (test_bit(id, data))
+                       printf("-A %s %s\n",
+                              set->name,
+                              ip_tostring(mysetdata->first_ip 
+                                          + id * mysetdata->hosts,
+                                          options));
+}
+
+void usage(void)
+{
+       printf
+           ("-N set ipmap --from IP --to IP [--netmask CIDR-netmask]\n"
+            "-N set ipmap --network IP/mask [--netmask CIDR-netmask]\n"
+            "-A set IP\n"
+            "-D set IP\n"
+            "-T set IP\n");
+}
+
+static struct settype settype_ipmap = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_ipmap_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .adt_size = sizeof(struct ip_set_req_ipmap),
+       .adt_parser = &adt_parser,
+
+       /* Printing */
+       .header_size = sizeof(struct ip_set_ipmap),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printips_sorted,   /* We only have sorted version */
+       .printips_sorted = &printips_sorted,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+       
+       /* Bindings */
+       .bindip_tostring = &binding_ip_tostring,
+       .bindip_parse   = &parse_ip,
+
+       .usage = &usage,
+};
+
+void _init(void)
+{
+       settype_register(&settype_ipmap);
+
+}
diff --git a/ipset/ipset_ipporthash.c b/ipset/ipset_ipporthash.c
new file mode 100644 (file)
index 0000000..684e6c2
--- /dev/null
@@ -0,0 +1,373 @@
+/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <asm/types.h>
+#include <limits.h>
+
+#include <linux/netfilter_ipv4/ip_set_ipporthash.h>
+#include <linux/netfilter_ipv4/ip_set_jhash.h>
+
+#include "ipset.h"
+
+#define OPT_CREATE_HASHSIZE    0x01U
+#define OPT_CREATE_PROBES      0x02U
+#define OPT_CREATE_RESIZE      0x04U
+#define OPT_CREATE_NETWORK     0x08U
+#define OPT_CREATE_FROM                0x10U
+#define OPT_CREATE_TO          0x20U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       struct ip_set_req_ipporthash_create *mydata =
+           (struct ip_set_req_ipporthash_create *) data;
+
+       DP("create INIT");
+
+       /* Default create parameters */ 
+       mydata->hashsize = 1024;
+       mydata->probes = 8;
+       mydata->resize = 50;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_ipporthash_create *mydata =
+           (struct ip_set_req_ipporthash_create *) data;
+       ip_set_ip_t value;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+
+               if (string_to_number(optarg, 1, UINT_MAX - 1, &mydata->hashsize))
+                       exit_error(PARAMETER_PROBLEM, "Invalid hashsize `%s' specified", optarg);
+
+               *flags |= OPT_CREATE_HASHSIZE;
+
+               DP("--hashsize %u", mydata->hashsize);
+               
+               break;
+
+       case '2':
+
+               if (string_to_number(optarg, 1, 65535, &value))
+                       exit_error(PARAMETER_PROBLEM, "Invalid probes `%s' specified", optarg);
+
+               mydata->probes = value;
+               *flags |= OPT_CREATE_PROBES;
+
+               DP("--probes %u", mydata->probes);
+               
+               break;
+
+       case '3':
+
+               if (string_to_number(optarg, 0, 65535, &value))
+                       exit_error(PARAMETER_PROBLEM, "Invalid resize `%s' specified", optarg);
+
+               mydata->resize = value;
+               *flags |= OPT_CREATE_RESIZE;
+
+               DP("--resize %u", mydata->resize);
+               
+               break;
+
+       case '4':
+               parse_ip(optarg, &mydata->from);
+
+               *flags |= OPT_CREATE_FROM;
+
+               DP("--from %x (%s)", mydata->from,
+                  ip_tostring_numeric(mydata->from));
+
+               break;
+
+       case '5':
+               parse_ip(optarg, &mydata->to);
+
+               *flags |= OPT_CREATE_TO;
+
+               DP("--to %x (%s)", mydata->to,
+                  ip_tostring_numeric(mydata->to));
+
+               break;
+
+       case '6':
+               parse_ipandmask(optarg, &mydata->from, &mydata->to);
+
+               /* Make to the last of from + mask */
+               if (mydata->to)
+                       mydata->to = mydata->from | ~(mydata->to);
+               else {
+                       mydata->from = 0x00000000;
+                       mydata->to = 0xFFFFFFFF;
+               }
+               *flags |= OPT_CREATE_NETWORK;
+
+               DP("--network from %x (%s)", 
+                  mydata->from, ip_tostring_numeric(mydata->from));
+               DP("--network to %x (%s)", 
+                  mydata->to, ip_tostring_numeric(mydata->to));
+
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+       struct ip_set_req_ipporthash_create *mydata =
+           (struct ip_set_req_ipporthash_create *) data;
+
+#ifdef IPSET_DEBUG
+       DP("hashsize %u probes %u resize %u",
+          mydata->hashsize, mydata->probes, mydata->resize);
+#endif
+
+       if (flags & OPT_CREATE_NETWORK) {
+               /* --network */
+               if ((flags & OPT_CREATE_FROM) || (flags & OPT_CREATE_TO))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --from or --to with --network\n");
+       } else if (flags & (OPT_CREATE_FROM | OPT_CREATE_TO)) {
+               /* --from --to */
+               if (!(flags & OPT_CREATE_FROM) || !(flags & OPT_CREATE_TO))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need to specify both --from and --to\n");
+       } else {
+               exit_error(PARAMETER_PROBLEM,
+                          "Need to specify --from and --to, or --network\n");
+
+       }
+
+       DP("from : %x to: %x diff: %x", 
+          mydata->from, mydata->to,
+          mydata->to - mydata->from);
+
+       if (mydata->from > mydata->to)
+               exit_error(PARAMETER_PROBLEM,
+                          "From can't be higher than to.\n");
+
+       if (mydata->to - mydata->from > MAX_RANGE)
+               exit_error(PARAMETER_PROBLEM,
+                          "Range too large. Max is %d IPs in range\n",
+                          MAX_RANGE+1);
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"hashsize", 1, 0, '1'},
+       {"probes", 1, 0, '2'},
+       {"resize", 1, 0, '3'},
+       {"from", 1, 0, '4'},
+       {"to", 1, 0, '5'},
+       {"network", 1, 0, '6'},
+       {0}
+};
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_ipporthash *mydata =
+           (struct ip_set_req_ipporthash *) data;
+       char *saved = ipset_strdup(optarg);
+       char *ptr, *tmp = saved;
+
+       DP("ipporthash: %p %p", optarg, data);
+
+       ptr = strsep(&tmp, ":%");
+       parse_ip(ptr, &mydata->ip);
+
+       if (tmp)
+               parse_port(tmp, &mydata->port);
+       else
+               exit_error(PARAMETER_PROBLEM,
+                          "IP address and port must be specified: ip%%port");
+       free(saved);
+       return 1;       
+};
+
+/*
+ * Print and save
+ */
+
+void initheader(struct set *set, const void *data)
+{
+       struct ip_set_req_ipporthash_create *header =
+           (struct ip_set_req_ipporthash_create *) data;
+       struct ip_set_ipporthash *map =
+               (struct ip_set_ipporthash *) set->settype->header;
+
+       memset(map, 0, sizeof(struct ip_set_ipporthash));
+       map->hashsize = header->hashsize;
+       map->probes = header->probes;
+       map->resize = header->resize;
+       map->first_ip = header->from;
+       map->last_ip = header->to;
+}
+
+void printheader(struct set *set, unsigned options)
+{
+       struct ip_set_ipporthash *mysetdata =
+           (struct ip_set_ipporthash *) set->settype->header;
+
+       printf(" from: %s", ip_tostring(mysetdata->first_ip, options));
+       printf(" to: %s", ip_tostring(mysetdata->last_ip, options));
+       printf(" hashsize: %u", mysetdata->hashsize);
+       printf(" probes: %u", mysetdata->probes);
+       printf(" resize: %u\n", mysetdata->resize);
+}
+
+void printips(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_ipporthash *mysetdata =
+           (struct ip_set_ipporthash *) set->settype->header;
+       size_t offset = 0;
+       ip_set_ip_t *ipptr, ip;
+       uint16_t port;
+
+       while (offset < len) {
+               ipptr = data + offset;
+               if (*ipptr) {
+                       ip = (*ipptr>>16) + mysetdata->first_ip;
+                       port = (uint16_t) *ipptr;
+                       printf("%s:%s\n", 
+                              ip_tostring(ip, options),
+                              port_tostring(port, options));
+               }
+               offset += sizeof(ip_set_ip_t);
+       }
+}
+
+void saveheader(struct set *set, unsigned options)
+{
+       struct ip_set_ipporthash *mysetdata =
+           (struct ip_set_ipporthash *) set->settype->header;
+
+       printf("-N %s %s --from %s",
+              set->name, set->settype->typename,
+              ip_tostring(mysetdata->first_ip, options));
+       printf(" --to %s",
+              ip_tostring(mysetdata->last_ip, options));
+       printf(" --hashsize %u --probes %u --resize %u\n",
+              mysetdata->hashsize, mysetdata->probes, mysetdata->resize);
+}
+
+/* Print save for an IP */
+void saveips(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_ipporthash *mysetdata =
+           (struct ip_set_ipporthash *) set->settype->header;
+       size_t offset = 0;
+       ip_set_ip_t *ipptr, ip;
+       uint16_t port;
+
+       while (offset < len) {
+               ipptr = data + offset;
+               if (*ipptr) {
+                       ip = (*ipptr>>16) + mysetdata->first_ip;
+                       port = (uint16_t) *ipptr;
+                       printf("-A %s %s:%s\n", set->name, 
+                              ip_tostring(ip, options),
+                              port_tostring(port, options));
+               }
+               offset += sizeof(ip_set_ip_t);
+       }
+}
+
+static char buffer[22];
+
+static char * unpack_ipport_tostring(struct set *set, ip_set_ip_t bip, unsigned options)
+{
+       struct ip_set_ipporthash *mysetdata =
+           (struct ip_set_ipporthash *) set->settype->header;
+       ip_set_ip_t ip, port;
+       
+       ip = (bip>>16) + mysetdata->first_ip;
+       port = (uint16_t) bip;
+       sprintf(buffer, "%s:%s", 
+               ip_tostring(ip, options), port_tostring(port, options));
+               
+       return buffer;
+}
+
+void usage(void)
+{
+       printf
+           ("-N set ipporthash --from IP --to IP\n"
+            "   [--hashsize hashsize] [--probes probes ] [--resize resize]\n"
+            "-N set ipporthash --network IP/mask\n"
+            "   [--hashsize hashsize] [--probes probes ] [--resize resize]\n"
+            "-A set IP:port\n"
+            "-D set IP:port\n"
+            "-T set IP:port\n");
+}
+
+static struct settype settype_ipporthash = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_ipporthash_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .adt_size = sizeof(struct ip_set_req_ipporthash),
+       .adt_parser = &adt_parser,
+
+       /* Printing */
+       .header_size = sizeof(struct ip_set_ipporthash),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printips,          /* We only have the unsorted version */
+       .printips_sorted = &printips,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+       
+       /* Bindings */
+       .bindip_tostring = &unpack_ipport_tostring,
+       .bindip_parse = &parse_ip,
+       
+       .usage = &usage,
+};
+
+void _init(void)
+{
+       settype_register(&settype_ipporthash);
+
+}
diff --git a/ipset/ipset_iptree.c b/ipset/ipset_iptree.c
new file mode 100644 (file)
index 0000000..b68601a
--- /dev/null
@@ -0,0 +1,224 @@
+/* Copyright 2005 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <limits.h>
+
+#include <linux/netfilter_ipv4/ip_set_iptree.h>
+#include "ipset.h"
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_TIMEOUT    0x01U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       struct ip_set_req_iptree_create *mydata =
+           (struct ip_set_req_iptree_create *) data;
+
+       DP("create INIT");
+       mydata->timeout = 0;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_iptree_create *mydata =
+           (struct ip_set_req_iptree_create *) data;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+               string_to_number(optarg, 0, UINT_MAX, &mydata->timeout);
+
+               *flags |= OPT_CREATE_TIMEOUT;
+
+               DP("--timeout %u", mydata->timeout);
+
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"timeout", 1, 0, '1'},
+       {0}
+};
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_iptree *mydata =
+           (struct ip_set_req_iptree *) data;
+       char *saved = ipset_strdup(optarg);
+       char *ptr, *tmp = saved;
+
+       DP("iptree: %p %p", optarg, data);
+
+       ptr = strsep(&tmp, ":%");
+       parse_ip(ptr, &mydata->ip);
+
+       if (tmp)
+               string_to_number(tmp, 0, UINT_MAX, &mydata->timeout);
+       else
+               mydata->timeout = 0;    
+
+       free(saved);
+       return 1;       
+}
+
+/*
+ * Print and save
+ */
+
+void initheader(struct set *set, const void *data)
+{
+       struct ip_set_req_iptree_create *header =
+           (struct ip_set_req_iptree_create *) data;
+       struct ip_set_iptree *map =
+               (struct ip_set_iptree *) set->settype->header;
+               
+       map->timeout = header->timeout;
+}
+
+void printheader(struct set *set, unsigned options)
+{
+       struct ip_set_iptree *mysetdata =
+           (struct ip_set_iptree *) set->settype->header;
+
+       if (mysetdata->timeout)
+               printf(" timeout: %u", mysetdata->timeout);
+       printf("\n");
+}
+
+void printips_sorted(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_iptree *mysetdata =
+           (struct ip_set_iptree *) set->settype->header;
+       struct ip_set_req_iptree *req;
+       size_t offset = 0;
+
+       while (len >= offset + sizeof(struct ip_set_req_iptree)) {
+               req = (struct ip_set_req_iptree *)(data + offset);
+               if (mysetdata->timeout)
+                       printf("%s:%u\n", ip_tostring(req->ip, options),
+                                         req->timeout);
+               else
+                       printf("%s\n", ip_tostring(req->ip, options));
+               offset += sizeof(struct ip_set_req_iptree);
+       }
+}
+
+void saveheader(struct set *set, unsigned options)
+{
+       struct ip_set_iptree *mysetdata =
+           (struct ip_set_iptree *) set->settype->header;
+
+       if (mysetdata->timeout)
+               printf("-N %s %s --timeout %u\n",
+                      set->name, set->settype->typename,
+                      mysetdata->timeout);
+       else
+               printf("-N %s %s\n",
+                      set->name, set->settype->typename);
+}
+
+void saveips(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_iptree *mysetdata =
+           (struct ip_set_iptree *) set->settype->header;
+       struct ip_set_req_iptree *req;
+       size_t offset = 0;
+
+       DP("%s", set->name);
+
+       while (len >= offset + sizeof(struct ip_set_req_iptree)) {
+               req = (struct ip_set_req_iptree *)(data + offset);
+               if (mysetdata->timeout)
+                       printf("-A %s %s:%u\n",
+                               set->name, 
+                               ip_tostring(req->ip, options),
+                               req->timeout);
+               else
+                       printf("-A %s %s\n", 
+                               set->name,
+                               ip_tostring(req->ip, options));
+               offset += sizeof(struct ip_set_req_iptree);
+       }
+}
+
+void usage(void)
+{
+       printf
+           ("-N set iptree [--timeout value]\n"
+            "-A set IP[:timeout]\n"
+            "-D set IP\n"
+            "-T set IP\n");
+}
+
+static struct settype settype_iptree = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_iptree_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .adt_size = sizeof(struct ip_set_req_iptree),
+       .adt_parser = &adt_parser,
+
+       /* Printing */
+       .header_size = sizeof(struct ip_set_iptree),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printips_sorted,   /* We only have sorted version */
+       .printips_sorted = &printips_sorted,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+       
+       /* Bindings */
+       .bindip_tostring = &binding_ip_tostring,
+       .bindip_parse   = &parse_ip,
+
+       .usage = &usage,
+};
+
+void _init(void)
+{
+       settype_register(&settype_iptree);
+
+}
diff --git a/ipset/ipset_iptreemap.c b/ipset/ipset_iptreemap.c
new file mode 100644 (file)
index 0000000..b627b6e
--- /dev/null
@@ -0,0 +1,207 @@
+/* Copyright 2007 Sven Wegener <sven.wegener@stealer.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <limits.h>
+
+#include <linux/netfilter_ipv4/ip_set_iptreemap.h>
+
+#include "ipset.h"
+
+#define OPT_CREATE_GC 0x1
+
+void
+create_init(void *data)
+{
+       struct ip_set_req_iptreemap_create *mydata = (struct ip_set_req_iptreemap_create *) data;
+
+       mydata->gc_interval = 0;
+}
+
+int
+create_parse(int c, char *argv[], void *data, unsigned int *flags)
+{
+       struct ip_set_req_iptreemap_create *mydata = (struct ip_set_req_iptreemap_create *) data;
+
+       switch (c) {
+               case 'g':
+                       string_to_number(optarg, 0, UINT_MAX, &mydata->gc_interval);
+
+                       *flags |= OPT_CREATE_GC;
+               break;
+               default:
+                       return 0;
+               break;
+       }
+
+       return 1;
+}
+
+void
+create_final(void *data, unsigned int flags)
+{
+}
+
+static struct option create_opts[] = {
+       {"gc", 1, 0, 'g'},
+       {0}
+};
+
+ip_set_ip_t
+adt_parser(unsigned int cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_iptreemap *mydata = (struct ip_set_req_iptreemap *) data;
+       ip_set_ip_t mask;
+
+       char *saved = ipset_strdup(optarg);
+       char *ptr, *tmp = saved;
+
+       if (strchr(tmp, '/')) {
+               parse_ipandmask(tmp, &mydata->start, &mask);
+               mydata->end = mydata->start | ~mask;
+       } else {
+               ptr = strsep(&tmp, ":");
+               parse_ip(ptr, &mydata->start);
+
+               if (tmp) {
+                       parse_ip(tmp, &mydata->end);
+               } else {
+                       mydata->end = mydata->start;
+               }
+       }
+
+       return 1;
+}
+
+void
+initheader(struct set *set, const void *data)
+{
+       struct ip_set_req_iptreemap_create *header = (struct ip_set_req_iptreemap_create *) data;
+       struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->settype->header;
+
+       map->gc_interval = header->gc_interval;
+}
+
+void
+printheader(struct set *set, unsigned int options)
+{
+       struct ip_set_iptreemap *mysetdata = (struct ip_set_iptreemap *) set->settype->header;
+
+       if (mysetdata->gc_interval)
+               printf(" gc: %u", mysetdata->gc_interval);
+
+       printf("\n");
+}
+
+void
+printips_sorted(struct set *set, void *data, size_t len, unsigned int options)
+{
+       struct ip_set_req_iptreemap *req;
+       size_t offset = 0;
+
+       while (len >= offset + sizeof(struct ip_set_req_iptreemap)) {
+               req = (struct ip_set_req_iptreemap *) (data + offset);
+
+               printf("%s", ip_tostring(req->start, options));
+               if (req->start != req->end)
+                       printf(":%s", ip_tostring(req->end, options));
+               printf("\n");
+
+               offset += sizeof(struct ip_set_req_iptreemap);
+       }
+}
+
+void
+saveheader(struct set *set, unsigned int options)
+{
+       struct ip_set_iptreemap *mysetdata = (struct ip_set_iptreemap *) set->settype->header;
+
+       printf("-N %s %s", set->name, set->settype->typename);
+
+       if (mysetdata->gc_interval)
+               printf(" --gc %u", mysetdata->gc_interval);
+
+       printf("\n");
+}
+
+void
+saveips(struct set *set, void *data, size_t len, unsigned int options)
+{
+       struct ip_set_req_iptreemap *req;
+       size_t offset = 0;
+
+       while (len >= offset + sizeof(struct ip_set_req_iptreemap)) {
+               req = (struct ip_set_req_iptreemap *) (data + offset);
+
+               printf("-A %s %s", set->name, ip_tostring(req->start, options));
+
+               if (req->start != req->end)
+                       printf(":%s", ip_tostring(req->end, options));
+
+               printf("\n");
+
+               offset += sizeof(struct ip_set_req_iptreemap);
+       }
+}
+
+void
+usage(void)
+{
+       printf(
+               "-N set iptreemap --gc interval\n"
+               "-A set IP\n"
+               "-D set IP\n"
+               "-T set IP\n"
+       );
+}
+
+static struct settype settype_iptreemap = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       .create_size = sizeof(struct ip_set_req_iptreemap_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       .adt_size = sizeof(struct ip_set_req_iptreemap),
+       .adt_parser = &adt_parser,
+
+       .header_size = sizeof(struct ip_set_iptreemap),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printips_sorted,
+       .printips_sorted = &printips_sorted,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+
+       .bindip_tostring = &binding_ip_tostring,
+       .bindip_parse = &parse_ip,
+
+       .usage = &usage,
+};
+
+void
+_init(void)
+{
+       settype_register(&settype_iptreemap);
+}
diff --git a/ipset/ipset_macipmap.c b/ipset/ipset_macipmap.c
new file mode 100644 (file)
index 0000000..3385f19
--- /dev/null
@@ -0,0 +1,339 @@
+/* Copyright 2000, 2001, 2002 Joakim Axelsson (gozem@linux.nu)
+ *                            Patrick Schaaf (bof@bof.de)
+ *                            Martin Josefsson (gandalf@wlug.westbo.se)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <linux/if_ether.h>
+
+#include <linux/netfilter_ipv4/ip_set_macipmap.h>
+#include "ipset.h"
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_FROM    0x01U
+#define OPT_CREATE_TO      0x02U
+#define OPT_CREATE_NETWORK 0x04U
+#define OPT_CREATE_MATCHUNSET  0x08U
+
+#define OPT_ADDDEL_IP      0x01U
+#define OPT_ADDDEL_MAC     0x02U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       DP("create INIT");
+       /* Nothing */
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_macipmap_create *mydata =
+           (struct ip_set_req_macipmap_create *) data;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+               parse_ip(optarg, &mydata->from);
+
+               *flags |= OPT_CREATE_FROM;
+
+               DP("--from %x (%s)", mydata->from,
+                  ip_tostring_numeric(mydata->from));
+
+               break;
+
+       case '2':
+               parse_ip(optarg, &mydata->to);
+
+               *flags |= OPT_CREATE_TO;
+
+               DP("--to %x (%s)", mydata->to,
+                  ip_tostring_numeric(mydata->to));
+
+               break;
+
+       case '3':
+               parse_ipandmask(optarg, &mydata->from, &mydata->to);
+
+               /* Make to the last of from + mask */
+               mydata->to = mydata->from | (~mydata->to);
+
+               *flags |= OPT_CREATE_NETWORK;
+
+               DP("--network from %x (%s)", 
+                  mydata->from, ip_tostring_numeric(mydata->from));
+               DP("--network to %x (%s)", 
+                  mydata->to, ip_tostring_numeric(mydata->to));
+
+               break;
+
+       case '4':
+               mydata->flags |= IPSET_MACIP_MATCHUNSET;
+
+               *flags |= OPT_CREATE_MATCHUNSET;
+
+               DP("--matchunset");
+
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+       struct ip_set_req_macipmap_create *mydata =
+           (struct ip_set_req_macipmap_create *) data;
+
+       if (flags == 0)
+               exit_error(PARAMETER_PROBLEM,
+                          "Need to specify --from and --to, or --network\n");
+
+       if (flags & OPT_CREATE_NETWORK) {
+               /* --network */
+               if ((flags & OPT_CREATE_FROM) || (flags & OPT_CREATE_TO))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --from or --to with --network\n");
+       } else {
+               /* --from --to */
+               if ((flags & OPT_CREATE_FROM) == 0
+                   || (flags & OPT_CREATE_TO) == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need to specify both --from and --to\n");
+       }
+
+
+       DP("from : %x to: %x  diff: %d  match unset: %d", mydata->from,
+          mydata->to, mydata->to - mydata->from,
+          flags & OPT_CREATE_MATCHUNSET);
+
+       if (mydata->from > mydata->to)
+               exit_error(PARAMETER_PROBLEM,
+                          "From can't be lower than to.\n");
+
+       if (mydata->to - mydata->from > MAX_RANGE)
+               exit_error(PARAMETER_PROBLEM,
+                          "Range too large. Max is %d IPs in range\n",
+                          MAX_RANGE+1);
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"from", 1, 0, '1'},
+       {"to", 1, 0, '2'},
+       {"network", 1, 0, '3'},
+       {"matchunset", 0, 0, '4'},
+       {0}
+};
+
+static void parse_mac(const char *mac, unsigned char *ethernet)
+{
+       unsigned int i = 0;
+
+       if (strlen(mac) != ETH_ALEN * 3 - 1)
+               exit_error(PARAMETER_PROBLEM, "Bad mac address `%s'", mac);
+
+       for (i = 0; i < ETH_ALEN; i++) {
+               long number;
+               char *end;
+
+               number = strtol(mac + i * 3, &end, 16);
+
+               if (end == mac + i * 3 + 2 && number >= 0 && number <= 255)
+                       ethernet[i] = number;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Bad mac address `%s'", mac);
+       }
+}
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_macipmap *mydata =
+           (struct ip_set_req_macipmap *) data;
+       char *saved = ipset_strdup(optarg);
+       char *ptr, *tmp = saved;
+
+       DP("macipmap: %p %p", optarg, data);
+
+       ptr = strsep(&tmp, ":%");
+       parse_ip(ptr, &mydata->ip);
+
+       if (tmp)
+               parse_mac(tmp, mydata->ethernet);
+       else
+               memset(mydata->ethernet, 0, ETH_ALEN);  
+
+       free(saved);
+       return 1;       
+}
+
+/*
+ * Print and save
+ */
+
+void initheader(struct set *set, const void *data)
+{
+       struct ip_set_req_macipmap_create *header =
+           (struct ip_set_req_macipmap_create *) data;
+       struct ip_set_macipmap *map =
+               (struct ip_set_macipmap *) set->settype->header;
+
+       memset(map, 0, sizeof(struct ip_set_macipmap));
+       map->first_ip = header->from;
+       map->last_ip = header->to;
+       map->flags = header->flags;
+}
+
+void printheader(struct set *set, unsigned options)
+{
+       struct ip_set_macipmap *mysetdata =
+           (struct ip_set_macipmap *) set->settype->header;
+
+       printf(" from: %s", ip_tostring(mysetdata->first_ip, options));
+       printf(" to: %s", ip_tostring(mysetdata->last_ip, options));
+
+       if (mysetdata->flags & IPSET_MACIP_MATCHUNSET)
+               printf(" matchunset");
+       printf("\n");
+}
+
+static void print_mac(unsigned char macaddress[ETH_ALEN])
+{
+       unsigned int i;
+
+       printf("%02X", macaddress[0]);
+       for (i = 1; i < ETH_ALEN; i++)
+               printf(":%02X", macaddress[i]);
+}
+
+void printips_sorted(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_macipmap *mysetdata =
+           (struct ip_set_macipmap *) set->settype->header;
+       struct ip_set_macip *table =
+           (struct ip_set_macip *) data;
+       u_int32_t addr = mysetdata->first_ip;
+
+       while (addr <= mysetdata->last_ip) {
+               if (test_bit(IPSET_MACIP_ISSET,
+                            (void *)&table[addr - mysetdata->first_ip].flags)) {
+                       printf("%s:", ip_tostring(addr, options));
+                       print_mac(table[addr - mysetdata->first_ip].
+                                 ethernet);
+                       printf("\n");
+               }
+               addr++;
+       }
+}
+
+void saveheader(struct set *set, unsigned options)
+{
+       struct ip_set_macipmap *mysetdata =
+           (struct ip_set_macipmap *) set->settype->header;
+
+       printf("-N %s %s --from %s",
+              set->name, set->settype->typename,
+              ip_tostring(mysetdata->first_ip, options));
+       printf(" --to %s", ip_tostring(mysetdata->last_ip, options));
+
+       if (mysetdata->flags & IPSET_MACIP_MATCHUNSET)
+               printf(" --matchunset");
+       printf("\n");
+}
+
+void saveips(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_macipmap *mysetdata =
+           (struct ip_set_macipmap *) set->settype->header;
+       struct ip_set_macip *table =
+           (struct ip_set_macip *) data;
+       u_int32_t addr = mysetdata->first_ip;
+
+       while (addr <= mysetdata->last_ip) {
+               if (test_bit(IPSET_MACIP_ISSET,
+                            (void *)&table[addr - mysetdata->first_ip].flags)) {
+                       printf("-A %s %s:",
+                              set->name, ip_tostring(addr, options));
+                       print_mac(table[addr - mysetdata->first_ip].
+                                 ethernet);
+                       printf("\n");
+               }
+               addr++;
+       }
+}
+
+void usage(void)
+{
+       printf
+           ("-N set macipmap --from IP --to IP [--matchunset]\n"
+            "-N set macipmap --network IP/mask [--matchunset]\n"
+            "-A set IP:MAC\n"
+            "-D set IP[:MAC]\n"
+            "-T set IP[:MAC]\n");
+}
+
+static struct settype settype_macipmap = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_macipmap_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .adt_size = sizeof(struct ip_set_req_macipmap),
+       .adt_parser = &adt_parser,
+
+       /* Printing */
+       .header_size = sizeof(struct ip_set_macipmap),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printips_sorted,   /* We only have sorted version */
+       .printips_sorted = &printips_sorted,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+
+       /* Bindings */
+       .bindip_tostring = &binding_ip_tostring,
+       .bindip_parse = &parse_ip,
+
+       .usage = &usage,
+};
+
+void _init(void)
+{
+       settype_register(&settype_macipmap);
+
+}
diff --git a/ipset/ipset_nethash.c b/ipset/ipset_nethash.c
new file mode 100644 (file)
index 0000000..8744368
--- /dev/null
@@ -0,0 +1,366 @@
+/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <asm/types.h>
+#include <limits.h>
+
+#include <linux/netfilter_ipv4/ip_set_nethash.h>
+#include <linux/netfilter_ipv4/ip_set_jhash.h>
+
+#include "ipset.h"
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_HASHSIZE    0x01U
+#define OPT_CREATE_PROBES      0x02U
+#define OPT_CREATE_RESIZE      0x04U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       struct ip_set_req_nethash_create *mydata =
+           (struct ip_set_req_nethash_create *) data;
+
+       DP("create INIT");
+
+       /* Default create parameters */ 
+       mydata->hashsize = 1024;
+       mydata->probes = 4;
+       mydata->resize = 50;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_nethash_create *mydata =
+           (struct ip_set_req_nethash_create *) data;
+       ip_set_ip_t value;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+
+               if (string_to_number(optarg, 1, UINT_MAX - 1, &mydata->hashsize))
+                       exit_error(PARAMETER_PROBLEM, "Invalid hashsize `%s' specified", optarg);
+
+               *flags |= OPT_CREATE_HASHSIZE;
+
+               DP("--hashsize %u", mydata->hashsize);
+               
+               break;
+
+       case '2':
+
+               if (string_to_number(optarg, 1, 65535, &value))
+                       exit_error(PARAMETER_PROBLEM, "Invalid probes `%s' specified", optarg);
+
+               mydata->probes = value;
+               *flags |= OPT_CREATE_PROBES;
+
+               DP("--probes %u", mydata->probes);
+               
+               break;
+
+       case '3':
+
+               if (string_to_number(optarg, 0, 65535, &value))
+                       exit_error(PARAMETER_PROBLEM, "Invalid resize `%s' specified", optarg);
+
+               mydata->resize = value;
+               *flags |= OPT_CREATE_RESIZE;
+
+               DP("--resize %u", mydata->resize);
+               
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+#ifdef IPSET_DEBUG
+       struct ip_set_req_nethash_create *mydata =
+           (struct ip_set_req_nethash_create *) data;
+
+       DP("hashsize %u probes %u resize %u",
+          mydata->hashsize, mydata->probes, mydata->resize);
+#endif
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"hashsize", 1, 0, '1'},
+       {"probes", 1, 0, '2'},
+       {"resize", 1, 0, '3'},
+       {0}
+};
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_nethash *mydata =
+           (struct ip_set_req_nethash *) data;
+       char *saved = ipset_strdup(optarg);
+       char *ptr, *tmp = saved;
+       ip_set_ip_t cidr;
+
+       ptr = strsep(&tmp, "/");
+       
+       if (tmp == NULL) {
+               if (cmd == CMD_TEST)
+                       cidr = 32;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Missing cidr from `%s'", optarg);
+       } else
+               if (string_to_number(tmp, 1, 31, &cidr))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Out of range cidr `%s' specified", optarg);
+       
+       mydata->cidr = cidr;
+       parse_ip(ptr, &mydata->ip);
+       if (!mydata->ip)
+               exit_error(PARAMETER_PROBLEM,
+                         "Zero valued IP address `%s' specified", ptr);
+       free(saved);
+
+       return mydata->ip;      
+};
+
+/*
+ * Print and save
+ */
+
+void initheader(struct set *set, const void *data)
+{
+       struct ip_set_req_nethash_create *header =
+           (struct ip_set_req_nethash_create *) data;
+       struct ip_set_nethash *map =
+               (struct ip_set_nethash *) set->settype->header;
+
+       memset(map, 0, sizeof(struct ip_set_nethash));
+       map->hashsize = header->hashsize;
+       map->probes = header->probes;
+       map->resize = header->resize;
+}
+
+unsigned int
+mask_to_bits(ip_set_ip_t mask)
+{
+       unsigned int bits = 32;
+       ip_set_ip_t maskaddr;
+       
+       if (mask == 0xFFFFFFFF)
+               return bits;
+       
+       maskaddr = 0xFFFFFFFE;
+       while (--bits >= 0 && maskaddr != mask)
+               maskaddr <<= 1;
+       
+       return bits;
+}
+       
+void printheader(struct set *set, unsigned options)
+{
+       struct ip_set_nethash *mysetdata =
+           (struct ip_set_nethash *) set->settype->header;
+
+       printf(" hashsize: %u", mysetdata->hashsize);
+       printf(" probes: %u", mysetdata->probes);
+       printf(" resize: %u\n", mysetdata->resize);
+}
+
+static char buf[20];
+
+static char * unpack_ip_tostring(ip_set_ip_t ip, unsigned options)
+{
+       int i, j = 3;
+       unsigned char a, b;
+
+       ip = htonl(ip); 
+       for (i = 3; i >= 0; i--)
+               if (((unsigned char *)&ip)[i] != 0) {
+                       j = i;
+                       break;
+               }
+                       
+       a = ((unsigned char *)&ip)[j];
+       if (a <= 128) {
+               a = (a - 1) * 2;
+               b = 7;
+       } else if (a <= 192) {
+               a = (a - 129) * 4;
+               b = 6;
+       } else if (a <= 224) {
+               a = (a - 193) * 8;
+               b = 5;
+       } else if (a <= 240) {
+               a = (a - 225) * 16;
+               b = 4;
+       } else if (a <= 248) {
+               a = (a - 241) * 32;
+               b = 3;
+       } else if (a <= 252) {
+               a = (a - 249) * 64;
+               b = 2;
+       } else if (a <= 254) {
+               a = (a - 253) * 128;
+               b = 1;
+       } else {
+               a = b = 0;
+       }
+       ((unsigned char *)&ip)[j] = a;
+       b += j * 8;
+       
+       sprintf(buf, "%u.%u.%u.%u/%u",
+               ((unsigned char *)&ip)[0],
+               ((unsigned char *)&ip)[1],
+               ((unsigned char *)&ip)[2],
+               ((unsigned char *)&ip)[3],
+               b);
+
+       DP("%s %s", ip_tostring(ntohl(ip), options), buf);
+       return buf;
+}
+
+void printips(struct set *set, void *data, size_t len, unsigned options)
+{
+       size_t offset = 0;
+       ip_set_ip_t *ip;
+
+       while (offset < len) {
+               ip = data + offset;
+               if (*ip)
+                       printf("%s\n", unpack_ip_tostring(*ip, options));
+               offset += sizeof(ip_set_ip_t);
+       }
+}
+
+void saveheader(struct set *set, unsigned options)
+{
+       struct ip_set_nethash *mysetdata =
+           (struct ip_set_nethash *) set->settype->header;
+
+       printf("-N %s %s --hashsize %u --probes %u --resize %u\n",
+              set->name, set->settype->typename,
+              mysetdata->hashsize, mysetdata->probes, mysetdata->resize);
+}
+
+/* Print save for an IP */
+void saveips(struct set *set, void *data, size_t len, unsigned options)
+{
+       size_t offset = 0;
+       ip_set_ip_t *ip;
+
+       while (offset < len) {
+               ip = data + offset;
+               if (*ip)
+                       printf("-A %s %s\n", set->name, 
+                              unpack_ip_tostring(*ip, options));
+               offset += sizeof(ip_set_ip_t);
+       }
+}
+
+static char * net_tostring(struct set *set, ip_set_ip_t ip, unsigned options)
+{
+       return unpack_ip_tostring(ip, options);
+}
+
+static void parse_net(const char *str, ip_set_ip_t *ip)
+{
+       char *saved = strdup(str);
+       char *ptr, *tmp = saved;
+       ip_set_ip_t cidr;
+
+       ptr = strsep(&tmp, "/");
+       
+       if (tmp == NULL)
+               exit_error(PARAMETER_PROBLEM,
+                          "Missing cidr from `%s'", str);
+
+       if (string_to_number(tmp, 1, 31, &cidr))
+               exit_error(PARAMETER_PROBLEM,
+                          "Out of range cidr `%s' specified", str);
+       
+       parse_ip(ptr, ip);
+       free(saved);
+       
+       *ip = pack(*ip, cidr);
+}
+
+void usage(void)
+{
+       printf
+           ("-N set nethash [--hashsize hashsize] [--probes probes ]\n"
+            "               [--resize resize]\n"
+            "-A set IP/cidr\n"
+            "-D set IP/cidr\n"
+            "-T set IP/cidr\n");
+}
+
+static struct settype settype_nethash = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_nethash_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .adt_size = sizeof(struct ip_set_req_nethash),
+       .adt_parser = &adt_parser,
+
+       /* Printing */
+       .header_size = sizeof(struct ip_set_nethash),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printips,          /* We only have the unsorted version */
+       .printips_sorted = &printips,
+       .saveheader = &saveheader,
+       .saveips = &saveips,
+       
+       /* Bindings */
+       .bindip_tostring = &net_tostring,
+       .bindip_parse = &parse_net,
+
+       .usage = &usage,
+};
+
+void _init(void)
+{
+       settype_register(&settype_nethash);
+
+}
diff --git a/ipset/ipset_portmap.c b/ipset/ipset_portmap.c
new file mode 100644 (file)
index 0000000..1d4f807
--- /dev/null
@@ -0,0 +1,244 @@
+/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify   
+ * it under the terms of the GNU General Public License as published by   
+ * the Free Software Foundation; either version 2 of the License, or      
+ * (at your option) any later version.                                    
+ *                                                                         
+ * This program is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU General Public License for more details.                           
+ *                                                                         
+ * You should have received a copy of the GNU General Public License      
+ * along with this program; if not, write to the Free Software            
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <linux/netfilter_ipv4/ip_set_portmap.h>
+#include "ipset.h"
+
+
+#define BUFLEN 30;
+
+#define OPT_CREATE_FROM    0x01U
+#define OPT_CREATE_TO      0x02U
+
+#define OPT_ADDDEL_PORT      0x01U
+
+/* Initialize the create. */
+void create_init(void *data)
+{
+       DP("create INIT");
+       /* Nothing */
+}
+
+/* Function which parses command options; returns true if it ate an option */
+int create_parse(int c, char *argv[], void *data, unsigned *flags)
+{
+       struct ip_set_req_portmap_create *mydata =
+           (struct ip_set_req_portmap_create *) data;
+
+       DP("create_parse");
+
+       switch (c) {
+       case '1':
+               parse_port(optarg, &mydata->from);
+
+               *flags |= OPT_CREATE_FROM;
+
+               DP("--from %x (%s)", mydata->from,
+                  port_tostring(mydata->from, 0));
+
+               break;
+
+       case '2':
+               parse_port(optarg, &mydata->to);
+
+               *flags |= OPT_CREATE_TO;
+
+               DP("--to %x (%s)", mydata->to,
+                  port_tostring(mydata->to, 0));
+
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; exit if not ok. */
+void create_final(void *data, unsigned int flags)
+{
+       struct ip_set_req_portmap_create *mydata =
+           (struct ip_set_req_portmap_create *) data;
+
+       if (flags == 0) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Need to specify --from and --to\n");
+       } else {
+               /* --from --to */
+               if ((flags & OPT_CREATE_FROM) == 0
+                   || (flags & OPT_CREATE_TO) == 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Need to specify both --from and --to\n");
+       }
+
+       DP("from : %x to: %x  diff: %d", mydata->from, mydata->to,
+          mydata->to - mydata->from);
+
+       if (mydata->from > mydata->to)
+               exit_error(PARAMETER_PROBLEM,
+                          "From can't be lower than to.\n");
+
+       if (mydata->to - mydata->from > MAX_RANGE)
+               exit_error(PARAMETER_PROBLEM,
+                          "Range too large. Max is %d ports in range\n",
+                          MAX_RANGE+1);
+}
+
+/* Create commandline options */
+static struct option create_opts[] = {
+       {"from", 1, 0, '1'},
+       {"to", 1, 0, '2'},
+       {0}
+};
+
+/* Add, del, test parser */
+ip_set_ip_t adt_parser(unsigned cmd, const char *optarg, void *data)
+{
+       struct ip_set_req_portmap *mydata =
+           (struct ip_set_req_portmap *) data;
+
+       parse_port(optarg, &mydata->port);
+       DP("%s", port_tostring(mydata->port, 0));
+
+       return 1;       
+}
+
+/*
+ * Print and save
+ */
+
+void initheader(struct set *set, const void *data)
+{
+       struct ip_set_req_portmap_create *header =
+           (struct ip_set_req_portmap_create *) data;
+       struct ip_set_portmap *map =
+               (struct ip_set_portmap *) set->settype->header;
+
+       memset(map, 0, sizeof(struct ip_set_portmap));
+       map->first_port = header->from;
+       map->last_port = header->to;
+}
+
+void printheader(struct set *set, unsigned options)
+{
+       struct ip_set_portmap *mysetdata =
+           (struct ip_set_portmap *) set->settype->header;
+
+       printf(" from: %s", port_tostring(mysetdata->first_port, options));
+       printf(" to: %s\n", port_tostring(mysetdata->last_port, options));
+}
+
+void printports_sorted(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_portmap *mysetdata =
+           (struct ip_set_portmap *) set->settype->header;
+       u_int32_t addr = mysetdata->first_port;
+
+       DP("%u -- %u", mysetdata->first_port, mysetdata->last_port);
+       while (addr <= mysetdata->last_port) {
+               if (test_bit(addr - mysetdata->first_port, data))
+                       printf("%s\n", port_tostring(addr, options));
+               addr++;
+       }
+}
+
+char * binding_port_tostring(struct set *set, ip_set_ip_t ip, unsigned options)
+{
+       return port_tostring(ip, options);
+}
+
+void saveheader(struct set *set, unsigned options)
+{
+       struct ip_set_portmap *mysetdata =
+           (struct ip_set_portmap *) set->settype->header;
+
+       printf("-N %s %s --from %s", 
+              set->name,
+              set->settype->typename,
+              port_tostring(mysetdata->first_port, options));
+       printf(" --to %s\n", 
+              port_tostring(mysetdata->last_port, options));
+}
+
+void saveports(struct set *set, void *data, size_t len, unsigned options)
+{
+       struct ip_set_portmap *mysetdata =
+           (struct ip_set_portmap *) set->settype->header;
+       u_int32_t addr = mysetdata->first_port;
+
+       while (addr <= mysetdata->last_port) {
+               if (test_bit(addr - mysetdata->first_port, data))
+                       printf("-A %s %s\n",
+                              set->name,
+                              port_tostring(addr, options));
+               addr++;
+       }
+}
+
+void usage(void)
+{
+       printf
+           ("-N set portmap --from PORT --to PORT\n"
+            "-A set PORT\n"
+            "-D set PORT\n"
+            "-T set PORT\n");
+}
+
+static struct settype settype_portmap = {
+       .typename = SETTYPE_NAME,
+       .protocol_version = IP_SET_PROTOCOL_VERSION,
+
+       /* Create */
+       .create_size = sizeof(struct ip_set_req_portmap_create),
+       .create_init = &create_init,
+       .create_parse = &create_parse,
+       .create_final = &create_final,
+       .create_opts = create_opts,
+
+       /* Add/del/test */
+       .adt_size = sizeof(struct ip_set_req_portmap),
+       .adt_parser = &adt_parser,
+
+       /* Printing */
+       .header_size = sizeof(struct ip_set_portmap),
+       .initheader = &initheader,
+       .printheader = &printheader,
+       .printips = &printports_sorted, /* We only have sorted version */
+       .printips_sorted = &printports_sorted,
+       .saveheader = &saveheader,
+       .saveips = &saveports,
+       
+       /* Bindings */
+       .bindip_tostring = &binding_port_tostring,
+       .bindip_parse = &parse_port,
+
+       .usage = &usage,
+};
+
+void _init(void)
+{
+       settype_register(&settype_portmap);
+
+}
diff --git a/ipset/libipt_set.h b/ipset/libipt_set.h
new file mode 100644 (file)
index 0000000..0521251
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef _LIBIPT_SET_H
+#define _LIBIPT_SET_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+static int get_set_getsockopt(void *data, size_t * size)
+{
+       int sockfd = -1;
+       sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0)
+               exit_error(OTHER_PROBLEM,
+                          "Can't open socket to ipset.\n");
+       /* Send! */
+       return getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
+}
+
+static void
+parse_bindings(const char *optarg, struct ipt_set_info *info)
+{
+       char *saved = strdup(optarg);
+       char *ptr, *tmp = saved;
+       int i = 0;
+       
+       while (i < IP_SET_MAX_BINDINGS && tmp != NULL) {
+               ptr = strsep(&tmp, ",");
+               if (strncmp(ptr, "src", 3) == 0)
+                       info->flags[i++] |= IPSET_SRC;
+               else if (strncmp(ptr, "dst", 3) == 0)
+                       info->flags[i++] |= IPSET_DST;
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "You must spefify (the comma separated list of) 'src' or 'dst'.");
+       }
+
+       if (tmp)
+               exit_error(PARAMETER_PROBLEM,
+                          "Can't follow bindings deeper than %i.", 
+                          IP_SET_MAX_BINDINGS);
+
+       free(saved);
+}
+
+#endif /*_LIBIPT_SET_H*/
diff --git a/iptables-config b/iptables-config
new file mode 100644 (file)
index 0000000..c7d81cf
--- /dev/null
@@ -0,0 +1,37 @@
+# Load additional iptables modules (nat helpers)
+#   Default: -none-
+# Space separated list of nat helpers (e.g. 'ip_nat_ftp ip_nat_irc'), which
+# are loaded after the firewall rules are applied. Options for the helpers are
+# stored in /etc/modules.conf.
+IPTABLES_MODULES="ip_conntrack vnet_tun"
+
+# Unload modules on restart and stop
+#   Value: yes|no,  default: yes
+# This option has to be 'yes' to get to a sane state for a firewall
+# restart or stop. Only set to 'no' if there are problems unloading netfilter
+# modules.
+#IPTABLES_MODULES_UNLOAD="yes"
+
+# Save current firewall rules on stop.
+#   Value: yes|no,  default: no
+# Saves all firewall rules to /etc/sysconfig/iptables if firewall gets stopped
+# (e.g. on system shutdown).
+#IPTABLES_SAVE_ON_STOP="no"
+
+# Save current firewall rules on restart.
+#   Value: yes|no,  default: no
+# Saves all firewall rules to /etc/sysconfig/iptables if firewall gets
+# restarted.
+#IPTABLES_SAVE_ON_RESTART="no"
+
+# Save (and restore) rule and chain counter.
+#   Value: yes|no,  default: no
+# Save counters for rules and chains to /etc/sysconfig/iptables if
+# 'service iptables save' is called or on stop or restart if SAVE_ON_STOP or
+# SAVE_ON_RESTART is enabled.
+#IPTABLES_SAVE_COUNTER="no"
+
+# Numeric status output
+#   Value: yes|no,  default: no
+# Print IP addresses and port numbers in numeric format in the status output.
+#IPTABLES_STATUS_NUMERIC="no"
diff --git a/iptables-multi.c b/iptables-multi.c
new file mode 100644 (file)
index 0000000..7ade333
--- /dev/null
@@ -0,0 +1,35 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+
+int iptables_main(int argc, char **argv);
+int iptables_save_main(int argc, char **argv);
+int iptables_restore_main(int argc, char **argv);
+int iptables_xml_main(int argc, char **argv);
+
+int main(int argc, char **argv) {
+  char *progname;
+
+  if (argc == 0) {
+    fprintf(stderr, "no argv[0]?");
+    exit(1);
+  } else {
+    progname = basename(argv[0]);
+
+    if (!strcmp(progname, "iptables"))
+      return iptables_main(argc, argv);
+    
+    if (!strcmp(progname, "iptables-save"))
+      return iptables_save_main(argc, argv);
+    
+    if (!strcmp(progname, "iptables-restore"))
+      return iptables_restore_main(argc, argv);
+    
+    if (!strcmp(progname, "iptables-xml"))
+      return iptables_xml_main(argc, argv);
+    
+    fprintf(stderr, "iptables multi-purpose version: unknown applet name %s\n", progname);
+    exit(1);
+  }
+}
diff --git a/iptables-restore.8 b/iptables-restore.8
new file mode 100644 (file)
index 0000000..e2649e5
--- /dev/null
@@ -0,0 +1,49 @@
+.TH IPTABLES-RESTORE 8 "Jan 04, 2001" "" ""
+.\"
+.\" Man page written by Harald Welte <laforge@gnumonks.org>
+.\" It is based on the iptables man page.
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+iptables-restore \- Restore IP Tables
+.SH SYNOPSIS
+.BR "iptables-restore " "[-c] [-n]"
+.br
+.SH DESCRIPTION
+.PP
+.B iptables-restore
+is used to restore IP Tables from data specified on STDIN. Use 
+I/O redirection provided by your shell to read from a file
+.TP
+\fB\-c\fR, \fB\-\-counters\fR
+restore the values of all packet and byte counters
+.TP
+\fB\-n\fR, \fB\-\-noflush\fR 
+.TP
+don't flush the previous contents of the table. If not specified, 
+.B iptables-restore
+flushes (deletes) all previous contents of the respective IP Table.
+.SH BUGS
+None known as of iptables-1.2.1 release
+.SH AUTHOR
+Harald Welte <laforge@gnumonks.org>
+.SH SEE ALSO
+.BR iptables-save "(8), " iptables "(8) "
+.PP
+The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
+which details NAT, and the netfilter-hacking-HOWTO which details the
+internals.
diff --git a/iptables-restore.c b/iptables-restore.c
new file mode 100644 (file)
index 0000000..1cbade7
--- /dev/null
@@ -0,0 +1,445 @@
+/* Code to restore the iptables state, from file by iptables-save. 
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * based on previous code from Rusty Russell <rusty@linuxcare.com.au>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ *
+ * $Id: iptables-restore.c 6828 2007-05-10 15:00:39Z /C=EU/ST=EU/CN=Patrick McHardy/emailAddress=kaber@trash.net $
+ */
+
+#include <getopt.h>
+#include <sys/errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "iptables.h"
+#include "libiptc/libiptc.h"
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...) 
+#endif
+
+static int binary = 0, counters = 0, verbose = 0, noflush = 0;
+
+/* Keeping track of external matches and targets.  */
+static struct option options[] = {
+       { "binary", 0, 0, 'b' },
+       { "counters", 0, 0, 'c' },
+       { "verbose", 0, 0, 'v' },
+       { "test", 0, 0, 't' },
+       { "help", 0, 0, 'h' },
+       { "noflush", 0, 0, 'n'},
+       { "modprobe", 1, 0, 'M'},
+       { 0 }
+};
+
+static void print_usage(const char *name, const char *version) __attribute__((noreturn));
+
+static void print_usage(const char *name, const char *version)
+{
+       fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
+                       "          [ --binary ]\n"
+                       "          [ --counters ]\n"
+                       "          [ --verbose ]\n"
+                       "          [ --test ]\n"
+                       "          [ --help ]\n"
+                       "          [ --noflush ]\n"
+                       "          [ --modprobe=<command>]\n", name);
+               
+       exit(1);
+}
+
+iptc_handle_t create_handle(const char *tablename, const char* modprobe )
+{
+       iptc_handle_t handle;
+
+       handle = iptc_init(tablename);
+
+       if (!handle) {
+               /* try to insmod the module if iptc_init failed */
+               iptables_insmod("ip_tables", modprobe, 0);
+               handle = iptc_init(tablename);
+       }
+
+       if (!handle) {
+               exit_error(PARAMETER_PROBLEM, "%s: unable to initialize "
+                       "table '%s'\n", program_name, tablename);
+               exit(1);
+       }
+       return handle;
+}
+
+static int parse_counters(char *string, struct ipt_counters *ctr)
+{
+       return (sscanf(string, "[%llu:%llu]", (unsigned long long *)&ctr->pcnt, (unsigned long long *)&ctr->bcnt) == 2);
+}
+
+/* global new argv and argc */
+static char *newargv[255];
+static int newargc;
+
+/* function adding one argument to newargv, updating newargc 
+ * returns true if argument added, false otherwise */
+static int add_argv(char *what) {
+       DEBUGP("add_argv: %s\n", what);
+       if (what && ((newargc + 1) < sizeof(newargv)/sizeof(char *))) {
+               newargv[newargc] = strdup(what);
+               newargc++;
+               return 1;
+       } else 
+               return 0;
+}
+
+static void free_argv(void) {
+       int i;
+
+       for (i = 0; i < newargc; i++)
+               free(newargv[i]);
+}
+
+#ifdef IPTABLES_MULTI
+int
+iptables_restore_main(int argc, char *argv[])
+#else
+int
+main(int argc, char *argv[])
+#endif
+{
+       iptc_handle_t handle = NULL;
+       char buffer[10240];
+       int c;
+       char curtable[IPT_TABLE_MAXNAMELEN + 1];
+       FILE *in;
+       const char *modprobe = 0;
+       int in_table = 0, testing = 0;
+
+       program_name = "iptables-restore";
+       program_version = IPTABLES_VERSION;
+       line = 0;
+
+       lib_dir = getenv("IPTABLES_LIB_DIR");
+       if (!lib_dir)
+               lib_dir = IPT_LIB_DIR;
+
+#ifdef NO_SHARED_LIBS
+       init_extensions();
+#endif
+
+       while ((c = getopt_long(argc, argv, "bcvthnM:", options, NULL)) != -1) {
+               switch (c) {
+                       case 'b':
+                               binary = 1;
+                               break;
+                       case 'c':
+                               counters = 1;
+                               break;
+                       case 'v':
+                               verbose = 1;
+                               break;
+                       case 't':
+                               testing = 1;
+                               break;
+                       case 'h':
+                               print_usage("iptables-restore",
+                                           IPTABLES_VERSION);
+                               break;
+                       case 'n':
+                               noflush = 1;
+                               break;
+                       case 'M':
+                               modprobe = optarg;
+                               break;
+               }
+       }
+       
+       if (optind == argc - 1) {
+               in = fopen(argv[optind], "r");
+               if (!in) {
+                       fprintf(stderr, "Can't open %s: %s\n", argv[optind],
+                               strerror(errno));
+                       exit(1);
+               }
+       }
+       else if (optind < argc) {
+               fprintf(stderr, "Unknown arguments found on commandline\n");
+               exit(1);
+       }
+       else in = stdin;
+       
+       /* Grab standard input. */
+       while (fgets(buffer, sizeof(buffer), in)) {
+               int ret = 0;
+
+               line++;
+               if (buffer[0] == '\n')
+                       continue;
+               else if (buffer[0] == '#') {
+                       if (verbose)
+                               fputs(buffer, stdout);
+                       continue;
+               } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
+                       if (!testing) {
+                               DEBUGP("Calling commit\n");
+                               ret = iptc_commit(&handle);
+                       } else {
+                               DEBUGP("Not calling commit, testing\n");
+                               ret = 1;
+                       }
+                       in_table = 0;
+               } else if ((buffer[0] == '*') && (!in_table)) {
+                       /* New table */
+                       char *table;
+
+                       table = strtok(buffer+1, " \t\n");
+                       DEBUGP("line %u, table '%s'\n", line, table);
+                       if (!table) {
+                               exit_error(PARAMETER_PROBLEM, 
+                                       "%s: line %u table name invalid\n",
+                                       program_name, line);
+                               exit(1);
+                       }
+                       strncpy(curtable, table, IPT_TABLE_MAXNAMELEN);
+                       curtable[IPT_TABLE_MAXNAMELEN] = '\0';
+
+                       if (handle)
+                               iptc_free(&handle);
+
+                       handle = create_handle(table, modprobe);
+                       if (noflush == 0) {
+                               DEBUGP("Cleaning all chains of table '%s'\n",
+                                       table);
+                               for_each_chain(flush_entries, verbose, 1, 
+                                               &handle);
+       
+                               DEBUGP("Deleting all user-defined chains "
+                                      "of table '%s'\n", table);
+                               for_each_chain(delete_chain, verbose, 0, 
+                                               &handle) ;
+                       }
+
+                       ret = 1;
+                       in_table = 1;
+
+               } else if ((buffer[0] == ':') && (in_table)) {
+                       /* New chain. */
+                       char *policy, *chain;
+
+                       chain = strtok(buffer+1, " \t\n");
+                       DEBUGP("line %u, chain '%s'\n", line, chain);
+                       if (!chain) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u chain name invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+
+                       if (iptc_builtin(chain, handle) <= 0) {
+                               if (noflush && iptc_is_chain(chain, handle)) {
+                                       DEBUGP("Flushing existing user defined chain '%s'\n", chain);
+                                       if (!iptc_flush_entries(chain, &handle))
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "error flushing chain "
+                                                          "'%s':%s\n", chain,
+                                                          strerror(errno));
+                               } else {
+                                       DEBUGP("Creating new chain '%s'\n", chain);
+                                       if (!iptc_create_chain(chain, &handle))
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "error creating chain "
+                                                          "'%s':%s\n", chain,
+                                                          strerror(errno));
+                               }
+                       }
+
+                       policy = strtok(NULL, " \t\n");
+                       DEBUGP("line %u, policy '%s'\n", line, policy);
+                       if (!policy) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u policy invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+
+                       if (strcmp(policy, "-") != 0) {
+                               struct ipt_counters count;
+
+                               if (counters) {
+                                       char *ctrs;
+                                       ctrs = strtok(NULL, " \t\n");
+
+                                       if (!ctrs || !parse_counters(ctrs, &count))
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "invalid policy counters "
+                                                          "for chain '%s'\n", chain);
+
+                               } else {
+                                       memset(&count, 0, 
+                                              sizeof(struct ipt_counters));
+                               }
+
+                               DEBUGP("Setting policy of chain %s to %s\n",
+                                       chain, policy);
+
+                               if (!iptc_set_policy(chain, policy, &count,
+                                                    &handle))
+                                       exit_error(OTHER_PROBLEM,
+                                               "Can't set policy `%s'"
+                                               " on `%s' line %u: %s\n",
+                                               chain, policy, line,
+                                               iptc_strerror(errno));
+                       }
+
+                       ret = 1;
+
+               } else if (in_table) {
+                       int a;
+                       char *ptr = buffer;
+                       char *pcnt = NULL;
+                       char *bcnt = NULL;
+                       char *parsestart;
+
+                       /* the parser */
+                       char *curchar;
+                       int quote_open;
+                       int param_len;
+
+                       /* reset the newargv */
+                       newargc = 0;
+
+                       if (buffer[0] == '[') {
+                               /* we have counters in our input */
+                               ptr = strchr(buffer, ']');
+                               if (!ptr)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need ]\n",
+                                                  line);
+
+                               pcnt = strtok(buffer+1, ":");
+                               if (!pcnt)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need :\n",
+                                                  line);
+
+                               bcnt = strtok(NULL, "]");
+                               if (!bcnt)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need ]\n",
+                                                  line);
+
+                               /* start command parsing after counter */
+                               parsestart = ptr + 1;
+                       } else {
+                               /* start command parsing at start of line */
+                               parsestart = buffer;
+                       }
+
+                       add_argv(argv[0]);
+                       add_argv("-t");
+                       add_argv((char *) &curtable);
+                       
+                       if (counters && pcnt && bcnt) {
+                               add_argv("--set-counters");
+                               add_argv((char *) pcnt);
+                               add_argv((char *) bcnt);
+                       }
+
+                       /* After fighting with strtok enough, here's now
+                        * a 'real' parser. According to Rusty I'm now no
+                        * longer a real hacker, but I can live with that */
+
+                       quote_open = 0;
+                       param_len = 0;
+                       
+                       for (curchar = parsestart; *curchar; curchar++) {
+                               char param_buffer[1024];
+
+                               if (*curchar == '"') {
+                                       /* quote_open cannot be true if there
+                                        * was no previous character.  Thus, 
+                                        * curchar-1 has to be within bounds */
+                                       if (quote_open && 
+                                           *(curchar-1) != '\\') {
+                                               quote_open = 0;
+                                               *curchar = ' ';
+                                       } else if (!quote_open) {
+                                               quote_open = 1;
+                                               continue;
+                                       }
+                               } 
+                               if (*curchar == ' '
+                                   || *curchar == '\t'
+                                   || * curchar == '\n') {
+
+                                       if (quote_open) {
+                                               param_buffer[param_len++] = 
+                                                               *curchar;
+                                               continue;
+                                       }
+
+                                       if (!param_len) {
+                                               /* two spaces? */
+                                               continue;
+                                       }
+
+                                       param_buffer[param_len] = '\0';
+
+                                       /* check if table name specified */
+                                       if (!strncmp(param_buffer, "-t", 3)
+                                            || !strncmp(param_buffer, "--table", 8)) {
+                                               exit_error(PARAMETER_PROBLEM, 
+                                                  "Line %u seems to have a "
+                                                  "-t table option.\n", line);
+                                               exit(1);
+                                       }
+
+                                       add_argv(param_buffer);
+                                       param_len = 0;
+                               } else {
+                                       /* Skip backslash that escapes quote: 
+                                        * the standard input does not require
+                                        * escaping. However, the output
+                                        * generated by iptables-save
+                                        * introduces bashlash to keep
+                                        * consistent with iptables
+                                        */
+                                       if (quote_open &&
+                                           *curchar == '\\' &&
+                                           *(curchar+1) == '"')
+                                               continue;
+
+                                       /* regular character, copy to buffer */
+                                       param_buffer[param_len++] = *curchar;
+
+                                       if (param_len >= sizeof(param_buffer))
+                                               exit_error(PARAMETER_PROBLEM, 
+                                                  "Parameter too long!");
+                               }
+                       }
+
+                       DEBUGP("calling do_command(%u, argv, &%s, handle):\n",
+                               newargc, curtable);
+
+                       for (a = 0; a < newargc; a++)
+                               DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+
+                       ret = do_command(newargc, newargv, 
+                                        &newargv[2], &handle);
+
+                       free_argv();
+               }
+               if (!ret) {
+                       fprintf(stderr, "%s: line %u failed\n",
+                                       program_name, line);
+                       exit(1);
+               }
+       }
+       if (in_table) {
+               fprintf(stderr, "%s: COMMIT expected at line %u\n",
+                               program_name, line + 1);
+               exit(1);
+       }
+
+       return 0;
+}
diff --git a/iptables-restore.c.orig b/iptables-restore.c.orig
new file mode 100644 (file)
index 0000000..7cde347
--- /dev/null
@@ -0,0 +1,428 @@
+/* Code to restore the iptables state, from file by iptables-save. 
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * based on previous code from Rusty Russell <rusty@linuxcare.com.au>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ *
+ * $Id: iptables-restore.c 6706 2006-12-09 13:06:04Z /C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=yasuyuki/emailAddress=yasuyuki@netfilter.org $
+ */
+
+#include <getopt.h>
+#include <sys/errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "iptables.h"
+#include "libiptc/libiptc.h"
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...) 
+#endif
+
+static int binary = 0, counters = 0, verbose = 0, noflush = 0;
+
+/* Keeping track of external matches and targets.  */
+static struct option options[] = {
+       { "binary", 0, 0, 'b' },
+       { "counters", 0, 0, 'c' },
+       { "verbose", 0, 0, 'v' },
+       { "test", 0, 0, 't' },
+       { "help", 0, 0, 'h' },
+       { "noflush", 0, 0, 'n'},
+       { "modprobe", 1, 0, 'M'},
+       { 0 }
+};
+
+static void print_usage(const char *name, const char *version) __attribute__((noreturn));
+
+static void print_usage(const char *name, const char *version)
+{
+       fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
+                       "          [ --binary ]\n"
+                       "          [ --counters ]\n"
+                       "          [ --verbose ]\n"
+                       "          [ --test ]\n"
+                       "          [ --help ]\n"
+                       "          [ --noflush ]\n"
+                       "          [ --modprobe=<command>]\n", name);
+               
+       exit(1);
+}
+
+iptc_handle_t create_handle(const char *tablename, const char* modprobe )
+{
+       iptc_handle_t handle;
+
+       handle = iptc_init(tablename);
+
+       if (!handle) {
+               /* try to insmod the module if iptc_init failed */
+               iptables_insmod("ip_tables", modprobe);
+               handle = iptc_init(tablename);
+       }
+
+       if (!handle) {
+               exit_error(PARAMETER_PROBLEM, "%s: unable to initialize"
+                       "table '%s'\n", program_name, tablename);
+               exit(1);
+       }
+       return handle;
+}
+
+static int parse_counters(char *string, struct ipt_counters *ctr)
+{
+       return (sscanf(string, "[%llu:%llu]", (unsigned long long *)&ctr->pcnt, (unsigned long long *)&ctr->bcnt) == 2);
+}
+
+/* global new argv and argc */
+static char *newargv[255];
+static int newargc;
+
+/* function adding one argument to newargv, updating newargc 
+ * returns true if argument added, false otherwise */
+static int add_argv(char *what) {
+       DEBUGP("add_argv: %s\n", what);
+       if (what && ((newargc + 1) < sizeof(newargv)/sizeof(char *))) {
+               newargv[newargc] = strdup(what);
+               newargc++;
+               return 1;
+       } else 
+               return 0;
+}
+
+static void free_argv(void) {
+       int i;
+
+       for (i = 0; i < newargc; i++)
+               free(newargv[i]);
+}
+
+#ifdef IPTABLES_MULTI
+int
+iptables_restore_main(int argc, char *argv[])
+#else
+int
+main(int argc, char *argv[])
+#endif
+{
+       iptc_handle_t handle = NULL;
+       char buffer[10240];
+       int c;
+       char curtable[IPT_TABLE_MAXNAMELEN + 1];
+       FILE *in;
+       const char *modprobe = 0;
+       int in_table = 0, testing = 0;
+
+       program_name = "iptables-restore";
+       program_version = IPTABLES_VERSION;
+       line = 0;
+
+       lib_dir = getenv("IPTABLES_LIB_DIR");
+       if (!lib_dir)
+               lib_dir = IPT_LIB_DIR;
+
+#ifdef NO_SHARED_LIBS
+       init_extensions();
+#endif
+
+       while ((c = getopt_long(argc, argv, "bcvthnM:", options, NULL)) != -1) {
+               switch (c) {
+                       case 'b':
+                               binary = 1;
+                               break;
+                       case 'c':
+                               counters = 1;
+                               break;
+                       case 'v':
+                               verbose = 1;
+                               break;
+                       case 't':
+                               testing = 1;
+                               break;
+                       case 'h':
+                               print_usage("iptables-restore",
+                                           IPTABLES_VERSION);
+                               break;
+                       case 'n':
+                               noflush = 1;
+                               break;
+                       case 'M':
+                               modprobe = optarg;
+                               break;
+               }
+       }
+       
+       if (optind == argc - 1) {
+               in = fopen(argv[optind], "r");
+               if (!in) {
+                       fprintf(stderr, "Can't open %s: %s", argv[optind],
+                               strerror(errno));
+                       exit(1);
+               }
+       }
+       else if (optind < argc) {
+               fprintf(stderr, "Unknown arguments found on commandline");
+               exit(1);
+       }
+       else in = stdin;
+       
+       /* Grab standard input. */
+       while (fgets(buffer, sizeof(buffer), in)) {
+               int ret = 0;
+
+               line++;
+               if (buffer[0] == '\n')
+                       continue;
+               else if (buffer[0] == '#') {
+                       if (verbose)
+                               fputs(buffer, stdout);
+                       continue;
+               } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
+                       if (!testing) {
+                               DEBUGP("Calling commit\n");
+                               ret = iptc_commit(&handle);
+                       } else {
+                               DEBUGP("Not calling commit, testing\n");
+                               ret = 1;
+                       }
+                       in_table = 0;
+               } else if ((buffer[0] == '*') && (!in_table)) {
+                       /* New table */
+                       char *table;
+
+                       table = strtok(buffer+1, " \t\n");
+                       DEBUGP("line %u, table '%s'\n", line, table);
+                       if (!table) {
+                               exit_error(PARAMETER_PROBLEM, 
+                                       "%s: line %u table name invalid\n",
+                                       program_name, line);
+                               exit(1);
+                       }
+                       strncpy(curtable, table, IPT_TABLE_MAXNAMELEN);
+                       curtable[IPT_TABLE_MAXNAMELEN] = '\0';
+
+                       if (handle)
+                               iptc_free(&handle);
+
+                       handle = create_handle(table, modprobe);
+                       if (noflush == 0) {
+                               DEBUGP("Cleaning all chains of table '%s'\n",
+                                       table);
+                               for_each_chain(flush_entries, verbose, 1, 
+                                               &handle);
+       
+                               DEBUGP("Deleting all user-defined chains "
+                                      "of table '%s'\n", table);
+                               for_each_chain(delete_chain, verbose, 0, 
+                                               &handle) ;
+                       }
+
+                       ret = 1;
+                       in_table = 1;
+
+               } else if ((buffer[0] == ':') && (in_table)) {
+                       /* New chain. */
+                       char *policy, *chain;
+
+                       chain = strtok(buffer+1, " \t\n");
+                       DEBUGP("line %u, chain '%s'\n", line, chain);
+                       if (!chain) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u chain name invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+
+                       if (iptc_builtin(chain, handle) <= 0) {
+                               if (noflush && iptc_is_chain(chain, handle)) {
+                                       DEBUGP("Flushing existing user defined chain '%s'\n", chain);
+                                       if (!iptc_flush_entries(chain, &handle))
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "error flushing chain "
+                                                          "'%s':%s\n", chain,
+                                                          strerror(errno));
+                               } else {
+                                       DEBUGP("Creating new chain '%s'\n", chain);
+                                       if (!iptc_create_chain(chain, &handle))
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "error creating chain "
+                                                          "'%s':%s\n", chain,
+                                                          strerror(errno));
+                               }
+                       }
+
+                       policy = strtok(NULL, " \t\n");
+                       DEBUGP("line %u, policy '%s'\n", line, policy);
+                       if (!policy) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u policy invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+
+                       if (strcmp(policy, "-") != 0) {
+                               struct ipt_counters count;
+
+                               if (counters) {
+                                       char *ctrs;
+                                       ctrs = strtok(NULL, " \t\n");
+
+                                       if (!ctrs || !parse_counters(ctrs, &count))
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "invalid policy counters "
+                                                          "for chain '%s'\n", chain);
+
+                               } else {
+                                       memset(&count, 0, 
+                                              sizeof(struct ipt_counters));
+                               }
+
+                               DEBUGP("Setting policy of chain %s to %s\n",
+                                       chain, policy);
+
+                               if (!iptc_set_policy(chain, policy, &count,
+                                                    &handle))
+                                       exit_error(OTHER_PROBLEM,
+                                               "Can't set policy `%s'"
+                                               " on `%s' line %u: %s\n",
+                                               chain, policy, line,
+                                               iptc_strerror(errno));
+                       }
+
+                       ret = 1;
+
+               } else if (in_table) {
+                       int a;
+                       char *ptr = buffer;
+                       char *pcnt = NULL;
+                       char *bcnt = NULL;
+                       char *parsestart;
+
+                       /* the parser */
+                       char *param_start, *curchar;
+                       int quote_open;
+
+                       /* reset the newargv */
+                       newargc = 0;
+
+                       if (buffer[0] == '[') {
+                               /* we have counters in our input */
+                               ptr = strchr(buffer, ']');
+                               if (!ptr)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need ]\n",
+                                                  line);
+
+                               pcnt = strtok(buffer+1, ":");
+                               if (!pcnt)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need :\n",
+                                                  line);
+
+                               bcnt = strtok(NULL, "]");
+                               if (!bcnt)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need ]\n",
+                                                  line);
+
+                               /* start command parsing after counter */
+                               parsestart = ptr + 1;
+                       } else {
+                               /* start command parsing at start of line */
+                               parsestart = buffer;
+                       }
+
+                       add_argv(argv[0]);
+                       add_argv("-t");
+                       add_argv((char *) &curtable);
+                       
+                       if (counters && pcnt && bcnt) {
+                               add_argv("--set-counters");
+                               add_argv((char *) pcnt);
+                               add_argv((char *) bcnt);
+                       }
+
+                       /* After fighting with strtok enough, here's now
+                        * a 'real' parser. According to Rusty I'm now no
+                        * longer a real hacker, but I can live with that */
+
+                       quote_open = 0;
+                       param_start = parsestart;
+                       
+                       for (curchar = parsestart; *curchar; curchar++) {
+                               if (*curchar == '"') {
+                                       /* quote_open cannot be true if there
+                                        * was no previous character.  Thus, 
+                                        * curchar-1 has to be within bounds */
+                                       if (quote_open && 
+                                           *(curchar-1) != '\\') {
+                                               quote_open = 0;
+                                               *curchar = ' ';
+                                       } else {
+                                               quote_open = 1;
+                                               param_start++;
+                                       }
+                               } 
+                               if (*curchar == ' '
+                                   || *curchar == '\t'
+                                   || * curchar == '\n') {
+                                       char param_buffer[1024];
+                                       int param_len = curchar-param_start;
+
+                                       if (quote_open)
+                                               continue;
+
+                                       if (!param_len) {
+                                               /* two spaces? */
+                                               param_start++;
+                                               continue;
+                                       }
+                                       
+                                       /* end of one parameter */
+                                       strncpy(param_buffer, param_start,
+                                               param_len);
+                                       *(param_buffer+param_len) = '\0';
+
+                                       /* check if table name specified */
+                                       if (!strncmp(param_buffer, "-t", 3)
+                                            || !strncmp(param_buffer, "--table", 8)) {
+                                               exit_error(PARAMETER_PROBLEM, 
+                                                  "Line %u seems to have a "
+                                                  "-t table option.\n", line);
+                                               exit(1);
+                                       }
+
+                                       add_argv(param_buffer);
+                                       param_start += param_len + 1;
+                               } else {
+                                       /* regular character, skip */
+                               }
+                       }
+
+                       DEBUGP("calling do_command(%u, argv, &%s, handle):\n",
+                               newargc, curtable);
+
+                       for (a = 0; a < newargc; a++)
+                               DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+
+                       ret = do_command(newargc, newargv, 
+                                        &newargv[2], &handle);
+
+                       free_argv();
+               }
+               if (!ret) {
+                       fprintf(stderr, "%s: line %u failed\n",
+                                       program_name, line);
+                       exit(1);
+               }
+       }
+       if (in_table) {
+               fprintf(stderr, "%s: COMMIT expected at line %u\n",
+                               program_name, line + 1);
+               exit(1);
+       }
+
+       return 0;
+}
diff --git a/iptables-save.8 b/iptables-save.8
new file mode 100644 (file)
index 0000000..f9c7d65
--- /dev/null
@@ -0,0 +1,48 @@
+.TH IPTABLES-SAVE 8 "Jan 04, 2001" "" ""
+.\"
+.\" Man page written by Harald Welte <laforge@gnumonks.org>
+.\" It is based on the iptables man page.
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+iptables-save \- Save IP Tables
+.SH SYNOPSIS
+.BR "iptables-save " "[-c] [-t table]"
+.br
+.SH DESCRIPTION
+.PP
+.B iptables-save
+is used to dump the contents of an IP Table in easily parseable format
+to STDOUT. Use I/O-redirection provided by your shell to write to a file.
+.TP
+\fB\-c\fR, \fB\-\-counters\fR
+include the current values of all packet and byte counters in the output
+.TP
+\fB\-t\fR, \fB\-\-table\fR \fBtablename\fR
+.TP
+restrict output to only one table. If not specified, output includes all
+available tables.
+.SH BUGS
+None known as of iptables-1.2.1 release
+.SH AUTHOR
+Harald Welte <laforge@gnumonks.org>
+.SH SEE ALSO
+.BR iptables-restore "(8), " iptables "(8) "
+.PP
+The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
+which details NAT, and the netfilter-hacking-HOWTO which details the
+internals.
diff --git a/iptables-save.c b/iptables-save.c
new file mode 100644 (file)
index 0000000..a8a17aa
--- /dev/null
@@ -0,0 +1,376 @@
+/* Code to save the iptables state, in human readable-form. */
+/* (C) 1999 by Paul 'Rusty' Russell <rusty@rustcorp.com.au> and
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ *
+ */
+#include <getopt.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <time.h>
+#include <netdb.h>
+#include "libiptc/libiptc.h"
+#include "iptables.h"
+
+static int binary = 0, counters = 0;
+
+static struct option options[] = {
+       { "binary", 0, 0, 'b' },
+       { "counters", 0, 0, 'c' },
+       { "dump", 0, 0, 'd' },
+       { "table", 1, 0, 't' },
+       { 0 }
+};
+
+#define IP_PARTS_NATIVE(n)                     \
+(unsigned int)((n)>>24)&0xFF,                  \
+(unsigned int)((n)>>16)&0xFF,                  \
+(unsigned int)((n)>>8)&0xFF,                   \
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+/* This assumes that mask is contiguous, and byte-bounded. */
+static void
+print_iface(char letter, const char *iface, const unsigned char *mask,
+           int invert)
+{
+       unsigned int i;
+
+       if (mask[0] == 0)
+               return;
+
+       printf("-%c %s", letter, invert ? "! " : "");
+
+       for (i = 0; i < IFNAMSIZ; i++) {
+               if (mask[i] != 0) {
+                       if (iface[i] != '\0')
+                               printf("%c", iface[i]);
+               } else {
+                       /* we can access iface[i-1] here, because 
+                        * a few lines above we make sure that mask[0] != 0 */
+                       if (iface[i-1] != '\0')
+                               printf("+");
+                       break;
+               }
+       }
+
+       printf(" ");
+}
+
+/* These are hardcoded backups in iptables.c, so they are safe */
+struct pprot {
+       char *name;
+       u_int8_t num;
+};
+
+/* FIXME: why don't we use /etc/protocols ? */
+static const struct pprot chain_protos[] = {
+       { "tcp", IPPROTO_TCP },
+       { "udp", IPPROTO_UDP },
+       { "icmp", IPPROTO_ICMP },
+       { "esp", IPPROTO_ESP },
+       { "ah", IPPROTO_AH },
+       { "sctp", IPPROTO_SCTP },
+};
+
+static void print_proto(u_int16_t proto, int invert)
+{
+       if (proto) {
+               unsigned int i;
+               const char *invertstr = invert ? "! " : "";
+
+               struct protoent *pent = getprotobynumber(proto);
+               if (pent) {
+                       printf("-p %s%s ", invertstr, pent->p_name);
+                       return;
+               }
+
+               for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+                       if (chain_protos[i].num == proto) {
+                               printf("-p %s%s ",
+                                      invertstr, chain_protos[i].name);
+                               return;
+                       }
+
+               printf("-p %s%u ", invertstr, proto);
+       }
+}
+
+#if 0
+static int non_zero(const void *ptr, size_t size)
+{
+       unsigned int i;
+
+       for (i = 0; i < size; i++)
+               if (((char *)ptr)[i])
+                       return 0;
+
+       return 1;
+}
+#endif
+
+static int print_match(const struct ipt_entry_match *e,
+                       const struct ipt_ip *ip)
+{
+       struct iptables_match *match
+               = find_match(e->u.user.name, TRY_LOAD, NULL);
+
+       if (match) {
+               printf("-m %s ", e->u.user.name);
+
+               /* some matches don't provide a save function */
+               if (match->save)
+                       match->save(ip, e);
+       } else {
+               if (e->u.match_size) {
+                       fprintf(stderr,
+                               "Can't find library for match `%s'\n",
+                               e->u.user.name);
+                       exit(1);
+               }
+       }
+       return 0;
+}
+
+/* print a given ip including mask if neccessary */
+static void print_ip(char *prefix, u_int32_t ip, u_int32_t mask, int invert)
+{
+       if (!mask && !ip && !invert)
+               return;
+
+       printf("%s %s%u.%u.%u.%u",
+               prefix,
+               invert ? "! " : "",
+               IP_PARTS(ip));
+
+       if (mask != 0xffffffff) 
+               printf("/%u.%u.%u.%u ", IP_PARTS(mask));
+       else
+               printf(" ");
+}
+
+/* We want this to be readable, so only print out neccessary fields.
+ * Because that's the kind of world I want to live in.  */
+static void print_rule(const struct ipt_entry *e, 
+               iptc_handle_t *h, const char *chain, int counters)
+{
+       struct ipt_entry_target *t;
+       const char *target_name;
+
+       /* print counters */
+       if (counters)
+               printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+
+       /* print chain name */
+       printf("-A %s ", chain);
+
+       /* Print IP part. */
+       print_ip("-s", e->ip.src.s_addr,e->ip.smsk.s_addr,
+                       e->ip.invflags & IPT_INV_SRCIP);        
+
+       print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr,
+                       e->ip.invflags & IPT_INV_DSTIP);
+
+       print_iface('i', e->ip.iniface, e->ip.iniface_mask,
+                   e->ip.invflags & IPT_INV_VIA_IN);
+
+       print_iface('o', e->ip.outiface, e->ip.outiface_mask,
+                   e->ip.invflags & IPT_INV_VIA_OUT);
+
+       print_proto(e->ip.proto, e->ip.invflags & IPT_INV_PROTO);
+
+       if (e->ip.flags & IPT_F_FRAG)
+               printf("%s-f ",
+                      e->ip.invflags & IPT_INV_FRAG ? "! " : "");
+
+       /* Print matchinfo part */
+       if (e->target_offset) {
+               IPT_MATCH_ITERATE(e, print_match, &e->ip);
+       }
+
+       /* Print target name */ 
+       target_name = iptc_get_target(e, h);
+       if (target_name && (*target_name != '\0'))
+#ifdef IPT_F_GOTO
+               printf("-%c %s ", e->ip.flags & IPT_F_GOTO ? 'g' : 'j', target_name);
+#else
+               printf("-j %s ", target_name);
+#endif
+
+       /* Print targinfo part */
+       t = ipt_get_target((struct ipt_entry *)e);
+       if (t->u.user.name[0]) {
+               struct iptables_target *target
+                       = find_target(t->u.user.name, TRY_LOAD);
+
+               if (!target) {
+                       fprintf(stderr, "Can't find library for target `%s'\n",
+                               t->u.user.name);
+                       exit(1);
+               }
+
+               if (target->save)
+                       target->save(&e->ip, t);
+               else {
+                       /* If the target size is greater than ipt_entry_target
+                        * there is something to be saved, we just don't know
+                        * how to print it */
+                       if (t->u.target_size != 
+                           sizeof(struct ipt_entry_target)) {
+                               fprintf(stderr, "Target `%s' is missing "
+                                               "save function\n",
+                                       t->u.user.name);
+                               exit(1);
+                       }
+               }
+       }
+       printf("\n");
+}
+
+/* Debugging prototype. */
+static int for_each_table(int (*func)(const char *tablename))
+{
+        int ret = 1;
+       FILE *procfile = NULL;
+       char tablename[IPT_TABLE_MAXNAMELEN+1];
+
+       procfile = fopen("/proc/net/ip_tables_names", "r");
+       if (!procfile)
+               return 0;
+
+       while (fgets(tablename, sizeof(tablename), procfile)) {
+               if (tablename[strlen(tablename) - 1] != '\n')
+                       exit_error(OTHER_PROBLEM, 
+                                  "Badly formed tablename `%s'\n",
+                                  tablename);
+               tablename[strlen(tablename) - 1] = '\0';
+               ret &= func(tablename);
+       }
+
+       return ret;
+}
+       
+
+static int do_output(const char *tablename)
+{
+       iptc_handle_t h;
+       const char *chain = NULL;
+
+       if (!tablename)
+               return for_each_table(&do_output);
+
+       h = iptc_init(tablename);
+       if (!h)
+               exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",
+                          iptc_strerror(errno));
+
+       if (!binary) {
+               time_t now = time(NULL);
+
+               printf("# Generated by iptables-save v%s on %s",
+                      IPTABLES_VERSION, ctime(&now));
+               printf("*%s\n", tablename);
+
+               /* Dump out chain names first, 
+                * thereby preventing dependency conflicts */
+               for (chain = iptc_first_chain(&h);
+                    chain;
+                    chain = iptc_next_chain(&h)) {
+                       
+                       printf(":%s ", chain);
+                       if (iptc_builtin(chain, h)) {
+                               struct ipt_counters count;
+                               printf("%s ",
+                                      iptc_get_policy(chain, &count, &h));
+                               printf("[%llu:%llu]\n", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
+                       } else {
+                               printf("- [0:0]\n");
+                       }
+               }
+                               
+
+               for (chain = iptc_first_chain(&h);
+                    chain;
+                    chain = iptc_next_chain(&h)) {
+                       const struct ipt_entry *e;
+
+                       /* Dump out rules */
+                       e = iptc_first_rule(chain, &h);
+                       while(e) {
+                               print_rule(e, &h, chain, counters);
+                               e = iptc_next_rule(e, &h);
+                       }
+               }
+
+               now = time(NULL);
+               printf("COMMIT\n");
+               printf("# Completed on %s", ctime(&now));
+       } else {
+               /* Binary, huh?  OK. */
+               exit_error(OTHER_PROBLEM, "Binary NYI\n");
+       }
+
+       iptc_free(&h);
+
+       return 1;
+}
+
+/* Format:
+ * :Chain name POLICY packets bytes
+ * rule
+ */
+#ifdef IPTABLES_MULTI
+int
+iptables_save_main(int argc, char *argv[])
+#else
+int
+main(int argc, char *argv[])
+#endif
+{
+       const char *tablename = NULL;
+       int c;
+
+       program_name = "iptables-save";
+       program_version = IPTABLES_VERSION;
+
+       lib_dir = getenv("IPTABLES_LIB_DIR");
+       if (!lib_dir)
+               lib_dir = IPT_LIB_DIR;
+
+#ifdef NO_SHARED_LIBS
+       init_extensions();
+#endif
+
+       while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
+               switch (c) {
+               case 'b':
+                       binary = 1;
+                       break;
+
+               case 'c':
+                       counters = 1;
+                       break;
+
+               case 't':
+                       /* Select specific table. */
+                       tablename = optarg;
+                       break;
+               case 'd':
+                       do_output(tablename);
+                       exit(0);
+               }
+       }
+
+       if (optind < argc) {
+               fprintf(stderr, "Unknown arguments found on commandline\n");
+               exit(1);
+       }
+
+       return !do_output(tablename);
+}
diff --git a/iptables-standalone.c b/iptables-standalone.c
new file mode 100644 (file)
index 0000000..e5c7841
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * Based on the ipchains code by Paul Russell and Michael Neuling
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ *                 Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ *                 Marc Boucher <marc+nf@mbsi.ca>
+ *                 James Morris <jmorris@intercode.com.au>
+ *                 Harald Welte <laforge@gnumonks.org>
+ *                 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ *     iptables -- IP firewall administration for kernels with
+ *     firewall table (aimed for the 2.3 kernels)
+ *
+ *     See the accompanying manual page iptables(8) for information
+ *     about proper usage of this program.
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <iptables.h>
+
+#ifdef IPTABLES_MULTI
+int
+iptables_main(int argc, char *argv[])
+#else
+int
+main(int argc, char *argv[])
+#endif
+{
+       int ret;
+       char *table = "filter";
+       iptc_handle_t handle = NULL;
+
+       program_name = "iptables";
+       program_version = IPTABLES_VERSION;
+
+       lib_dir = getenv("IPTABLES_LIB_DIR");
+       if (!lib_dir)
+               lib_dir = IPT_LIB_DIR;
+
+#ifdef NO_SHARED_LIBS
+       init_extensions();
+#endif
+
+       ret = do_command(argc, argv, &table, &handle);
+       if (ret)
+               ret = iptc_commit(&handle);
+
+       if (!ret) {
+               fprintf(stderr, "iptables: %s\n",
+                       iptc_strerror(errno));
+               if (errno == EAGAIN) {
+                       exit(RESOURCE_PROBLEM);
+               }
+       }
+
+       exit(!ret);
+}
diff --git a/iptables-xml.c b/iptables-xml.c
new file mode 100644 (file)
index 0000000..ce3049c
--- /dev/null
@@ -0,0 +1,860 @@
+/* Code to convert iptables-save format to xml format,
+ * (C) 2006 Ufo Mechanic <azez@ufomechanic.net>
+ * based on iptables-restor (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * based on previous code from Rusty Russell <rusty@linuxcare.com.au>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ *
+ * $Id: iptables-xml.c,v 1.4 2006/11/09 12:02:17 azez Exp $
+ */
+
+#include <getopt.h>
+#include <sys/errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "iptables.h"
+#include "libiptc/libiptc.h"
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...)
+#endif
+
+/* no need to link with iptables.o */
+const char *program_name;
+const char *program_version;
+
+#ifndef IPTABLES_MULTI
+int line = 0;
+void exit_error(enum exittype status, char *msg, ...)
+{
+       va_list args;
+
+       va_start(args, msg);
+       fprintf(stderr, "%s v%s: ", program_name, program_version);
+       vfprintf(stderr, msg, args);
+       va_end(args);
+       fprintf(stderr, "\n");
+       /* On error paths, make sure that we don't leak memory */
+       exit(status);
+}
+#endif
+
+static void print_usage(const char *name, const char *version)
+           __attribute__ ((noreturn));
+
+static int verbose = 0;
+/* Whether to combine actions of sequential rules with identical conditions */
+static int combine = 0;
+/* Keeping track of external matches and targets.  */
+static struct option options[] = {
+       {"verbose", 0, 0, 'v'},
+       {"combine", 0, 0, 'c'},
+       {"help", 0, 0, 'h'},
+       {0}
+};
+
+static void
+print_usage(const char *name, const char *version)
+{
+       fprintf(stderr, "Usage: %s [-c] [-v] [-h]\n"
+               "          [--combine ]\n"
+               "          [ --verbose ]\n" "      [ --help ]\n", name);
+
+       exit(1);
+}
+
+static int
+parse_counters(char *string, struct ipt_counters *ctr)
+{
+       if (string != NULL)
+               return (sscanf
+                       (string, "[%llu:%llu]",
+                        (unsigned long long *) &ctr->pcnt,
+                        (unsigned long long *) &ctr->bcnt) == 2);
+       else
+               return (0 == 2);
+}
+
+/* global new argv and argc */
+static char *newargv[255];
+static int newargc = 0;
+
+static char *oldargv[255];
+static int oldargc = 0;
+
+/* arg meta data, were they quoted, frinstance */
+static int newargvattr[255];
+
+#define IPT_CHAIN_MAXNAMELEN IPT_TABLE_MAXNAMELEN
+char closeActionTag[IPT_TABLE_MAXNAMELEN + 1];
+char closeRuleTag[IPT_TABLE_MAXNAMELEN + 1];
+char curTable[IPT_TABLE_MAXNAMELEN + 1];
+char curChain[IPT_CHAIN_MAXNAMELEN + 1];
+
+typedef struct chain
+{
+       char *chain;
+       char *policy;
+       struct ipt_counters count;
+       int created;
+} chain;
+
+#define maxChains 10240                /* max chains per table */
+static chain chains[maxChains];
+static int nextChain = 0;
+
+/* funCtion adding one argument to newargv, updating newargc 
+ * returns true if argument added, false otherwise */
+static int
+add_argv(char *what, int quoted)
+{
+       DEBUGP("add_argv: %d %s\n", newargc, what);
+       if (what && ((newargc + 1) < sizeof(newargv) / sizeof(char *))) {
+               newargv[newargc] = strdup(what);
+               newargvattr[newargc] = quoted;
+               newargc++;
+               return 1;
+       } else
+               return 0;
+}
+
+static void
+free_argv(void)
+{
+       int i;
+
+       for (i = 0; i < newargc; i++) {
+               free(newargv[i]);
+               newargv[i] = NULL;
+       }
+       newargc = 0;
+
+       for (i = 0; i < oldargc; i++) {
+               free(oldargv[i]);
+               oldargv[i] = NULL;
+       }
+       oldargc = 0;
+}
+
+/* save parsed rule for comparison with next rule 
+   to perform action agregation on duplicate conditions */
+static void
+save_argv(void)
+{
+       int i;
+
+       for (i = 0; i < oldargc; i++)
+               free(oldargv[i]);
+       oldargc = newargc;
+       newargc = 0;
+       for (i = 0; i < oldargc; i++) {
+               oldargv[i] = newargv[i];
+               newargv[i] = NULL;
+       }
+}
+
+/* like puts but with xml encoding */
+static void
+xmlEncode(char *text)
+{
+       while (text && *text) {
+               if ((unsigned char) (*text) >= 127)
+                       printf("&#%d;", (unsigned char) (*text));
+               else if (*text == '&')
+                       printf("&amp;");
+               else if (*text == '<')
+                       printf("&lt;");
+               else if (*text == '>')
+                       printf("&gt;");
+               else if (*text == '"')
+                       printf("&quot;");
+               else
+                       putchar(*text);
+               text++;
+       }
+}
+
+/* Output text as a comment, avoiding a double hyphen */
+static void
+xmlCommentEscape(char *comment)
+{
+       int h_count = 0;
+
+       while (comment && *comment) {
+               if (*comment == '-') {
+                       h_count++;
+                       if (h_count >= 2) {
+                               h_count = 0;
+                               putchar(' ');
+                       }
+                       putchar('*');
+               }
+               /* strip trailing newline */
+               if (*comment == '\n' && *(comment + 1) == 0);
+               else
+                       putchar(*comment);
+               comment++;
+       }
+}
+
+static void
+xmlComment(char *comment)
+{
+       printf("<!-- ");
+       xmlCommentEscape(comment);
+       printf(" -->\n");
+}
+
+static void
+xmlAttrS(char *name, char *value)
+{
+       printf("%s=\"", name);
+       xmlEncode(value);
+       printf("\" ");
+}
+
+static void
+xmlAttrI(char *name, long long int num)
+{
+       printf("%s=\"%lld\" ", name, num);
+}
+
+static void
+closeChain()
+{
+       if (curChain[0] == 0)
+               return;
+
+       if (closeActionTag[0])
+               printf("%s\n", closeActionTag);
+       closeActionTag[0] = 0;
+       if (closeRuleTag[0])
+               printf("%s\n", closeRuleTag);
+       closeRuleTag[0] = 0;
+       if (curChain[0])
+               printf("    </chain>\n");
+       curChain[0] = 0;
+       //lastRule[0]=0;
+}
+
+static void
+openChain(char *chain, char *policy, struct ipt_counters *ctr, char close)
+{
+       closeChain();
+
+       strncpy(curChain, chain, IPT_CHAIN_MAXNAMELEN);
+       curChain[IPT_CHAIN_MAXNAMELEN] = '\0';
+
+       printf("    <chain ");
+       xmlAttrS("name", curChain);
+       if (strcmp(policy, "-") != 0)
+               xmlAttrS("policy", policy);
+       xmlAttrI("packet-count", (unsigned long long) ctr->pcnt);
+       xmlAttrI("byte-count", (unsigned long long) ctr->bcnt);
+       if (close) {
+               printf("%c", close);
+               curChain[0] = 0;
+       }
+       printf(">\n");
+}
+
+static int
+existsChain(char *chain)
+{
+       /* open a saved chain */
+       int c = 0;
+
+       if (0 == strcmp(curChain, chain))
+               return 1;
+       for (c = 0; c < nextChain; c++)
+               if (chains[c].chain && strcmp(chains[c].chain, chain) == 0)
+                       return 1;
+       return 0;
+}
+
+static void
+needChain(char *chain)
+{
+       /* open a saved chain */
+       int c = 0;
+
+       if (0 == strcmp(curChain, chain))
+               return;
+
+       for (c = 0; c < nextChain; c++)
+               if (chains[c].chain && strcmp(chains[c].chain, chain) == 0) {
+                       openChain(chains[c].chain, chains[c].policy,
+                                 &(chains[c].count), '\0');
+                       /* And, mark it as done so we don't create 
+                          an empty chain at table-end time */
+                       chains[c].created = 1;
+               }
+}
+
+static void
+saveChain(char *chain, char *policy, struct ipt_counters *ctr)
+{
+       if (nextChain >= maxChains) {
+               exit_error(PARAMETER_PROBLEM,
+                          "%s: line %u chain name invalid\n",
+                          program_name, line);
+               exit(1);
+       };
+       chains[nextChain].chain = strdup(chain);
+       chains[nextChain].policy = strdup(policy);
+       chains[nextChain].count = *ctr;
+       chains[nextChain].created = 0;
+       nextChain++;
+}
+
+static void
+finishChains()
+{
+       int c;
+
+       for (c = 0; c < nextChain; c++)
+               if (!chains[c].created) {
+                       openChain(chains[c].chain, chains[c].policy,
+                                 &(chains[c].count), '/');
+                       free(chains[c].chain);
+                       free(chains[c].policy);
+               }
+       nextChain = 0;
+}
+
+static void
+closeTable()
+{
+       closeChain();
+       finishChains();
+       if (curTable[0])
+               printf("  </table>\n");
+       curTable[0] = 0;
+}
+
+static void
+openTable(char *table)
+{
+       closeTable();
+
+       strncpy(curTable, table, IPT_TABLE_MAXNAMELEN);
+       curTable[IPT_TABLE_MAXNAMELEN] = '\0';
+
+       printf("  <table ");
+       xmlAttrS("name", curTable);
+       printf(">\n");
+}
+
+// is char* -j --jump -g or --goto
+static int
+isTarget(char *arg)
+{
+       return ((arg)
+               && (strcmp((arg), "-j") == 0 || strcmp((arg), "--jump") == 0
+                   || strcmp((arg), "-g") == 0
+                   || strcmp((arg), "--goto") == 0));
+}
+
+// part=-1 means do conditions, part=1 means do rules, part=0 means do both
+static void
+do_rule_part(char *leveltag1, char *leveltag2, int part, int argc,
+            char *argv[], int argvattr[])
+{
+       int arg = 1;            // ignore leading -A
+       char invert_next = 0;
+       char *thisChain = NULL;
+       char *spacer = "";      // space when needed to assemble arguments
+       char *level1 = NULL;
+       char *level2 = NULL;
+       char *leveli1 = "        ";
+       char *leveli2 = "          ";
+
+#define CLOSE_LEVEL(LEVEL) \
+       do { \
+               if (level ## LEVEL) printf("</%s>\n", \
+               (leveltag ## LEVEL)?(leveltag ## LEVEL):(level ## LEVEL)); \
+               level ## LEVEL=NULL;\
+       } while(0)
+
+#define OPEN_LEVEL(LEVEL,TAG) \
+       do {\
+               level ## LEVEL=TAG;\
+               if (leveltag ## LEVEL) {\
+                       printf("%s<%s ", (leveli ## LEVEL), \
+                               (leveltag ## LEVEL));\
+                       xmlAttrS("type", (TAG)); \
+               } else printf("%s<%s ", (leveli ## LEVEL), (level ## LEVEL)); \
+       } while(0)
+
+       thisChain = argv[arg++];
+
+       if (part == 1) {        /* skip */
+               /* use argvattr to tell which arguments were quoted 
+                  to avoid comparing quoted arguments, like comments, to -j, */
+               while (arg < argc && (argvattr[arg] || !isTarget(argv[arg])))
+                       arg++;
+       }
+
+       /* Before we start, if the first arg is -[^-] and not -m or -j or -g 
+          then start a dummy <match> tag for old style built-in matches.  
+          We would do this in any case, but no need if it would be empty */
+       if (arg < argc && argv[arg][0] == '-' && !isTarget(argv[arg])
+           && strcmp(argv[arg], "-m") != 0) {
+               OPEN_LEVEL(1, "match");
+               printf(">\n");
+       }
+       while (arg < argc) {
+               // If ! is followed by -* then apply to that else output as data
+               // Stop, if we need to
+               if (part == -1 && !argvattr[arg] && (isTarget(argv[arg]))) {
+                       break;
+               } else if (!argvattr[arg] && strcmp(argv[arg], "!") == 0) {
+                       if ((arg + 1) < argc && argv[arg + 1][0] == '-')
+                               invert_next = '!';
+                       else
+                               printf("%s%s", spacer, argv[arg]);
+                       spacer = " ";
+               } else if (!argvattr[arg] && isTarget(argv[arg])
+                          && existsChain(argv[arg + 1])
+                          && (2 + arg >= argc)) {
+                       if (!((1 + arg) < argc))
+                               // no args to -j, -m or -g, ignore & finish loop
+                               break;
+                       CLOSE_LEVEL(2);
+                       if (level1)
+                               printf("%s", leveli1);
+                       CLOSE_LEVEL(1);
+                       spacer = "";
+                       invert_next = 0;
+                       if (strcmp(argv[arg], "-g") == 0
+                           || strcmp(argv[arg], "--goto") == 0) {
+                               /* goto user chain */
+                               OPEN_LEVEL(1, "goto");
+                               printf(">\n");
+                               arg++;
+                               OPEN_LEVEL(2, argv[arg]);
+                               printf("/>\n");
+                               level2 = NULL;
+                       } else {
+                               /* call user chain */
+                               OPEN_LEVEL(1, "call");
+                               printf(">\n");
+                               arg++;
+                               OPEN_LEVEL(2, argv[arg]);
+                               printf("/>\n");
+                               level2 = NULL;
+                       }
+               } else if (!argvattr[arg]
+                          && (isTarget(argv[arg])
+                              || strcmp(argv[arg], "-m") == 0
+                              || strcmp(argv[arg], "--module") == 0)) {
+                       if (!((1 + arg) < argc))
+                               // no args to -j, -m or -g, ignore & finish loop
+                               break;
+                       CLOSE_LEVEL(2);
+                       if (level1)
+                               printf("%s", leveli1);
+                       CLOSE_LEVEL(1);
+                       spacer = "";
+                       invert_next = 0;
+                       arg++;
+                       OPEN_LEVEL(1, (argv[arg]));
+                       // Optimize case, can we close this tag already?
+                       if ((arg + 1) >= argc || (!argvattr[arg + 1]
+                                                 && (isTarget(argv[arg + 1])
+                                                     || strcmp(argv[arg + 1],
+                                                               "-m") == 0
+                                                     || strcmp(argv[arg + 1],
+                                                               "--module") ==
+                                                     0))) {
+                               printf(" />\n");
+                               level1 = NULL;
+                       } else {
+                               printf(">\n");
+                       }
+               } else if (!argvattr[arg] && argv[arg][0] == '-') {
+                       char *tag;
+                       CLOSE_LEVEL(2);
+                       // Skip past any -
+                       tag = argv[arg];
+                       while (*tag == '-' && *tag)
+                               tag++;
+
+                       spacer = "";
+                       OPEN_LEVEL(2, tag);
+                       if (invert_next)
+                               printf(" invert=\"1\"");
+                       invert_next = 0;
+
+                       // Optimize case, can we close this tag already?
+                       if (!((arg + 1) < argc)
+                           || (argv[arg + 1][0] == '-' /* NOT QUOTED */ )) {
+                               printf(" />\n");
+                               level2 = NULL;
+                       } else {
+                               printf(">");
+                       }
+               } else {        // regular data
+                       char *spaces = strchr(argv[arg], ' ');
+                       printf("%s", spacer);
+                       if (spaces || argvattr[arg])
+                               printf("&quot;");
+                       // if argv[arg] contains a space, enclose in quotes
+                       xmlEncode(argv[arg]);
+                       if (spaces || argvattr[arg])
+                               printf("&quot;");
+                       spacer = " ";
+               }
+               arg++;
+       }
+       CLOSE_LEVEL(2);
+       if (level1)
+               printf("%s", leveli1);
+       CLOSE_LEVEL(1);
+
+       return;
+}
+
+static int
+compareRules()
+{
+       /* compare arguments up to -j or -g for match.
+          NOTE: We don't want to combine actions if there were no criteria 
+          in each rule, or rules didn't have an action 
+          NOTE: Depends on arguments being in some kind of "normal" order which 
+          is the case when processing the ACTUAL output of actual iptables-save 
+          rather than a file merely in a compatable format */
+
+       int old = 0;
+       int new = 0;
+
+       int compare = 0;
+
+       while (new < newargc && old < oldargc) {
+               if (isTarget(oldargv[old]) && isTarget(newargv[new])) {
+                       compare = 1;
+                       break;
+               }
+               // break when old!=new
+               if (strcmp(oldargv[old], newargv[new]) != 0) {
+                       compare = 0;
+                       break;
+               }
+
+               old++;
+               new++;
+       }
+       // We won't match unless both rules had a target. 
+       // This means we don't combine target-less rules, which is good
+
+       return compare == 1;
+}
+
+/* has a nice parsed rule starting with -A */
+static void
+do_rule(char *pcnt, char *bcnt, int argc, char *argv[], int argvattr[])
+{
+       /* are these conditions the same as the previous rule?
+        * If so, skip arg straight to -j or -g */
+       if (combine && argc > 2 && !isTarget(argv[2]) && compareRules()) {
+               xmlComment("Combine action from next rule");
+       } else {
+
+               if (closeActionTag[0]) {
+                       printf("%s\n", closeActionTag);
+                       closeActionTag[0] = 0;
+               }
+               if (closeRuleTag[0]) {
+                       printf("%s\n", closeRuleTag);
+                       closeRuleTag[0] = 0;
+               }
+
+               printf("      <rule ");
+               //xmlAttrS("table",curTable); // not needed in full mode 
+               //xmlAttrS("chain",argv[1]); // not needed in full mode 
+               if (pcnt)
+                       xmlAttrS("packet-count", pcnt);
+               if (bcnt)
+                       xmlAttrS("byte-count", bcnt);
+               printf(">\n");
+
+               strncpy(closeRuleTag, "      </rule>\n", IPT_TABLE_MAXNAMELEN);
+               closeRuleTag[IPT_TABLE_MAXNAMELEN] = '\0';
+
+               /* no point in writing out condition if there isn't one */
+               if (argc >= 3 && !isTarget(argv[2])) {
+                       printf("       <conditions>\n");
+                       do_rule_part(NULL, NULL, -1, argc, argv, argvattr);
+                       printf("       </conditions>\n");
+               }
+       }
+       /* Write out the action */
+       //do_rule_part("action","arg",1,argc,argv,argvattr);
+       if (!closeActionTag[0]) {
+               printf("       <actions>\n");
+               strncpy(closeActionTag, "       </actions>\n",
+                       IPT_TABLE_MAXNAMELEN);
+               closeActionTag[IPT_TABLE_MAXNAMELEN] = '\0';
+       }
+       do_rule_part(NULL, NULL, 1, argc, argv, argvattr);
+}
+
+
+#ifdef IPTABLES_MULTI
+int
+iptables_xml_main(int argc, char *argv[])
+#else
+int
+main(int argc, char *argv[])
+#endif
+{
+       char buffer[10240];
+       int c;
+       FILE *in;
+
+       program_name = "iptables-xml";
+       program_version = IPTABLES_VERSION;
+       line = 0;
+
+       while ((c = getopt_long(argc, argv, "cvh", options, NULL)) != -1) {
+               switch (c) {
+               case 'c':
+                       combine = 1;
+                       break;
+               case 'v':
+                       printf("xptables-xml\n");
+                       verbose = 1;
+                       break;
+               case 'h':
+                       print_usage("iptables-xml", IPTABLES_VERSION);
+                       break;
+               }
+       }
+
+       if (optind == argc - 1) {
+               in = fopen(argv[optind], "r");
+               if (!in) {
+                       fprintf(stderr, "Can't open %s: %s", argv[optind],
+                               strerror(errno));
+                       exit(1);
+               }
+       } else if (optind < argc) {
+               fprintf(stderr, "Unknown arguments found on commandline");
+               exit(1);
+       } else
+               in = stdin;
+
+       printf("<iptables-rules version=\"1.0\">\n");
+
+       /* Grab standard input. */
+       while (fgets(buffer, sizeof(buffer), in)) {
+               int ret = 0;
+
+               line++;
+
+               if (buffer[0] == '\n')
+                       continue;
+               else if (buffer[0] == '#') {
+                       xmlComment(buffer);
+                       continue;
+               }
+
+               if (verbose) {
+                       printf("<!-- line %d ", line);
+                       xmlCommentEscape(buffer);
+                       printf(" -->\n");
+               }
+
+               if ((strcmp(buffer, "COMMIT\n") == 0) && (curTable[0])) {
+                       DEBUGP("Calling commit\n");
+                       closeTable();
+                       ret = 1;
+               } else if ((buffer[0] == '*')) {
+                       /* New table */
+                       char *table;
+
+                       table = strtok(buffer + 1, " \t\n");
+                       DEBUGP("line %u, table '%s'\n", line, table);
+                       if (!table) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u table name invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+                       openTable(table);
+
+                       ret = 1;
+               } else if ((buffer[0] == ':') && (curTable[0])) {
+                       /* New chain. */
+                       char *policy, *chain;
+                       struct ipt_counters count;
+                       char *ctrs;
+
+                       chain = strtok(buffer + 1, " \t\n");
+                       DEBUGP("line %u, chain '%s'\n", line, chain);
+                       if (!chain) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u chain name invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+
+                       DEBUGP("Creating new chain '%s'\n", chain);
+
+                       policy = strtok(NULL, " \t\n");
+                       DEBUGP("line %u, policy '%s'\n", line, policy);
+                       if (!policy) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u policy invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+
+                       ctrs = strtok(NULL, " \t\n");
+                       parse_counters(ctrs, &count);
+                       saveChain(chain, policy, &count);
+
+                       ret = 1;
+               } else if (curTable[0]) {
+                       int a;
+                       char *ptr = buffer;
+                       char *pcnt = NULL;
+                       char *bcnt = NULL;
+                       char *parsestart;
+                       char *chain = NULL;
+
+                       /* the parser */
+                       char *param_start, *curchar;
+                       int quote_open, quoted;
+
+                       /* reset the newargv */
+                       newargc = 0;
+
+                       if (buffer[0] == '[') {
+                               /* we have counters in our input */
+                               ptr = strchr(buffer, ']');
+                               if (!ptr)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need ]\n",
+                                                  line);
+
+                               pcnt = strtok(buffer + 1, ":");
+                               if (!pcnt)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need :\n",
+                                                  line);
+
+                               bcnt = strtok(NULL, "]");
+                               if (!bcnt)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need ]\n",
+                                                  line);
+
+                               /* start command parsing after counter */
+                               parsestart = ptr + 1;
+                       } else {
+                               /* start command parsing at start of line */
+                               parsestart = buffer;
+                       }
+
+
+                       /* This is a 'real' parser crafted in artist mode
+                        * not hacker mode. If the author can live with that
+                        * then so can everyone else */
+
+                       quote_open = 0;
+                       /* We need to know which args were quoted so we 
+                          can preserve quote */
+                       quoted = 0;
+                       param_start = parsestart;
+
+                       for (curchar = parsestart; *curchar; curchar++) {
+                               if (*curchar == '"') {
+                                       /* quote_open cannot be true if there
+                                        * was no previous character.  Thus, 
+                                        * curchar-1 has to be within bounds */
+                                       if (quote_open &&
+                                           *(curchar - 1) != '\\') {
+                                               quote_open = 0;
+                                               *curchar = ' ';
+                                       } else {
+                                               quote_open = 1;
+                                               quoted = 1;
+                                               param_start++;
+                                       }
+                               }
+                               if (*curchar == ' '
+                                   || *curchar == '\t' || *curchar == '\n') {
+                                       char param_buffer[1024];
+                                       int param_len = curchar - param_start;
+
+                                       if (quote_open)
+                                               continue;
+
+                                       if (!param_len) {
+                                               /* two spaces? */
+                                               param_start++;
+                                               continue;
+                                       }
+
+                                       /* end of one parameter */
+                                       strncpy(param_buffer, param_start,
+                                               param_len);
+                                       *(param_buffer + param_len) = '\0';
+
+                                       /* check if table name specified */
+                                       if (!strncmp(param_buffer, "-t", 3)
+                                           || !strncmp(param_buffer,
+                                                       "--table", 8)) {
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "Line %u seems to have a "
+                                                          "-t table option.\n",
+                                                          line);
+                                               exit(1);
+                                       }
+
+                                       add_argv(param_buffer, quoted);
+                                       if (newargc >= 2
+                                           && 0 ==
+                                           strcmp(newargv[newargc - 2], "-A"))
+                                               chain = newargv[newargc - 1];
+                                       quoted = 0;
+                                       param_start += param_len + 1;
+                               } else {
+                                       /* regular character, skip */
+                               }
+                       }
+
+                       DEBUGP("calling do_command(%u, argv, &%s, handle):\n",
+                              newargc, curTable);
+
+                       for (a = 0; a < newargc; a++)
+                               DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+
+                       needChain(chain);// Should we explicitly look for -A
+                       do_rule(pcnt, bcnt, newargc, newargv, newargvattr);
+
+                       save_argv();
+                       ret = 1;
+               }
+               if (!ret) {
+                       fprintf(stderr, "%s: line %u failed\n",
+                               program_name, line);
+                       exit(1);
+               }
+       }
+       if (curTable[0]) {
+               fprintf(stderr, "%s: COMMIT expected at line %u\n",
+                       program_name, line + 1);
+               exit(1);
+       }
+
+       printf("</iptables-rules>\n");
+       free_argv();
+
+       return 0;
+}
diff --git a/iptables.8 b/iptables.8
new file mode 100644 (file)
index 0000000..92f18e8
--- /dev/null
@@ -0,0 +1,2054 @@
+.TH IPTABLES 8 "Mar 09, 2002" "" ""
+.\"
+.\" Man page written by Herve Eychenne <rv@wallfire.org> (May 1999)
+.\" It is based on ipchains page.
+.\" TODO: add a word for protocol helpers (FTP, IRC, SNMP-ALG)
+.\"
+.\" ipchains page by Paul ``Rusty'' Russell March 1997
+.\" Based on the original ipfwadm man page by Jos Vos <jos@xos.nl>
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+iptables \- administration tool for IPv4 packet filtering and NAT
+.SH SYNOPSIS
+.BR "iptables [-t table] -[AD] " "chain rule-specification [options]"
+.br
+.BR "iptables [-t table] -I " "chain [rulenum] rule-specification [options]"
+.br
+.BR "iptables [-t table] -R " "chain rulenum rule-specification [options]"
+.br
+.BR "iptables [-t table] -D " "chain rulenum [options]"
+.br
+.BR "iptables [-t table] -[LFZ] " "[chain] [options]"
+.br
+.BR "iptables [-t table] -N " "chain"
+.br
+.BR "iptables [-t table] -X " "[chain]"
+.br
+.BR "iptables [-t table] -P " "chain target [options]"
+.br
+.BR "iptables [-t table] -E " "old-chain-name new-chain-name"
+.SH DESCRIPTION
+.B Iptables
+is used to set up, maintain, and inspect the tables of IP packet
+filter rules in the Linux kernel.  Several different tables
+may be defined.  Each table contains a number of built-in
+chains and may also contain user-defined chains.
+
+Each chain is a list of rules which can match a set of packets.  Each
+rule specifies what to do with a packet that matches.  This is called
+a `target', which may be a jump to a user-defined chain in the same
+table.
+
+.SH TARGETS
+A firewall rule specifies criteria for a packet, and a target.  If the
+packet does not match, the next rule in the chain is the examined; if
+it does match, then the next rule is specified by the value of the
+target, which can be the name of a user-defined chain or one of the
+special values 
+.IR ACCEPT ,
+.IR DROP ,
+.IR QUEUE ,
+or
+.IR RETURN .
+.PP
+.I ACCEPT 
+means to let the packet through.
+.I DROP
+means to drop the packet on the floor.
+.I QUEUE
+means to pass the packet to userspace.  (How the packet can be received
+by a userspace process differs by the particular queue handler.  2.4.x
+and 2.6.x kernels up to 2.6.13 include the 
+.B
+ip_queue
+queue handler.  Kernels 2.6.14 and later additionally include the 
+.B
+nfnetlink_queue
+queue handler.  Packets with a target of QUEUE will be sent to queue number '0'
+in this case. Please also see the
+.B
+NFQUEUE
+target as described later in this man page.)
+.I RETURN
+means stop traversing this chain and resume at the next rule in the
+previous (calling) chain.  If the end of a built-in chain is reached
+or a rule in a built-in chain with target
+.I RETURN
+is matched, the target specified by the chain policy determines the
+fate of the packet.
+.SH TABLES
+There are currently three independent tables (which tables are present
+at any time depends on the kernel configuration options and which
+modules are present).
+.TP
+.BI "-t, --table " "table"
+This option specifies the packet matching table which the command
+should operate on.  If the kernel is configured with automatic module
+loading, an attempt will be made to load the appropriate module for
+that table if it is not already there.
+
+The tables are as follows:
+.RS
+.TP .4i
+.BR "filter" :
+This is the default table (if no -t option is passed).  It contains
+the built-in chains
+.B INPUT
+(for packets destined to local sockets),
+.B FORWARD
+(for packets being routed through the box), and
+.B OUTPUT
+(for locally-generated packets).
+.TP
+.BR "nat" :
+This table is consulted when a packet that creates a new
+connection is encountered.  It consists of three built-ins:
+.B PREROUTING
+(for altering packets as soon as they come in),
+.B OUTPUT
+(for altering locally-generated packets before routing), and
+.B POSTROUTING
+(for altering packets as they are about to go out).
+.TP
+.BR "mangle" :
+This table is used for specialized packet alteration.  Until kernel
+2.4.17 it had two built-in chains:
+.B PREROUTING
+(for altering incoming packets before routing) and
+.B OUTPUT
+(for altering locally-generated packets before routing).
+Since kernel 2.4.18, three other built-in chains are also supported:
+.B INPUT
+(for packets coming into the box itself),
+.B FORWARD
+(for altering packets being routed through the box), and
+.B POSTROUTING
+(for altering packets as they are about to go out).
+.TP
+.BR "raw" :
+This table is used mainly for configuring exemptions from connection
+tracking in combination with the NOTRACK target.  It registers at the netfilter
+hooks with higher priority and is thus called before ip_conntrack, or any other
+IP tables.  It provides the following built-in chains:
+.B PREROUTING
+(for packets arriving via any network interface)
+.B OUTPUT
+(for packets generated by local processes)
+.RE
+.SH OPTIONS
+The options that are recognized by
+.B iptables
+can be divided into several different groups.
+.SS COMMANDS
+These options specify the specific action to perform.  Only one of them
+can be specified on the command line unless otherwise specified
+below.  For all the long versions of the command and option names, you
+need to use only enough letters to ensure that
+.B iptables
+can differentiate it from all other options.
+.TP
+.BI "-A, --append " "chain rule-specification"
+Append one or more rules to the end of the selected chain.
+When the source and/or destination names resolve to more than one
+address, a rule will be added for each possible address combination.
+.TP
+.BI "-D, --delete " "chain rule-specification"
+.ns
+.TP
+.BI "-D, --delete " "chain rulenum"
+Delete one or more rules from the selected chain.  There are two
+versions of this command: the rule can be specified as a number in the
+chain (starting at 1 for the first rule) or a rule to match.
+.TP
+.BR "-I, --insert " "\fIchain\fP [\fIrulenum\fP] \fIrule-specification\fP"
+Insert one or more rules in the selected chain as the given rule
+number.  So, if the rule number is 1, the rule or rules are inserted
+at the head of the chain.  This is also the default if no rule number
+is specified.
+.TP
+.BI "-R, --replace " "chain rulenum rule-specification"
+Replace a rule in the selected chain.  If the source and/or
+destination names resolve to multiple addresses, the command will
+fail.  Rules are numbered starting at 1.
+.TP
+.BR "-L, --list " "[\fIchain\fP]"
+List all rules in the selected chain.  If no chain is selected, all
+chains are listed.  As every other iptables command, it applies to the
+specified table (filter is the default), so NAT rules get listed by
+.nf
+ iptables -t nat -n -L
+.fi
+Please note that it is often used with the
+.B -n
+option, in order to avoid long reverse DNS lookups.
+It is legal to specify the
+.B -Z
+(zero) option as well, in which case the chain(s) will be atomically
+listed and zeroed.  The exact output is affected by the other
+arguments given. The exact rules are suppressed until you use
+.nf
+ iptables -L -v
+.fi
+.TP
+.BR "-F, --flush " "[\fIchain\fP]"
+Flush the selected chain (all the chains in the table if none is given).
+This is equivalent to deleting all the rules one by one.
+.TP
+.BR "-Z, --zero " "[\fIchain\fP]"
+Zero the packet and byte counters in all chains.  It is legal to
+specify the
+.B "-L, --list"
+(list) option as well, to see the counters immediately before they are
+cleared. (See above.)
+.TP
+.BI "-N, --new-chain " "chain"
+Create a new user-defined chain by the given name.  There must be no
+target of that name already.
+.TP
+.BR "-X, --delete-chain " "[\fIchain\fP]"
+Delete the optional user-defined chain specified.  There must be no references
+to the chain.  If there are, you must delete or replace the referring rules
+before the chain can be deleted.  The chain must be empty, i.e. not contain
+any rules.  If no argument is given, it will attempt to delete every
+non-builtin chain in the table.
+.TP
+.BI "-P, --policy " "chain target"
+Set the policy for the chain to the given target.  See the section
+.B TARGETS
+for the legal targets.  Only built-in (non-user-defined) chains can have
+policies, and neither built-in nor user-defined chains can be policy
+targets.
+.TP
+.BI "-E, --rename-chain " "old-chain new-chain"
+Rename the user specified chain to the user supplied name.  This is
+cosmetic, and has no effect on the structure of the table.
+.TP
+.B -h
+Help.
+Give a (currently very brief) description of the command syntax.
+.SS PARAMETERS
+The following parameters make up a rule specification (as used in the
+add, delete, insert, replace and append commands).
+.TP
+.BR "-p, --protocol " "[!] \fIprotocol\fP"
+The protocol of the rule or of the packet to check.
+The specified protocol can be one of
+.IR tcp ,
+.IR udp ,
+.IR icmp ,
+or
+.IR all ,
+or it can be a numeric value, representing one of these protocols or a
+different one.  A protocol name from /etc/protocols is also allowed.
+A "!" argument before the protocol inverts the
+test.  The number zero is equivalent to
+.IR all .
+Protocol
+.I all
+will match with all protocols and is taken as default when this
+option is omitted.
+.TP
+.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+Source specification.
+.I Address
+can be either a network name, a hostname (please note that specifying
+any name to be resolved with a remote query such as DNS is a really bad idea),
+a network IP address (with /mask), or a plain IP address.
+The
+.I mask
+can be either a network mask or a plain number,
+specifying the number of 1's at the left side of the network mask.
+Thus, a mask of
+.I 24
+is equivalent to
+.IR 255.255.255.0 .
+A "!" argument before the address specification inverts the sense of
+the address. The flag
+.B --src
+is an alias for this option.
+.TP
+.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]"
+Destination specification. 
+See the description of the
+.B -s
+(source) flag for a detailed description of the syntax.  The flag
+.B --dst
+is an alias for this option.
+.TP
+.BI "-j, --jump " "target"
+This specifies the target of the rule; i.e., what to do if the packet
+matches it.  The target can be a user-defined chain (other than the
+one this rule is in), one of the special builtin targets which decide
+the fate of the packet immediately, or an extension (see
+.B EXTENSIONS
+below).  If this
+option is omitted in a rule (and
+.B -g
+is not used), then matching the rule will have no
+effect on the packet's fate, but the counters on the rule will be
+incremented.
+.TP
+.BI "-g, --goto " "chain"
+This specifies that the processing should continue in a user
+specified chain. Unlike the --jump option return will not continue
+processing in this chain but instead in the chain that called us via
+--jump.
+.TP
+.BR "-i, --in-interface " "[!] \fIname\fP"
+Name of an interface via which a packet was received (only for
+packets entering the 
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains).  When the "!" argument is used before the interface name, the
+sense is inverted.  If the interface name ends in a "+", then any
+interface which begins with this name will match.  If this option is
+omitted, any interface name will match.
+.TP
+.BR "-o, --out-interface " "[!] \fIname\fP"
+Name of an interface via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD ,
+.B OUTPUT
+and
+.B POSTROUTING
+chains).  When the "!" argument is used before the interface name, the
+sense is inverted.  If the interface name ends in a "+", then any
+interface which begins with this name will match.  If this option is
+omitted, any interface name will match.
+.TP
+.B "[!] " "-f, --fragment"
+This means that the rule only refers to second and further fragments
+of fragmented packets.  Since there is no way to tell the source or
+destination ports of such a packet (or ICMP type), such a packet will
+not match any rules which specify them.  When the "!" argument
+precedes the "-f" flag, the rule will only match head fragments, or
+unfragmented packets.
+.TP
+.BI "-c, --set-counters " "PKTS BYTES"
+This enables the administrator to initialize the packet and byte
+counters of a rule (during
+.B INSERT,
+.B APPEND,
+.B REPLACE
+operations).
+.SS "OTHER OPTIONS"
+The following additional options can be specified:
+.TP
+.B "-v, --verbose"
+Verbose output.  This option makes the list command show the interface
+name, the rule options (if any), and the TOS masks.  The packet and
+byte counters are also listed, with the suffix 'K', 'M' or 'G' for
+1000, 1,000,000 and 1,000,000,000 multipliers respectively (but see
+the
+.B -x
+flag to change this).
+For appending, insertion, deletion and replacement, this causes
+detailed information on the rule or rules to be printed.
+.TP
+.B "-n, --numeric"
+Numeric output.
+IP addresses and port numbers will be printed in numeric format.
+By default, the program will try to display them as host names,
+network names, or services (whenever applicable).
+.TP
+.B "-x, --exact"
+Expand numbers.
+Display the exact value of the packet and byte counters,
+instead of only the rounded number in K's (multiples of 1000)
+M's (multiples of 1000K) or G's (multiples of 1000M).  This option is
+only relevant for the 
+.B -L
+command.
+.TP
+.B "--line-numbers"
+When listing rules, add line numbers to the beginning of each rule,
+corresponding to that rule's position in the chain.
+.TP
+.B "--modprobe=command"
+When adding or inserting rules into a chain, use
+.B command
+to load any necessary modules (targets, match extensions, etc).
+.SH MATCH EXTENSIONS
+iptables can use extended packet matching modules.  These are loaded
+in two ways: implicitly, when
+.B -p
+or
+.B --protocol
+is specified, or with the
+.B -m
+or
+.B --match
+options, followed by the matching module name; after these, various
+extra command line options become available, depending on the specific
+module.  You can specify multiple extended match modules in one line,
+and you can use the
+.B -h
+or
+.B --help
+options after the module has been specified to receive help specific
+to that module.
+
+The following are included in the base package, and most of these can
+be preceded by a
+.B !
+to invert the sense of the match.
+.\" @MATCH@
+.SS account
+Account traffic for all hosts in defined network/netmask.
+
+Features:
+
+- long (one counter per protocol TCP/UDP/IMCP/Other) and short statistics
+
+- one iptables rule for all hosts in network/netmask
+
+- loading/saving counters (by reading/writting to procfs entries)
+
+.TP
+.BI "--aaddr " "network/netmask"
+defines network/netmask for which make statistics.
+.TP
+.BI "--aname " "name"
+defines name of list where statistics will be kept. If no is
+specified DEFAULT will be used.
+.TP
+.B "--ashort"
+table will colect only short statistics (only total counters
+without splitting it into protocols.
+.P
+Example usage:
+
+account traffic for/to 192.168.0.0/24 network into table mynetwork:
+
+# iptables -A FORWARD -m account --aname mynetwork --aaddr 192.168.0.0/24
+
+account traffic for/to WWW serwer for 192.168.0.0/24 network into table mywwwserver:
+
+# iptables -A INPUT -p tcp --dport 80
+  -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort
+
+# iptables -A OUTPUT -p tcp --sport 80
+  -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort
+
+read counters:
+
+# cat /proc/net/ipt_account/mynetwork
+# cat /proc/net/ipt_account/mywwwserver
+
+set counters:
+
+# echo "ip = 192.168.0.1 packets_src = 0" > /proc/net/ipt_account/mywwserver
+
+Webpage:
+  http://www.barbara.eu.org/~quaker/ipt_account/
+.SS addrtype
+This module matches packets based on their 
+.B address type.
+Address types are used within the kernel networking stack and categorize
+addresses into various groups.  The exact definition of that group depends on the specific layer three protocol.
+.TP
+The following address types are possible:
+.TP
+.BI "UNSPEC"
+an unspecified address (i.e. 0.0.0.0)
+.BI "UNICAST"
+an unicast address
+.BI "LOCAL"
+a local address
+.BI "BROADCAST"
+a broadcast address
+.BI "ANYCAST"
+an anycast packet
+.BI "MULTICAST"
+a multicast address
+.BI "BLACKHOLE"
+a blackhole address
+.BI "UNREACHABLE"
+an unreachable address
+.BI "PROHIBIT"
+a prohibited address
+.BI "THROW"
+FIXME
+.BI "NAT"
+FIXME
+.BI "XRESOLVE"
+FIXME
+.TP
+.BI "--src-type " "type"
+Matches if the source address is of given type
+.TP
+.BI "--dst-type " "type"
+Matches if the destination address is of given type
+.SS ah
+This module matches the SPIs in Authentication header of IPsec packets.
+.TP
+.BR "--ahspi " "[!] \fIspi\fP[:\fIspi\fP]"
+.SS childlevel
+This is an experimental module.  It matches on whether the 
+packet is part of a master connection or one of its children (or grandchildren,
+etc).  For instance, most packets are level 0.  FTP data transfer is level 1.
+.TP
+.BR "--childlevel " "[!] \fIlevel\fP"
+.SS comment
+Allows you to add comments (up to 256 characters) to any rule.
+.TP
+.BI "--comment " "comment"
+.TP
+Example:
+iptables -A INPUT -s 192.168.0.0/16 -m comment --comment "A privatized IP block"
+.SS condition
+This matches if a specific /proc filename is '0' or '1'.
+.TP
+.BI "--condition " "[!] \fIfilename\fP"
+Match on boolean value stored in /proc/net/ipt_condition/filename file
+.SS connbytes
+Match by how many bytes or packets a connection (or one of the two
+flows constituting the connection) have tranferred so far, or by
+average bytes per packet.
+
+The counters are 64bit and are thus not expected to overflow ;)
+
+The primary use is to detect long-lived downloads and mark them to be
+scheduled using a lower priority band in traffic control.
+
+The transfered bytes per connection can also be viewed through
+/proc/net/ip_conntrack and accessed via ctnetlink
+.TP
+[\fB!\fR]\fB --connbytes \fIfrom\fB:\fR[\fIto\fR]
+match packets from a connection whose packets/bytes/average packet
+size is more than FROM and less than TO bytes/packets. if TO is
+omitted only FROM check is done. "!" is used to match packets not
+falling in the range.
+.TP
+\fB--connbytes-dir\fR [\fBoriginal\fR|\fBreply\fR|\fBboth\fR]
+which packets to consider
+.TP
+\fB--connbytes-mode\fR [\fBpackets\fR|\fBbytes\fR|\fBavgpkt\fR]
+whether to check the amount of packets, number of bytes transferred or
+the average size (in bytes) of all packets received so far. Note that
+when "both" is used together with "avgpkt", and data is going (mainly)
+only in one direction (for example HTTP), the average packet size will
+be about half of the actual data packets.
+.TP
+Example:
+iptables .. -m connbytes --connbytes 10000:100000 --connbytes-dir both --connbytes-mode bytes ...
+.SS connlimit
+Allows you to restrict the number of parallel TCP connections to a
+server per client IP address (or address block).
+.TP
+[\fB!\fR] \fB--connlimit-above \fIn\fR
+match if the number of existing tcp connections is (not) above n
+.TP
+.BI "--connlimit-mask " "bits"
+group hosts using mask
+.P
+Examples:
+.TP
+# allow 2 telnet connections per client host
+iptables -p tcp --syn --dport 23 -m connlimit --connlimit-above 2 -j REJECT
+.TP
+# you can also match the other way around:
+iptables -p tcp --syn --dport 23 -m connlimit ! --connlimit-above 2 -j ACCEPT
+.TP
+# limit the nr of parallel http requests to 16 per class C sized \
+network (24 bit netmask)
+iptables -p tcp --syn --dport 80 -m connlimit --connlimit-above 16
+--connlimit-mask 24 -j REJECT
+.SS connmark
+This module matches the netfilter mark field associated with a connection
+(which can be set using the
+.B CONNMARK
+target below).
+.TP
+.BI "--mark " "value[/mask]"
+Matches packets in connections with the given mark value (if a mask is
+specified, this is logically ANDed with the mark before the
+comparison).
+.SS connrate
+This module matches the current transfer rate in a connection.
+.TP
+.BI "--connrate " "[!] [\fIfrom\fP]:[\fIto\fP]"
+Match against the current connection transfer rate being within 'from'
+and 'to' bytes per second. When the "!" argument is used before the
+range, the sense of the match is inverted.
+.SS conntrack
+This module, when combined with connection tracking, allows access to
+more connection tracking information than the "state" match.
+(this module is present only if iptables was compiled under a kernel
+supporting this feature)
+.TP
+.BI "--ctstate " "state"
+Where state is a comma separated list of the connection states to
+match.  Possible states are
+.B INVALID
+meaning that the packet is associated with no known connection,
+.B ESTABLISHED
+meaning that the packet is associated with a connection which has seen
+packets in both directions,
+.B NEW
+meaning that the packet has started a new connection, or otherwise
+associated with a connection which has not seen packets in both
+directions, and
+.B RELATED
+meaning that the packet is starting a new connection, but is
+associated with an existing connection, such as an FTP data transfer,
+or an ICMP error.
+.B SNAT
+A virtual state, matching if the original source address differs from
+the reply destination.
+.B DNAT
+A virtual state, matching if the original destination differs from the
+reply source.
+.TP
+.BI "--ctproto " "proto"
+Protocol to match (by number or name)
+.TP
+.BI "--ctorigsrc " "[!] \fIaddress\fP[/\fImask\fP]"
+Match against original source address
+.TP
+.BI "--ctorigdst " "[!] \fIaddress\fP[/\fImask\fP]"
+Match against original destination address
+.TP
+.BI "--ctreplsrc " "[!] \fIaddress\fP[/\fImask\fP]"
+Match against reply source address
+.TP
+.BI "--ctrepldst " "[!] \fIaddress\fB[/\fImask\fP]"
+Match against reply destination address
+.TP
+.BI "--ctstatus " "[\fINONE|EXPECTED|SEEN_REPLY|ASSURED\fP][,...]"
+Match against internal conntrack states
+.TP
+.BI "--ctexpire " "\fItime\fP[\fI:time\fP]"
+Match remaining lifetime in seconds against given value
+or range of values (inclusive)
+.SS dccp
+.TP
+\fB--source-port\fR,\fB--sport \fR[\fB!\fR] \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB--destination-port\fR,\fB--dport \fR[\fB!\fR] \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB--dccp-types\fR [\fB!\fR] \fImask\fP
+Match when the DCCP packet type is one of 'mask'. 'mask' is a comma-separated
+list of packet types.  Packet types are: 
+.BR "REQUEST RESPONSE DATA ACK DATAACK CLOSEREQ CLOSE RESET SYNC SYNCACK INVALID" .
+.TP
+\fB--dccp-option\fR [\fB!\fR\] \fInumber\fP
+Match if DCP option set.
+.SS dscp
+This module matches the 6 bit DSCP field within the TOS field in the
+IP header.  DSCP has superseded TOS within the IETF.
+.TP
+.BI "--dscp " "value"
+Match against a numeric (decimal or hex) value [0-32].
+.TP
+.BI "--dscp-class " "\fIDiffServ Class\fP"
+Match the DiffServ class. This value may be any of the
+BE, EF, AFxx or CSx classes.  It will then be converted
+into it's according numeric value.
+.SS dstlimit
+This module allows you to limit the packet per second (pps) rate on a per
+destination IP or per destination port base.  As opposed to the `limit' match,
+every destination ip / destination port has it's own limit.
+.TP
+THIS MODULE IS DEPRECATED AND HAS BEEN REPLACED BY ``hashlimit''
+.TP
+.BI "--dstlimit " "avg"
+Maximum average match rate (packets per second unless followed by /sec /minute /hour /day postfixes).
+.TP
+.BI "--dstlimit-mode " "mode"
+The limiting hashmode.  Is the specified limit per
+.B dstip, dstip-dstport
+tuple, 
+.B srcip-dstip
+tuple, or per
+.B srcipdstip-dstport
+tuple.
+.TP
+.BI "--dstlimit-name " "name"
+Name for /proc/net/ipt_dstlimit/* file entry
+.TP
+.BI "[" "--dstlimit-burst " "burst" "]"
+Number of packets to match in a burst.  Default: 5
+.TP
+.BI "[" "--dstlimit-htable-size " "size" "]"
+Number of buckets in the hashtable
+.TP
+.BI "[" "--dstlimit-htable-max " "max" "]"
+Maximum number of entries in the hashtable
+.TP
+.BI "[" "--dstlimit-htable-gcinterval " "interval" "]"
+Interval between garbage collection runs of the hashtable (in miliseconds).
+Default is 1000 (1 second).
+.TP
+.BI "[" "--dstlimit-htable-expire " "time"
+After which time are idle entries expired from hashtable (in miliseconds)?
+Default is 10000 (10 seconds).
+.SS ecn
+This allows you to match the ECN bits of the IPv4 and TCP header.  ECN is the Explicit Congestion Notification mechanism as specified in RFC3168
+.TP
+.BI "--ecn-tcp-cwr"
+This matches if the TCP ECN CWR (Congestion Window Received) bit is set.
+.TP
+.BI "--ecn-tcp-ece"
+This matches if the TCP ECN ECE (ECN Echo) bit is set.
+.TP
+.BI "--ecn-ip-ect " "num"
+This matches a particular IPv4 ECT (ECN-Capable Transport). You have to specify
+a number between `0' and `3'.
+.SS esp
+This module matches the SPIs in ESP header of IPsec packets.
+.TP
+.BR "--espspi " "[!] \fIspi\fP[:\fIspi\fP]"
+.SS fuzzy
+This module matches a rate limit based on a fuzzy logic controller [FLC]
+.TP
+.BI "--lower-limit " "number"
+Specifies the lower limit (in packets per second).
+.TP
+.BI "--upper-limit " "number"
+Specifies the upper limit (in packets per second).
+.SS hashlimit
+This patch adds a new match called 'hashlimit'.
+The idea is to have something like 'limit', but either per
+destination-ip or per (destip,destport) tuple.
+
+It gives you the ability to express
+.IP
+ '1000 packets per second for every host in 192.168.0.0/16'
+.IP
+ '100 packets per second for every service of 192.168.1.1'
+.P
+with a single iptables rule.
+.TP
+.BI "--hashlimit " "rate"
+A rate just like the limit match
+.TP
+.BI "--hashlimit-burst " "num"
+Burst value, just like limit match
+.TP
+.BI "--hashlimit-mode " "destip | destip-destport"
+Limit per IP or per port
+.TP
+.BI "--hashlimit-name " "foo"
+The name for the /proc/net/ipt_hashlimit/foo entry
+.TP
+.BI "--hashlimit-htable-size " "num"
+The number of buckets of the hash table
+.TP
+.BI "--hashlimit-htable-max " "num"
+Maximum entries in the hash
+.TP
+.BI "--hashlimit-htable-expire " "num"
+After how many miliseconds do hash entries expire
+.TP
+.BI "--hashlimit-htable-gcinterval " "num"
+How many miliseconds between garbage collection intervals
+.SS helper
+This module matches packets related to a specific conntrack-helper.
+.TP
+.BI "--helper " "string"
+Matches packets related to the specified conntrack-helper.
+.RS
+.PP
+string can be "ftp" for packets related to a ftp-session on default port.
+For other ports append -portnr to the value, ie. "ftp-2121".
+.PP
+Same rules apply for other conntrack-helpers.
+.RE
+.SS icmp
+This extension is loaded if `--protocol icmp' is specified.  It
+provides the following option:
+.TP
+.BR "--icmp-type " "[!] \fItypename\fP"
+This allows specification of the ICMP type, which can be a numeric
+ICMP type, or one of the ICMP type names shown by the command
+.nf
+ iptables -p icmp -h
+.fi
+.SS iprange
+This matches on a given arbitrary range of IPv4 addresses
+.TP
+.BI "[!]" "--src-range " "ip-ip"
+Match source IP in the specified range.
+.TP
+.BI "[!]" "--dst-range " "ip-ip"
+Match destination IP in the specified range.
+.SS ipv4options
+Match on IPv4 header options like source routing, record route,
+timestamp and router-alert.
+.TP
+.B "--ssrr"
+To match packets with the flag strict source routing.
+.TP
+.B "--lsrr"
+To match packets with the flag loose source routing.
+.TP
+.B "--no-srr"
+To match packets with no flag for source routing.
+.TP
+.B "\fR[\fB!\fR]\fB --rr"
+To match packets with the RR flag.
+.TP
+.B "\fR[\fB!\fR]\fB --ts"
+To match packets with the TS flag.
+.TP
+.B "\fR[\fB!\fR]\fB --ra"
+To match packets with the router-alert option.
+.TP
+.B "\fR[\fB!\fR]\fB --any-opt"
+To match a packet with at least one IP option, or no IP option
+at all if ! is chosen.
+.TP
+Examples:
+.TP
+$ iptables -A input -m ipv4options --rr -j DROP
+will drop packets with the record-route flag.
+.TP
+$ iptables -A input -m ipv4options --ts -j DROP
+will drop packets with the timestamp flag.
+.SS length
+This module matches the length of a packet against a specific value
+or range of values.
+.TP
+.BR "--length " "[!] \fIlength\fP[:\fIlength\fP]"
+.SS limit
+This module matches at a limited rate using a token bucket filter.
+A rule using this extension will match until this limit is reached
+(unless the `!' flag is used).  It can be used in combination with the
+.B LOG
+target to give limited logging, for example.
+.TP
+.BI "--limit " "rate"
+Maximum average matching rate: specified as a number, with an optional
+`/second', `/minute', `/hour', or `/day' suffix; the default is
+3/hour.
+.TP
+.BI "--limit-burst " "number"
+Maximum initial number of packets to match: this number gets
+recharged by one every time the limit specified above is not reached,
+up to this number; the default is 5.
+.SS mac
+.TP
+.BR "--mac-source " "[!] \fIaddress\fP"
+Match source MAC address.  It must be of the form XX:XX:XX:XX:XX:XX.
+Note that this only makes sense for packets coming from an Ethernet device
+and entering the
+.BR PREROUTING ,
+.B FORWARD
+or
+.B INPUT
+chains.
+.SS mark
+This module matches the netfilter mark field associated with a packet
+(which can be set using the
+.B MARK
+target below).
+.TP
+.BR "--mark " "\fIvalue\fP[/\fImask\fP]"
+Matches packets with the given unsigned mark value (if a \fImask\fP is
+specified, this is logically ANDed with the \fImask\fP before the
+comparison).
+.SS mport
+This module matches a set of source or destination ports.  Up to 15
+ports can be specified.  It can only be used in conjunction with
+.B "-p tcp"
+or
+.BR "-p udp" .
+.TP
+.BR "--source-ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the source port is one of the given ports.  The flag
+.B --sports
+is a convenient alias for this option.
+.TP
+.BR "--destination-ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the destination port is one of the given ports.  The flag
+.B --dports
+is a convenient alias for this option.
+.TP
+.BR "--ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the both the source and destination ports are equal to each
+other and to one of the given ports.
+.SS multiport
+This module matches a set of source or destination ports.  Up to 15
+ports can be specified.  A port range (port:port) counts as two
+ports.  It can only be used in conjunction with
+.B "-p tcp"
+or
+.BR "-p udp" .
+.TP
+.BR "--source-ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if the source port is one of the given ports.  The flag
+.B --sports
+is a convenient alias for this option.
+.TP
+.BR "--destination-ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if the destination port is one of the given ports.  The flag
+.B --dports
+is a convenient alias for this option.
+.TP
+.BR "--ports " "\fI[!] port\fP[,\fIport\fP[,\fIport:port\fP...]]"
+Match if either the source or destination ports are equal to one of
+the given ports.
+.SS nth
+This module matches every `n'th packet
+.TP
+.BI "--every " "value"
+Match every `value' packet
+.TP
+.BI "[" "--counter " "num" "]"
+Use internal counter number `num'.  Default is `0'.
+.TP
+.BI "[" "--start " "num" "]"
+Initialize the counter at the number `num' insetad of `0'.  Most between `0'
+and `value'-1.
+.TP
+.BI "[" "--packet " "num" "]"
+Match on `num' packet.  Most be between `0' and `value'-1.
+.SS osf
+The idea of passive OS fingerprint matching exists for quite a long time,
+but was created as extension fo OpenBSD pf only some weeks ago.
+Original idea was lurked in some OpenBSD mailing list (thanks
+grange@open...) and than adopted for Linux netfilter in form of this code.
+
+Original fingerprint table was created by Michal Zalewski <lcamtuf@coredump.cx>.
+
+This module compares some data(WS, MSS, options and it's order, ttl,
+df and others) from first SYN packet (actually from packets with SYN
+bit set) with dynamically loaded OS fingerprints.
+.TP
+.B "--log 1/0" 
+If present, OSF will log determined genres even if they don't match
+desired one.   
+0 - log all determined entries, 
+1 - only first one.
+
+In syslog you find something like this:
+.IP
+ipt_osf: Windows [2000:SP3:Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> 11.22.33.44:139
+.IP
+ipt_osf: Unknown: 16384:106:1:48:020405B401010402 44.33.22.11:1239 -> 11.22.33.44:80
+.TP
+.B "--smart"
+if present, OSF will use some smartness to determine remote OS.
+OSF will use initial TTL only if source of connection is in our local network.
+.TP
+.B "--netlink"
+If present, OSF will log all events also through netlink NETLINK_NFLOG groupt 1.
+.TP
+.BI "--genre " "[!] string"
+Match a OS genre by passive fingerprinting
+.P
+Example:
+
+#iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 1 --smart
+
+NOTE: -p tcp is obviously required as it is a TCP match.
+
+Fingerprints can be loaded and read through /proc/sys/net/ipv4/osf file.
+One can flush all fingerprints with following command:
+.IP
+echo -en FLUSH > /proc/sys/net/ipv4/osf
+.P
+Only one fingerprint per open/write/close.
+
+Fingerprints can be downloaded from http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os
+.SS owner
+This module attempts to match various characteristics of the packet
+creator, for locally-generated packets.  It is only valid in the
+.B OUTPUT
+chain, and even this some packets (such as ICMP ping responses) may
+have no owner, and hence never match.
+.TP
+.BI "--uid-owner " "userid"
+Matches if the packet was created by a process with the given
+effective user id.
+.TP
+.BI "--gid-owner " "groupid"
+Matches if the packet was created by a process with the given
+effective group id.
+.TP
+.BI "--pid-owner " "processid"
+Matches if the packet was created by a process with the given
+process id.
+.TP
+.BI "--sid-owner " "sessionid"
+Matches if the packet was created by a process in the given session
+group.
+.TP
+.BI "--cmd-owner " "name"
+Matches if the packet was created by a process with the given command name.
+(this option is present only if iptables was compiled under a kernel
+supporting this feature)
+.TP
+.B NOTE: pid, sid and command matching are broken on SMP
+.SS physdev
+This module matches on the bridge port input and output devices enslaved
+to a bridge device. This module is a part of the infrastructure that enables
+a transparent bridging IP firewall and is only useful for kernel versions
+above version 2.5.44.
+.TP
+.BR --physdev-in " [!] \fIname\fP"
+Name of a bridge port via which a packet is received (only for
+packets entering the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains). If the interface name ends in a "+", then any
+interface which begins with this name will match. If the packet didn't arrive
+through a bridge device, this packet won't match this option, unless '!' is used.
+.TP
+.BR --physdev-out " [!] \fIname\fP"
+Name of a bridge port via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD ,
+.B OUTPUT
+and
+.B POSTROUTING
+chains).  If the interface name ends in a "+", then any
+interface which begins with this name will match. Note that in the
+.BR nat " and " mangle
+.B OUTPUT
+chains one cannot match on the bridge output port, however one can in the
+.B "filter OUTPUT"
+chain. If the packet won't leave by a bridge device or it is yet unknown what
+the output device will be, then the packet won't match this option, unless
+'!' is used.
+.TP
+.RB "[!] " --physdev-is-in
+Matches if the packet has entered through a bridge interface.
+.TP
+.RB "[!] " --physdev-is-out
+Matches if the packet will leave through a bridge interface.
+.TP
+.RB "[!] " --physdev-is-bridged
+Matches if the packet is being bridged and therefore is not being routed.
+This is only useful in the FORWARD and POSTROUTING chains.
+.SS pkttype
+This module matches the link-layer packet type.
+.TP
+.BI "--pkt-type " "[\fIunicast\fP|\fIbroadcast\fP|\fImulticast\fP]"
+.SS policy
+This modules matches the policy used by IPsec for handling a packet.
+.TP
+.BI "--dir " "in|out"
+Used to select whether to match the policy used for decapsulation or the
+policy that will be used for encapsulation.
+.B in
+is valid in the
+.B PREROUTING, INPUT and FORWARD
+chains,
+.B out
+is valid in the
+.B POSTROUTING, OUTPUT and FORWARD
+chains.
+.TP
+.BI "--pol " "none|ipsec"
+Matches if the packet is subject to IPsec processing.
+.TP
+.BI "--strict"
+Selects whether to match the exact policy or match if any rule of
+the policy matches the given policy.
+.TP
+.BI "--reqid " "id"
+Matches the reqid of the policy rule. The reqid can be specified with
+.B setkey(8)
+using
+.B unique:id
+as level.
+.TP
+.BI "--spi " "spi"
+Matches the SPI of the SA.
+.TP
+.BI "--proto " "ah|esp|ipcomp"
+Matches the encapsulation protocol.
+.TP
+.BI "--mode " "tunnel|transport"
+Matches the encapsulation mode.
+.TP
+.BI "--tunnel-src " "addr[/mask]"
+Matches the source end-point address of a tunnel mode SA.
+Only valid with --mode tunnel.
+.TP
+.BI "--tunnel-dst " "addr[/mask]"
+Matches the destination end-point address of a tunnel mode SA.
+Only valid with --mode tunnel.
+.TP
+.BI "--next"
+Start the next element in the policy specification. Can only be used with
+--strict
+.SS psd
+Attempt to detect TCP and UDP port scans. This match was derived from
+Solar Designer's scanlogd.
+.TP
+.BI "--psd-weight-threshold " "threshold"
+Total weight of the latest TCP/UDP packets with different
+destination ports coming from the same host to be treated as port
+scan sequence.
+.TP
+.BI "--psd-delay-threshold " "delay"
+Delay (in hundredths of second) for the packets with different
+destination ports coming from the same host to be treated as
+possible port scan subsequence.
+.TP
+.BI "--psd-lo-ports-weight " "weight"
+Weight of the packet with privileged (<=1024) destination port.
+.TP
+.BI "--psd-hi-ports-weight " "weight"
+Weight of the packet with non-priviliged destination port.
+.SS quota
+Implements network quotas by decrementing a byte counter with each
+packet.
+.TP
+.BI "--quota " "bytes"
+The quota in bytes.
+.P
+KNOWN BUGS: this does not work on SMP systems.
+.SS random
+This module randomly matches a certain percentage of all packets.
+.TP
+.BI "--average " "percent"
+Matches the given percentage.  If omitted, a probability of 50% is set. 
+.SS realm
+This matches the routing realm.  Routing realms are used in complex routing
+setups involving dynamic routing protocols like BGP.
+.TP
+.BI "--realm " "[!]" "value[/mask]"
+Matches a given realm number (and optionally mask).
+.SS recent
+Allows you to dynamically create a list of IP addresses and then match
+against that list in a few different ways.
+
+For example, you can create a `badguy' list out of people attempting
+to connect to port 139 on your firewall and then DROP all future
+packets from them without considering them.
+.TP
+.BI "--name " "name"
+Specify the list to use for the commands. If no name is given then 'DEFAULT'
+will be used.
+.TP
+[\fB!\fR] \fB--set\fR
+This will add the source address of the packet to the list. If the
+source address is already in the list, this will update the existing
+entry. This will always return success (or failure if `!' is passed
+in).
+.TP
+[\fB!\fR] \fB--rcheck\fR
+Check if the source address of the packet is currently in
+the list.
+.TP
+[\fB!\fR] \fB--update\fR
+Like \fB--rcheck\fR, except it will update the "last seen" timestamp if it
+matches.
+.TP
+[\fB!\fR] \fB--remove\fR
+Check if the source address of the packet is currently in the list and
+if so that address will be removed from the list and the rule will
+return true. If the address is not found, false is returned.
+.TP
+[\fB!\fR] \fB--seconds \fIseconds\fR
+This option must be used in conjunction with one of \fB--rcheck\fR or
+\fB--update\fR. When used, this will narrow the match to only happen
+when the address is in the list and was seen within the last given
+number of seconds.
+.TP
+[\fB!\fR] \fB--hitcount \fIhits\fR
+This option must be used in conjunction with one of \fB--rcheck\fR or
+\fB--update\fR. When used, this will narrow the match to only happen
+when the address is in the list and packets had been received greater
+than or equal to the given value. This option may be used along with
+\fB--seconds\fR to create an even narrower match requiring a certain
+number of hits within a specific time frame.
+.TP
+\fB--rttl\fR
+This option must be used in conjunction with one of \fB--rcheck\fR or
+\fB--update\fR. When used, this will narrow the match to only happen
+when the address is in the list and the TTL of the current packet
+matches that of the packet which hit the \fB--set\fR rule. This may be
+useful if you have problems with people faking their source address in
+order to DoS you via this module by disallowing others access to your
+site by sending bogus packets to you.
+.P
+Examples:
+.IP
+# iptables -A FORWARD -m recent --name badguy --rcheck --seconds 60 -j DROP
+
+# iptables -A FORWARD -p tcp -i eth0 --dport 139 -m recent --name badguy --set -j DROP
+.P
+Official website (http://snowman.net/projects/ipt_recent/) also has
+some examples of usage.
+
+/proc/net/ipt_recent/* are the current lists of addresses and information 
+about each entry of each list.
+
+Each file in /proc/net/ipt_recent/ can be read from to see the current list
+or written two using the following commands to modify the list:
+.TP
+echo xx.xx.xx.xx > /proc/net/ipt_recent/DEFAULT
+to Add to the DEFAULT list
+.TP
+echo -xx.xx.xx.xx > /proc/net/ipt_recent/DEFAULT
+to Remove from the DEFAULT list
+.TP
+echo clear > /proc/net/ipt_recent/DEFAULT
+to empty the DEFAULT list.
+.P
+The module itself accepts parameters, defaults shown:
+.TP
+.BI "ip_list_tot=" "100"
+Number of addresses remembered per table
+.TP
+.BI "ip_pkt_list_tot=" "20"
+Number of packets per address remembered
+.TP
+.BI "ip_list_hash_size=" "0"
+Hash table size. 0 means to calculate it based on ip_list_tot, default: 512
+.TP
+.BI "ip_list_perms=" "0644"
+Permissions for /proc/net/ipt_recent/* files
+.TP
+.BI "debug=" "0"
+Set to 1 to get lots of debugging info
+.SS sctp
+.TP
+\fB--source-port\fR,\fB--sport \fR[\fB!\fR] \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB--destination-port\fR,\fB--dport \fR[\fB!\fR] \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB--chunk-types\fR [\fB!\fR] \fBall\fR|\fBany\fR|\fBonly \fIchunktype\fR[\fB:\fIflags\fR] [...]
+The flag letter in upper case indicates that the flag is to match if set,
+in the lower case indicates to match if unset.
+
+Chunk types: DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK
+
+chunk type            available flags      
+.br
+DATA                  U B E u b e         
+.br
+ABORT                 T t                 
+.br
+SHUTDOWN_COMPLETE     T t                 
+
+(lowercase means flag should be "off", uppercase means "on")
+.P
+Examples:
+
+iptables -A INPUT -p sctp --dport 80 -j DROP
+
+iptables -A INPUT -p sctp --chunk-types any DATA,INIT -j DROP
+
+iptables -A INPUT -p sctp --chunk-types any DATA:Be -j ACCEPT
+.SS set
+This modules macthes IP sets which can be defined by ipset(8).
+.TP
+.BR "--set " "setname flag[,flag...]"
+where flags are
+.BR "src"
+and/or
+.BR "dst" 
+and there can be no more than six of them. Hence the command
+.nf
+ iptables -A FORWARD -m set --set test src,dst
+.fi
+will match packets, for which (depending on the type of the set) the source
+address or port number of the packet can be found in the specified set. If 
+there is a binding belonging to the mached set element or there is a default 
+binding for the given set, then the rule will match the packet only if 
+additionally (depending on the type of the set) the destination address or 
+port number of the packet can be found in the set according to the binding.
+.SS state
+This module, when combined with connection tracking, allows access to
+the connection tracking state for this packet.
+.TP
+.BI "--state " "state"
+Where state is a comma separated list of the connection states to
+match.  Possible states are
+.B INVALID
+meaning that the packet could not be identified for some reason which
+includes running out of memory and ICMP errors which don't correspond to any
+known connection,
+.B ESTABLISHED
+meaning that the packet is associated with a connection which has seen
+packets in both directions,
+.B NEW
+meaning that the packet has started a new connection, or otherwise
+associated with a connection which has not seen packets in both
+directions, and
+.B RELATED
+meaning that the packet is starting a new connection, but is
+associated with an existing connection, such as an FTP data transfer,
+or an ICMP error.
+.SS string
+This modules matches a given string by using some pattern matching strategy. It requires a linux kernel >= 2.6.14.
+.TP
+.BI "--algo  " "bm|kmp"
+Select the pattern matching strategy. (bm = Boyer-Moore, kmp = Knuth-Pratt-Morris)
+.TP
+.BI "--from " "offset"
+Set the offset from which it starts looking for any matching. If not passed, default is 0.
+.TP
+.BI "--to " "offset"
+Set the offset from which it starts looking for any matching. If not passed, default is the packet size.
+.TP
+.BI "--string " "pattern"
+Matches the given pattern.
+.BI "--hex-string " "pattern"
+Matches the given pattern in hex notation.
+.SS tcp
+These extensions are loaded if `--protocol tcp' is specified. It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification. This can either be a service
+name or a port number. An inclusive range can also be specified,
+using the format
+.IR port : port .
+If the first port is omitted, "0" is assumed; if the last is omitted,
+"65535" is assumed.
+If the second port greater then the first they will be swapped.
+The flag
+.B --sport
+is a convenient alias for this option.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification.  The flag
+.B --dport
+is a convenient alias for this option.
+.TP
+.BR "--tcp-flags " "[!] \fImask\fP \fIcomp\fP"
+Match when the TCP flags are as specified.  The first argument is the
+flags which we should examine, written as a comma-separated list, and
+the second argument is a comma-separated list of flags which must be
+set.  Flags are:
+.BR "SYN ACK FIN RST URG PSH ALL NONE" .
+Hence the command
+.nf
+ iptables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST SYN
+.fi
+will only match packets with the SYN flag set, and the ACK, FIN and
+RST flags unset.
+.TP
+.B "[!] --syn"
+Only match TCP packets with the SYN bit set and the ACK,RST and FIN bits
+cleared.  Such packets are used to request TCP connection initiation;
+for example, blocking such packets coming in an interface will prevent
+incoming TCP connections, but outgoing TCP connections will be
+unaffected.
+It is equivalent to \fB--tcp-flags SYN,RST,ACK,FIN SYN\fP.
+If the "!" flag precedes the "--syn", the sense of the
+option is inverted.
+.TP
+.BR "--tcp-option " "[!] \fInumber\fP"
+Match if TCP option set.
+.TP
+.BR "--mss " "\fIvalue\fP[:\fIvalue\fP]"
+Match TCP SYN or SYN/ACK packets with the specified MSS value (or range),
+which control the maximum packet size for that connection.
+.SS tcpmss
+This matches the TCP MSS (maximum segment size) field of the TCP header.  You can only use this on TCP SYN or SYN/ACK packets, since the MSS is only negotiated during the TCP handshake at connection startup time.
+.TP
+.BI "[!] "--mss " "value[:value]"
+Match a given TCP MSS value or range.
+.SS time
+This matches if the packet arrival time/date is within a given range. All options are facultative.
+.TP
+.BI " --timestart " "value"
+Match only if it is after `value' (Inclusive, format: HH:MM ; default 00:00).
+.TP
+.BI "--timestop  " "value"
+Match only if it is before `value' (Inclusive, format: HH:MM ; default 23:59).
+.TP
+.BI "--days " "listofdays"
+Match only if today is one of the given days. (format: Mon,Tue,Wed,Thu,Fri,Sat,Sun ; default everyday)
+.TP
+.BI "--datestart " "date"
+Match only if it is after `date' (Inclusive, format: YYYY[:MM[:DD[:hh[:mm[:ss]]]]] ; h,m,s start from 0 ; default to 1970)
+.TP
+.BI "--datestop " "date"
+Match only if it is before `date' (Inclusive, format: YYYY[:MM[:DD[:hh[:mm[:ss]]]]] ; h,m,s start from 0 ; default to 2037)
+.SS tos
+This module matches the 8 bits of Type of Service field in the IP
+header (ie. including the precedence bits).
+.TP
+.BI "--tos " "tos"
+The argument is either a standard name, (use
+.br
+ iptables -m tos -h
+.br
+to see the list), or a numeric value to match.
+.SS ttl
+This module matches the time to live field in the IP header.
+.TP
+.BI "--ttl-eq " "ttl"
+Matches the given TTL value.
+.TP
+.BI "--ttl-gt " "ttl"
+Matches if TTL is greater than the given TTL value.
+.TP
+.BI "--ttl-lt " "ttl"
+Matches if TTL is less than the given TTL value.
+.SS u32
+U32 allows you to extract quantities of up to 4 bytes from a packet,
+AND them with specified masks, shift them by specified amounts and
+test whether the results are in any of a set of specified ranges.
+The specification of what to extract is general enough to skip over
+headers with lengths stored in the packet, as in IP or TCP header
+lengths.
+
+Details and examples are in the kernel module source.
+.SS udp
+These extensions are loaded if `--protocol udp' is specified.  It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification.
+See the description of the
+.B --source-port
+option of the TCP extension for details.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification.
+See the description of the
+.B --destination-port
+option of the TCP extension for details.
+.SS unclean
+This module takes no options, but attempts to match packets which seem
+malformed or unusual.  This is regarded as experimental.
+.SH TARGET EXTENSIONS
+iptables can use extended target modules: the following are included
+in the standard distribution.
+.\" @TARGET@
+.SS BALANCE
+This allows you to DNAT connections in a round-robin way over a given range of destination addresses.
+.TP
+.BI "--to-destination " "ipaddr-ipaddr"
+Address range to round-robin over.
+.SS CLASSIFY
+This module allows you to set the skb->priority value (and thus classify the packet into a specific CBQ class).
+.TP
+.BI "--set-class " "MAJOR:MINOR"
+Set the major and minor class value.
+.SS CLUSTERIP
+This module allows you to configure a simple cluster of nodes that share
+a certain IP and MAC address without an explicit load balancer in front of
+them.  Connections are statically distributed between the nodes in this
+cluster.
+.TP
+.BI "--new "
+Create a new ClusterIP.  You always have to set this on the first rule
+for a given ClusterIP.
+.TP
+.BI "--hashmode " "mode"
+Specify the hashing mode.  Has to be one of
+.B sourceip, sourceip-sourceport, sourceip-sourceport-destport
+.TP
+.BI "--clustermac " "mac"
+Specify the ClusterIP MAC address.  Has to be a link-layer multicast address
+.TP
+.BI "--total-nodes " "num"
+Number of total nodes within this cluster.
+.TP
+.BI "--local-node " "num"
+Local node number within this cluster.
+.TP
+.BI "--hash-init " "rnd"
+Specify the random seed used for hash initialization.
+.SS CONNMARK
+This module sets the netfilter mark value associated with a connection
+.TP
+.B --set-mark mark[/mask]
+Set connection mark. If a mask is specified then only those bits set in the
+mask is modified.
+.TP
+.B --save-mark [--mask mask]
+Copy the netfilter packet mark value to the connection mark. If a mask
+is specified then only those bits are copied.
+.TP
+.B --restore-mark [--mask mask]
+Copy the connection mark value to the packet. If a mask is specified
+then only those bits are copied. This is only valid in the
+.B mangle
+table.
+.SS DNAT
+This target is only valid in the
+.B nat
+table, in the
+.B PREROUTING
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains.  It specifies that the destination address of the packet
+should be modified (and all future packets in this connection will
+also be mangled), and rules should cease being examined.  It takes one
+type of option:
+.TP
+.BR "--to-destination " "\fIipaddr\fP[-\fIipaddr\fP][:\fIport\fP-\fIport\fP]"
+which can specify a single new destination IP address, an inclusive
+range of IP addresses, and optionally, a port range (which is only
+valid if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" ).
+If no port range is specified, then the destination port will never be
+modified.
+.RS
+.PP
+In Kernels up to 2.6.10 you can add several --to-destination options.  For
+those kernels, if you specify more than one destination address, either via an
+address range or multiple --to-destination options, a simple round-robin (one
+after another in cycle) load balancing takes place between these addresses.
+Later Kernels (>= 2.6.11-rc1) don't have the ability to NAT to multiple ranges
+anymore.
+
+.SS DSCP
+This target allows to alter the value of the DSCP bits within the TOS
+header of the IPv4 packet.  As this manipulates a packet, it can only
+be used in the mangle table.
+.TP
+.BI "--set-dscp " "value"
+Set the DSCP field to a numerical value (can be decimal or hex)
+.TP
+.BI "--set-dscp-class " "class"
+Set the DSCP field to a DiffServ class.
+.SS ECN
+This target allows to selectively work around known ECN blackholes.
+It can only be used in the mangle table.
+.TP
+.BI "--ecn-tcp-remove"
+Remove all ECN bits from the TCP header.  Of course, it can only be used
+in conjunction with
+.BR "-p tcp" .
+.SS IPMARK
+Allows you to mark a received packet basing on its IP address. This
+can replace many mangle/mark entries with only one, if you use
+firewall based classifier.
+
+This target is to be used inside the mangle table, in the PREROUTING,
+POSTROUTING or FORWARD hooks.
+.TP
+.BI "--addr " "src/dst"
+Use source or destination IP address.
+.TP
+.BI "--and-mask " "mask"
+Perform bitwise `and' on the IP address and this mask.
+.TP
+.BI "--or-mask " "mask"
+Perform bitwise `or' on the IP address and this mask.
+.P
+The order of IP address bytes is reversed to meet "human order of bytes":
+192.168.0.1 is 0xc0a80001. At first the `and' operation is performed, then
+`or'.
+
+Examples:
+
+We create a queue for each user, the queue number is adequate
+to the IP address of the user, e.g.: all packets going to/from 192.168.5.2
+are directed to 1:0502 queue, 192.168.5.12 -> 1:050c etc.
+
+We have one classifier rule:
+.IP
+tc filter add dev eth3 parent 1:0 protocol ip fw
+.P
+Earlier we had many rules just like below:
+.IP
+iptables -t mangle -A POSTROUTING -o eth3 -d 192.168.5.2 -j MARK
+--set-mark 0x10502
+.IP
+iptables -t mangle -A POSTROUTING -o eth3 -d 192.168.5.3 -j MARK
+--set-mark 0x10503
+.P
+Using IPMARK target we can replace all the mangle/mark rules with only one:
+.IP
+iptables -t mangle -A POSTROUTING -o eth3 -j IPMARK --addr=dst
+--and-mask=0xffff --or-mask=0x10000
+.P
+On the routers with hundreds of users there should be significant load
+decrease (e.g. twice).
+.SS IPV4OPTSSTRIP
+Strip all the IP options from a packet.
+
+The target doesn't take any option, and therefore is extremly easy to use :
+
+# iptables -t mangle -A PREROUTING -j IPV4OPTSSTRIP
+.SS LOG
+Turn on kernel logging of matching packets.  When this option is set
+for a rule, the Linux kernel will print some information on all
+matching packets (like most IP header fields) via the kernel log
+(where it can be read with
+.I dmesg
+or 
+.IR syslogd (8)).
+This is a "non-terminating target", i.e. rule traversal continues at
+the next rule.  So if you want to LOG the packets you refuse, use two
+separate rules with the same matching criteria, first using target LOG
+then DROP (or REJECT).
+.TP
+.BI "--log-level " "level"
+Level of logging (numeric or see \fIsyslog.conf\fP(5)).
+.TP
+.BI "--log-prefix " "prefix"
+Prefix log messages with the specified prefix; up to 29 letters long,
+and useful for distinguishing messages in the logs.
+.TP
+.B --log-tcp-sequence
+Log TCP sequence numbers. This is a security risk if the log is
+readable by users.
+.TP
+.B --log-tcp-options
+Log options from the TCP packet header.
+.TP
+.B --log-ip-options
+Log options from the IP packet header.
+.TP
+.B --log-uid
+Log the userid of the process which generated the packet.
+.SS MARK
+This is used to set the netfilter mark value associated with the
+packet.  It is only valid in the
+.B mangle
+table.  It can for example be used in conjunction with iproute2.
+.TP
+.BI "--set-mark " "mark"
+.SS MASQUERADE
+This target is only valid in the
+.B nat
+table, in the
+.B POSTROUTING
+chain.  It should only be used with dynamically assigned IP (dialup)
+connections: if you have a static IP address, you should use the SNAT
+target.  Masquerading is equivalent to specifying a mapping to the IP
+address of the interface the packet is going out, but also has the
+effect that connections are
+.I forgotten
+when the interface goes down.  This is the correct behavior when the
+next dialup is unlikely to have the same interface address (and hence
+any established connections are lost anyway).  It takes one option:
+.TP
+.BR "--to-ports " "\fIport\fP[-\fIport\fP]"
+This specifies a range of source ports to use, overriding the default
+.B SNAT
+source port-selection heuristics (see above).  This is only valid
+if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" .
+.SS MIRROR
+This is an experimental demonstration target which inverts the source
+and destination fields in the IP header and retransmits the packet.
+It is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains, and user-defined chains which are only called from those
+chains.  Note that the outgoing packets are
+.B NOT
+seen by any packet filtering chains, connection tracking or NAT, to
+avoid loops and other problems.
+.SS NETMAP
+This target allows you to statically map a whole network of addresses onto
+another network of addresses.  It can only be used from rules in the
+.B nat
+table.
+.TP
+.BI "--to "  "address[/mask]"
+Network address to map to.  The resulting address will be constructed in the
+following way: All 'one' bits in the mask are filled in from the new `address'.
+All bits that are zero in the mask are filled in from the original address.
+.SS NFQUEUE
+This target is an extension of the QUEUE target. As opposed to QUEUE, it allows
+you to put a packet into any specific queue, identified by its 16-bit queue
+number.  
+.TP
+.BR "--queue-num " "\fIvalue"
+This specifies the QUEUE number to use. Valud queue numbers are 0 to 65535. The default value is 0.
+.TP
+It can only be used with Kernel versions 2.6.14 or later, since it requires
+the
+.B
+nfnetlink_queue
+kernel support.
+.SS NOTRACK
+This target disables connection tracking for all packets matching that rule.
+.TP
+It can only be used in the
+.B raw
+table.
+.SS REDIRECT
+This target is only valid in the
+.B nat
+table, in the
+.B PREROUTING
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains.  It redirects the packet to the machine itself by changing the
+destination IP to the primary address of the incoming interface
+(locally-generated packets are mapped to the 127.0.0.1 address).  It
+takes one option:
+.TP
+.BR "--to-ports " "\fIport\fP[-\fIport\fP]"
+This specifies a destination port or range of ports to use: without
+this, the destination port is never altered.  This is only valid
+if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" .
+.SS REJECT
+This is used to send back an error packet in response to the matched
+packet: otherwise it is equivalent to
+.B DROP
+so it is a terminating TARGET, ending rule traversal.
+This target is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains.  The following option controls the nature of the error packet
+returned:
+.TP
+.BI "--reject-with " "type"
+The type given can be
+.nf
+.B " icmp-net-unreachable"
+.B " icmp-host-unreachable"
+.B " icmp-port-unreachable"
+.B " icmp-proto-unreachable"
+.B " icmp-net-prohibited"
+.B " icmp-host-prohibited or"
+.B " icmp-admin-prohibited (*)"
+.fi
+which return the appropriate ICMP error message (\fBport-unreachable\fP is
+the default).  The option
+.B tcp-reset
+can be used on rules which only match the TCP protocol: this causes a
+TCP RST packet to be sent back.  This is mainly useful for blocking 
+.I ident
+(113/tcp) probes which frequently occur when sending mail to broken mail
+hosts (which won't accept your mail otherwise).
+.TP
+(*) Using icmp-admin-prohibited with kernels that do not support it will result in a plain DROP instead of REJECT
+.SS ROUTE
+This is used to explicitly override the core network stack's routing decision.
+.B mangle
+table.
+.TP
+.BI "--oif " "ifname"
+Route the packet through `ifname' network interface
+.TP
+.BI "--iif " "ifname"
+Change the packet's incoming interface to `ifname'
+.TP
+.BI "--gw " "IP_address"
+Route the packet via this gateway
+.TP
+.BI "--continue "
+Behave like a non-terminating target and continue traversing the rules.  Not valid in combination with `--iif' or `--tee'
+.TP
+.BI "--tee "
+Make a copy of the packet, and route that copy to the given destination. For the original, uncopied packet, behave like a non-terminating target and continue traversing the rules.  Not valid in combination with `--iif' or `--continue'
+.SS SAME
+Similar to SNAT/DNAT depending on chain: it takes a range of addresses
+(`--to 1.2.3.4-1.2.3.7') and gives a client the same
+source-/destination-address for each connection.
+.TP
+.BI "--to " "<ipaddr>-<ipaddr>"
+Addresses to map source to. May be specified more than once for
+multiple ranges.
+.TP
+.B "--nodst"
+Don't use the destination-ip in the calculations when selecting the
+new source-ip
+.SS SET
+This modules adds and/or deletes entries from IP sets which can be defined 
+by ipset(8).
+.TP
+.BR "--add-set " "setname flag[,flag...]"
+add the address(es)/port(s) of the packet to the sets
+.TP
+.BR "--del-set " "setname flag[,flag...]"
+delete the address(es)/port(s) of the packet from the sets,
+where flags are
+.BR "src"
+and/or
+.BR "dst"
+and there can be no more than six of them.
+.TP
+The bindings to follow must previously be defined in order to use 
+multilevel adding/deleting by the SET target.
+.SS SNAT
+This target is only valid in the
+.B nat
+table, in the
+.B POSTROUTING
+chain.  It specifies that the source address of the packet should be
+modified (and all future packets in this connection will also be
+mangled), and rules should cease being examined.  It takes one type
+of option:
+.TP
+.BR "--to-source  " "\fIipaddr\fP[-\fIipaddr\fP][:\fIport\fP-\fIport\fP]"
+which can specify a single new source IP address, an inclusive range
+of IP addresses, and optionally, a port range (which is only valid if
+the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" ).
+If no port range is specified, then source ports below 512 will be
+mapped to other ports below 512: those between 512 and 1023 inclusive
+will be mapped to ports below 1024, and other ports will be mapped to
+1024 or above. Where possible, no port alteration will occur.
+.RS
+.PP
+In Kernels up to 2.6.10, you can add several --to-source options.  For those
+kernels, if you specify more than one source address, either via an address
+range or multiple --to-source options, a simple round-robin (one after another
+in cycle) takes place between these addresses.
+Later Kernels (>= 2.6.11-rc1) don't have the ability to NAT to multiple ranges
+anymore.
+.SS TARPIT
+Captures and holds incoming TCP connections using no local
+per-connection resources. Connections are accepted, but immediately
+switched to the persist state (0 byte window), in which the remote
+side stops sending data and asks to continue every 60-240 seconds.
+Attempts to close the connection are ignored, forcing the remote side
+to time out the connection in 12-24 minutes.
+
+This offers similar functionality to LaBrea
+<http://www.hackbusters.net/LaBrea/> but doesn't require dedicated
+hardware or IPs. Any TCP port that you would normally DROP or REJECT
+can instead become a tarpit.
+
+To tarpit connections to TCP port 80 destined for the current machine:
+.IP
+iptables -A INPUT -p tcp -m tcp --dport 80 -j TARPIT
+.P
+To significantly slow down Code Red/Nimda-style scans of unused address
+space, forward unused ip addresses to a Linux box not acting as a router
+(e.g. "ip route 10.0.0.0 255.0.0.0 ip.of.linux.box" on a Cisco), enable IP
+forwarding on the Linux box, and add:
+.IP
+iptables -A FORWARD -p tcp -j TARPIT
+.IP
+iptables -A FORWARD -j DROP
+.TP
+NOTE:
+If you use the conntrack module while you are using TARPIT, you should
+also use the NOTRACK target, or the kernel will unnecessarily allocate
+resources for each TARPITted connection. To TARPIT incoming
+connections to the standard IRC port while using conntrack, you could:
+.IP
+iptables -t raw -A PREROUTING -p tcp --dport 6667 -j NOTRACK
+.IP
+iptables -A INPUT -p tcp --dport 6667 -j TARPIT
+.SS TCPMSS
+This target allows to alter the MSS value of TCP SYN packets, to control
+the maximum size for that connection (usually limiting it to your
+outgoing interface's MTU minus 40).  Of course, it can only be used
+in conjunction with
+.BR "-p tcp" .
+It is only valid in the
+.BR mangle
+table.
+.br
+This target is used to overcome criminally braindead ISPs or servers
+which block ICMP Fragmentation Needed packets.  The symptoms of this
+problem are that everything works fine from your Linux
+firewall/router, but machines behind it can never exchange large
+packets:
+.PD 0
+.RS 0.1i
+.TP 0.3i
+1)
+Web browsers connect, then hang with no data received.
+.TP
+2)
+Small mail works fine, but large emails hang.
+.TP
+3)
+ssh works fine, but scp hangs after initial handshaking.
+.RE
+.PD
+Workaround: activate this option and add a rule to your firewall
+configuration like:
+.nf
+ iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \\
+             -j TCPMSS --clamp-mss-to-pmtu
+.fi
+.TP
+.BI "--set-mss " "value"
+Explicitly set MSS option to specified value.
+.TP
+.B "--clamp-mss-to-pmtu"
+Automatically clamp MSS value to (path_MTU - 40).
+.TP
+These options are mutually exclusive.
+.SS TOS
+This is used to set the 8-bit Type of Service field in the IP header.
+It is only valid in the
+.B mangle
+table.
+.TP
+.BI "--set-tos " "tos"
+You can use a numeric TOS values, or use
+.nf
+ iptables -j TOS -h
+.fi
+to see the list of valid TOS names.
+.SS TRACE
+This target has no options.  It just turns on 
+.B packet tracing
+for all packets that match this rule.
+.SS TTL
+This is used to modify the IPv4 TTL header field.  The TTL field determines
+how many hops (routers) a packet can traverse until it's time to live is
+exceeded.
+.TP
+Setting or incrementing the TTL field can potentially be very dangerous,
+so it should be avoided at any cost.  
+.TP
+.B Don't ever set or increment the value on packets that leave your local network!
+.B mangle
+table.
+.TP
+.BI "--ttl-set " "value"
+Set the TTL value to `value'.
+.TP
+.BI "--ttl-dec " "value"
+Decrement the TTL value `value' times.
+.TP
+.BI "--ttl-inc " "value"
+Increment the TTL value `value' times.
+.SS ULOG
+This target provides userspace logging of matching packets.  When this
+target is set for a rule, the Linux kernel will multicast this packet
+through a
+.IR netlink 
+socket. One or more userspace processes may then subscribe to various 
+multicast groups and receive the packets.
+Like LOG, this is a "non-terminating target", i.e. rule traversal
+continues at the next rule.
+.TP
+.BI "--ulog-nlgroup " "nlgroup"
+This specifies the netlink group (1-32) to which the packet is sent.
+Default value is 1.
+.TP
+.BI "--ulog-prefix " "prefix"
+Prefix log messages with the specified prefix; up to 32 characters
+long, and useful for distinguishing messages in the logs.
+.TP
+.BI "--ulog-cprange " "size"
+Number of bytes to be copied to userspace.  A value of 0 always copies
+the entire packet, regardless of its size.  Default is 0.
+.TP
+.BI "--ulog-qthreshold " "size"
+Number of packet to queue inside kernel.  Setting this value to, e.g. 10
+accumulates ten packets inside the kernel and transmits them as one
+netlink multipart message to userspace.  Default is 1 (for backwards
+compatibility).
+.br
+.SS XOR
+Encrypt TCP and UDP traffic using a simple XOR encryption
+.TP
+.BI "--key " "string"
+Set key to "string"
+.TP
+.BI "--block-size"
+Set block size
+.SH DIAGNOSTICS
+Various error messages are printed to standard error.  The exit code
+is 0 for correct functioning.  Errors which appear to be caused by
+invalid or abused command line parameters cause an exit code of 2, and
+other errors cause an exit code of 1.
+.SH BUGS
+Bugs?  What's this? ;-)
+Well, you might want to have a look at http://bugzilla.netfilter.org/
+.SH COMPATIBILITY WITH IPCHAINS
+This
+.B iptables
+is very similar to ipchains by Rusty Russell.  The main difference is
+that the chains
+.B INPUT
+and
+.B OUTPUT
+are only traversed for packets coming into the local host and
+originating from the local host respectively.  Hence every packet only
+passes through one of the three chains (except loopback traffic, which
+involves both INPUT and OUTPUT chains); previously a forwarded packet
+would pass through all three.
+.PP
+The other main difference is that
+.B -i
+refers to the input interface;
+.B -o
+refers to the output interface, and both are available for packets
+entering the
+.B FORWARD
+chain.
+.PP The various forms of NAT have been separated out; 
+.B iptables 
+is a pure packet filter when using the default `filter' table, with
+optional extension modules.  This should simplify much of the previous
+confusion over the combination of IP masquerading and packet filtering
+seen previously.  So the following options are handled differently:
+.nf
+ -j MASQ
+ -M -S
+ -M -L
+.fi
+There are several other changes in iptables.
+.SH SEE ALSO
+.BR iptables-save (8),
+.BR iptables-restore (8),
+.BR ip6tables (8),
+.BR ip6tables-save (8),
+.BR ip6tables-restore (8),
+.BR libipq (3).
+.P
+The packet-filtering-HOWTO details iptables usage for
+packet filtering, the NAT-HOWTO details NAT,
+the netfilter-extensions-HOWTO details the extensions that are
+not in the standard distribution,
+and the netfilter-hacking-HOWTO details the netfilter internals.
+.br
+See
+.BR "http://www.netfilter.org/" .
+.SH AUTHORS
+Rusty Russell originally wrote iptables, in early consultation with Michael
+Neuling.
+.PP
+Marc Boucher made Rusty abandon ipnatctl by lobbying for a generic packet
+selection framework in iptables, then wrote the mangle table, the owner match,
+the mark stuff, and ran around doing cool stuff everywhere.
+.PP
+James Morris wrote the TOS target, and tos match.
+.PP
+Jozsef Kadlecsik wrote the REJECT target.
+.PP
+Harald Welte wrote the ULOG and NFQUEUE target, the new libiptc, as well as the TTL, DSCP, ECN matches and targets.
+.PP
+The Netfilter Core Team is: Marc Boucher, Martin Josefsson, Jozsef Kadlecsik, 
+Patrick McHardy, James Morris, Harald Welte and Rusty Russell.
+.PP
+Man page originally written by Herve Eychenne <rv@wallfire.org>.
+.\" .. and did I mention that we are incredibly cool people?
+.\" .. sexy, too ..
+.\" .. witty, charming, powerful ..
+.\" .. and most of all, modest ..
diff --git a/iptables.8.in b/iptables.8.in
new file mode 100644 (file)
index 0000000..9904c48
--- /dev/null
@@ -0,0 +1,497 @@
+.TH IPTABLES 8 "Mar 09, 2002" "" ""
+.\"
+.\" Man page written by Herve Eychenne <rv@wallfire.org> (May 1999)
+.\" It is based on ipchains page.
+.\" TODO: add a word for protocol helpers (FTP, IRC, SNMP-ALG)
+.\"
+.\" ipchains page by Paul ``Rusty'' Russell March 1997
+.\" Based on the original ipfwadm man page by Jos Vos <jos@xos.nl>
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+iptables \- administration tool for IPv4 packet filtering and NAT
+.SH SYNOPSIS
+.BR "iptables [-t table] -[AD] " "chain rule-specification [options]"
+.br
+.BR "iptables [-t table] -I " "chain [rulenum] rule-specification [options]"
+.br
+.BR "iptables [-t table] -R " "chain rulenum rule-specification [options]"
+.br
+.BR "iptables [-t table] -D " "chain rulenum [options]"
+.br
+.BR "iptables [-t table] -[LFZ] " "[chain] [options]"
+.br
+.BR "iptables [-t table] -N " "chain"
+.br
+.BR "iptables [-t table] -X " "[chain]"
+.br
+.BR "iptables [-t table] -P " "chain target [options]"
+.br
+.BR "iptables [-t table] -E " "old-chain-name new-chain-name"
+.SH DESCRIPTION
+.B Iptables
+is used to set up, maintain, and inspect the tables of IP packet
+filter rules in the Linux kernel.  Several different tables
+may be defined.  Each table contains a number of built-in
+chains and may also contain user-defined chains.
+
+Each chain is a list of rules which can match a set of packets.  Each
+rule specifies what to do with a packet that matches.  This is called
+a `target', which may be a jump to a user-defined chain in the same
+table.
+
+.SH TARGETS
+A firewall rule specifies criteria for a packet, and a target.  If the
+packet does not match, the next rule in the chain is the examined; if
+it does match, then the next rule is specified by the value of the
+target, which can be the name of a user-defined chain or one of the
+special values 
+.IR ACCEPT ,
+.IR DROP ,
+.IR QUEUE ,
+or
+.IR RETURN .
+.PP
+.I ACCEPT 
+means to let the packet through.
+.I DROP
+means to drop the packet on the floor.
+.I QUEUE
+means to pass the packet to userspace.  (How the packet can be received
+by a userspace process differs by the particular queue handler.  2.4.x
+and 2.6.x kernels up to 2.6.13 include the 
+.B
+ip_queue
+queue handler.  Kernels 2.6.14 and later additionally include the 
+.B
+nfnetlink_queue
+queue handler.  Packets with a target of QUEUE will be sent to queue number '0'
+in this case. Please also see the
+.B
+NFQUEUE
+target as described later in this man page.)
+.I RETURN
+means stop traversing this chain and resume at the next rule in the
+previous (calling) chain.  If the end of a built-in chain is reached
+or a rule in a built-in chain with target
+.I RETURN
+is matched, the target specified by the chain policy determines the
+fate of the packet.
+.SH TABLES
+There are currently three independent tables (which tables are present
+at any time depends on the kernel configuration options and which
+modules are present).
+.TP
+.BI "-t, --table " "table"
+This option specifies the packet matching table which the command
+should operate on.  If the kernel is configured with automatic module
+loading, an attempt will be made to load the appropriate module for
+that table if it is not already there.
+
+The tables are as follows:
+.RS
+.TP .4i
+.BR "filter" :
+This is the default table (if no -t option is passed).  It contains
+the built-in chains
+.B INPUT
+(for packets destined to local sockets),
+.B FORWARD
+(for packets being routed through the box), and
+.B OUTPUT
+(for locally-generated packets).
+.TP
+.BR "nat" :
+This table is consulted when a packet that creates a new
+connection is encountered.  It consists of three built-ins:
+.B PREROUTING
+(for altering packets as soon as they come in),
+.B OUTPUT
+(for altering locally-generated packets before routing), and
+.B POSTROUTING
+(for altering packets as they are about to go out).
+.TP
+.BR "mangle" :
+This table is used for specialized packet alteration.  Until kernel
+2.4.17 it had two built-in chains:
+.B PREROUTING
+(for altering incoming packets before routing) and
+.B OUTPUT
+(for altering locally-generated packets before routing).
+Since kernel 2.4.18, three other built-in chains are also supported:
+.B INPUT
+(for packets coming into the box itself),
+.B FORWARD
+(for altering packets being routed through the box), and
+.B POSTROUTING
+(for altering packets as they are about to go out).
+.TP
+.BR "raw" :
+This table is used mainly for configuring exemptions from connection
+tracking in combination with the NOTRACK target.  It registers at the netfilter
+hooks with higher priority and is thus called before ip_conntrack, or any other
+IP tables.  It provides the following built-in chains:
+.B PREROUTING
+(for packets arriving via any network interface)
+.B OUTPUT
+(for packets generated by local processes)
+.RE
+.SH OPTIONS
+The options that are recognized by
+.B iptables
+can be divided into several different groups.
+.SS COMMANDS
+These options specify the specific action to perform.  Only one of them
+can be specified on the command line unless otherwise specified
+below.  For all the long versions of the command and option names, you
+need to use only enough letters to ensure that
+.B iptables
+can differentiate it from all other options.
+.TP
+.BI "-A, --append " "chain rule-specification"
+Append one or more rules to the end of the selected chain.
+When the source and/or destination names resolve to more than one
+address, a rule will be added for each possible address combination.
+.TP
+.BI "-D, --delete " "chain rule-specification"
+.ns
+.TP
+.BI "-D, --delete " "chain rulenum"
+Delete one or more rules from the selected chain.  There are two
+versions of this command: the rule can be specified as a number in the
+chain (starting at 1 for the first rule) or a rule to match.
+.TP
+.BR "-I, --insert " "\fIchain\fP [\fIrulenum\fP] \fIrule-specification\fP"
+Insert one or more rules in the selected chain as the given rule
+number.  So, if the rule number is 1, the rule or rules are inserted
+at the head of the chain.  This is also the default if no rule number
+is specified.
+.TP
+.BI "-R, --replace " "chain rulenum rule-specification"
+Replace a rule in the selected chain.  If the source and/or
+destination names resolve to multiple addresses, the command will
+fail.  Rules are numbered starting at 1.
+.TP
+.BR "-L, --list " "[\fIchain\fP]"
+List all rules in the selected chain.  If no chain is selected, all
+chains are listed.  As every other iptables command, it applies to the
+specified table (filter is the default), so NAT rules get listed by
+.nf
+ iptables -t nat -n -L
+.fi
+Please note that it is often used with the
+.B -n
+option, in order to avoid long reverse DNS lookups.
+It is legal to specify the
+.B -Z
+(zero) option as well, in which case the chain(s) will be atomically
+listed and zeroed.  The exact output is affected by the other
+arguments given. The exact rules are suppressed until you use
+.nf
+ iptables -L -v
+.fi
+.TP
+.BR "-F, --flush " "[\fIchain\fP]"
+Flush the selected chain (all the chains in the table if none is given).
+This is equivalent to deleting all the rules one by one.
+.TP
+.BR "-Z, --zero " "[\fIchain\fP]"
+Zero the packet and byte counters in all chains.  It is legal to
+specify the
+.B "-L, --list"
+(list) option as well, to see the counters immediately before they are
+cleared. (See above.)
+.TP
+.BI "-N, --new-chain " "chain"
+Create a new user-defined chain by the given name.  There must be no
+target of that name already.
+.TP
+.BR "-X, --delete-chain " "[\fIchain\fP]"
+Delete the optional user-defined chain specified.  There must be no references
+to the chain.  If there are, you must delete or replace the referring rules
+before the chain can be deleted.  The chain must be empty, i.e. not contain
+any rules.  If no argument is given, it will attempt to delete every
+non-builtin chain in the table.
+.TP
+.BI "-P, --policy " "chain target"
+Set the policy for the chain to the given target.  See the section
+.B TARGETS
+for the legal targets.  Only built-in (non-user-defined) chains can have
+policies, and neither built-in nor user-defined chains can be policy
+targets.
+.TP
+.BI "-E, --rename-chain " "old-chain new-chain"
+Rename the user specified chain to the user supplied name.  This is
+cosmetic, and has no effect on the structure of the table.
+.TP
+.B -h
+Help.
+Give a (currently very brief) description of the command syntax.
+.SS PARAMETERS
+The following parameters make up a rule specification (as used in the
+add, delete, insert, replace and append commands).
+.TP
+.BR "-p, --protocol " "[!] \fIprotocol\fP"
+The protocol of the rule or of the packet to check.
+The specified protocol can be one of
+.IR tcp ,
+.IR udp ,
+.IR icmp ,
+or
+.IR all ,
+or it can be a numeric value, representing one of these protocols or a
+different one.  A protocol name from /etc/protocols is also allowed.
+A "!" argument before the protocol inverts the
+test.  The number zero is equivalent to
+.IR all .
+Protocol
+.I all
+will match with all protocols and is taken as default when this
+option is omitted.
+.TP
+.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+Source specification.
+.I Address
+can be either a network name, a hostname (please note that specifying
+any name to be resolved with a remote query such as DNS is a really bad idea),
+a network IP address (with /mask), or a plain IP address.
+The
+.I mask
+can be either a network mask or a plain number,
+specifying the number of 1's at the left side of the network mask.
+Thus, a mask of
+.I 24
+is equivalent to
+.IR 255.255.255.0 .
+A "!" argument before the address specification inverts the sense of
+the address. The flag
+.B --src
+is an alias for this option.
+.TP
+.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]"
+Destination specification. 
+See the description of the
+.B -s
+(source) flag for a detailed description of the syntax.  The flag
+.B --dst
+is an alias for this option.
+.TP
+.BI "-j, --jump " "target"
+This specifies the target of the rule; i.e., what to do if the packet
+matches it.  The target can be a user-defined chain (other than the
+one this rule is in), one of the special builtin targets which decide
+the fate of the packet immediately, or an extension (see
+.B EXTENSIONS
+below).  If this
+option is omitted in a rule (and
+.B -g
+is not used), then matching the rule will have no
+effect on the packet's fate, but the counters on the rule will be
+incremented.
+.TP
+.BI "-g, --goto " "chain"
+This specifies that the processing should continue in a user
+specified chain. Unlike the --jump option return will not continue
+processing in this chain but instead in the chain that called us via
+--jump.
+.TP
+.BR "-i, --in-interface " "[!] \fIname\fP"
+Name of an interface via which a packet was received (only for
+packets entering the 
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains).  When the "!" argument is used before the interface name, the
+sense is inverted.  If the interface name ends in a "+", then any
+interface which begins with this name will match.  If this option is
+omitted, any interface name will match.
+.TP
+.BR "-o, --out-interface " "[!] \fIname\fP"
+Name of an interface via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD ,
+.B OUTPUT
+and
+.B POSTROUTING
+chains).  When the "!" argument is used before the interface name, the
+sense is inverted.  If the interface name ends in a "+", then any
+interface which begins with this name will match.  If this option is
+omitted, any interface name will match.
+.TP
+.B "[!] " "-f, --fragment"
+This means that the rule only refers to second and further fragments
+of fragmented packets.  Since there is no way to tell the source or
+destination ports of such a packet (or ICMP type), such a packet will
+not match any rules which specify them.  When the "!" argument
+precedes the "-f" flag, the rule will only match head fragments, or
+unfragmented packets.
+.TP
+.BI "-c, --set-counters " "PKTS BYTES"
+This enables the administrator to initialize the packet and byte
+counters of a rule (during
+.B INSERT,
+.B APPEND,
+.B REPLACE
+operations).
+.SS "OTHER OPTIONS"
+The following additional options can be specified:
+.TP
+.B "-v, --verbose"
+Verbose output.  This option makes the list command show the interface
+name, the rule options (if any), and the TOS masks.  The packet and
+byte counters are also listed, with the suffix 'K', 'M' or 'G' for
+1000, 1,000,000 and 1,000,000,000 multipliers respectively (but see
+the
+.B -x
+flag to change this).
+For appending, insertion, deletion and replacement, this causes
+detailed information on the rule or rules to be printed.
+.TP
+.B "-n, --numeric"
+Numeric output.
+IP addresses and port numbers will be printed in numeric format.
+By default, the program will try to display them as host names,
+network names, or services (whenever applicable).
+.TP
+.B "-x, --exact"
+Expand numbers.
+Display the exact value of the packet and byte counters,
+instead of only the rounded number in K's (multiples of 1000)
+M's (multiples of 1000K) or G's (multiples of 1000M).  This option is
+only relevant for the 
+.B -L
+command.
+.TP
+.B "--line-numbers"
+When listing rules, add line numbers to the beginning of each rule,
+corresponding to that rule's position in the chain.
+.TP
+.B "--modprobe=command"
+When adding or inserting rules into a chain, use
+.B command
+to load any necessary modules (targets, match extensions, etc).
+.SH MATCH EXTENSIONS
+iptables can use extended packet matching modules.  These are loaded
+in two ways: implicitly, when
+.B -p
+or
+.B --protocol
+is specified, or with the
+.B -m
+or
+.B --match
+options, followed by the matching module name; after these, various
+extra command line options become available, depending on the specific
+module.  You can specify multiple extended match modules in one line,
+and you can use the
+.B -h
+or
+.B --help
+options after the module has been specified to receive help specific
+to that module.
+
+The following are included in the base package, and most of these can
+be preceded by a
+.B !
+to invert the sense of the match.
+.\" @MATCH@
+.SH TARGET EXTENSIONS
+iptables can use extended target modules: the following are included
+in the standard distribution.
+.\" @TARGET@
+.SH DIAGNOSTICS
+Various error messages are printed to standard error.  The exit code
+is 0 for correct functioning.  Errors which appear to be caused by
+invalid or abused command line parameters cause an exit code of 2, and
+other errors cause an exit code of 1.
+.SH BUGS
+Bugs?  What's this? ;-)
+Well, you might want to have a look at http://bugzilla.netfilter.org/
+.SH COMPATIBILITY WITH IPCHAINS
+This
+.B iptables
+is very similar to ipchains by Rusty Russell.  The main difference is
+that the chains
+.B INPUT
+and
+.B OUTPUT
+are only traversed for packets coming into the local host and
+originating from the local host respectively.  Hence every packet only
+passes through one of the three chains (except loopback traffic, which
+involves both INPUT and OUTPUT chains); previously a forwarded packet
+would pass through all three.
+.PP
+The other main difference is that
+.B -i
+refers to the input interface;
+.B -o
+refers to the output interface, and both are available for packets
+entering the
+.B FORWARD
+chain.
+.PP The various forms of NAT have been separated out; 
+.B iptables 
+is a pure packet filter when using the default `filter' table, with
+optional extension modules.  This should simplify much of the previous
+confusion over the combination of IP masquerading and packet filtering
+seen previously.  So the following options are handled differently:
+.nf
+ -j MASQ
+ -M -S
+ -M -L
+.fi
+There are several other changes in iptables.
+.SH SEE ALSO
+.BR iptables-save (8),
+.BR iptables-restore (8),
+.BR ip6tables (8),
+.BR ip6tables-save (8),
+.BR ip6tables-restore (8),
+.BR libipq (3).
+.P
+The packet-filtering-HOWTO details iptables usage for
+packet filtering, the NAT-HOWTO details NAT,
+the netfilter-extensions-HOWTO details the extensions that are
+not in the standard distribution,
+and the netfilter-hacking-HOWTO details the netfilter internals.
+.br
+See
+.BR "http://www.netfilter.org/" .
+.SH AUTHORS
+Rusty Russell originally wrote iptables, in early consultation with Michael
+Neuling.
+.PP
+Marc Boucher made Rusty abandon ipnatctl by lobbying for a generic packet
+selection framework in iptables, then wrote the mangle table, the owner match,
+the mark stuff, and ran around doing cool stuff everywhere.
+.PP
+James Morris wrote the TOS target, and tos match.
+.PP
+Jozsef Kadlecsik wrote the REJECT target.
+.PP
+Harald Welte wrote the ULOG and NFQUEUE target, the new libiptc, as well as the TTL, DSCP, ECN matches and targets.
+.PP
+The Netfilter Core Team is: Marc Boucher, Martin Josefsson, Yasuyuki Kozakai,
+Jozsef Kadlecsik, Patrick McHardy, James Morris, Pablo Neira Ayuso,
+Harald Welte and Rusty Russell.
+.PP
+Man page originally written by Herve Eychenne <rv@wallfire.org>.
+.\" .. and did I mention that we are incredibly cool people?
+.\" .. sexy, too ..
+.\" .. witty, charming, powerful ..
+.\" .. and most of all, modest ..
diff --git a/iptables.c b/iptables.c
new file mode 100644 (file)
index 0000000..893b02d
--- /dev/null
@@ -0,0 +1,2617 @@
+/* Code to take an iptables-style command line and do it. */
+
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ *                 Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ *                 Marc Boucher <marc+nf@mbsi.ca>
+ *                 James Morris <jmorris@intercode.com.au>
+ *                 Harald Welte <laforge@gnumonks.org>
+ *                 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <unistd.h>
+#include <iptables.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/utsname.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+
+#define FMT_NUMERIC    0x0001
+#define FMT_NOCOUNTS   0x0002
+#define FMT_KILOMEGAGIGA 0x0004
+#define FMT_OPTIONS    0x0008
+#define FMT_NOTABLE    0x0010
+#define FMT_NOTARGET   0x0020
+#define FMT_VIA                0x0040
+#define FMT_NONEWLINE  0x0080
+#define FMT_LINENUMBERS 0x0100
+
+#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
+                       | FMT_NUMERIC | FMT_NOTABLE)
+#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
+
+
+#define CMD_NONE               0x0000U
+#define CMD_INSERT             0x0001U
+#define CMD_DELETE             0x0002U
+#define CMD_DELETE_NUM         0x0004U
+#define CMD_REPLACE            0x0008U
+#define CMD_APPEND             0x0010U
+#define CMD_LIST               0x0020U
+#define CMD_FLUSH              0x0040U
+#define CMD_ZERO               0x0080U
+#define CMD_NEW_CHAIN          0x0100U
+#define CMD_DELETE_CHAIN       0x0200U
+#define CMD_SET_POLICY         0x0400U
+#define CMD_RENAME_CHAIN       0x0800U
+#define NUMBER_OF_CMD  13
+static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+                                'N', 'X', 'P', 'E' };
+
+#define OPTION_OFFSET 256
+
+#define OPT_NONE       0x00000U
+#define OPT_NUMERIC    0x00001U
+#define OPT_SOURCE     0x00002U
+#define OPT_DESTINATION        0x00004U
+#define OPT_PROTOCOL   0x00008U
+#define OPT_JUMP       0x00010U
+#define OPT_VERBOSE    0x00020U
+#define OPT_EXPANDED   0x00040U
+#define OPT_VIANAMEIN  0x00080U
+#define OPT_VIANAMEOUT 0x00100U
+#define OPT_FRAGMENT    0x00200U
+#define OPT_LINENUMBERS 0x00400U
+#define OPT_COUNTERS   0x00800U
+#define NUMBER_OF_OPT  12
+static const char optflags[NUMBER_OF_OPT]
+= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '0', 'c'};
+
+static struct option original_opts[] = {
+       { "append", 1, 0, 'A' },
+       { "delete", 1, 0,  'D' },
+       { "insert", 1, 0,  'I' },
+       { "replace", 1, 0,  'R' },
+       { "list", 2, 0,  'L' },
+       { "flush", 2, 0,  'F' },
+       { "zero", 2, 0,  'Z' },
+       { "new-chain", 1, 0,  'N' },
+       { "delete-chain", 2, 0,  'X' },
+       { "rename-chain", 1, 0,  'E' },
+       { "policy", 1, 0,  'P' },
+       { "source", 1, 0, 's' },
+       { "destination", 1, 0,  'd' },
+       { "src", 1, 0,  's' }, /* synonym */
+       { "dst", 1, 0,  'd' }, /* synonym */
+       { "protocol", 1, 0,  'p' },
+       { "in-interface", 1, 0, 'i' },
+       { "jump", 1, 0, 'j' },
+       { "table", 1, 0, 't' },
+       { "match", 1, 0, 'm' },
+       { "numeric", 0, 0, 'n' },
+       { "out-interface", 1, 0, 'o' },
+       { "verbose", 0, 0, 'v' },
+       { "exact", 0, 0, 'x' },
+       { "fragments", 0, 0, 'f' },
+       { "version", 0, 0, 'V' },
+       { "help", 2, 0, 'h' },
+       { "line-numbers", 0, 0, '0' },
+       { "modprobe", 1, 0, 'M' },
+       { "set-counters", 1, 0, 'c' },
+       { "goto", 1, 0, 'g' },
+       { 0 }
+};
+
+/* we need this for iptables-restore.  iptables-restore.c sets line to the
+ * current line of the input file, in order  to give a more precise error
+ * message.  iptables itself doesn't need this, so it is initialized to the
+ * magic number of -1 */
+int line = -1;
+
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
+/* Table of legal combinations of commands and options.  If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ *  +  compulsory
+ *  x  illegal
+ *     optional
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+       /*     -n  -s  -d  -p  -j  -v  -x  -i  -o  -f  --line -c */
+/*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x',' '},
+/*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x','x'},
+/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x',' '},
+/*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x',' '},
+/*LIST*/      {' ','x','x','x','x',' ',' ','x','x','x',' ','x'},
+/*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x','x','x'}
+};
+
+static int inverse_for_options[NUMBER_OF_OPT] =
+{
+/* -n */ 0,
+/* -s */ IPT_INV_SRCIP,
+/* -d */ IPT_INV_DSTIP,
+/* -p */ IPT_INV_PROTO,
+/* -j */ 0,
+/* -v */ 0,
+/* -x */ 0,
+/* -i */ IPT_INV_VIA_IN,
+/* -o */ IPT_INV_VIA_OUT,
+/* -f */ IPT_INV_FRAG,
+/*--line*/ 0,
+/* -c */ 0,
+};
+
+const char *program_version;
+const char *program_name;
+char *lib_dir;
+
+int kernel_version;
+
+/* the path to command to load kernel module */
+const char *modprobe = NULL;
+
+/* Keeping track of external matches and targets: linked lists.  */
+struct iptables_match *iptables_matches = NULL;
+struct iptables_target *iptables_targets = NULL;
+
+/* Extra debugging from libiptc */
+extern void dump_entries(const iptc_handle_t handle);
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+   /etc/protocols */
+struct pprot {
+       char *name;
+       u_int8_t num;
+};
+
+/* Primitive headers... */
+/* defined in netinet/in.h */
+#if 0
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+#endif
+
+static const struct pprot chain_protos[] = {
+       { "tcp", IPPROTO_TCP },
+       { "udp", IPPROTO_UDP },
+       { "udplite", IPPROTO_UDPLITE },
+       { "icmp", IPPROTO_ICMP },
+       { "esp", IPPROTO_ESP },
+       { "ah", IPPROTO_AH },
+       { "sctp", IPPROTO_SCTP },
+       { "all", 0 },
+};
+
+static char *
+proto_to_name(u_int8_t proto, int nolookup)
+{
+       unsigned int i;
+
+       if (proto && !nolookup) {
+               struct protoent *pent = getprotobynumber(proto);
+               if (pent)
+                       return pent->p_name;
+       }
+
+       for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+               if (chain_protos[i].num == proto)
+                       return chain_protos[i].name;
+
+       return NULL;
+}
+
+int
+service_to_port(const char *name, const char *proto)
+{
+       struct servent *service;
+
+       if ((service = getservbyname(name, proto)) != NULL)
+               return ntohs((unsigned short) service->s_port);
+
+       return -1;
+}
+
+u_int16_t
+parse_port(const char *port, const char *proto)
+{
+       unsigned int portnum;
+
+       if ((string_to_number(port, 0, 65535, &portnum)) != -1 ||
+           (portnum = service_to_port(port, proto)) != -1)
+               return (u_int16_t)portnum;
+
+       exit_error(PARAMETER_PROBLEM,
+                  "invalid port/service `%s' specified", port);
+}
+
+enum {
+       IPT_DOTTED_ADDR = 0,
+       IPT_DOTTED_MASK
+};
+
+static struct in_addr *
+__dotted_to_addr(const char *dotted, int type)
+{
+       static struct in_addr addr;
+       unsigned char *addrp;
+       char *p, *q;
+       unsigned int onebyte;
+       int i;
+       char buf[20];
+
+       /* copy dotted string, because we need to modify it */
+       strncpy(buf, dotted, sizeof(buf) - 1);
+       buf[sizeof(buf) - 1] = '\0';
+       addrp = (unsigned char *) &(addr.s_addr);
+
+       p = buf;
+       for (i = 0; i < 3; i++) {
+               if ((q = strchr(p, '.')) == NULL) {
+                       if (type == IPT_DOTTED_ADDR) {
+                               /* autocomplete, this is a network address */
+                               if (string_to_number(p, 0, 255, &onebyte) == -1)
+                                       return (struct in_addr *) NULL;
+
+                               addrp[i] = (unsigned char) onebyte;
+                               while (i < 3)
+                                       addrp[++i] = 0;
+
+                               return &addr;
+                       } else
+                               return (struct in_addr *) NULL;
+               }
+
+               *q = '\0';
+               if (string_to_number(p, 0, 255, &onebyte) == -1)
+                       return (struct in_addr *) NULL;
+
+               addrp[i] = (unsigned char) onebyte;
+               p = q + 1;
+       }
+
+       /* we've checked 3 bytes, now we check the last one */
+       if (string_to_number(p, 0, 255, &onebyte) == -1)
+               return (struct in_addr *) NULL;
+
+       addrp[3] = (unsigned char) onebyte;
+
+       return &addr;
+}
+
+struct in_addr *
+dotted_to_addr(const char *dotted)
+{
+       return __dotted_to_addr(dotted, IPT_DOTTED_ADDR);
+}
+
+struct in_addr *
+dotted_to_mask(const char *dotted)
+{
+       return __dotted_to_addr(dotted, IPT_DOTTED_MASK);
+}
+
+static struct in_addr *
+network_to_addr(const char *name)
+{
+       struct netent *net;
+       static struct in_addr addr;
+
+       if ((net = getnetbyname(name)) != NULL) {
+               if (net->n_addrtype != AF_INET)
+                       return (struct in_addr *) NULL;
+               addr.s_addr = htonl((unsigned long) net->n_net);
+               return &addr;
+       }
+
+       return (struct in_addr *) NULL;
+}
+
+static void
+inaddrcpy(struct in_addr *dst, struct in_addr *src)
+{
+       /* memcpy(dst, src, sizeof(struct in_addr)); */
+       dst->s_addr = src->s_addr;
+}
+
+static void free_opts(int reset_offset)
+{
+       if (opts != original_opts) {
+               free(opts);
+               opts = original_opts;
+               if (reset_offset)
+                       global_option_offset = 0;
+       }
+}
+
+void
+exit_error(enum exittype status, char *msg, ...)
+{
+       va_list args;
+
+       va_start(args, msg);
+       fprintf(stderr, "%s v%s: ", program_name, program_version);
+       vfprintf(stderr, msg, args);
+       va_end(args);
+       fprintf(stderr, "\n");
+       if (status == PARAMETER_PROBLEM)
+               exit_tryhelp(status);
+       if (status == VERSION_PROBLEM)
+               fprintf(stderr,
+                       "Perhaps iptables or your kernel needs to be upgraded.\n");
+       /* On error paths, make sure that we don't leak memory */
+       free_opts(1);
+       exit(status);
+}
+
+void
+exit_tryhelp(int status)
+{
+       if (line != -1)
+               fprintf(stderr, "Error occurred at line: %d\n", line);
+       fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+                       program_name, program_name );
+       free_opts(1);
+       exit(status);
+}
+
+void
+exit_printhelp(struct iptables_rule_match *matches)
+{
+       struct iptables_rule_match *matchp = NULL;
+       struct iptables_target *t = NULL;
+
+       printf("%s v%s\n\n"
+"Usage: %s -[AD] chain rule-specification [options]\n"
+"       %s -[RI] chain rulenum rule-specification [options]\n"
+"       %s -D chain rulenum [options]\n"
+"       %s -[LFZ] [chain] [options]\n"
+"       %s -[NX] chain\n"
+"       %s -E old-chain-name new-chain-name\n"
+"       %s -P chain target [options]\n"
+"       %s -h (print this help information)\n\n",
+              program_name, program_version, program_name, program_name,
+              program_name, program_name, program_name, program_name,
+              program_name, program_name);
+
+       printf(
+"Commands:\n"
+"Either long or short options are allowed.\n"
+"  --append  -A chain          Append to chain\n"
+"  --delete  -D chain          Delete matching rule from chain\n"
+"  --delete  -D chain rulenum\n"
+"                              Delete rule rulenum (1 = first) from chain\n"
+"  --insert  -I chain [rulenum]\n"
+"                              Insert in chain as rulenum (default 1=first)\n"
+"  --replace -R chain rulenum\n"
+"                              Replace rule rulenum (1 = first) in chain\n"
+"  --list    -L [chain]                List the rules in a chain or all chains\n"
+"  --flush   -F [chain]                Delete all rules in  chain or all chains\n"
+"  --zero    -Z [chain]                Zero counters in chain or all chains\n"
+"  --new     -N chain          Create a new user-defined chain\n"
+"  --delete-chain\n"
+"            -X [chain]                Delete a user-defined chain\n"
+"  --policy  -P chain target\n"
+"                              Change policy on chain to target\n"
+"  --rename-chain\n"
+"            -E old-chain new-chain\n"
+"                              Change chain name, (moving any references)\n"
+
+"Options:\n"
+"  --proto     -p [!] proto    protocol: by number or name, eg. `tcp'\n"
+"  --source    -s [!] address[/mask]\n"
+"                              source specification\n"
+"  --destination -d [!] address[/mask]\n"
+"                              destination specification\n"
+"  --in-interface -i [!] input name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --jump      -j target\n"
+"                              target for rule (may load target extension)\n"
+#ifdef IPT_F_GOTO
+"  --goto      -g chain\n"
+"                              jump to chain with no return\n"
+#endif
+"  --match     -m match\n"
+"                              extended match (may load extension)\n"
+"  --numeric   -n              numeric output of addresses and ports\n"
+"  --out-interface -o [!] output name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --table     -t table        table to manipulate (default: `filter')\n"
+"  --verbose   -v              verbose mode\n"
+"  --line-numbers              print line numbers when listing\n"
+"  --exact     -x              expand numbers (display exact values)\n"
+"[!] --fragment        -f              match second or further fragments only\n"
+"  --modprobe=<command>                try to insert modules using this command\n"
+"  --set-counters PKTS BYTES   set the counter during insert/append\n"
+"[!] --version -V              print package version.\n");
+
+       /* Print out any special helps. A user might like to be able
+          to add a --help to the commandline, and see expected
+          results. So we call help for all specified matches & targets */
+       for (t = iptables_targets; t ;t = t->next) {
+               if (t->used) {
+                       printf("\n");
+                       t->help();
+               }
+       }
+       for (matchp = matches; matchp; matchp = matchp->next) {
+               printf("\n");
+               matchp->match->help();
+       }
+       exit(0);
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+       int i, j, legal = 0;
+
+       /* Check that commands are valid with options.  Complicated by the
+        * fact that if an option is legal with *any* command given, it is
+        * legal overall (ie. -z and -l).
+        */
+       for (i = 0; i < NUMBER_OF_OPT; i++) {
+               legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+               for (j = 0; j < NUMBER_OF_CMD; j++) {
+                       if (!(command & (1<<j)))
+                               continue;
+
+                       if (!(options & (1<<i))) {
+                               if (commands_v_options[j][i] == '+')
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "You need to supply the `-%c' "
+                                                  "option for this command\n",
+                                                  optflags[i]);
+                       } else {
+                               if (commands_v_options[j][i] != 'x')
+                                       legal = 1;
+                               else if (legal == 0)
+                                       legal = -1;
+                       }
+               }
+               if (legal == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Illegal option `-%c' with this command\n",
+                                  optflags[i]);
+       }
+}
+
+static char
+opt2char(int option)
+{
+       const char *ptr;
+       for (ptr = optflags; option > 1; option >>= 1, ptr++);
+
+       return *ptr;
+}
+
+static char
+cmd2char(int option)
+{
+       const char *ptr;
+       for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
+
+       return *ptr;
+}
+
+static void
+add_command(unsigned int *cmd, const int newcmd, const int othercmds, 
+           int invert)
+{
+       if (invert)
+               exit_error(PARAMETER_PROBLEM, "unexpected ! flag");
+       if (*cmd & (~othercmds))
+               exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
+                          cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
+       *cmd |= newcmd;
+}
+
+int
+check_inverse(const char option[], int *invert, int *optind, int argc)
+{
+       if (option && strcmp(option, "!") == 0) {
+               if (*invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Multiple `!' flags not allowed");
+               *invert = TRUE;
+               if (optind) {
+                       *optind = *optind+1;
+                       if (argc && *optind > argc)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "no argument following `!'");
+               }
+
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static void *
+fw_calloc(size_t count, size_t size)
+{
+       void *p;
+
+       if ((p = calloc(count, size)) == NULL) {
+               perror("iptables: calloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static void *
+fw_malloc(size_t size)
+{
+       void *p;
+
+       if ((p = malloc(size)) == NULL) {
+               perror("iptables: malloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static struct in_addr *
+host_to_addr(const char *name, unsigned int *naddr)
+{
+       struct hostent *host;
+       struct in_addr *addr;
+       unsigned int i;
+
+       *naddr = 0;
+       if ((host = gethostbyname(name)) != NULL) {
+               if (host->h_addrtype != AF_INET ||
+                   host->h_length != sizeof(struct in_addr))
+                       return (struct in_addr *) NULL;
+
+               while (host->h_addr_list[*naddr] != (char *) NULL)
+                       (*naddr)++;
+               addr = fw_calloc(*naddr, sizeof(struct in_addr) * *naddr);
+               for (i = 0; i < *naddr; i++)
+                       inaddrcpy(&(addr[i]),
+                                 (struct in_addr *) host->h_addr_list[i]);
+               return addr;
+       }
+
+       return (struct in_addr *) NULL;
+}
+
+static char *
+addr_to_host(const struct in_addr *addr)
+{
+       struct hostent *host;
+
+       if ((host = gethostbyaddr((char *) addr,
+                                 sizeof(struct in_addr), AF_INET)) != NULL)
+               return (char *) host->h_name;
+
+       return (char *) NULL;
+}
+
+/*
+ *     All functions starting with "parse" should succeed, otherwise
+ *     the program fails.
+ *     Most routines return pointers to static data that may change
+ *     between calls to the same or other routines with a few exceptions:
+ *     "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
+ *     return global static data.
+*/
+
+static struct in_addr *
+parse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+       struct in_addr *addrp, *addrptmp;
+
+       if ((addrptmp = dotted_to_addr(name)) != NULL ||
+           (addrptmp = network_to_addr(name)) != NULL) {
+               addrp = fw_malloc(sizeof(struct in_addr));
+               inaddrcpy(addrp, addrptmp);
+               *naddrs = 1;
+               return addrp;
+       }
+       if ((addrp = host_to_addr(name, naddrs)) != NULL)
+               return addrp;
+
+       exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static struct in_addr *
+parse_mask(char *mask)
+{
+       static struct in_addr maskaddr;
+       struct in_addr *addrp;
+       unsigned int bits;
+
+       if (mask == NULL) {
+               /* no mask at all defaults to 32 bits */
+               maskaddr.s_addr = 0xFFFFFFFF;
+               return &maskaddr;
+       }
+       if ((addrp = dotted_to_mask(mask)) != NULL)
+               /* dotted_to_addr already returns a network byte order addr */
+               return addrp;
+       if (string_to_number(mask, 0, 32, &bits) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "invalid mask `%s' specified", mask);
+       if (bits != 0) {
+               maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
+               return &maskaddr;
+       }
+
+       maskaddr.s_addr = 0L;
+       return &maskaddr;
+}
+
+void
+parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
+                     struct in_addr *maskp, unsigned int *naddrs)
+{
+       struct in_addr *addrp;
+       char buf[256];
+       char *p;
+       int i, j, k, n;
+
+       strncpy(buf, name, sizeof(buf) - 1);
+       buf[sizeof(buf) - 1] = '\0';
+       if ((p = strrchr(buf, '/')) != NULL) {
+               *p = '\0';
+               addrp = parse_mask(p + 1);
+       } else
+               addrp = parse_mask(NULL);
+       inaddrcpy(maskp, addrp);
+
+       /* if a null mask is given, the name is ignored, like in "any/0" */
+       if (maskp->s_addr == 0L)
+               strcpy(buf, "0.0.0.0");
+
+       addrp = *addrpp = parse_hostnetwork(buf, naddrs);
+       n = *naddrs;
+       for (i = 0, j = 0; i < n; i++) {
+               addrp[j++].s_addr &= maskp->s_addr;
+               for (k = 0; k < j - 1; k++) {
+                       if (addrp[k].s_addr == addrp[j - 1].s_addr) {
+                               (*naddrs)--;
+                               j--;
+                               break;
+                       }
+               }
+       }
+}
+
+struct iptables_match *
+find_match(const char *name, enum ipt_tryload tryload, struct iptables_rule_match **matches)
+{
+       struct iptables_match *ptr;
+
+       for (ptr = iptables_matches; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0) {
+                       struct iptables_match *clone;
+                       
+                       /* First match of this type: */
+                       if (ptr->m == NULL)
+                               break;
+
+                       /* Second and subsequent clones */
+                       clone = fw_malloc(sizeof(struct iptables_match));
+                       memcpy(clone, ptr, sizeof(struct iptables_match));
+                       clone->mflags = 0;
+                       /* This is a clone: */
+                       clone->next = clone;
+
+                       ptr = clone;
+                       break;
+               }
+       }               
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) {
+               char path[strlen(lib_dir) + sizeof("/libipt_.so")
+                        + strlen(name)];
+               sprintf(path, "%s/libipt_%s.so", lib_dir, name);
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified target as match. */
+                       ptr = find_match(name, DONT_LOAD, NULL);
+
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load match `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load match `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find match `%s'\n", name);
+       }
+#endif
+
+       if (ptr && matches) {
+               struct iptables_rule_match **i;
+               struct iptables_rule_match *newentry;
+
+               newentry = fw_malloc(sizeof(struct iptables_rule_match));
+
+               for (i = matches; *i; i = &(*i)->next) {
+                       if (strcmp(name, (*i)->match->name) == 0)
+                               (*i)->completed = 1;
+               }
+               newentry->match = ptr;
+               newentry->completed = 0;
+               newentry->next = NULL;
+               *i = newentry;
+       }
+
+       return ptr;
+}
+
+/* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
+static struct iptables_match *
+find_proto(const char *pname, enum ipt_tryload tryload, int nolookup, struct iptables_rule_match **matches)
+{
+       unsigned int proto;
+
+       if (string_to_number(pname, 0, 255, &proto) != -1) {
+               char *protoname = proto_to_name(proto, nolookup);
+
+               if (protoname)
+                       return find_match(protoname, tryload, matches);
+       } else
+               return find_match(pname, tryload, matches);
+
+       return NULL;
+}
+
+u_int16_t
+parse_protocol(const char *s)
+{
+       unsigned int proto;
+
+       if (string_to_number(s, 0, 255, &proto) == -1) {
+               struct protoent *pent;
+
+               /* first deal with the special case of 'all' to prevent
+                * people from being able to redefine 'all' in nsswitch
+                * and/or provoke expensive [not working] ldap/nis/... 
+                * lookups */
+               if (!strcmp(s, "all"))
+                       return 0;
+
+               if ((pent = getprotobyname(s)))
+                       proto = pent->p_proto;
+               else {
+                       unsigned int i;
+                       for (i = 0;
+                            i < sizeof(chain_protos)/sizeof(struct pprot);
+                            i++) {
+                               if (strcmp(s, chain_protos[i].name) == 0) {
+                                       proto = chain_protos[i].num;
+                                       break;
+                               }
+                       }
+                       if (i == sizeof(chain_protos)/sizeof(struct pprot))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unknown protocol `%s' specified",
+                                          s);
+               }
+       }
+
+       return (u_int16_t)proto;
+}
+
+void parse_interface(const char *arg, char *vianame, unsigned char *mask)
+{
+       int vialen = strlen(arg);
+       unsigned int i;
+
+       memset(mask, 0, IFNAMSIZ);
+       memset(vianame, 0, IFNAMSIZ);
+
+       if (vialen + 1 > IFNAMSIZ)
+               exit_error(PARAMETER_PROBLEM,
+                          "interface name `%s' must be shorter than IFNAMSIZ"
+                          " (%i)", arg, IFNAMSIZ-1);
+
+       strcpy(vianame, arg);
+       if ((vialen == 0) || (vialen == 1 && vianame[0] == '+'))
+               memset(mask, 0, IFNAMSIZ);
+       else if (vianame[vialen - 1] == '+') {
+               memset(mask, 0xFF, vialen - 1);
+               memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
+               /* Don't remove `+' here! -HW */
+       } else {
+               /* Include nul-terminator in match */
+               memset(mask, 0xFF, vialen + 1);
+               memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
+               for (i = 0; vianame[i]; i++) {
+                       if (vianame[i] == ':' ||
+                           vianame[i] == '!' ||
+                           vianame[i] == '*') {
+                               printf("Warning: weird character in interface"
+                                      " `%s' (No aliases, :, ! or *).\n",
+                                      vianame);
+                               break;
+                       }
+               }
+       }
+}
+
+/* Can't be zero. */
+static int
+parse_rulenumber(const char *rule)
+{
+       unsigned int rulenum;
+
+       if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid rule number `%s'", rule);
+
+       return rulenum;
+}
+
+static const char *
+parse_target(const char *targetname)
+{
+       const char *ptr;
+
+       if (strlen(targetname) < 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name (too short)");
+
+       if (strlen(targetname)+1 > sizeof(ipt_chainlabel))
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name `%s' (%u chars max)",
+                          targetname, (unsigned int)sizeof(ipt_chainlabel)-1);
+
+       for (ptr = targetname; *ptr; ptr++)
+               if (isspace(*ptr))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid target name `%s'", targetname);
+       return targetname;
+}
+
+static char *
+addr_to_network(const struct in_addr *addr)
+{
+       struct netent *net;
+
+       if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
+               return (char *) net->n_name;
+
+       return (char *) NULL;
+}
+
+char *
+addr_to_dotted(const struct in_addr *addrp)
+{
+       static char buf[20];
+       const unsigned char *bytep;
+
+       bytep = (const unsigned char *) &(addrp->s_addr);
+       sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
+       return buf;
+}
+
+char *
+addr_to_anyname(const struct in_addr *addr)
+{
+       char *name;
+
+       if ((name = addr_to_host(addr)) != NULL ||
+           (name = addr_to_network(addr)) != NULL)
+               return name;
+
+       return addr_to_dotted(addr);
+}
+
+char *
+mask_to_dotted(const struct in_addr *mask)
+{
+       int i;
+       static char buf[20];
+       u_int32_t maskaddr, bits;
+
+       maskaddr = ntohl(mask->s_addr);
+
+       if (maskaddr == 0xFFFFFFFFL)
+               /* we don't want to see "/32" */
+               return "";
+
+       i = 32;
+       bits = 0xFFFFFFFEL;
+       while (--i >= 0 && maskaddr != bits)
+               bits <<= 1;
+       if (i >= 0)
+               sprintf(buf, "/%d", i);
+       else
+               /* mask was not a decent combination of 1's and 0's */
+               sprintf(buf, "/%s", addr_to_dotted(mask));
+
+       return buf;
+}
+
+int
+string_to_number_ll(const char *s, unsigned long long min, unsigned long long max,
+                unsigned long long *ret)
+{
+       unsigned long long number;
+       char *end;
+
+       /* Handle hex, octal, etc. */
+       errno = 0;
+       number = strtoull(s, &end, 0);
+       if (*end == '\0' && end != s) {
+               /* we parsed a number, let's see if we want this */
+               if (errno != ERANGE && min <= number && (!max || number <= max)) {
+                       *ret = number;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+int
+string_to_number_l(const char *s, unsigned long min, unsigned long max,
+                unsigned long *ret)
+{
+       int result;
+       unsigned long long number;
+
+       result = string_to_number_ll(s, min, max, &number);
+       *ret = (unsigned long)number;
+
+       return result;
+}
+
+int string_to_number(const char *s, unsigned int min, unsigned int max,
+               unsigned int *ret)
+{
+       int result;
+       unsigned long number;
+
+       result = string_to_number_l(s, min, max, &number);
+       *ret = (unsigned int)number;
+
+       return result;
+}
+
+static void
+set_option(unsigned int *options, unsigned int option, u_int8_t *invflg,
+          int invert)
+{
+       if (*options & option)
+               exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
+                          opt2char(option));
+       *options |= option;
+
+       if (invert) {
+               unsigned int i;
+               for (i = 0; 1 << i != option; i++);
+
+               if (!inverse_for_options[i])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "cannot have ! before -%c",
+                                  opt2char(option));
+               *invflg |= inverse_for_options[i];
+       }
+}
+
+struct iptables_target *
+find_target(const char *name, enum ipt_tryload tryload)
+{
+       struct iptables_target *ptr;
+
+       /* Standard target? */
+       if (strcmp(name, "") == 0
+           || strcmp(name, IPTC_LABEL_ACCEPT) == 0
+           || strcmp(name, IPTC_LABEL_DROP) == 0
+           || strcmp(name, IPTC_LABEL_QUEUE) == 0
+           || strcmp(name, IPTC_LABEL_RETURN) == 0)
+               name = "standard";
+
+       for (ptr = iptables_targets; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0)
+                       break;
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) {
+               char path[strlen(lib_dir) + sizeof("/libipt_.so")
+                        + strlen(name)];
+               sprintf(path, "%s/libipt_%s.so", lib_dir, name);
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified match as a target. */
+                       ptr = find_target(name, DONT_LOAD);
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load target `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load target `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find target `%s'\n", name);
+       }
+#endif
+
+       if (ptr)
+               ptr->used = 1;
+
+       return ptr;
+}
+
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+             unsigned int *option_offset)
+{
+       unsigned int num_old, num_new, i;
+       struct option *merge;
+
+       for (num_old = 0; oldopts[num_old].name; num_old++);
+       for (num_new = 0; newopts[num_new].name; num_new++);
+
+       global_option_offset += OPTION_OFFSET;
+       *option_offset = global_option_offset;
+
+       merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+       memcpy(merge, oldopts, num_old * sizeof(struct option));
+       free_opts(0); /* Release previous options merged if any */
+       for (i = 0; i < num_new; i++) {
+               merge[num_old + i] = newopts[i];
+               merge[num_old + i].val += *option_offset;
+       }
+       memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+       return merge;
+}
+
+static int compatible_revision(const char *name, u_int8_t revision, int opt)
+{
+       struct ipt_get_revision rev;
+       socklen_t s = sizeof(rev);
+       int max_rev, sockfd;
+
+       sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0) {
+               fprintf(stderr, "Could not open socket to kernel: %s\n",
+                       strerror(errno));
+               exit(1);
+       }
+
+       load_iptables_ko(modprobe, 1);
+
+       strcpy(rev.name, name);
+       rev.revision = revision;
+
+       max_rev = getsockopt(sockfd, IPPROTO_IP, opt, &rev, &s);
+       if (max_rev < 0) {
+               /* Definitely don't support this? */
+               if (errno == EPROTONOSUPPORT) {
+                       close(sockfd);
+                       return 0;
+               } else if (errno == ENOPROTOOPT) {
+                       close(sockfd);
+                       /* Assume only revision 0 support (old kernel) */
+                       return (revision == 0);
+               } else {
+                       fprintf(stderr, "getsockopt failed strangely: %s\n",
+                               strerror(errno));
+                       exit(1);
+               }
+       }
+       close(sockfd);
+       return 1;
+}
+
+static int compatible_match_revision(const char *name, u_int8_t revision)
+{
+       return compatible_revision(name, revision, IPT_SO_GET_REVISION_MATCH);
+}
+
+static int compatible_target_revision(const char *name, u_int8_t revision)
+{
+       return compatible_revision(name, revision, IPT_SO_GET_REVISION_TARGET);
+}
+
+void
+register_match(struct iptables_match *me)
+{
+       struct iptables_match **i, *old;
+
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       /* Revision field stole a char from name. */
+       if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) {
+               fprintf(stderr, "%s: target `%s' has invalid name\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       old = find_match(me->name, DURING_LOAD, NULL);
+       if (old) {
+               if (old->revision == me->revision) {
+                       fprintf(stderr,
+                               "%s: match `%s' already registered.\n",
+                               program_name, me->name);
+                       exit(1);
+               }
+
+               /* Now we have two (or more) options, check compatibility. */
+               if (compatible_match_revision(old->name, old->revision)
+                   && old->revision > me->revision)
+                       return;
+
+               /* Replace if compatible. */
+               if (!compatible_match_revision(me->name, me->revision))
+                       return;
+
+               /* Delete old one. */
+               for (i = &iptables_matches; *i!=old; i = &(*i)->next);
+               *i = old->next;
+       }
+
+       if (me->size != IPT_ALIGN(me->size)) {
+               fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
+                       program_name, me->name, (unsigned int)me->size);
+               exit(1);
+       }
+
+       /* Append to list. */
+       for (i = &iptables_matches; *i; i = &(*i)->next);
+       me->next = NULL;
+       *i = me;
+
+       me->m = NULL;
+       me->mflags = 0;
+}
+
+void
+register_target(struct iptables_target *me)
+{
+       struct iptables_target *old;
+
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       /* Revision field stole a char from name. */
+       if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) {
+               fprintf(stderr, "%s: target `%s' has invalid name\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       old = find_target(me->name, DURING_LOAD);
+       if (old) {
+               struct iptables_target **i;
+
+               if (old->revision == me->revision) {
+                       fprintf(stderr,
+                               "%s: target `%s' already registered.\n",
+                               program_name, me->name);
+                       exit(1);
+               }
+
+               /* Now we have two (or more) options, check compatibility. */
+               if (compatible_target_revision(old->name, old->revision)
+                   && old->revision > me->revision)
+                       return;
+
+               /* Replace if compatible. */
+               if (!compatible_target_revision(me->name, me->revision))
+                       return;
+
+               /* Delete old one. */
+               for (i = &iptables_targets; *i!=old; i = &(*i)->next);
+               *i = old->next;
+       }
+
+       if (me->size != IPT_ALIGN(me->size)) {
+               fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
+                       program_name, me->name, (unsigned int)me->size);
+               exit(1);
+       }
+
+       /* Prepend to list. */
+       me->next = iptables_targets;
+       iptables_targets = me;
+       me->t = NULL;
+       me->tflags = 0;
+}
+
+static void
+print_num(u_int64_t number, unsigned int format)
+{
+       if (format & FMT_KILOMEGAGIGA) {
+               if (number > 99999) {
+                       number = (number + 500) / 1000;
+                       if (number > 9999) {
+                               number = (number + 500) / 1000;
+                               if (number > 9999) {
+                                       number = (number + 500) / 1000;
+                                       if (number > 9999) {
+                                               number = (number + 500) / 1000;
+                                               printf(FMT("%4lluT ","%lluT "), (unsigned long long)number);
+                                       }
+                                       else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number);
+                               }
+                               else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number);
+                       } else
+                               printf(FMT("%4lluK ","%lluK "), (unsigned long long)number);
+               } else
+                       printf(FMT("%5llu ","%llu "), (unsigned long long)number);
+       } else
+               printf(FMT("%8llu ","%llu "), (unsigned long long)number);
+}
+
+
+static void
+print_header(unsigned int format, const char *chain, iptc_handle_t *handle)
+{
+       struct ipt_counters counters;
+       const char *pol = iptc_get_policy(chain, &counters, handle);
+       printf("Chain %s", chain);
+       if (pol) {
+               printf(" (policy %s", pol);
+               if (!(format & FMT_NOCOUNTS)) {
+                       fputc(' ', stdout);
+                       print_num(counters.pcnt, (format|FMT_NOTABLE));
+                       fputs("packets, ", stdout);
+                       print_num(counters.bcnt, (format|FMT_NOTABLE));
+                       fputs("bytes", stdout);
+               }
+               printf(")\n");
+       } else {
+               unsigned int refs;
+               if (!iptc_get_references(&refs, chain, handle))
+                       printf(" (ERROR obtaining refs)\n");
+               else
+                       printf(" (%u references)\n", refs);
+       }
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4s ", "%s "), "num");
+       if (!(format & FMT_NOCOUNTS)) {
+               if (format & FMT_KILOMEGAGIGA) {
+                       printf(FMT("%5s ","%s "), "pkts");
+                       printf(FMT("%5s ","%s "), "bytes");
+               } else {
+                       printf(FMT("%8s ","%s "), "pkts");
+                       printf(FMT("%10s ","%s "), "bytes");
+               }
+       }
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ","%s "), "target");
+       fputs(" prot ", stdout);
+       if (format & FMT_OPTIONS)
+               fputs("opt", stdout);
+       if (format & FMT_VIA) {
+               printf(FMT(" %-6s ","%s "), "in");
+               printf(FMT("%-6s ","%s "), "out");
+       }
+       printf(FMT(" %-19s ","%s "), "source");
+       printf(FMT(" %-19s "," %s "), "destination");
+       printf("\n");
+}
+
+
+static int
+print_match(const struct ipt_entry_match *m,
+           const struct ipt_ip *ip,
+           int numeric)
+{
+       struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD, NULL);
+
+       if (match) {
+               if (match->print)
+                       match->print(ip, m, numeric);
+               else
+                       printf("%s ", match->name);
+       } else {
+               if (m->u.user.name[0])
+                       printf("UNKNOWN match `%s' ", m->u.user.name);
+       }
+       /* Don't stop iterating. */
+       return 0;
+}
+
+/* e is called `fw' here for hysterical raisins */
+static void
+print_firewall(const struct ipt_entry *fw,
+              const char *targname,
+              unsigned int num,
+              unsigned int format,
+              const iptc_handle_t handle)
+{
+       struct iptables_target *target = NULL;
+       const struct ipt_entry_target *t;
+       u_int8_t flags;
+       char buf[BUFSIZ];
+
+       if (!iptc_is_chain(targname, handle))
+               target = find_target(targname, TRY_LOAD);
+       else
+               target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED);
+
+       t = ipt_get_target((struct ipt_entry *)fw);
+       flags = fw->ip.flags;
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4u ", "%u "), num+1);
+
+       if (!(format & FMT_NOCOUNTS)) {
+               print_num(fw->counters.pcnt, format);
+               print_num(fw->counters.bcnt, format);
+       }
+
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ", "%s "), targname);
+
+       fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout);
+       {
+               char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC);
+               if (pname)
+                       printf(FMT("%-5s", "%s "), pname);
+               else
+                       printf(FMT("%-5hu", "%hu "), fw->ip.proto);
+       }
+
+       if (format & FMT_OPTIONS) {
+               if (format & FMT_NOTABLE)
+                       fputs("opt ", stdout);
+               fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout);
+               fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout);
+               fputc(' ', stdout);
+       }
+
+       if (format & FMT_VIA) {
+               char iface[IFNAMSIZ+2];
+
+               if (fw->ip.invflags & IPT_INV_VIA_IN) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ip.iniface[0] != '\0') {
+                       strcat(iface, fw->ip.iniface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT(" %-6s ","in %s "), iface);
+
+               if (fw->ip.invflags & IPT_INV_VIA_OUT) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ip.outiface[0] != '\0') {
+                       strcat(iface, fw->ip.outiface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT("%-6s ","out %s "), iface);
+       }
+
+       fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
+       if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s ","%s "), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src)));
+               strcat(buf, mask_to_dotted(&(fw->ip.smsk)));
+               printf(FMT("%-19s ","%s "), buf);
+       }
+
+       fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
+       if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s ","-> %s"), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst)));
+               strcat(buf, mask_to_dotted(&(fw->ip.dmsk)));
+               printf(FMT("%-19s ","-> %s"), buf);
+       }
+
+       if (format & FMT_NOTABLE)
+               fputs("  ", stdout);
+
+#ifdef IPT_F_GOTO
+       if(fw->ip.flags & IPT_F_GOTO)
+               printf("[goto] ");
+#endif
+
+       IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
+
+       if (target) {
+               if (target->print)
+                       /* Print the target information. */
+                       target->print(&fw->ip, t, format & FMT_NUMERIC);
+       } else if (t->u.target_size != sizeof(*t))
+               printf("[%u bytes of unknown target data] ",
+                      (unsigned int)(t->u.target_size - sizeof(*t)));
+
+       if (!(format & FMT_NONEWLINE))
+               fputc('\n', stdout);
+}
+
+static void
+print_firewall_line(const struct ipt_entry *fw,
+                   const iptc_handle_t h)
+{
+       struct ipt_entry_target *t;
+
+       t = ipt_get_target((struct ipt_entry *)fw);
+       print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h);
+}
+
+static int
+append_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_append_entry(chain, fw, handle);
+               }
+       }
+
+       return ret;
+}
+
+static int
+replace_entry(const ipt_chainlabel chain,
+             struct ipt_entry *fw,
+             unsigned int rulenum,
+             const struct in_addr *saddr,
+             const struct in_addr *daddr,
+             int verbose,
+             iptc_handle_t *handle)
+{
+       fw->ip.src.s_addr = saddr->s_addr;
+       fw->ip.dst.s_addr = daddr->s_addr;
+
+       if (verbose)
+               print_firewall_line(fw, *handle);
+       return iptc_replace_entry(chain, fw, rulenum, handle);
+}
+
+static int
+insert_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int rulenum,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_insert_entry(chain, fw, rulenum, handle);
+               }
+       }
+
+       return ret;
+}
+
+static unsigned char *
+make_delete_mask(struct ipt_entry *fw, struct iptables_rule_match *matches)
+{
+       /* Establish mask for comparison */
+       unsigned int size;
+       struct iptables_rule_match *matchp;
+       unsigned char *mask, *mptr;
+
+       size = sizeof(struct ipt_entry);
+       for (matchp = matches; matchp; matchp = matchp->next)
+               size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + matchp->match->size;
+
+       mask = fw_calloc(1, size
+                        + IPT_ALIGN(sizeof(struct ipt_entry_target))
+                        + iptables_targets->size);
+
+       memset(mask, 0xFF, sizeof(struct ipt_entry));
+       mptr = mask + sizeof(struct ipt_entry);
+
+       for (matchp = matches; matchp; matchp = matchp->next) {
+               memset(mptr, 0xFF,
+                      IPT_ALIGN(sizeof(struct ipt_entry_match))
+                      + matchp->match->userspacesize);
+               mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + matchp->match->size;
+       }
+
+       memset(mptr, 0xFF,
+              IPT_ALIGN(sizeof(struct ipt_entry_target))
+              + iptables_targets->userspacesize);
+
+       return mask;
+}
+
+static int
+delete_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle,
+            struct iptables_rule_match *matches)
+{
+       unsigned int i, j;
+       int ret = 1;
+       unsigned char *mask;
+
+       mask = make_delete_mask(fw, matches);
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_delete_entry(chain, fw, mask, handle);
+               }
+       }
+       free(mask);
+
+       return ret;
+}
+
+int
+for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *),
+              int verbose, int builtinstoo, iptc_handle_t *handle)
+{
+        int ret = 1;
+       const char *chain;
+       char *chains;
+       unsigned int i, chaincount = 0;
+
+       chain = iptc_first_chain(handle);
+       while (chain) {
+               chaincount++;
+               chain = iptc_next_chain(handle);
+        }
+
+       chains = fw_malloc(sizeof(ipt_chainlabel) * chaincount);
+       i = 0;
+       chain = iptc_first_chain(handle);
+       while (chain) {
+               strcpy(chains + i*sizeof(ipt_chainlabel), chain);
+               i++;
+               chain = iptc_next_chain(handle);
+        }
+
+       for (i = 0; i < chaincount; i++) {
+               if (!builtinstoo
+                   && iptc_builtin(chains + i*sizeof(ipt_chainlabel),
+                                   *handle) == 1)
+                       continue;
+               ret &= fn(chains + i*sizeof(ipt_chainlabel), verbose, handle);
+       }
+
+       free(chains);
+        return ret;
+}
+
+int
+flush_entries(const ipt_chainlabel chain, int verbose,
+             iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(flush_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Flushing chain `%s'\n", chain);
+       return iptc_flush_entries(chain, handle);
+}
+
+static int
+zero_entries(const ipt_chainlabel chain, int verbose,
+            iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(zero_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Zeroing chain `%s'\n", chain);
+       return iptc_zero_entries(chain, handle);
+}
+
+int
+delete_chain(const ipt_chainlabel chain, int verbose,
+            iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(delete_chain, verbose, 0, handle);
+
+       if (verbose)
+               fprintf(stdout, "Deleting chain `%s'\n", chain);
+       return iptc_delete_chain(chain, handle);
+}
+
+static int
+list_entries(const ipt_chainlabel chain, int verbose, int numeric,
+            int expanded, int linenumbers, iptc_handle_t *handle)
+{
+       int found = 0;
+       unsigned int format;
+       const char *this;
+
+       format = FMT_OPTIONS;
+       if (!verbose)
+               format |= FMT_NOCOUNTS;
+       else
+               format |= FMT_VIA;
+
+       if (numeric)
+               format |= FMT_NUMERIC;
+
+       if (!expanded)
+               format |= FMT_KILOMEGAGIGA;
+
+       if (linenumbers)
+               format |= FMT_LINENUMBERS;
+
+       for (this = iptc_first_chain(handle);
+            this;
+            this = iptc_next_chain(handle)) {
+               const struct ipt_entry *i;
+               unsigned int num;
+
+               if (chain && strcmp(chain, this) != 0)
+                       continue;
+
+               if (found) printf("\n");
+
+               print_header(format, this, handle);
+               i = iptc_first_rule(this, handle);
+
+               num = 0;
+               while (i) {
+                       print_firewall(i,
+                                      iptc_get_target(i, handle),
+                                      num++,
+                                      format,
+                                      *handle);
+                       i = iptc_next_rule(i, handle);
+               }
+               found = 1;
+       }
+
+       errno = ENOENT;
+       return found;
+}
+
+static char *get_modprobe(void)
+{
+       int procfile;
+       char *ret;
+
+#define PROCFILE_BUFSIZ        1024
+       procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+       if (procfile < 0)
+               return NULL;
+
+       ret = (char *) malloc(PROCFILE_BUFSIZ);
+       if (ret) {
+               memset(ret, 0, PROCFILE_BUFSIZ);
+               switch (read(procfile, ret, PROCFILE_BUFSIZ)) {
+               case -1: goto fail;
+               case PROCFILE_BUFSIZ: goto fail; /* Partial read.  Wierd */
+               }
+               if (ret[strlen(ret)-1]=='\n') 
+                       ret[strlen(ret)-1]=0;
+               close(procfile);
+               return ret;
+       }
+ fail:
+       free(ret);
+       close(procfile);
+       return NULL;
+}
+
+int iptables_insmod(const char *modname, const char *modprobe, int quiet)
+{
+       char *buf = NULL;
+       char *argv[4];
+       int status;
+
+       /* If they don't explicitly set it, read out of kernel */
+       if (!modprobe) {
+               buf = get_modprobe();
+               if (!buf)
+                       return -1;
+               modprobe = buf;
+       }
+
+       switch (fork()) {
+       case 0:
+               argv[0] = (char *)modprobe;
+               argv[1] = (char *)modname;
+               if (quiet) {
+                       argv[2] = "-q";
+                       argv[3] = NULL;
+               } else {
+                       argv[2] = NULL;
+                       argv[3] = NULL;
+               }
+               execv(argv[0], argv);
+
+               /* not usually reached */
+               exit(1);
+       case -1:
+               return -1;
+
+       default: /* parent */
+               wait(&status);
+       }
+
+       free(buf);
+       if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+               return 0;
+       return -1;
+}
+
+int load_iptables_ko(const char *modprobe, int quiet)
+{
+       static int loaded = 0;
+       static int ret = -1;
+
+       if (!loaded) {
+               ret = iptables_insmod("ip_tables", modprobe, quiet);
+               loaded = (ret == 0);
+       }
+
+       return ret;
+}
+
+static struct ipt_entry *
+generate_entry(const struct ipt_entry *fw,
+              struct iptables_rule_match *matches,
+              struct ipt_entry_target *target)
+{
+       unsigned int size;
+       struct iptables_rule_match *matchp;
+       struct ipt_entry *e;
+
+       size = sizeof(struct ipt_entry);
+       for (matchp = matches; matchp; matchp = matchp->next)
+               size += matchp->match->m->u.match_size;
+
+       e = fw_malloc(size + target->u.target_size);
+       *e = *fw;
+       e->target_offset = size;
+       e->next_offset = size + target->u.target_size;
+
+       size = 0;
+       for (matchp = matches; matchp; matchp = matchp->next) {
+               memcpy(e->elems + size, matchp->match->m, matchp->match->m->u.match_size);
+               size += matchp->match->m->u.match_size;
+       }
+       memcpy(e->elems + size, target, target->u.target_size);
+
+       return e;
+}
+
+void clear_rule_matches(struct iptables_rule_match **matches)
+{
+       struct iptables_rule_match *matchp, *tmp;
+
+       for (matchp = *matches; matchp;) {
+               tmp = matchp->next;
+               if (matchp->match->m) {
+                       free(matchp->match->m);
+                       matchp->match->m = NULL;
+               }
+               if (matchp->match == matchp->match->next) {
+                       free(matchp->match);
+                       matchp->match = NULL;
+               }
+               free(matchp);
+               matchp = tmp;
+       }
+
+       *matches = NULL;
+}
+
+static void set_revision(char *name, u_int8_t revision)
+{
+       /* Old kernel sources don't have ".revision" field,
+          but we stole a byte from name. */
+       name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0';
+       name[IPT_FUNCTION_MAXNAMELEN - 1] = revision;
+}
+
+void
+get_kernel_version(void) {
+       static struct utsname uts;
+       int x = 0, y = 0, z = 0;
+
+       if (uname(&uts) == -1) {
+               fprintf(stderr, "Unable to retrieve kernel version.\n");
+               free_opts(1);
+               exit(1); 
+       }
+
+       sscanf(uts.release, "%d.%d.%d", &x, &y, &z);
+       kernel_version = LINUX_VERSION(x, y, z);
+}
+
+int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
+{
+       struct ipt_entry fw, *e = NULL;
+       int invert = 0;
+       unsigned int nsaddrs = 0, ndaddrs = 0;
+       struct in_addr *saddrs = NULL, *daddrs = NULL;
+
+       int c, verbose = 0;
+       const char *chain = NULL;
+       const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
+       const char *policy = NULL, *newname = NULL;
+       unsigned int rulenum = 0, options = 0, command = 0;
+       const char *pcnt = NULL, *bcnt = NULL;
+       int ret = 1;
+       struct iptables_match *m;
+       struct iptables_rule_match *matches = NULL;
+       struct iptables_rule_match *matchp;
+       struct iptables_target *target = NULL;
+       struct iptables_target *t;
+       const char *jumpto = "";
+       char *protocol = NULL;
+       int proto_used = 0;
+
+       memset(&fw, 0, sizeof(fw));
+
+       /* re-set optind to 0 in case do_command gets called
+        * a second time */
+       optind = 0;
+
+       /* clear mflags in case do_command gets called a second time
+        * (we clear the global list of all matches for security)*/
+       for (m = iptables_matches; m; m = m->next)
+               m->mflags = 0;
+
+       for (t = iptables_targets; t; t = t->next) {
+               t->tflags = 0;
+               t->used = 0;
+       }
+
+       /* Suppress error messages: we may add new options if we
+           demand-load a protocol. */
+       opterr = 0;
+
+       while ((c = getopt_long(argc, argv,
+          "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:",
+                                          opts, NULL)) != -1) {
+               switch (c) {
+                       /*
+                        * Command selection
+                        */
+               case 'A':
+                       add_command(&command, CMD_APPEND, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'D':
+                       add_command(&command, CMD_DELETE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!') {
+                               rulenum = parse_rulenumber(argv[optind++]);
+                               command = CMD_DELETE_NUM;
+                       }
+                       break;
+
+               case 'R':
+                       add_command(&command, CMD_REPLACE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a rule number",
+                                          cmd2char(CMD_REPLACE));
+                       break;
+
+               case 'I':
+                       add_command(&command, CMD_INSERT, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else rulenum = 1;
+                       break;
+
+               case 'L':
+                       add_command(&command, CMD_LIST, CMD_ZERO,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'F':
+                       add_command(&command, CMD_FLUSH, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'Z':
+                       add_command(&command, CMD_ZERO, CMD_LIST,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                               && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'N':
+                       if (optarg && (*optarg == '-' || *optarg == '!'))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name not allowed to start "
+                                          "with `%c'\n", *optarg);
+                       if (find_target(optarg, TRY_LOAD))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name may not clash "
+                                          "with target name\n");
+                       add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'X':
+                       add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'E':
+                       add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               newname = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires old-chain-name and "
+                                          "new-chain-name",
+                                           cmd2char(CMD_RENAME_CHAIN));
+                       break;
+
+               case 'P':
+                       add_command(&command, CMD_SET_POLICY, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               policy = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a chain and a policy",
+                                          cmd2char(CMD_SET_POLICY));
+                       break;
+
+               case 'h':
+                       if (!optarg)
+                               optarg = argv[optind];
+
+                       /* iptables -p icmp -h */
+                       if (!matches && protocol)
+                               find_match(protocol, TRY_LOAD, &matches);
+
+                       exit_printhelp(matches);
+
+                       /*
+                        * Option selection
+                        */
+               case 'p':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_PROTOCOL, &fw.ip.invflags,
+                                  invert);
+
+                       /* Canonicalize into lower case */
+                       for (protocol = argv[optind-1]; *protocol; protocol++)
+                               *protocol = tolower(*protocol);
+
+                       protocol = argv[optind-1];
+                       fw.ip.proto = parse_protocol(protocol);
+
+                       if (fw.ip.proto == 0
+                           && (fw.ip.invflags & IPT_INV_PROTO))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "rule would never match protocol");
+                       break;
+
+               case 's':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_SOURCE, &fw.ip.invflags,
+                                  invert);
+                       shostnetworkmask = argv[optind-1];
+                       break;
+
+               case 'd':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_DESTINATION, &fw.ip.invflags,
+                                  invert);
+                       dhostnetworkmask = argv[optind-1];
+                       break;
+
+#ifdef IPT_F_GOTO
+               case 'g':
+                       set_option(&options, OPT_JUMP, &fw.ip.invflags,
+                                  invert);
+                       fw.ip.flags |= IPT_F_GOTO;
+                       jumpto = parse_target(optarg);
+                       break;
+#endif
+
+               case 'j':
+                       set_option(&options, OPT_JUMP, &fw.ip.invflags,
+                                  invert);
+                       jumpto = parse_target(optarg);
+                       /* TRY_LOAD (may be chain name) */
+                       target = find_target(jumpto, TRY_LOAD);
+
+                       if (target) {
+                               size_t size;
+
+                               size = IPT_ALIGN(sizeof(struct ipt_entry_target))
+                                       + target->size;
+
+                               target->t = fw_calloc(1, size);
+                               target->t->u.target_size = size;
+                               strcpy(target->t->u.user.name, jumpto);
+                               set_revision(target->t->u.user.name,
+                                            target->revision);
+                               if (target->init != NULL)
+                                       target->init(target->t, &fw.nfcache);
+                               opts = merge_options(opts, target->extra_opts, &target->option_offset);
+                       }
+                       break;
+
+
+               case 'i':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ip.iniface,
+                                       fw.ip.iniface_mask);
+                       break;
+
+               case 'o':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEOUT, &fw.ip.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ip.outiface,
+                                       fw.ip.outiface_mask);
+                       break;
+
+               case 'f':
+                       set_option(&options, OPT_FRAGMENT, &fw.ip.invflags,
+                                  invert);
+                       fw.ip.flags |= IPT_F_FRAG;
+                       break;
+
+               case 'v':
+                       if (!verbose)
+                               set_option(&options, OPT_VERBOSE,
+                                          &fw.ip.invflags, invert);
+                       verbose++;
+                       break;
+
+               case 'm': {
+                       size_t size;
+
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --match");
+
+                       m = find_match(optarg, LOAD_MUST_SUCCEED, &matches);
+                       size = IPT_ALIGN(sizeof(struct ipt_entry_match))
+                                        + m->size;
+                       m->m = fw_calloc(1, size);
+                       m->m->u.match_size = size;
+                       strcpy(m->m->u.user.name, m->name);
+                       set_revision(m->m->u.user.name, m->revision);
+                       if (m->init != NULL)
+                               m->init(m->m, &fw.nfcache);
+                       if (m != m->next)
+                               /* Merge options for non-cloned matches */
+                               opts = merge_options(opts, m->extra_opts, &m->option_offset);
+               }
+               break;
+
+               case 'n':
+                       set_option(&options, OPT_NUMERIC, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 't':
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --table");
+                       *table = argv[optind-1];
+                       break;
+
+               case 'x':
+                       set_option(&options, OPT_EXPANDED, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 'V':
+                       if (invert)
+                               printf("Not %s ;-)\n", program_version);
+                       else
+                               printf("%s v%s\n",
+                                      program_name, program_version);
+                       exit(0);
+
+               case '0':
+                       set_option(&options, OPT_LINENUMBERS, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 'M':
+                       modprobe = optarg;
+                       break;
+
+               case 'c':
+
+                       set_option(&options, OPT_COUNTERS, &fw.ip.invflags,
+                                  invert);
+                       pcnt = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               bcnt = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c requires packet and byte counter",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(pcnt, "%llu", (unsigned long long *)&fw.counters.pcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c packet counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(bcnt, "%llu", (unsigned long long *)&fw.counters.bcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c byte counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+                       
+                       break;
+
+
+               case 1: /* non option */
+                       if (optarg[0] == '!' && optarg[1] == '\0') {
+                               if (invert)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "multiple consecutive ! not"
+                                                  " allowed");
+                               invert = TRUE;
+                               optarg[0] = '\0';
+                               continue;
+                       }
+                       printf("Bad argument `%s'\n", optarg);
+                       exit_tryhelp(2);
+
+               default:
+                       if (!target
+                           || !(target->parse(c - target->option_offset,
+                                              argv, invert,
+                                              &target->tflags,
+                                              &fw, &target->t))) {
+                               for (matchp = matches; matchp; matchp = matchp->next) {
+                                       if (matchp->completed) 
+                                               continue;
+                                       if (matchp->match->parse(c - matchp->match->option_offset,
+                                                    argv, invert,
+                                                    &matchp->match->mflags,
+                                                    &fw,
+                                                    &fw.nfcache,
+                                                    &matchp->match->m))
+                                               break;
+                               }
+                               m = matchp ? matchp->match : NULL;
+
+                               /* If you listen carefully, you can
+                                  actually hear this code suck. */
+
+                               /* some explanations (after four different bugs
+                                * in 3 different releases): If we encounter a
+                                * parameter, that has not been parsed yet,
+                                * it's not an option of an explicitly loaded
+                                * match or a target.  However, we support
+                                * implicit loading of the protocol match
+                                * extension.  '-p tcp' means 'l4 proto 6' and
+                                * at the same time 'load tcp protocol match on
+                                * demand if we specify --dport'.
+                                *
+                                * To make this work, we need to make sure:
+                                * - the parameter has not been parsed by
+                                *   a match (m above)
+                                * - a protocol has been specified
+                                * - the protocol extension has not been
+                                *   loaded yet, or is loaded and unused
+                                *   [think of iptables-restore!]
+                                * - the protocol extension can be successively
+                                *   loaded
+                                */
+                               if (m == NULL
+                                   && protocol
+                                   && (!find_proto(protocol, DONT_LOAD,
+                                                  options&OPT_NUMERIC, NULL) 
+                                       || (find_proto(protocol, DONT_LOAD,
+                                                       options&OPT_NUMERIC, NULL)
+                                           && (proto_used == 0))
+                                      )
+                                   && (m = find_proto(protocol, TRY_LOAD,
+                                                      options&OPT_NUMERIC, &matches))) {
+                                       /* Try loading protocol */
+                                       size_t size;
+                                       
+                                       proto_used = 1;
+
+                                       size = IPT_ALIGN(sizeof(struct ipt_entry_match))
+                                                        + m->size;
+
+                                       m->m = fw_calloc(1, size);
+                                       m->m->u.match_size = size;
+                                       strcpy(m->m->u.user.name, m->name);
+                                       set_revision(m->m->u.user.name,
+                                                    m->revision);
+                                       if (m->init != NULL)
+                                               m->init(m->m, &fw.nfcache);
+
+                                       opts = merge_options(opts,
+                                           m->extra_opts, &m->option_offset);
+
+                                       optind--;
+                                       continue;
+                               }
+                               if (!m)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Unknown arg `%s'",
+                                                  argv[optind-1]);
+                       }
+               }
+               invert = FALSE;
+       }
+
+       for (matchp = matches; matchp; matchp = matchp->next)
+               matchp->match->final_check(matchp->match->mflags);
+
+       if (target)
+               target->final_check(target->tflags);
+
+       /* Fix me: must put inverse options checking here --MN */
+
+       if (optind < argc)
+               exit_error(PARAMETER_PROBLEM,
+                          "unknown arguments found on commandline");
+       if (!command)
+               exit_error(PARAMETER_PROBLEM, "no command specified");
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "nothing appropriate following !");
+
+       if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
+               if (!(options & OPT_DESTINATION))
+                       dhostnetworkmask = "0.0.0.0/0";
+               if (!(options & OPT_SOURCE))
+                       shostnetworkmask = "0.0.0.0/0";
+       }
+
+       if (shostnetworkmask)
+               parse_hostnetworkmask(shostnetworkmask, &saddrs,
+                                     &(fw.ip.smsk), &nsaddrs);
+
+       if (dhostnetworkmask)
+               parse_hostnetworkmask(dhostnetworkmask, &daddrs,
+                                     &(fw.ip.dmsk), &ndaddrs);
+
+       if ((nsaddrs > 1 || ndaddrs > 1) &&
+           (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
+               exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+                          " source or destination IP addresses");
+
+       if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
+               exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
+                          "specify a unique address");
+
+       generic_opt_check(command, options);
+
+       if (chain && strlen(chain) > IPT_FUNCTION_MAXNAMELEN)
+               exit_error(PARAMETER_PROBLEM,
+                          "chain name `%s' too long (must be under %i chars)",
+                          chain, IPT_FUNCTION_MAXNAMELEN);
+
+       /* only allocate handle if we weren't called with a handle */
+       if (!*handle)
+               *handle = iptc_init(*table);
+
+       /* try to insmod the module if iptc_init failed */
+       if (!*handle && load_iptables_ko(modprobe, 0) != -1)
+               *handle = iptc_init(*table);
+
+       if (!*handle)
+               exit_error(VERSION_PROBLEM,
+                          "can't initialize iptables table `%s': %s",
+                          *table, iptc_strerror(errno));
+
+       if (command == CMD_APPEND
+           || command == CMD_DELETE
+           || command == CMD_INSERT
+           || command == CMD_REPLACE) {
+               if (strcmp(chain, "PREROUTING") == 0
+                   || strcmp(chain, "INPUT") == 0) {
+                       /* -o not valid with incoming packets. */
+                       if (options & OPT_VIANAMEOUT)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEOUT),
+                                          chain);
+               }
+
+               if (strcmp(chain, "POSTROUTING") == 0
+                   || strcmp(chain, "OUTPUT") == 0) {
+                       /* -i not valid with outgoing packets */
+                       if (options & OPT_VIANAMEIN)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEIN),
+                                          chain);
+               }
+
+               if (target && iptc_is_chain(jumpto, *handle)) {
+                       printf("Warning: using chain %s, not extension\n",
+                              jumpto);
+
+                       if (target->t)
+                               free(target->t);
+
+                       target = NULL;
+               }
+
+               /* If they didn't specify a target, or it's a chain
+                  name, use standard. */
+               if (!target
+                   && (strlen(jumpto) == 0
+                       || iptc_is_chain(jumpto, *handle))) {
+                       size_t size;
+
+                       target = find_target(IPT_STANDARD_TARGET,
+                                            LOAD_MUST_SUCCEED);
+
+                       size = sizeof(struct ipt_entry_target)
+                               + target->size;
+                       target->t = fw_calloc(1, size);
+                       target->t->u.target_size = size;
+                       strcpy(target->t->u.user.name, jumpto);
+                       if (!iptc_is_chain(jumpto, *handle))
+                               set_revision(target->t->u.user.name,
+                                            target->revision);
+                       if (target->init != NULL)
+                               target->init(target->t, &fw.nfcache);
+               }
+
+               if (!target) {
+                       /* it is no chain, and we can't load a plugin.
+                        * We cannot know if the plugin is corrupt, non
+                        * existant OR if the user just misspelled a
+                        * chain. */
+#ifdef IPT_F_GOTO
+                       if (fw.ip.flags & IPT_F_GOTO)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "goto '%s' is not a chain\n", jumpto);
+#endif
+                       find_target(jumpto, LOAD_MUST_SUCCEED);
+               } else {
+                       e = generate_entry(&fw, matches, target->t);
+                       free(target->t);
+               }
+       }
+
+       switch (command) {
+       case CMD_APPEND:
+               ret = append_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_DELETE:
+               ret = delete_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle, matches);
+               break;
+       case CMD_DELETE_NUM:
+               ret = iptc_delete_num_entry(chain, rulenum - 1, handle);
+               break;
+       case CMD_REPLACE:
+               ret = replace_entry(chain, e, rulenum - 1,
+                                   saddrs, daddrs, options&OPT_VERBOSE,
+                                   handle);
+               break;
+       case CMD_INSERT:
+               ret = insert_entry(chain, e, rulenum - 1,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_LIST:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               break;
+       case CMD_FLUSH:
+               ret = flush_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_ZERO:
+               ret = zero_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_LIST|CMD_ZERO:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               if (ret)
+                       ret = zero_entries(chain,
+                                          options&OPT_VERBOSE, handle);
+               break;
+       case CMD_NEW_CHAIN:
+               ret = iptc_create_chain(chain, handle);
+               break;
+       case CMD_DELETE_CHAIN:
+               ret = delete_chain(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_RENAME_CHAIN:
+               ret = iptc_rename_chain(chain, newname, handle);
+               break;
+       case CMD_SET_POLICY:
+               ret = iptc_set_policy(chain, policy, NULL, handle);
+               break;
+       default:
+               /* We should never reach this... */
+               exit_tryhelp(2);
+       }
+
+       if (verbose > 1)
+               dump_entries(*handle);
+
+       clear_rule_matches(&matches);
+
+       if (e != NULL) {
+               free(e);
+               e = NULL;
+       }
+
+       free(saddrs);
+       free(daddrs);
+       free_opts(1);
+
+       return ret;
+}
diff --git a/iptables.init b/iptables.init
new file mode 100755 (executable)
index 0000000..ec50ee0
--- /dev/null
@@ -0,0 +1,373 @@
+#!/bin/sh
+#
+# iptables     Start iptables firewall
+#
+# chkconfig: 2345 08 92
+# description: Starts, stops and saves iptables firewall
+#
+# config: /etc/sysconfig/iptables
+# config: /etc/sysconfig/iptables-config
+
+# Source function library.
+. /etc/init.d/functions
+
+IPTABLES=iptables
+IPTABLES_DATA=/etc/sysconfig/$IPTABLES
+IPTABLES_CONFIG=/etc/sysconfig/${IPTABLES}-config
+IPV=${IPTABLES%tables} # ip for ipv4 | ip6 for ipv6
+PROC_IPTABLES_NAMES=/proc/net/${IPV}_tables_names
+VAR_SUBSYS_IPTABLES=/var/lock/subsys/$IPTABLES
+
+if [ ! -x /sbin/$IPTABLES ]; then
+    echo -n $"/sbin/$IPTABLES does not exist."; warning; echo
+    exit 0
+fi
+
+if lsmod 2>/dev/null | grep -q ipchains ; then
+    echo -n $"ipchains and $IPTABLES can not be used together."; warning; echo
+    exit 0
+fi
+
+# Old or new modutils
+/sbin/modprobe --version 2>&1 | grep -q module-init-tools \
+    && NEW_MODUTILS=1 \
+    || NEW_MODUTILS=0
+
+# Default firewall configuration:
+IPTABLES_MODULES=""
+IPTABLES_MODULES_UNLOAD="yes"
+IPTABLES_SAVE_ON_STOP="no"
+IPTABLES_SAVE_ON_RESTART="no"
+IPTABLES_SAVE_COUNTER="no"
+IPTABLES_STATUS_NUMERIC="no"
+
+# Load firewall configuration.
+[ -f "$IPTABLES_CONFIG" ] && . "$IPTABLES_CONFIG"
+
+rmmod_r() {
+    # Unload module with all referring modules.
+    # At first all referring modules will be unloaded, then the module itself.
+    local mod=$1
+    local ret=0
+    local ref=
+
+    # Get referring modules.
+    # New modutils have another output format.
+    [ $NEW_MODUTILS = 1 ] \
+       && ref=`lsmod | awk "/^${mod}/ { print \\\$4; }" | tr ',' ' '` \
+       || ref=`lsmod | grep ^${mod} | cut -d "[" -s -f 2 | cut -d "]" -s -f 1`
+
+    # recursive call for all referring modules
+    for i in $ref; do
+       rmmod_r $i
+       let ret+=$?;
+    done
+
+    # Unload module.
+    # The extra test is for 2.6: The module might have autocleaned,
+    # after all referring modules are unloaded.
+    if grep -q "^${mod}" /proc/modules ; then
+       modprobe -r $mod > /dev/null 2>&1
+       let ret+=$?;
+    fi
+
+    return $ret
+}
+
+flush_n_delete() {
+    # Flush firewall rules and delete chains.
+    [ -e "$PROC_IPTABLES_NAMES" ] || return 1
+
+    # Check if firewall is configured (has tables)
+    tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null`
+    [ -z "$tables" ] && return 1
+
+    echo -n $"Flushing firewall rules: "
+    ret=0
+    # For all tables
+    for i in $tables; do
+        # Flush firewall rules.
+       $IPTABLES -t $i -F;
+       let ret+=$?;
+
+        # Delete firewall chains.
+       $IPTABLES -t $i -X;
+       let ret+=$?;
+
+       # Set counter to zero.
+       $IPTABLES -t $i -Z;
+       let ret+=$?;
+    done
+
+    [ $ret -eq 0 ] && success || failure
+    echo
+    return $ret
+}
+
+set_policy() {
+    # Set policy for configured tables.
+    policy=$1
+
+    # Check if iptable module is loaded
+    [ ! -e "$PROC_IPTABLES_NAMES" ] && return 1
+
+    # Check if firewall is configured (has tables)
+    tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null`
+    [ -z "$tables" ] && return 1
+
+    echo -n $"Setting chains to policy $policy: "
+    ret=0
+    for i in $tables; do
+       echo -n "$i "
+       case "$i" in
+           filter)
+                $IPTABLES -t filter -P INPUT $policy \
+                   && $IPTABLES -t filter -P OUTPUT $policy \
+                   && $IPTABLES -t filter -P FORWARD $policy \
+                   || let ret+=1
+               ;;
+           nat)
+               $IPTABLES -t nat -P PREROUTING $policy \
+                   && $IPTABLES -t nat -P POSTROUTING $policy \
+                   && $IPTABLES -t nat -P OUTPUT $policy \
+                   || let ret+=1
+               ;;
+           mangle)
+               $IPTABLES -t mangle -P PREROUTING $policy \
+                   && $IPTABLES -t mangle -P POSTROUTING $policy \
+                   && $IPTABLES -t mangle -P INPUT $policy \
+                   && $IPTABLES -t mangle -P OUTPUT $policy \
+                   && $IPTABLES -t mangle -P FORWARD $policy \
+                   || let ret+=1
+               ;;
+           *)
+               let ret+=1
+               ;;
+        esac
+    done
+
+    [ $ret -eq 0 ] && success || failure
+    echo
+    return $ret
+}
+
+start() {
+    # Do not start if there is no config file.
+    [ -f "$IPTABLES_DATA" ] || return 1
+
+    echo -n $"Applying $IPTABLES firewall rules: "
+
+    OPT=
+    [ "x$IPTABLES_SAVE_COUNTER" = "xyes" ] && OPT="-c"
+
+    $IPTABLES-restore $OPT $IPTABLES_DATA
+    if [ $? -eq 0 ]; then
+       success; echo
+    else
+       failure; echo; return 1
+    fi
+    
+    # Tuntap initialization
+
+    if [ -z "$taps" -a -r /etc/planetlab/node_id ] ; then
+        # If this node is not "virtually multi-homed", just bring up
+        # the tap interface with a PLB private address. The PLB
+        # convention is to assign a unique 10.x.y.0/24 network to each
+        # node where x.y is the PlanetLab node ID of the machine in
+        # host order:
+        #
+        # x = (node_id / 256) % 256
+        # y = node_id % 256
+        #
+        node_id=$(cat /etc/planetlab/node_id)
+        taps="tap0"
+        tap0=$(printf 10.%d.%d.1 $((($node_id / 256) % 256)) $(($node_id % 256)))
+        tapmask=255.0.0.0
+    fi
+
+    # Load additional modules (helpers)
+    if [ -n "$IPTABLES_MODULES" ]; then
+       echo -n $"Loading additional $IPTABLES modules: "
+       ret=0
+       for mod in $IPTABLES_MODULES; do
+           echo -n "$mod "
+           modprobe $mod > /dev/null 2>&1
+           let ret+=$?;
+       done
+       [ $ret -eq 0 ] && success || failure
+       echo
+    fi
+
+    for tap in $taps ; do
+        # Configuration for this tap (address/proxy)
+        eval cfg=\$$tap
+        addr=${cfg%/*}
+        proxy=${cfg#*/}
+
+        # Set MAC address to something predictable
+        mac=$(printf 00:FF:%X:%X:%X:%X $(echo $addr | sed -e 's/\./ /g'))
+
+        # Bring up this interface. Optimize the MTU for the PlanetLab
+        # Backbone (1500/Ethernet - 4/GRE - 8/UDP - 20/IP = 1468).
+            ifconfig $tap down && \
+            ifconfig $tap hw ether $mac mtu 1468 && \
+            ifconfig $tap $addr ${proxy:+pointopoint $proxy} netmask ${tapmask:=255.255.255.255} up
+
+        # Stuffing the proxy for this address in the pointopoint field
+        # creates a static route to the proxy that we do not want
+        # present.
+        if [ -n "$proxy" -a "$proxy" != "$addr" ] ; then
+                ip route del $proxy
+        fi
+
+        # Enable route through this interface
+            ip route add default dev $tap tab 1 && \
+            ip rule add from $addr tab 1
+    done
+
+    
+    touch $VAR_SUBSYS_IPTABLES
+    return $ret
+}
+
+stop() {
+    
+        # Do not stop if iptables module is not loaded.
+    [ -e "$PROC_IPTABLES_NAMES" ] || return 1
+
+    flush_n_delete
+    set_policy ACCEPT
+    
+    if [ "x$IPTABLES_MODULES_UNLOAD" = "xyes" ]; then
+       echo -n $"Unloading $IPTABLES modules: "
+       ret=0
+       rmmod_r ${IPV}_tables
+       let ret+=$?;
+       rmmod_r ${IPV}_conntrack
+       let ret+=$?;
+       [ $ret -eq 0 ] && success || failure
+       echo
+    fi
+
+    # Take down vnet interfaces
+    for dev in $taps tap0 ; do
+        action $"Shutting down interface $dev: " \
+            ifconfig $dev 0.0.0.0 down
+    done               
+
+    rm -f $VAR_SUBSYS_IPTABLES
+    return $ret
+}
+
+save() {
+    # Check if iptable module is loaded
+    [ ! -e "$PROC_IPTABLES_NAMES" ] && return 1
+
+    # Check if firewall is configured (has tables)
+    tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null`
+    [ -z "$tables" ] && return 1
+
+    echo -n $"Saving firewall rules to $IPTABLES_DATA: "
+
+    OPT=
+    [ "x$IPTABLES_SAVE_COUNTER" = "xyes" ] && OPT="-c"
+
+    ret=0
+    TMP_FILE=`/bin/mktemp -q /tmp/$IPTABLES.XXXXXX` \
+       && chmod 600 "$TMP_FILE" \
+       && $IPTABLES-save $OPT > $TMP_FILE 2>/dev/null \
+       && size=`stat -c '%s' $TMP_FILE` && [ $size -gt 0 ] \
+       || ret=1
+    if [ $ret -eq 0 ]; then
+       if [ -e $IPTABLES_DATA ]; then
+           cp -f $IPTABLES_DATA $IPTABLES_DATA.save \
+               && chmod 600 $IPTABLES_DATA.save \
+               || ret=1
+       fi
+       if [ $ret -eq 0 ]; then
+           cp -f $TMP_FILE $IPTABLES_DATA \
+               && chmod 600 $IPTABLES_DATA \
+               || ret=1
+       fi
+    fi
+    [ $ret -eq 0 ] && success || failure
+    echo
+    rm -f $TMP_FILE
+    return $ret
+}
+
+status() {
+    # Do not print status if lockfile is missing and iptables modules are not 
+    # loaded.
+    # Check if iptable module is loaded
+    if [ ! -f "$VAR_SUBSYS_IPTABLES" ]; then
+       echo $"Firewall is stopped."
+       return 1
+    fi
+
+    # Check if firewall is configured (has tables)
+    if [ ! -e "$PROC_IPTABLES_NAMES" ]; then
+       echo $"Firewall is not configured. "
+       return 1
+    fi
+    tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null`
+    if [ -z "$tables" ]; then
+       echo $"Firewall is not configured. "
+       return 1
+    fi
+
+    NUM=
+    [ "x$IPTABLES_STATUS_NUMERIC" = "xyes" ] && NUM="-n"
+
+    for table in $tables; do
+       echo $"Table: $table"
+       $IPTABLES -t $table --list $NUM && echo
+    done
+
+    return 0
+}
+
+restart() {
+    [ "x$IPTABLES_SAVE_ON_RESTART" = "xyes" ] && save
+    stop
+    start
+}
+
+case "$1" in
+    start)
+       stop
+       start
+       RETVAL=$?
+       ;;
+    stop)
+       [ "x$IPTABLES_SAVE_ON_STOP" = "xyes" ] && save
+       stop
+       RETVAL=$?
+       ;;
+    restart)
+       restart
+       RETVAL=$?
+       ;;
+    condrestart)
+       [ -e "$VAR_SUBSYS_IPTABLES" ] && restart
+       ;;
+    status)
+       status
+       RETVAL=$?
+       ;;
+    panic)
+       flush_n_delete
+       set_policy DROP
+       RETVAL=$?
+        ;;
+    save)
+       save
+       RETVAL=$?
+       ;;
+    *)
+       echo $"Usage: $0 {start|stop|restart|condrestart|status|panic|save}"
+       exit 1
+       ;;
+esac
+
+exit $RETVAL
diff --git a/iptables.spec b/iptables.spec
new file mode 100644 (file)
index 0000000..0c98e5e
--- /dev/null
@@ -0,0 +1,415 @@
+#
+# $Id: iproute.spec 7668 2008-01-08 11:49:43Z thierry $
+#
+%define url $URL: svn+ssh://thierry@svn.planet-lab.org/svn/iproute2/trunk/iproute.spec $
+
+%define name iptables
+%define version 1.3.8
+%define taglevel 9
+
+%define release %{taglevel}%{?pldistro:.%{pldistro}}%{?date:.%{date}}
+
+Vendor: PlanetLab
+Packager: PlanetLab Central <support@planet-lab.org>
+Distribution: PlanetLab %{plrelease}
+URL: %(echo %{url} | cut -d ' ' -f 2)
+
+%define build_devel 1
+%define linux_header 0
+
+Summary: Tools for managing Linux kernel packet filtering capabilities.
+Name: %{name}
+Version: %{version}
+Release: %{release}
+Source: http://www.netfilter.org/%{name}-%{version}.tar.bz2
+%define SOURCE1 iptables.init
+%define SOURCE2 iptables-config
+%define SOURCE3 planetlab-config
+Group: System Environment/Base
+#URL: http://www.netfilter.org/
+BuildRoot: %{_tmppath}/%{name}-buildroot
+License: GPL
+BuildPrereq: /usr/bin/perl
+Requires: kernel >= 2.4.20
+Requires(post,postun): chkconfig
+Prefix: %{_prefix}
+BuildRequires: kernel-devel
+
+%package ipv6
+Summary: IPv6 support for iptables.
+Group: System Environment/Base
+Requires: %{name} = %{version}
+
+%if %{build_devel}
+%package devel
+Summary: Development package for iptables.
+Group: System Environment/Base
+Requires: %{name} = %{version}
+%endif
+
+%description
+The iptables utility controls the network packet filtering code in the
+Linux kernel. If you need to set up firewalls and/or IP masquerading,
+you should install this package.
+
+%description ipv6
+The iptables package contains IPv6 (the next version of the IP
+protocol) support for iptables. Iptables controls the Linux kernel
+network packet filtering code, allowing you to set up firewalls and IP
+masquerading. 
+
+Install iptables-ipv6 if you need to set up firewalling for your
+network and you are using ipv6.
+
+%if %{build_devel}
+%description devel
+The iptables utility controls the network packet filtering code in the
+Linux kernel. If you need to set up firewalls and/or IP masquerading,
+you should install this package.
+%endif
+
+%prep
+rm -rf %{buildroot}
+
+%setup -q
+
+# Put it to a reasonable place
+find . -type f -exec perl -pi -e "s,/usr,%{prefix},g" {} \;
+
+%build
+TOPDIR=`pwd`
+OPT="$RPM_OPT_FLAGS -I$TOPDIR/include"
+
+%define KERNEL %(rpm -q --qf '%%{VERSION}-%%{RELEASE}-%%{ARCH}\\n' kernel-devel | tail -n 1 )
+count=$(rpm -q kernel-devel| wc -l)
+if [ $count -gt 1 ] ; then
+       echo "WARNING: choosing kernel-devel-%{KERNEL}"
+       echo "  but there are other kernel-devel packages installed: $(rpm -q kernel-devel)"
+fi
+       
+%define KERNEL_DIR "/usr/src/kernels/%{KERNEL}"
+
+make COPT_FLAGS="$OPT" KERNEL_DIR=%{KERNEL_DIR} LIBDIR=/%{_lib}
+make COPT_FLAGS="$OPT" KERNEL_DIR=%{KERNEL_DIR} LIBDIR=/%{_lib} iptables-save iptables-restore
+make COPT_FLAGS="$OPT" KERNEL_DIR=%{KERNEL_DIR} LIBDIR=/%{_lib} ip6tables-save ip6tables-restore
+
+%install
+make install DESTDIR=%{buildroot} KERNEL_DIR=%{KERNEL_DIR} BINDIR=/sbin LIBDIR=/%{_lib} MANDIR=%{_mandir}
+%if %{build_devel}
+make install-devel DESTDIR=%{buildroot} KERNEL_DIR=%{KERNEL_DIR} BINDIR=/sbin LIBDIR=%{_libdir} MANDIR=%{_mandir} INCDIR=%{_includedir}
+%endif
+cp ip{6,}tables-{save,restore} $RPM_BUILD_ROOT/sbin
+cp iptables-*.8 $RPM_BUILD_ROOT%{_mandir}/man8
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
+install -c -m755 %{SOURCE1} $RPM_BUILD_ROOT/etc/rc.d/init.d/iptables
+sed -e 's;iptables;ip6tables;g' -e 's;IPTABLES;IP6TABLES;g' < %{SOURCE1} > ip6tables.init
+install -c -m755 ip6tables.init $RPM_BUILD_ROOT/etc/rc.d/init.d/ip6tables
+mkdir -p $RPM_BUILD_ROOT/etc/sysconfig
+install -c -m755 %{SOURCE2} $RPM_BUILD_ROOT/etc/sysconfig/iptables-config
+install -c -m755 %{SOURCE3} $RPM_BUILD_ROOT/etc/sysconfig/iptables
+sed -e 's;iptables;ip6tables;g' -e 's;IPTABLES;IP6TABLES;g' < %{SOURCE2} > ip6tables-config
+install -c -m755 ip6tables-config $RPM_BUILD_ROOT/etc/sysconfig/ip6tables-config
+
+%clean
+rm -rf $RPM_BUILD_ROOT 
+
+%post
+/sbin/chkconfig --add iptables
+if [ "$PL_BOOTCD" != "1" ] ; then
+    /sbin/service iptables restart
+fi
+
+%preun
+if [ "$1" = 0 ]; then
+    /sbin/chkconfig --del iptables
+fi
+
+%post ipv6
+/sbin/chkconfig --add ip6tables
+if [ "$PL_BOOTCD" != "1" ] ; then
+    /sbin/service ip6tables restart
+fi
+
+%preun ipv6
+if [ "$1" = 0 ]; then
+    /sbin/chkconfig --del ip6tables
+fi
+
+%files
+%defattr(-,root,root,0755)
+%doc COPYING INSTALL INCOMPATIBILITIES
+%config %attr(0755,root,root) /etc/rc.d/init.d/iptables
+%config(noreplace) %attr(0600,root,root) /etc/sysconfig/iptables-config
+%config(noreplace) %attr(0600,root,root) /etc/sysconfig/iptables
+/sbin/iptables*
+%{_mandir}/man8/iptables*
+%dir /%{_lib}/iptables
+/%{_lib}/iptables/libipt*
+/sbin/ipset*
+%{_mandir}/man8/ipset*
+%dir /%{_lib}/ipset
+/%{_lib}/ipset/libipset*
+
+%files ipv6
+%defattr(-,root,root,0755)
+%config %attr(0755,root,root) /etc/rc.d/init.d/ip6tables
+%config(noreplace) %attr(0600,root,root) /etc/sysconfig/ip6tables-config
+/sbin/ip6tables*
+%{_mandir}/man8/ip6tables*
+/%{_lib}/iptables/libip6t*
+
+%if %{build_devel}
+%files devel
+%defattr(-,root,root,0755)
+%{_includedir}/libipq.h
+%{_libdir}/libipq.a
+#%{_libdir}/libiptc.a
+%{_mandir}/man3/*
+%endif
+
+%changelog
+* Wed Sep 10 2008 Thierry Parmentelat <thierry.parmentelat@sophia.inria.fr> - iptables-1.3.8-9
+- fixes for building on f9/gcc-4.3 - no functional change
+
+* Sat Aug 30 2008 Thierry Parmentelat <thierry.parmentelat@sophia.inria.fr> - iptables-1.3.8-8
+- use PL_BOOTCD to prevent the service from getting started at build-time
+
+* Mon Aug 04 2008 Sapan Bhatia <sapanb@cs.princeton.edu> - iptables-1.3.8-7
+- Codemux/PF change
+
+* Mon Jul 28 2008 Faiyaz Ahmed <faiyaza@cs.princeton.edu> - iptables-1.3.8-6
+- Syntax error in the spec file prevented this package from installing.  Fixed.
+
+* Fri Jul 25 2008 Andy Bavier <acb@cs.princeton.edu> - iptables-1.3.8-5
+- * --add-mark action in CLASSIFY target
+- * Remove chkconfig from .spec file
+- * Fix iptables-save/-restore bug for MARK with --copy-xid
+- * Fix default PlanetLab iptables config
+
+* Mon Jul 21 2008 Sapan Bhatia <sapanb@cs.princeton.edu> - iptables-1.3.8-4
+- Load tun/tap
+
+* Tue Jul 15 2008 Sapan Bhatia <sapanb@cs.princeton.edu> - iptables-1.3.8-3
+- * Load required kernel modules before configuring tun/tap devices
+
+* Wed Jul 09 2008 Sapan Bhatia <sapanb@cs.princeton.edu> - iptables-1.3.8-2
+- Added tuntap support. Let's start it in the iptables initscripts for now.
+
+* Wed Apr 23 2008 Stephen Soltesz <soltesz@cs.princeton.edu> - iptables-1.3.8-1
+- Commit for latest fixes in trunk for the iptables config.  This one in
+- particular loads ip_conntrack in order to have the new vnet support ping right
+- away.
+- 
+
+* Tue Mar 02 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Thu Feb 26 2004 Thomas Woerner <twoerner@redhat.com> 1.2.9-2.3
+- fixed iptables-restore -c fault if there are no counters (#116421)
+
+* Fri Feb 13 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Sun Jan  25 2004 Dan Walsh <dwalsh@redhat.com> 1.2.9-1.2
+- Close File descriptors to prevent SELinux error message
+
+* Wed Jan  7 2004 Thomas Woerner <twoerner@redhat.com> 1.2.9-1.1
+- rebuild
+
+* Wed Dec 17 2003 Thomas Woerner <twoerner@redhat.com> 1.2.9-1
+- vew version 1.2.9
+- new config options in ipXtables-config:
+  IPTABLES_MODULES_UNLOAD
+- more documentation in ipXtables-config
+- fix for netlink security issue in libipq (devel package)
+- print fix for libipt_icmp (#109546)
+
+* Thu Oct 23 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-13
+- marked all messages in iptables init script for translation (#107462)
+- enabled devel package (#105884, #106101)
+- bumped build for fedora for libipt_recent.so (#106002)
+
+* Tue Sep 23 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-12.1
+- fixed lost udp port range in ip6tables-save (#104484)
+- fixed non numeric multiport port output in ipXtables-savs
+
+* Mon Sep 22 2003 Florian La Roche <Florian.LaRoche@redhat.de> 1.2.8-11
+- do not link against -lnsl
+
+* Wed Sep 17 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-10
+- made variables in rmmod_r local
+
+* Tue Jul 22 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-9
+- fixed permission for init script
+
+* Sat Jul 19 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-8
+- fixed save when iptables file is missing and iptables-config permissions
+
+* Tue Jul  8 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-7
+- fixes for ip6tables: module unloading, setting policy only for existing 
+  tables
+
+* Thu Jul  3 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-6
+- IPTABLES_SAVE_COUNTER defaults to no, now
+- install config file in /etc/sysconfig
+- exchange unload of ip_tables and ip_conntrack
+- fixed start function
+
+* Wed Jul  2 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-5
+- new config option IPTABLES_SAVE_ON_RESTART
+- init script: new status, save and restart
+- fixes #44905, #65389, #80785, #82860, #91040, #91560 and #91374
+
+* Mon Jun 30 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-4
+- new config option IPTABLES_STATUS_NUMERIC
+- cleared IPTABLES_MODULES in iptables-config
+
+* Mon Jun 30 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-3
+- new init scripts
+
+* Sat Jun 28 2003 Florian La Roche <Florian.LaRoche@redhat.de>
+- remove check for very old kernel versions in init scripts
+- sync up both init scripts and remove some further ugly things
+- add some docu into rpm
+
+* Thu Jun 26  2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-2
+- rebuild
+
+* Mon Jun 16 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-1
+- update to 1.2.8
+
+* Wed Jan 22 2003 Tim Powers <timp@redhat.com>
+- rebuilt
+
+* Mon Jan 13 2003 Bill Nottingham <notting@redhat.com> 1.2.7a-1
+- update to 1.2.7a
+- add a plethora of bugfixes courtesy Michael Schwendt <mschewndt@yahoo.com>
+
+* Fri Dec 13 2002 Elliot Lee <sopwith@redhat.com> 1.2.6a-3
+- Fix multilib
+
+* Wed Aug 07 2002 Karsten Hopp <karsten@redhat.de>
+- fixed iptables and ip6tables initscript output, based on #70511
+- check return status of all iptables calls, not just the last one
+  in a 'for' loop.
+
+* Mon Jul 29 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.6a-1
+- 1.2.6a (bugfix release, #69747)
+
+* Fri Jun 21 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Thu May 23 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Mon Mar  4 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-3
+- Add some fixes from CVS, fixing bug #60465
+
+* Tue Feb 12 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-2
+- Merge ip6tables improvements from Ian Prowell <iprowell@prowell.org>
+  #59402
+- Update URL (#59354)
+- Use /sbin/chkconfig rather than chkconfig in %postun script
+
+* Fri Jan 11 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-1
+- 1.2.5
+
+* Wed Jan 09 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Mon Nov  5 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.4-2
+- Fix %preun script
+
+* Tue Oct 30 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.4-1
+- Update to 1.2.4 (various fixes, including security fixes; among others:
+  #42990, #50500, #53325, #54280)
+- Fix init script (#31133)
+
+* Mon Sep  3 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.3-1
+- 1.2.3 (5 security fixes, some other fixes)
+- Fix updating (#53032)
+
+* Mon Aug 27 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-4
+- Fix #50990
+- Add some fixes from current CVS; should fix #52620
+
+* Mon Jul 16 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-3
+- Add some fixes from the current CVS tree; fixes #49154 and some IPv6
+  issues
+
+* Tue Jun 26 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-2
+- Fix iptables-save reject-with (#45632), Patch from Michael Schwendt
+  <mschwendt@yahoo.com>
+
+* Tue May  8 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-1
+- 1.2.2
+
+* Wed Mar 21 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- 1.2.1a, fixes #28412, #31136, #31460, #31133
+
+* Thu Mar  1 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Yet another initscript fix (#30173)
+- Fix the fixes; they fixed some issues but broke more important
+  stuff :/ (#30176)
+
+* Tue Feb 27 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Fix up initscript (#27962)
+- Add fixes from CVS to iptables-{restore,save}, fixing #28412
+
+* Fri Feb 09 2001 Karsten Hopp <karsten@redhat.de>
+- create /etc/sysconfig/iptables mode 600 (same problem as #24245)
+
+* Mon Feb 05 2001 Karsten Hopp <karsten@redhat.de>
+- fix bugzilla #25986 (initscript not marked as config file)
+- fix bugzilla #25962 (iptables-restore)
+- mv chkconfig --del from postun to preun
+
+* Thu Feb  1 2001 Trond Eivind Glomsrød <teg@redhat.com>
+- Fix check for ipchains
+
+* Mon Jan 29 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Some fixes to init scripts
+
+* Wed Jan 24 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Add some fixes from CVS, fixes among other things Bug #24732
+
+* Wed Jan 17 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Add missing man pages, fix up init script (Bug #17676)
+
+* Mon Jan 15 2001 Bill Nottingham <notting@redhat.com>
+- add init script
+
+* Mon Jan 15 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- 1.2
+- fix up ipv6 split
+- add init script
+- Move the plugins from /usr/lib/iptables to /lib/iptables.
+  This needs to work before /usr is mounted...
+- Use -O1 on alpha (compiler bug)
+
+* Sat Jan  6 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- 1.1.2
+- Add IPv6 support (in separate package)
+
+* Thu Aug 17 2000 Bill Nottingham <notting@redhat.com>
+- build everywhere
+
+* Tue Jul 25 2000 Bernhard Rosenkraenzer <bero@redhat.com>
+- 1.1.1
+
+* Thu Jul 13 2000 Prospector <bugzilla@redhat.com>
+- automatic rebuild
+
+* Tue Jun 27 2000 Preston Brown <pbrown@redhat.com>
+- move iptables to /sbin.
+- excludearch alpha for now, not building there because of compiler bug(?)
+
+* Fri Jun  9 2000 Bill Nottingham <notting@redhat.com>
+- don't obsolete ipchains either
+- update to 1.1.0
+
+* Mon Jun  4 2000 Bill Nottingham <notting@redhat.com>
+- remove explicit kernel requirement
+
+* Tue May  2 2000 Bernhard Rosenkränzer <bero@redhat.com>
+- initial package
diff --git a/iptables.xslt b/iptables.xslt
new file mode 100644 (file)
index 0000000..4cf8419
--- /dev/null
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- Converts from simple xml iptables format to iptables-save format  
+     Copyright 2006 UfoMechanic 
+     Author: azez@ufomechanic.net 
+     This code is distributed and licensed under the terms of GNU GPL v2
+     
+     This sample usage outputs roughly want goes in
+       iptables-save | iptables-xml -c | xsltproc iptables.xslt -
+     -->
+<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+  <xsl:output method = "text" />
+  <xsl:strip-space elements="*" />
+
+  <!-- output conditions of a rule but not an action -->
+  <xsl:template match="iptables-rules/table/chain/rule/conditions/*">
+    <!-- <match> is the psuedo module when a match module doesn't need to be loaded
+         and when -m does not need to be inserted -->
+    <xsl:if test="name() != 'match'">
+      <xsl:text> -m </xsl:text><xsl:value-of select="name()"/>
+    </xsl:if>
+    <xsl:apply-templates select="node()"/>
+  </xsl:template>
+
+  <!-- delete the actions or conditions containers, and process child nodes -->
+  <xsl:template match="iptables-rules/table/chain/rule/actions|table/chain/rule/conditions">
+    <xsl:apply-templates select="*"/>
+  </xsl:template>
+
+  <xsl:template match="iptables-rules/table/chain/rule/actions/goto">
+    <xsl:text> -g </xsl:text>
+    <xsl:apply-templates select="*"/>
+    <xsl:text>&#xA;</xsl:text>
+  </xsl:template>
+  <xsl:template match="iptables-rules/table/chain/rule/actions/call">
+    <xsl:text> -j </xsl:text>
+    <xsl:apply-templates select="*"/>
+    <xsl:text>&#xA;</xsl:text>
+  </xsl:template>
+  <!-- all other actions are module actions -->
+  <xsl:template match="iptables-rules/table/chain/rule/actions/*">
+    <xsl:text> -j </xsl:text><xsl:value-of select="name()"/>
+    <xsl:apply-templates select="*"/>
+    <xsl:text>&#xA;</xsl:text>
+  </xsl:template>
+  
+  <!-- all child action nodes -->
+  <xsl:template match="iptables-rules/table/chain/rule/actions/*/*|iptables-rules/table/chain/rule/actions/*//*|iptables-rules/table/chain/rule/conditions/*/*|iptables-rules/table/chain/rule/conditions/*//*">
+    <xsl:if test="@invert=1"><xsl:text> !</xsl:text></xsl:if>
+    <xsl:text> -</xsl:text>
+    <!-- if length of name is 1 character, then only do 1 - not 2 -->
+    <xsl:if test="string-length(name())&gt;1">
+      <xsl:text>-</xsl:text>
+    </xsl:if>
+    <xsl:value-of select="name()"/>
+    <xsl:text> </xsl:text><xsl:value-of select="."/>
+  </xsl:template>
+
+  <xsl:template match="iptables-rules/table/chain/rule/actions/call/*|iptables-rules/table/chain/rule/actions/goto/*">
+    <xsl:value-of select="name()"/>
+    <!-- I bet there are no child nodes, should we risk it? -->
+    <xsl:apply-templates select="node()"/>
+  </xsl:template>
+
+  <!-- output the head of the rule, and any conditions -->
+  <xsl:template name="rule-head">
+    <xsl:if test="string-length(@packet-count)+string-length(@byte-count)">
+      <xsl:call-template name="counters"><xsl:with-param name="node" select="."/></xsl:call-template>
+      <xsl:text> </xsl:text>
+    </xsl:if>
+    <xsl:text>-A </xsl:text><!-- a rule must be under a chain -->
+    <xsl:value-of select="../@name" />
+    <xsl:apply-templates select="conditions"/>
+  </xsl:template>
+
+  <!-- Output a single rule, perhaps as multiple rules if we have more than one action -->
+  <xsl:template match="iptables-rules/table/chain/rule">
+    <xsl:choose>
+      <xsl:when test="count(actions/*)&gt;0">
+        <xsl:for-each select="actions/*">
+          <!-- and a for-each to re-select the rule as the current node, to write the rule-head -->
+          <xsl:for-each select="../..">
+            <xsl:call-template name="rule-head"/>
+          </xsl:for-each>
+          <!-- now write the this action -->
+          <xsl:apply-templates select="."/>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:otherwise>
+        <!-- no need to loop if there are no actions, just output conditions -->
+        <xsl:call-template name="rule-head"/>
+        <xsl:text>&#xA;</xsl:text>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template match="iptables-rules/table">
+    <xsl:text># Generated by iptables.xslt&#xA;</xsl:text>
+    <xsl:text>*</xsl:text><xsl:value-of select="@name"/><xsl:text>&#xA;</xsl:text>
+    <!-- Loop through each chain and output the chain header -->
+    <xsl:for-each select="chain">
+      <xsl:text>:</xsl:text>
+      <xsl:value-of select="@name"/>
+      <xsl:text> </xsl:text>
+      <xsl:choose>
+        <xsl:when test="not(string-length(@policy))"><xsl:text>-</xsl:text></xsl:when>
+        <xsl:otherwise><xsl:value-of select="@policy"/></xsl:otherwise>
+      </xsl:choose>
+      <xsl:text> </xsl:text>
+      <xsl:call-template name="counters"><xsl:with-param name="node" select="."/></xsl:call-template>
+      <xsl:text>&#xA;</xsl:text>
+    </xsl:for-each>
+    <!-- Loop through each chain and output the rules -->
+    <xsl:apply-templates select="node()"/>
+    <xsl:text>COMMIT&#xA;# Completed&#xA;</xsl:text>
+  </xsl:template>
+  
+  <xsl:template name="counters">
+    <xsl:param name="$node"/>
+    <xsl:text>[</xsl:text>
+    <xsl:if test="string-length($node/@packet-count)"><xsl:value-of select="$node/@packet-count"/></xsl:if>
+    <xsl:if test="string-length($node/@packet-count)=0">0</xsl:if>
+    <xsl:text>:</xsl:text>
+    <xsl:if test="string-length($node/@byte-count)"><xsl:value-of select="$node/@byte-count"/></xsl:if>
+    <xsl:if test="string-length($node/@byte-count)=0">0</xsl:if>
+    <xsl:text>]</xsl:text>
+  </xsl:template>  
+  
+  <!-- the bit that automatically recurses for us, NOTE: we use * not node(), we don't want to copy every white space text -->
+  <xsl:template match="@*|node()">
+    <xsl:copy>
+      <!-- with libxslt xsltproc we can't do @*|node() or the nodes may get processed before the attributes -->
+      <xsl:apply-templates select="@*"/>
+      <xsl:apply-templates select="node()"/>
+    </xsl:copy>
+  </xsl:template>
+
+</xsl:transform>
diff --git a/libipq/Makefile b/libipq/Makefile
new file mode 100644 (file)
index 0000000..64633f3
--- /dev/null
@@ -0,0 +1,28 @@
+#! /usr/bin/make
+
+EXTRAS+=libipq/libipq.a
+#CFLAGS+=-DDEBUG_LIBIPTQ
+
+DEVEL_MAN3+=libipq/ipq_create_handle.3 \
+       libipq/ipq_destroy_handle.3 \
+       libipq/ipq_errstr.3 \
+       libipq/ipq_get_msgerr.3 \
+       libipq/ipq_get_packet.3 \
+       libipq/ipq_message_type.3 \
+       libipq/ipq_perror.3 \
+       libipq/ipq_read.3 \
+       libipq/ipq_set_mode.3 \
+       libipq/ipq_set_verdict.3 \
+       libipq/libipq.3
+
+DEVEL_LIBS+=libipq/libipq.a
+
+DEVEL_HEADERS+=include/libipq/libipq.h
+
+ifndef TOPLEVEL_INCLUDED
+local:
+       cd .. && $(MAKE) $(SHARED_LIBS) $(EXTRAS)
+else
+libipq/libipq.a: libipq/libipq.a(libipq/libipq.o)
+endif
+
diff --git a/libipq/ipq_create_handle.3 b/libipq/ipq_create_handle.3
new file mode 100644 (file)
index 0000000..6e745b3
--- /dev/null
@@ -0,0 +1,84 @@
+.TH IPQ_CREATE_HANDLE 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+\" $Id: ipq_create_handle.3 1056 2001-11-24 15:09:19Z jamesm $
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ipq_create_handle, ipq_destroy_handle - create and destroy libipq handles.
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "struct ipq_handle *ipq_create_handle(u_int32_t " flags ", u_int32_t " protocol ");"
+.br
+.BI "int ipq_destroy_handle(struct ipq_handle *" h );
+.SH DESCRIPTION
+The
+.B ipq_create_handle
+function initialises libipq for an application, attempts to bind to the
+Netlink socket used by ip_queue, and returns an opaque context handle.  It
+should be the first libipq function to be called by an application.  The
+handle returned should be used in all subsequent library calls which 
+require a handle parameter.
+.PP
+The
+.I flags
+parameter is not currently used and should be set to zero by the application
+for forward compatibility.
+.PP
+The
+.I protocol
+parameter is used to specify the protocol of the packets to be queued.
+Valid values are PF_INET for IPv4 and PF_INET6 for IPv6.  Currently, 
+only one protocol may be queued at a time for a handle.
+.PP
+The
+.B ipq_destroy_handle
+function frees up resources allocated by
+.BR ipq_create_handle ,
+and should be used when the handle is no longer required by the application.
+.SH RETURN VALUES
+On success,
+.B ipq_create_handle
+returns a pointer to a context handle.
+.br
+On failure, NULL is returned.
+.PP
+On success,
+.B ipq_destroy_handle
+returns zero.
+.br
+On failure, -1 is returned.
+.SH ERRORS
+On failure, a descriptive error message will be available
+via the
+.B ipq_errstr
+function.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3).
diff --git a/libipq/ipq_destroy_handle.3 b/libipq/ipq_destroy_handle.3
new file mode 100644 (file)
index 0000000..29dcd98
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ipq_create_handle.3
diff --git a/libipq/ipq_errstr.3 b/libipq/ipq_errstr.3
new file mode 100644 (file)
index 0000000..6e29b6d
--- /dev/null
@@ -0,0 +1,66 @@
+.TH IPQ_ERRSTR 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\" $Id: ipq_errstr.3 1041 2001-10-16 14:41:02Z jamesm $
+.\"
+.\"     Copyright (c) 2000 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ipq_errstr, ipq_perror - libipq error handling routines
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "char *ipq_errstr(" void );
+.br
+.BI "void ipq_perror(const char *" s );
+.SH DESCRIPTION
+The
+.B ipq_errstr
+function returns a descriptive error message based on the current
+value of the internal
+.B ipq_errno
+variable.  All libipq API functions set this internal variable
+upon failure.
+.PP
+The
+.B ipq_perror
+function prints an error message to stderr corresponding to the
+current value of the internal
+.B ipq_error
+variable, and the global
+.B errno
+variable (if set).  The error message is prefixed with the string
+.I s
+as supplied by the application. If
+.I s
+is NULL, the error message is prefixed with the string "ERROR".
+.SH RETURN VALUE
+.B ipq_errstr
+returns an error message as outlined above.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3).
diff --git a/libipq/ipq_get_msgerr.3 b/libipq/ipq_get_msgerr.3
new file mode 100644 (file)
index 0000000..8a28be3
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ipq_message_type.3
diff --git a/libipq/ipq_get_packet.3 b/libipq/ipq_get_packet.3
new file mode 100644 (file)
index 0000000..8a28be3
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ipq_message_type.3
diff --git a/libipq/ipq_message_type.3 b/libipq/ipq_message_type.3
new file mode 100644 (file)
index 0000000..9c079d8
--- /dev/null
@@ -0,0 +1,136 @@
+.TH IPQ_MESSAGE_TYPE 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\" $Id: ipq_message_type.3 1041 2001-10-16 14:41:02Z jamesm $
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ipq_message_type, ipq_get_packet, ipq_getmsgerr - query queue messages
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "int ipq_message_type(const unsigned char *" buf ");"
+.br
+.BI "ipq_packet_msg_t *ipq_get_packet(const unsigned char *" buf ");"
+.br
+.BI "int ipq_get_msgerr(const unsigned char *" buf ");"
+.SH DESCRIPTION
+The
+.B ipq_message_type
+function returns the type of queue message returned to userspace
+via
+.BR ipq_read .
+.PP
+.B ipq_message_type
+should always be called following a successful call to
+.B ipq_read
+to determine whether the message is a packet message or an
+error message. The
+.I buf
+parameter should be the same data obtained from
+the previous call to
+.BR ipq_read .
+.PP
+.B ipq_message_type
+will return one of the following values:
+.TP
+.B NLMSG_ERROR
+An error message generated by the Netlink transport.
+.PP
+.TP
+.B IPQM_PACKET
+A packet message containing packet metadata and optional packet payload data.
+.PP
+The
+.B ipq_get_packet
+function should be called if
+.B ipq_message_type
+returns
+.BR IPQM_PACKET .
+The
+.I buf
+parameter should point to the same data used for the call to
+.BR ipq_message_type .
+The pointer returned by
+.B ipq_get_packet
+points to a packet message, which is declared as follows:
+.PP
+.RS
+.nf
+typedef struct ipq_packet_msg {
+       unsigned long packet_id;        /* ID of queued packet */
+       unsigned long mark;             /* Netfilter mark value */
+       long timestamp_sec;             /* Packet arrival time (seconds) */
+       long timestamp_usec;            /* Packet arrvial time (+useconds) */
+       unsigned int hook;              /* Netfilter hook we rode in on */
+       char indev_name[IFNAMSIZ];      /* Name of incoming interface */
+       char outdev_name[IFNAMSIZ];     /* Name of outgoing interface */
+       unsigned short hw_protocol;     /* Hardware protocol (network order) */
+       unsigned short hw_type;         /* Hardware type */
+       unsigned char hw_addrlen;       /* Hardware address length */
+       unsigned char hw_addr[8];       /* Hardware address */
+       size_t data_len;                /* Length of packet data */
+       unsigned char payload[0];       /* Optional packet data */
+} ipq_packet_msg_t;
+.fi
+.RE
+.PP
+Each of these fields may be read by the application.  If the queue mode
+is
+.B IPQ_COPY_PACKET
+and the
+.I data_len
+value is greater than zero, the packet payload contents may be accessed
+in the memory following the
+.B ipq_packet_msg_t
+structure to a range of
+.I data_len.
+.PP
+The
+.I packet_id
+field contains a packet identifier to be used when calling
+.BR ipq_set_verdict .
+.PP
+The
+.B ipq_get_msgerr
+function should be called if
+.B ipq_message_type
+returns
+.BR NLMSG_ERROR.
+The
+.I buf
+parameter should point to the same data used for the call to
+.BR ipq_message_type .
+The value returned by
+.B ipq_get_msgerr
+is set by higher level kernel code and corresponds to standard
+.B errno
+values.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3).
diff --git a/libipq/ipq_perror.3 b/libipq/ipq_perror.3
new file mode 100644 (file)
index 0000000..6efd53d
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ipq_errstr.3
diff --git a/libipq/ipq_read.3 b/libipq/ipq_read.3
new file mode 100644 (file)
index 0000000..d21d203
--- /dev/null
@@ -0,0 +1,106 @@
+.TH IPQ_READ 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\" $Id: ipq_read.3 1042 2001-10-16 16:58:25Z jamesm $
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ipq_read - read queue messages from ip_queue and read into supplied buffer
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "ssize_t ipq_read(const struct ipq_handle *" h ", unsigned char *" buf ", size_t " len ", int " timeout ");"
+.SH DESCRIPTION
+The
+.B ipq_read
+function reads a queue message from the kernel and copies it to
+the memory pointed to by 
+.I buf
+to a maximum length of
+. IR len .
+.PP
+The
+.I h
+parameter is a context handle which must previously have been returned 
+successfully from a call to
+.BR ipq_create_handle .
+.PP
+The caller is responsible for ensuring that the memory pointed to by
+.I buf
+is large enough to contain
+.I len
+bytes.
+.PP
+The
+.I timeout
+parameter may be used to set a timeout for the operation, specified in microseconds.
+This is implemented internally by the library via the
+.BR select
+system call.  A value of zero provides normal, backwards-compatible blocking behaviour
+with no timeout.  A negative value causes the function to return immediately.
+.PP
+Data returned via
+.I buf
+should not be accessed directly.  Use the 
+.BR ipq_message_type ,
+.BR ipq_get_packet ", and"
+.BR ipq_get_msgerr
+functions to access the queue message in the buffer.
+.SH RETURN VALUE
+On failure, -1 is returned.
+.br
+On success, a non-zero positive value is returned when no timeout
+value is specified.
+.br
+On success with a timeout value specified, zero is returned if no data
+was available to read, or if a non-blocked signal was caught.  In the
+latter case, the global
+.B errno
+value will be set to 
+.BR EINTR .
+.SH ERRORS
+On error, a descriptive error message will be available
+via the
+.B ipq_errstr
+function.
+.SH DIAGNOSTICS
+While the
+.B ipq_read
+function may return successfully, the queue message copied to the buffer
+may itself be an error message from a higher level kernel component.  Use
+.B ipq_message_type
+to determine if it is an error message, and
+.B ipq_get_msgerr
+to access the value of the message.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH CREDITS
+Joost Remijn implemented the timeout feature, which appeared in the 1.2.4 release of iptables.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3),
+.BR select (2).
+
diff --git a/libipq/ipq_set_mode.3 b/libipq/ipq_set_mode.3
new file mode 100644 (file)
index 0000000..de275ef
--- /dev/null
@@ -0,0 +1,107 @@
+.TH IPQ_SET_MODE 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\" $Id: ipq_set_mode.3 1041 2001-10-16 14:41:02Z jamesm $
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ipq_set_mode - set the ip_queue queuing mode
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "int ipq_set_mode(const struct ipq_handle *" h ", u_int8_t " mode ", size_t " range );
+.SH DESCRIPTION
+The
+.B ipq_set_mode
+function sends a message to the kernel ip_queue module, specifying whether
+packet metadata only, or packet payloads as well as metadata should be copied to
+userspace.
+.PP
+The
+.I h
+parameter is a context handle which must previously have been returned 
+successfully from a call to
+.BR ipq_create_handle .
+.PP
+The
+.I mode
+parameter must be one of:
+.TP
+.B IPQ_COPY_META
+Copy only packet metadata to userspace.
+.TP
+.B IPQ_COPY_PACKET
+Copy packet metadata and packet payloads to userspace.
+.PP
+The
+.I range
+parameter is used to specify how many bytes of the payload to copy
+to userspace.  It is only valid for
+.B IPQ_COPY_PACKET
+mode and is otherwise ignored.  The maximum useful value for
+.I range
+is 65535 (greater values will be clamped to this by ip_queue).
+.PP
+.B ipq_set_mode
+is usually used immediately following
+.B ipq_create_handle
+to enable the flow of packets to userspace.
+.PP
+Note that as the underlying Netlink messaging transport is connectionless,
+the ip_queue module does not know that a userspace application is ready to
+communicate until it receives a message such as this.
+.SH RETURN VALUE
+On failure, -1 is returned.
+.br
+On success, a non-zero positive value is returned.
+.SH ERRORS
+On failure, a descriptive error message will be available
+via the
+.B ipq_errstr
+function.
+.SH DIAGNOSTICS
+A relatively common failure may occur if the ip_queue module is not loaded.
+In this case, the following code excerpt:
+.PP
+.RS
+.nf
+status = ipq_set_mode(h, IPQ_COPY_META, 0);
+if (status < 0) {
+       ipq_perror("myapp");
+       ipq_destroy_handle(h);
+       exit(1);
+}
+.RE
+.fi
+.PP
+would generate the following output:
+.PP
+.I myapp: Failed to send netlink message: Connection refused
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR libipq (3),
+.BR iptables (8).
diff --git a/libipq/ipq_set_verdict.3 b/libipq/ipq_set_verdict.3
new file mode 100644 (file)
index 0000000..004e673
--- /dev/null
@@ -0,0 +1,95 @@
+.TH IPQ_SET_VERDICT 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\" $Id: ipq_set_verdict.3 1041 2001-10-16 14:41:02Z jamesm $
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ipq_set_verdict - issue verdict and optionally modified packet to kernel
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "int ipq_set_verdict(const struct ipq_handle *" h ", ipq_id_t " id ", unsigned int " verdict ", size_t " data_len ", unsigned char *" buf ");"
+.SH DESCRIPTION
+The
+.B ipq_set_verdict
+function issues a verdict on a packet previously obtained with
+.BR ipq_read ,
+specifing the intended disposition of the packet, and optionally
+supplying a modified version of the payload data.
+.PP
+The
+.I h
+parameter is a context handle which must previously have been returned 
+successfully from a call to
+.BR ipq_create_handle .
+.PP
+The
+.I id
+parameter is the packet identifier obtained via
+.BR ipq_get_packet .
+.PP
+The
+.I verdict
+parameter must be one of:
+.TP
+.B NF_ACCEPT
+Accept the packet and continue traversal within the kernel.
+.br
+.TP
+.B NF_DROP
+Drop the packet.
+.PP
+The
+.I data_len
+parameter is the length of the data pointed to
+by
+.IR buf ,
+the optional replacement payload data.
+.PP
+If simply setting a verdict without modifying the payload data, use zero
+for
+.I data_len
+and NULL for
+.IR buf .
+.PP
+The application is responsible for recalculating any packet checksums
+when modifying packets.
+.SH RETURN VALUE
+On failure, -1 is returned.
+.br
+On success, a non-zero positive value is returned.
+.SH ERRORS
+On error, a descriptive error message will be available
+via the
+.B ipq_errstr
+function.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3).
+
diff --git a/libipq/libipq.3 b/libipq/libipq.3
new file mode 100644 (file)
index 0000000..033bde0
--- /dev/null
@@ -0,0 +1,279 @@
+.TH LIBIPQ 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\" $Id: libipq.3 1056 2001-11-24 15:09:19Z jamesm $
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+libipq \- iptables userspace packet queuing library.
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.SH DESCRIPTION
+libipq is a development library for iptables userspace packet queuing.
+.SS Userspace Packet Queuing
+Netfilter provides a mechanism for passing packets out of the stack for
+queueing to userspace, then receiving these packets back into the kernel
+with a verdict specifying what to do with the packets (such as ACCEPT
+or DROP).  These packets may also be modified in userspace prior to
+reinjection back into the kernel.
+.PP
+For each supported protocol, a kernel module called a
+.I queue handler
+may register with Netfilter to perform the mechanics of passing
+packets to and from userspace.
+.PP
+The standard queue handler for IPv4 is ip_queue.  It is provided as an
+experimental module with 2.4 kernels, and uses a Netlink socket for
+kernel/userspace communication.
+.PP
+Once ip_queue is loaded, IP packets may be selected with iptables
+and queued for userspace processing via the QUEUE target.  For example,
+running the following commands:
+.PP
+       # modprobe iptable_filter
+.br    
+       # modprobe ip_queue
+.br    
+       # iptables -A OUTPUT -p icmp -j QUEUE
+.PP
+will cause any locally generated ICMP packets (e.g. ping output) to
+be sent to the ip_queue module, which will then attempt to deliver the
+packets to a userspace application.  If no userspace application is waiting,
+the packets will be dropped
+.PP
+An application may receive and process these packets via libipq.
+.PP
+.PP
+.SS Libipq Overview
+Libipq provides an API for communicating with ip_queue.  The following is
+an overview of API usage, refer to individual man pages for more details
+on each function.
+.PP
+.B Initialisation
+.br
+To initialise the library, call
+.BR ipq_create_handle (3).
+This will attempt to bind to the Netlink socket used by ip_queue and
+return an opaque context handle for subsequent library calls.
+.PP
+.B Setting the Queue Mode
+.br
+.BR ipq_set_mode (3)
+allows the application to specify whether packet metadata, or packet
+payloads as well as metadata are copied to userspace.  It is also used to
+initially notify ip_queue that an application is ready to receive queue
+messages.
+.PP
+.B Receiving Packets from the Queue
+.br
+.BR ipq_read (3)
+waits for queue messages to arrive from ip_queue and copies
+them into a supplied buffer.
+Queue messages may be
+.I packet messages
+or
+.I error messages.
+.PP
+The type of packet may be determined with
+.BR ipq_message_type (3).
+.PP
+If it's a packet message, the metadata and optional payload may be retrieved with
+.BR ipq_get_packet (3).
+.PP
+To retrieve the value of an error message, use
+.BR ipq_get_msgerr (3).
+.PP
+.B Issuing Verdicts on Packets
+.br
+To issue a verdict on a packet, and optionally return a modified version
+of the packet to the kernel, call
+.BR ipq_set_verdict (3).
+.PP
+.B Error Handling
+.br
+An error string corresponding to the current value of the internal error
+variable
+.B ipq_errno
+may be obtained with
+.BR ipq_errstr (3).
+.PP
+For simple applications, calling
+.BR ipq_perror (3)
+will print the same message as
+.BR ipq_errstr (3),
+as well as the string corresponding to the global
+.B errno
+value (if set) to stderr.
+.PP
+.B Cleaning Up
+.br
+To free up the Netlink socket and destroy resources associated with
+the context handle, call
+.BR ipq_destroy_handle (3).
+.SH SUMMARY
+.TP 4
+.BR ipq_create_handle (3)
+Initialise library, return context handle.
+.TP
+.BR ipq_set_mode (3)
+Set the queue mode, to copy either packet metadata, or payloads
+as well as metadata to userspace.
+.TP
+.BR ipq_read (3)
+Wait for a queue message to arrive from ip_queue and read it into
+a buffer.
+.TP
+.BR ipq_message_type (3)
+Determine message type in the buffer.
+.TP
+.BR ipq_get_packet (3)
+Retrieve a packet message from the buffer.
+.TP
+.BR ipq_get_msgerr (3)
+Retrieve an error message from the buffer.
+.TP
+.BR ipq_set_verdict (3)
+Set a verdict on a packet, optionally replacing its contents.
+.TP
+.BR ipq_errstr (3)
+Return an error message corresponding to the internal ipq_errno variable.
+.TP
+.BR ipq_perror (3)
+Helper function to print error messages to stderr.
+.TP
+.BR ipq_destroy_handle (3)
+Destroy context handle and associated resources.
+.SH EXAMPLE
+The following is an example of a simple application which receives
+packets and issues NF_ACCEPT verdicts on each packet.
+.RS
+.nf
+/*
+ * This code is GPL.
+ */
+#include <linux/netfilter.h>
+#include <libipq.h>
+#include <stdio.h>
+
+#define BUFSIZE 2048 
+
+static void die(struct ipq_handle *h)
+{
+       ipq_perror("passer");
+       ipq_destroy_handle(h);
+       exit(1);
+}
+
+int main(int argc, char **argv)
+{
+       int status;
+       unsigned char buf[BUFSIZE];
+       struct ipq_handle *h;
+       
+       h = ipq_create_handle(0, PF_INET);
+       if (!h)
+               die(h);
+               
+       status = ipq_set_mode(h, IPQ_COPY_PACKET, BUFSIZE);
+       if (status < 0)
+               die(h);
+               
+       do{
+               status = ipq_read(h, buf, BUFSIZE, 0);
+               if (status < 0)
+                       die(h);
+                       
+               switch (ipq_message_type(buf)) {
+                       case NLMSG_ERROR:
+                               fprintf(stderr, "Received error message %d\\n",
+                                       ipq_get_msgerr(buf));
+                               break;
+                               
+                       case IPQM_PACKET: {
+                               ipq_packet_msg_t *m = ipq_get_packet(buf);
+                               
+                               status = ipq_set_verdict(h, m->packet_id,
+                                                        NF_ACCEPT, 0, NULL);
+                               if (status < 0)
+                                       die(h);
+                               break;
+                       }
+                       
+                       default:
+                               fprintf(stderr, "Unknown message type!\\n");
+                               break;
+               }
+       } while (1);
+       
+       ipq_destroy_handle(h);
+       return 0;
+}
+.RE
+.fi
+.PP
+Pointers to more libipq application examples may be found in The
+Netfilter FAQ.
+.SH DIAGNOSTICS
+For information about monitoring and tuning ip_queue, refer to the
+Linux 2.4 Packet Filtering HOWTO.
+.PP
+If an application modifies a packet, it needs to also update any
+checksums for the packet.  Typically, the kernel will silently discard
+modified packets with invalid checksums. 
+.SH SECURITY
+Processes require CAP_NET_ADMIN capabilty to access the kernel ip_queue
+module.  Such processes can potentially access and modify any IP packets
+received, generated or forwarded by the kernel.
+.SH TODO
+Per-handle
+.B ipq_errno
+values.
+.SH BUGS
+Probably.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH CREDITS
+Joost Remijn implemented the
+.B ipq_read
+timeout feature, which appeared in the 1.2.4 release of iptables.
+.PP
+Fernando Anton added support for IPv6.
+.SH SEE ALSO
+.BR iptables (8),
+.BR ipq_create_handle (3),
+.BR ipq_destroy_handle (3),
+.BR ipq_errstr (3),
+.BR ipq_get_msgerr (3),
+.BR ipq_get_packet (3),
+.BR ipq_message_type (3),
+.BR ipq_perror (3),
+.BR ipq_read (3),
+.BR ipq_set_mode (3),
+.BR ipq_set_verdict (3).
+.PP
+The Netfilter home page at http://netfilter.samba.org/
+which has links to The Networking Concepts HOWTO, The Linux 2.4 Packet
+Filtering HOWTO, The Linux 2.4 NAT HOWTO, The Netfilter Hacking HOWTO,
+The Netfilter FAQ and many other useful resources.
+
diff --git a/libipq/libipq.c b/libipq/libipq.c
new file mode 100644 (file)
index 0000000..658af97
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * libipq.c
+ *
+ * IPQ userspace library.
+ *
+ * Please note that this library is still developmental, and there may
+ * be some API changes.
+ *
+ * Author: James Morris <jmorris@intercode.com.au>
+ *
+ * 07-11-2001 Modified by Fernando Anton to add support for IPv6.
+ *
+ * Copyright (c) 2000-2001 Netfilter Core Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <libipq/libipq.h>
+
+/****************************************************************************
+ *
+ * Private interface
+ *
+ ****************************************************************************/
+
+enum {
+       IPQ_ERR_NONE = 0,
+       IPQ_ERR_IMPL,
+       IPQ_ERR_HANDLE,
+       IPQ_ERR_SOCKET,
+       IPQ_ERR_BIND,
+       IPQ_ERR_BUFFER,
+       IPQ_ERR_RECV,
+       IPQ_ERR_NLEOF,
+       IPQ_ERR_ADDRLEN,
+       IPQ_ERR_STRUNC,
+       IPQ_ERR_RTRUNC,
+       IPQ_ERR_NLRECV,
+       IPQ_ERR_SEND,
+       IPQ_ERR_SUPP,
+       IPQ_ERR_RECVBUF,
+       IPQ_ERR_TIMEOUT,
+        IPQ_ERR_PROTOCOL
+};
+#define IPQ_MAXERR IPQ_ERR_PROTOCOL
+
+struct ipq_errmap_t {
+       int errcode;
+       char *message;
+} ipq_errmap[] = {
+       { IPQ_ERR_NONE, "Unknown error" },
+       { IPQ_ERR_IMPL, "Implementation error" },
+       { IPQ_ERR_HANDLE, "Unable to create netlink handle" },
+       { IPQ_ERR_SOCKET, "Unable to create netlink socket" },
+       { IPQ_ERR_BIND, "Unable to bind netlink socket" },
+       { IPQ_ERR_BUFFER, "Unable to allocate buffer" },
+       { IPQ_ERR_RECV, "Failed to receive netlink message" },
+       { IPQ_ERR_NLEOF, "Received EOF on netlink socket" },
+       { IPQ_ERR_ADDRLEN, "Invalid peer address length" },
+       { IPQ_ERR_STRUNC, "Sent message truncated" },
+       { IPQ_ERR_RTRUNC, "Received message truncated" },
+       { IPQ_ERR_NLRECV, "Received error from netlink" },
+       { IPQ_ERR_SEND, "Failed to send netlink message" },
+       { IPQ_ERR_SUPP, "Operation not supported" },
+       { IPQ_ERR_RECVBUF, "Receive buffer size invalid" },
+       { IPQ_ERR_TIMEOUT, "Timeout"},
+       { IPQ_ERR_PROTOCOL, "Invalid protocol specified" }
+};
+
+static int ipq_errno = IPQ_ERR_NONE;
+
+static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
+                                  const void *msg, size_t len);
+
+static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
+                                    unsigned char *buf, size_t len,
+                                    int timeout);
+
+static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h,
+                                   const struct msghdr *msg,
+                                   unsigned int flags);
+
+static char *ipq_strerror(int errcode);
+
+static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
+                                  const void *msg, size_t len)
+{
+       int status = sendto(h->fd, msg, len, 0,
+                           (struct sockaddr *)&h->peer, sizeof(h->peer));
+       if (status < 0)
+               ipq_errno = IPQ_ERR_SEND;
+       return status;
+}
+
+static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h,
+                                   const struct msghdr *msg,
+                                   unsigned int flags)
+{
+       int status = sendmsg(h->fd, msg, flags);
+       if (status < 0)
+               ipq_errno = IPQ_ERR_SEND;
+       return status;
+}
+
+static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
+                                    unsigned char *buf, size_t len,
+                                    int timeout)
+{
+       unsigned int addrlen;
+       int status;
+       struct nlmsghdr *nlh;
+
+       if (len < sizeof(struct nlmsgerr)) {
+               ipq_errno = IPQ_ERR_RECVBUF;
+               return -1;
+       }
+       addrlen = sizeof(h->peer);
+
+       if (timeout != 0) {
+               int ret;
+               struct timeval tv;
+               fd_set read_fds;
+               
+               if (timeout < 0) {
+                       /* non-block non-timeout */
+                       tv.tv_sec = 0;
+                       tv.tv_usec = 0;
+               } else {
+                       tv.tv_sec = timeout / 1000000;
+                       tv.tv_usec = timeout % 1000000;
+               }
+
+               FD_ZERO(&read_fds);
+               FD_SET(h->fd, &read_fds);
+               ret = select(h->fd+1, &read_fds, NULL, NULL, &tv);
+               if (ret < 0) {
+                       if (errno == EINTR) {
+                               return 0;
+                       } else {
+                               ipq_errno = IPQ_ERR_RECV;
+                               return -1;
+                       }
+               }
+               if (!FD_ISSET(h->fd, &read_fds)) {
+                       ipq_errno = IPQ_ERR_TIMEOUT;
+                       return 0;
+               }
+       }
+       status = recvfrom(h->fd, buf, len, 0,
+                             (struct sockaddr *)&h->peer, &addrlen);
+       if (status < 0) {
+               ipq_errno = IPQ_ERR_RECV;
+               return status;
+       }
+       if (addrlen != sizeof(h->peer)) {
+               ipq_errno = IPQ_ERR_RECV;
+               return -1;
+       }
+       if (h->peer.nl_pid != 0) {
+               ipq_errno = IPQ_ERR_RECV;
+               return -1;
+       }
+       if (status == 0) {
+               ipq_errno = IPQ_ERR_NLEOF;
+               return -1;
+       }
+       nlh = (struct nlmsghdr *)buf;
+       if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) {
+               ipq_errno = IPQ_ERR_RTRUNC;
+               return -1;
+       }
+       return status;
+}
+
+static char *ipq_strerror(int errcode)
+{
+       if (errcode < 0 || errcode > IPQ_MAXERR)
+               errcode = IPQ_ERR_IMPL;
+       return ipq_errmap[errcode].message;
+}
+
+/****************************************************************************
+ *
+ * Public interface
+ *
+ ****************************************************************************/
+
+/*
+ * Create and initialise an ipq handle.
+ */
+struct ipq_handle *ipq_create_handle(u_int32_t flags, u_int32_t protocol)
+{
+       int status;
+       struct ipq_handle *h;
+
+       h = (struct ipq_handle *)malloc(sizeof(struct ipq_handle));
+       if (h == NULL) {
+               ipq_errno = IPQ_ERR_HANDLE;
+               return NULL;
+       }
+       
+       memset(h, 0, sizeof(struct ipq_handle));
+       
+        if (protocol == PF_INET)
+                h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);
+        else if (protocol == PF_INET6)
+                h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_IP6_FW);
+        else {
+               ipq_errno = IPQ_ERR_PROTOCOL;
+               free(h);
+               return NULL;
+        }
+        
+       if (h->fd == -1) {
+               ipq_errno = IPQ_ERR_SOCKET;
+               close(h->fd);
+               free(h);
+               return NULL;
+       }
+       memset(&h->local, 0, sizeof(struct sockaddr_nl));
+       h->local.nl_family = AF_NETLINK;
+       h->local.nl_pid = getpid();
+       h->local.nl_groups = 0;
+       status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local));
+       if (status == -1) {
+               ipq_errno = IPQ_ERR_BIND;
+               close(h->fd);
+               free(h);
+               return NULL;
+       }
+       memset(&h->peer, 0, sizeof(struct sockaddr_nl));
+       h->peer.nl_family = AF_NETLINK;
+       h->peer.nl_pid = 0;
+       h->peer.nl_groups = 0;
+       return h;
+}
+
+/*
+ * No error condition is checked here at this stage, but it may happen
+ * if/when reliable messaging is implemented.
+ */
+int ipq_destroy_handle(struct ipq_handle *h)
+{
+       if (h) {
+               close(h->fd);
+               free(h);
+       }
+       return 0;
+}
+
+int ipq_set_mode(const struct ipq_handle *h,
+                 u_int8_t mode, size_t range)
+{
+       struct {
+               struct nlmsghdr nlh;
+               ipq_peer_msg_t pm;
+       } req;
+
+       memset(&req, 0, sizeof(req));
+       req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req));
+       req.nlh.nlmsg_flags = NLM_F_REQUEST;
+       req.nlh.nlmsg_type = IPQM_MODE;
+       req.nlh.nlmsg_pid = h->local.nl_pid;
+       req.pm.msg.mode.value = mode;
+       req.pm.msg.mode.range = range;
+       return ipq_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len);
+}
+
+/*
+ * timeout is in microseconds (1 second is 1000000 (1 million) microseconds)
+ *
+ */
+ssize_t ipq_read(const struct ipq_handle *h,
+                 unsigned char *buf, size_t len, int timeout)
+{
+       return ipq_netlink_recvfrom(h, buf, len, timeout);
+}
+
+int ipq_message_type(const unsigned char *buf)
+{
+       return ((struct nlmsghdr*)buf)->nlmsg_type;
+}
+
+int ipq_get_msgerr(const unsigned char *buf)
+{
+       struct nlmsghdr *h = (struct nlmsghdr *)buf;
+       struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+       return -err->error;
+}
+
+ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf)
+{
+       return NLMSG_DATA((struct nlmsghdr *)(buf));
+}
+
+int ipq_set_verdict(const struct ipq_handle *h,
+                    ipq_id_t id,
+                    unsigned int verdict,
+                    size_t data_len,
+                    unsigned char *buf)
+{
+       unsigned char nvecs;
+       size_t tlen;
+       struct nlmsghdr nlh;
+       ipq_peer_msg_t pm;
+       struct iovec iov[3];
+       struct msghdr msg;
+
+       memset(&nlh, 0, sizeof(nlh));
+       nlh.nlmsg_flags = NLM_F_REQUEST;
+       nlh.nlmsg_type = IPQM_VERDICT;
+       nlh.nlmsg_pid = h->local.nl_pid;
+       memset(&pm, 0, sizeof(pm));
+       pm.msg.verdict.value = verdict;
+       pm.msg.verdict.id = id;
+       pm.msg.verdict.data_len = data_len;
+       iov[0].iov_base = &nlh;
+       iov[0].iov_len = sizeof(nlh);
+       iov[1].iov_base = &pm;
+       iov[1].iov_len = sizeof(pm);
+       tlen = sizeof(nlh) + sizeof(pm);
+       nvecs = 2;
+       if (data_len && buf) {
+               iov[2].iov_base = buf;
+               iov[2].iov_len = data_len;
+               tlen += data_len;
+               nvecs++;
+       }
+       msg.msg_name = (void *)&h->peer;
+       msg.msg_namelen = sizeof(h->peer);
+       msg.msg_iov = iov;
+       msg.msg_iovlen = nvecs;
+       msg.msg_control = NULL;
+       msg.msg_controllen = 0;
+       msg.msg_flags = 0;
+       nlh.nlmsg_len = tlen;
+       return ipq_netlink_sendmsg(h, &msg, 0);
+}
+
+/* Not implemented yet */
+int ipq_ctl(const struct ipq_handle *h, int request, ...)
+{
+       return 1;
+}
+
+char *ipq_errstr(void)
+{
+       return ipq_strerror(ipq_errno);
+}
+
+void ipq_perror(const char *s)
+{
+       if (s)
+               fputs(s, stderr);
+       else
+               fputs("ERROR", stderr);
+       if (ipq_errno)
+               fprintf(stderr, ": %s", ipq_errstr());
+       if (errno)
+               fprintf(stderr, ": %s", strerror(errno));
+       fputc('\n', stderr);
+}
diff --git a/libiptc/.cvsignore b/libiptc/.cvsignore
new file mode 100644 (file)
index 0000000..a438335
--- /dev/null
@@ -0,0 +1 @@
+*.d
diff --git a/libiptc/Makefile b/libiptc/Makefile
new file mode 100644 (file)
index 0000000..180de13
--- /dev/null
@@ -0,0 +1,24 @@
+#! /usr/bin/make
+
+EXTRAS+=libiptc/libiptc.a
+
+# libiptc is not a public interface and therefore not installed
+# DEVEL_LIBS+=libiptc/libiptc.a
+
+ifndef TOPLEVEL_INCLUDED
+local:
+       cd .. && $(MAKE) $(KERN_TARGETS) $(SHARED_LIBS) $(EXTRAS)
+
+else
+EXTRA_DEPENDS+=libiptc/libip4tc.d
+
+libiptc/libiptc.a: libiptc/libiptc.a(libiptc/libip4tc.o)
+
+ifeq ($(DO_IPV6), 1)
+EXTRA_DEPENDS+= libiptc/libip6tc.d
+libiptc/libiptc.a: libiptc/libiptc.a(libiptc/libip6tc.o)
+endif
+
+libiptc/libip4tc.d libiptc/libip6tc.d: %.d: %.c
+       @-$(CC) -M -MG $(CFLAGS) $< | sed -e 's@^.*\.o:@$*.d libiptc/libiptc.a($*.o):@' > $@
+endif
diff --git a/libiptc/libip4tc.c b/libiptc/libip4tc.c
new file mode 100644 (file)
index 0000000..a0cdc2f
--- /dev/null
@@ -0,0 +1,502 @@
+/* Library which manipulates firewall rules.  Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+   COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libiptc.h"
+
+#define IP_VERSION     4
+#define IP_OFFSET      0x1FFF
+
+#define HOOK_PRE_ROUTING       NF_IP_PRE_ROUTING
+#define HOOK_LOCAL_IN          NF_IP_LOCAL_IN
+#define HOOK_FORWARD           NF_IP_FORWARD
+#define HOOK_LOCAL_OUT         NF_IP_LOCAL_OUT
+#define HOOK_POST_ROUTING      NF_IP_POST_ROUTING
+#ifdef NF_IP_DROPPING
+#define HOOK_DROPPING          NF_IP_DROPPING
+#endif
+
+#define STRUCT_ENTRY_TARGET    struct ipt_entry_target
+#define STRUCT_ENTRY           struct ipt_entry
+#define STRUCT_ENTRY_MATCH     struct ipt_entry_match
+#define STRUCT_GETINFO         struct ipt_getinfo
+#define STRUCT_GET_ENTRIES     struct ipt_get_entries
+#define STRUCT_COUNTERS                struct ipt_counters
+#define STRUCT_COUNTERS_INFO   struct ipt_counters_info
+#define STRUCT_STANDARD_TARGET struct ipt_standard_target
+#define STRUCT_REPLACE         struct ipt_replace
+
+#define STRUCT_TC_HANDLE       struct iptc_handle
+#define TC_HANDLE_T            iptc_handle_t
+
+#define ENTRY_ITERATE          IPT_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN       IPT_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN    IPT_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET             ipt_get_target
+
+#define ERROR_TARGET           IPT_ERROR_TARGET
+#define NUMHOOKS               NF_IP_NUMHOOKS
+
+#define IPT_CHAINLABEL         ipt_chainlabel
+
+#define TC_DUMP_ENTRIES                dump_entries
+#define TC_IS_CHAIN            iptc_is_chain
+#define TC_FIRST_CHAIN         iptc_first_chain
+#define TC_NEXT_CHAIN          iptc_next_chain
+#define TC_FIRST_RULE          iptc_first_rule
+#define TC_NEXT_RULE           iptc_next_rule
+#define TC_GET_TARGET          iptc_get_target
+#define TC_BUILTIN             iptc_builtin
+#define TC_GET_POLICY          iptc_get_policy
+#define TC_INSERT_ENTRY                iptc_insert_entry
+#define TC_REPLACE_ENTRY       iptc_replace_entry
+#define TC_APPEND_ENTRY                iptc_append_entry
+#define TC_DELETE_ENTRY                iptc_delete_entry
+#define TC_DELETE_NUM_ENTRY    iptc_delete_num_entry
+#define TC_CHECK_PACKET                iptc_check_packet
+#define TC_FLUSH_ENTRIES       iptc_flush_entries
+#define TC_ZERO_ENTRIES                iptc_zero_entries
+#define TC_READ_COUNTER                iptc_read_counter
+#define TC_ZERO_COUNTER                iptc_zero_counter
+#define TC_SET_COUNTER         iptc_set_counter
+#define TC_CREATE_CHAIN                iptc_create_chain
+#define TC_GET_REFERENCES      iptc_get_references
+#define TC_DELETE_CHAIN                iptc_delete_chain
+#define TC_RENAME_CHAIN                iptc_rename_chain
+#define TC_SET_POLICY          iptc_set_policy
+#define TC_GET_RAW_SOCKET      iptc_get_raw_socket
+#define TC_INIT                        iptc_init
+#define TC_FREE                        iptc_free
+#define TC_COMMIT              iptc_commit
+#define TC_STRERROR            iptc_strerror
+#define TC_NUM_RULES           iptc_num_rules
+#define TC_GET_RULE            iptc_get_rule
+
+#define TC_AF                  AF_INET
+#define TC_IPPROTO             IPPROTO_IP
+
+#define SO_SET_REPLACE         IPT_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS    IPT_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO            IPT_SO_GET_INFO
+#define SO_GET_ENTRIES         IPT_SO_GET_ENTRIES
+#define SO_GET_VERSION         IPT_SO_GET_VERSION
+
+#define STANDARD_TARGET                IPT_STANDARD_TARGET
+#define LABEL_RETURN           IPTC_LABEL_RETURN
+#define LABEL_ACCEPT           IPTC_LABEL_ACCEPT
+#define LABEL_DROP             IPTC_LABEL_DROP
+#define LABEL_QUEUE            IPTC_LABEL_QUEUE
+
+#define ALIGN                  IPT_ALIGN
+#define RETURN                 IPT_RETURN
+
+#include "libiptc.c"
+
+#define IP_PARTS_NATIVE(n)                     \
+(unsigned int)((n)>>24)&0xFF,                  \
+(unsigned int)((n)>>16)&0xFF,                  \
+(unsigned int)((n)>>8)&0xFF,                   \
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+int
+dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle)
+{
+       size_t i;
+       STRUCT_ENTRY_TARGET *t;
+
+       printf("Entry %u (%lu):\n", iptcb_entry2index(handle, e),
+              iptcb_entry2offset(handle, e));
+       printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+              IP_PARTS(e->ip.src.s_addr),IP_PARTS(e->ip.smsk.s_addr));
+       printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+              IP_PARTS(e->ip.dst.s_addr),IP_PARTS(e->ip.dmsk.s_addr));
+       printf("Interface: `%s'/", e->ip.iniface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ip.iniface_mask[i] ? 'X' : '.');
+       printf("to `%s'/", e->ip.outiface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ip.outiface_mask[i] ? 'X' : '.');
+       printf("\nProtocol: %u\n", e->ip.proto);
+       printf("Flags: %02X\n", e->ip.flags);
+       printf("Invflags: %02X\n", e->ip.invflags);
+       printf("Counters: %llu packets, %llu bytes\n",
+              (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+       printf("Cache: %08X ", e->nfcache);
+       if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
+       if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
+       printf("\n");
+
+       IPT_MATCH_ITERATE(e, print_match);
+
+       t = GET_TARGET(e);
+       printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+       if (strcmp(t->u.user.name, STANDARD_TARGET) == 0) {
+               int pos = *(int *)t->data;
+               if (pos < 0)
+                       printf("verdict=%s\n",
+                              pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+                              : pos == -NF_DROP-1 ? "NF_DROP"
+                              : pos == -NF_QUEUE-1 ? "NF_QUEUE"
+                              : pos == RETURN ? "RETURN"
+                              : "UNKNOWN");
+               else
+                       printf("verdict=%u\n", pos);
+       } else if (strcmp(t->u.user.name, IPT_ERROR_TARGET) == 0)
+               printf("error=`%s'\n", t->data);
+
+       printf("\n");
+       return 0;
+}
+
+static unsigned char *
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask)
+{
+       unsigned int i;
+       unsigned char *mptr;
+
+       /* Always compare head structures: ignore mask here. */
+       if (a->ip.src.s_addr != b->ip.src.s_addr
+           || a->ip.dst.s_addr != b->ip.dst.s_addr
+           || a->ip.smsk.s_addr != b->ip.smsk.s_addr
+           || a->ip.dmsk.s_addr != b->ip.dmsk.s_addr
+           || a->ip.proto != b->ip.proto
+           || a->ip.flags != b->ip.flags
+           || a->ip.invflags != b->ip.invflags)
+               return NULL;
+
+       for (i = 0; i < IFNAMSIZ; i++) {
+               if (a->ip.iniface_mask[i] != b->ip.iniface_mask[i])
+                       return NULL;
+               if ((a->ip.iniface[i] & a->ip.iniface_mask[i])
+                   != (b->ip.iniface[i] & b->ip.iniface_mask[i]))
+                       return NULL;
+               if (a->ip.outiface_mask[i] != b->ip.outiface_mask[i])
+                       return NULL;
+               if ((a->ip.outiface[i] & a->ip.outiface_mask[i])
+                   != (b->ip.outiface[i] & b->ip.outiface_mask[i]))
+                       return NULL;
+       }
+
+       if (a->nfcache != b->nfcache
+           || a->target_offset != b->target_offset
+           || a->next_offset != b->next_offset)
+               return NULL;
+
+       mptr = matchmask + sizeof(STRUCT_ENTRY);
+       if (IPT_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+               return NULL;
+       mptr += IPT_ALIGN(sizeof(struct ipt_entry_target));
+
+       return mptr;
+}
+
+#if 0
+/***************************** DEBUGGING ********************************/
+static inline int
+unconditional(const struct ipt_ip *ip)
+{
+       unsigned int i;
+
+       for (i = 0; i < sizeof(*ip)/sizeof(u_int32_t); i++)
+               if (((u_int32_t *)ip)[i])
+                       return 0;
+
+       return 1;
+}
+
+static inline int
+check_match(const STRUCT_ENTRY_MATCH *m, unsigned int *off)
+{
+       assert(m->u.match_size >= sizeof(STRUCT_ENTRY_MATCH));
+       assert(ALIGN(m->u.match_size) == m->u.match_size);
+
+       (*off) += m->u.match_size;
+       return 0;
+}
+
+static inline int
+check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off,
+           unsigned int user_offset, int *was_return,
+           TC_HANDLE_T h)
+{
+       unsigned int toff;
+       STRUCT_STANDARD_TARGET *t;
+
+       assert(e->target_offset >= sizeof(STRUCT_ENTRY));
+       assert(e->next_offset >= e->target_offset
+              + sizeof(STRUCT_ENTRY_TARGET));
+       toff = sizeof(STRUCT_ENTRY);
+       IPT_MATCH_ITERATE(e, check_match, &toff);
+
+       assert(toff == e->target_offset);
+
+       t = (STRUCT_STANDARD_TARGET *)
+               GET_TARGET((STRUCT_ENTRY *)e);
+       /* next_offset will have to be multiple of entry alignment. */
+       assert(e->next_offset == ALIGN(e->next_offset));
+       assert(e->target_offset == ALIGN(e->target_offset));
+       assert(t->target.u.target_size == ALIGN(t->target.u.target_size));
+       assert(!TC_IS_CHAIN(t->target.u.user.name, h));
+
+       if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0) {
+               assert(t->target.u.target_size
+                      == ALIGN(sizeof(STRUCT_STANDARD_TARGET)));
+
+               assert(t->verdict == -NF_DROP-1
+                      || t->verdict == -NF_ACCEPT-1
+                      || t->verdict == RETURN
+                      || t->verdict < (int)h->entries->size);
+
+               if (t->verdict >= 0) {
+                       STRUCT_ENTRY *te = get_entry(h, t->verdict);
+                       int idx;
+
+                       idx = iptcb_entry2index(h, te);
+                       assert(strcmp(GET_TARGET(te)->u.user.name,
+                                     IPT_ERROR_TARGET)
+                              != 0);
+                       assert(te != e);
+
+                       /* Prior node must be error node, or this node. */
+                       assert(t->verdict == iptcb_entry2offset(h, e)+e->next_offset
+                              || strcmp(GET_TARGET(index2entry(h, idx-1))
+                                        ->u.user.name, IPT_ERROR_TARGET)
+                              == 0);
+               }
+
+               if (t->verdict == RETURN
+                   && unconditional(&e->ip)
+                   && e->target_offset == sizeof(*e))
+                       *was_return = 1;
+               else
+                       *was_return = 0;
+       } else if (strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0) {
+               assert(t->target.u.target_size
+                      == ALIGN(sizeof(struct ipt_error_target)));
+
+               /* If this is in user area, previous must have been return */
+               if (*off > user_offset)
+                       assert(*was_return);
+
+               *was_return = 0;
+       }
+       else *was_return = 0;
+
+       if (*off == user_offset)
+               assert(strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0);
+
+       (*off) += e->next_offset;
+       (*i)++;
+       return 0;
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+       unsigned int i, n;
+       unsigned int user_offset; /* Offset of first user chain */
+       int was_return;
+
+       assert(h->changed == 0 || h->changed == 1);
+       if (strcmp(h->info.name, "filter") == 0) {
+               assert(h->info.valid_hooks
+                      == (1 << NF_IP_LOCAL_IN
+                          | 1 << NF_IP_FORWARD
+                          | 1 << NF_IP_LOCAL_OUT));
+
+               /* Hooks should be first three */
+               assert(h->info.hook_entry[NF_IP_LOCAL_IN] == 0);
+
+               n = get_chain_end(h, 0);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+
+               n = get_chain_end(h, n);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+
+               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+       } else if (strcmp(h->info.name, "nat") == 0) {
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_POST_ROUTING
+                           | 1 << NF_IP_LOCAL_OUT)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_LOCAL_IN
+                           | 1 << NF_IP_POST_ROUTING
+                           | 1 << NF_IP_LOCAL_OUT)));
+
+               assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+               n = get_chain_end(h, n);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+                       user_offset = h->info.hook_entry[NF_IP_LOCAL_IN];
+               }
+
+       } else if (strcmp(h->info.name, "mangle") == 0) {
+               /* This code is getting ugly because linux < 2.4.18-pre6 had
+                * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+                * */
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_LOCAL_OUT)) || 
+                      (h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_LOCAL_IN
+                           | 1 << NF_IP_FORWARD
+                           | 1 << NF_IP_LOCAL_OUT
+                           | 1 << NF_IP_POST_ROUTING)));
+
+               /* Hooks should be first five */
+               assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               if (h->info.valid_hooks & (1 << NF_IP_FORWARD)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP_POST_ROUTING)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+                       user_offset = h->info.hook_entry[NF_IP_POST_ROUTING];
+               }
+       } else if (strcmp(h->info.name, "raw") == 0) {
+               assert(h->info.valid_hooks
+                      == (1 << NF_IP_PRE_ROUTING
+                          | 1 << NF_IP_LOCAL_OUT));
+
+               /* Hooks should be first three */
+               assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, n);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+
+               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+#ifdef NF_IP_DROPPING
+       } else if (strcmp(h->info.name, "drop") == 0) {
+               assert(h->info.valid_hooks == (1 << NF_IP_DROPPING));
+
+               /* Hook should be first */
+               assert(h->info.hook_entry[NF_IP_DROPPING] == 0);
+               user_offset = 0;
+#endif
+       } else {
+               fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+               abort();
+       }
+
+       /* User chain == end of last builtin + policy entry */
+       user_offset = get_chain_end(h, user_offset);
+       user_offset += get_entry(h, user_offset)->next_offset;
+
+       /* Overflows should be end of entry chains, and unconditional
+           policy nodes. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               STRUCT_ENTRY *e;
+               STRUCT_STANDARD_TARGET *t;
+
+               if (!(h->info.valid_hooks & (1 << i)))
+                       continue;
+               assert(h->info.underflow[i]
+                      == get_chain_end(h, h->info.hook_entry[i]));
+
+               e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+               assert(unconditional(&e->ip));
+               assert(e->target_offset == sizeof(*e));
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+               assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+               assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+               assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+               assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+               /* Hooks and underflows must be valid entries */
+               entry2index(h, get_entry(h, h->info.hook_entry[i]));
+               entry2index(h, get_entry(h, h->info.underflow[i]));
+       }
+
+       assert(h->info.size
+              >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+                                        +sizeof(STRUCT_STANDARD_TARGET)));
+
+       assert(h->entries.size
+              >= (h->new_number
+                  * (sizeof(STRUCT_ENTRY)
+                     + sizeof(STRUCT_STANDARD_TARGET))));
+       assert(strcmp(h->info.name, h->entries.name) == 0);
+
+       i = 0; n = 0;
+       was_return = 0;
+       /* Check all the entries. */
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     check_entry, &i, &n, user_offset, &was_return, h);
+
+       assert(i == h->new_number);
+       assert(n == h->entries.size);
+
+       /* Final entry must be error node */
+       assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+                     ->u.user.name,
+                     ERROR_TARGET) == 0);
+}
+#endif /*IPTC_DEBUG*/
+
+#endif
diff --git a/libiptc/libip6tc.c b/libiptc/libip6tc.c
new file mode 100644 (file)
index 0000000..5768aeb
--- /dev/null
@@ -0,0 +1,441 @@
+/* Library which manipulates firewall rules.  Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+   COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libip6tc.h"
+
+#define HOOK_PRE_ROUTING       NF_IP6_PRE_ROUTING
+#define HOOK_LOCAL_IN          NF_IP6_LOCAL_IN
+#define HOOK_FORWARD           NF_IP6_FORWARD
+#define HOOK_LOCAL_OUT         NF_IP6_LOCAL_OUT
+#define HOOK_POST_ROUTING      NF_IP6_POST_ROUTING
+
+#define STRUCT_ENTRY_TARGET    struct ip6t_entry_target
+#define STRUCT_ENTRY           struct ip6t_entry
+#define STRUCT_ENTRY_MATCH     struct ip6t_entry_match
+#define STRUCT_GETINFO         struct ip6t_getinfo
+#define STRUCT_GET_ENTRIES     struct ip6t_get_entries
+#define STRUCT_COUNTERS                struct ip6t_counters
+#define STRUCT_COUNTERS_INFO   struct ip6t_counters_info
+#define STRUCT_STANDARD_TARGET struct ip6t_standard_target
+#define STRUCT_REPLACE         struct ip6t_replace
+
+#define STRUCT_TC_HANDLE       struct ip6tc_handle
+#define TC_HANDLE_T            ip6tc_handle_t
+
+#define ENTRY_ITERATE          IP6T_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN       IP6T_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN    IP6T_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET             ip6t_get_target
+
+#define ERROR_TARGET           IP6T_ERROR_TARGET
+#define NUMHOOKS               NF_IP6_NUMHOOKS
+
+#define IPT_CHAINLABEL         ip6t_chainlabel
+
+#define TC_DUMP_ENTRIES                dump_entries6
+#define TC_IS_CHAIN            ip6tc_is_chain
+#define TC_FIRST_CHAIN         ip6tc_first_chain
+#define TC_NEXT_CHAIN          ip6tc_next_chain
+#define TC_FIRST_RULE          ip6tc_first_rule
+#define TC_NEXT_RULE           ip6tc_next_rule
+#define TC_GET_TARGET          ip6tc_get_target
+#define TC_BUILTIN             ip6tc_builtin
+#define TC_GET_POLICY          ip6tc_get_policy
+#define TC_INSERT_ENTRY                ip6tc_insert_entry
+#define TC_REPLACE_ENTRY       ip6tc_replace_entry
+#define TC_APPEND_ENTRY                ip6tc_append_entry
+#define TC_DELETE_ENTRY                ip6tc_delete_entry
+#define TC_DELETE_NUM_ENTRY    ip6tc_delete_num_entry
+#define TC_CHECK_PACKET                ip6tc_check_packet
+#define TC_FLUSH_ENTRIES       ip6tc_flush_entries
+#define TC_ZERO_ENTRIES                ip6tc_zero_entries
+#define TC_ZERO_COUNTER                ip6tc_zero_counter
+#define TC_READ_COUNTER                ip6tc_read_counter
+#define TC_SET_COUNTER         ip6tc_set_counter
+#define TC_CREATE_CHAIN                ip6tc_create_chain
+#define TC_GET_REFERENCES      ip6tc_get_references
+#define TC_DELETE_CHAIN                ip6tc_delete_chain
+#define TC_RENAME_CHAIN                ip6tc_rename_chain
+#define TC_SET_POLICY          ip6tc_set_policy
+#define TC_GET_RAW_SOCKET      ip6tc_get_raw_socket
+#define TC_INIT                        ip6tc_init
+#define TC_FREE                        ip6tc_free
+#define TC_COMMIT              ip6tc_commit
+#define TC_STRERROR            ip6tc_strerror
+#define TC_NUM_RULES           ip6tc_num_rules
+#define TC_GET_RULE            ip6tc_get_rule
+
+#define TC_AF                  AF_INET6
+#define TC_IPPROTO             IPPROTO_IPV6
+
+#define SO_SET_REPLACE         IP6T_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS    IP6T_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO            IP6T_SO_GET_INFO
+#define SO_GET_ENTRIES         IP6T_SO_GET_ENTRIES
+#define SO_GET_VERSION         IP6T_SO_GET_VERSION
+
+#define STANDARD_TARGET                IP6T_STANDARD_TARGET
+#define LABEL_RETURN           IP6TC_LABEL_RETURN
+#define LABEL_ACCEPT           IP6TC_LABEL_ACCEPT
+#define LABEL_DROP             IP6TC_LABEL_DROP
+#define LABEL_QUEUE            IP6TC_LABEL_QUEUE
+
+#define ALIGN                  IP6T_ALIGN
+#define RETURN                 IP6T_RETURN
+
+#include "libiptc.c"
+
+#define BIT6(a, l) \
+ ((ntohl(a->s6_addr32[(l) / 32]) >> (31 - ((l) & 31))) & 1)
+
+int
+ipv6_prefix_length(const struct in6_addr *a)
+{
+       int l, i;
+       for (l = 0; l < 128; l++) {
+               if (BIT6(a, l) == 0)
+                       break;
+       }
+       for (i = l + 1; i < 128; i++) {
+               if (BIT6(a, i) == 1)
+                       return -1;
+       }
+       return l;
+}
+
+static int
+dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
+{
+       size_t i;
+       char buf[40];
+       int len;
+       struct ip6t_entry_target *t;
+       
+       printf("Entry %u (%lu):\n", iptcb_entry2index(handle, e),
+              iptcb_entry2offset(handle, e));
+       puts("SRC IP: ");
+       inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof buf);
+       puts(buf);
+       putchar('/');
+       len = ipv6_prefix_length(&e->ipv6.smsk);
+       if (len != -1)
+               printf("%d", len);
+       else {
+               inet_ntop(AF_INET6, &e->ipv6.smsk, buf, sizeof buf);
+               puts(buf);
+       }
+       putchar('\n');
+       
+       puts("DST IP: ");
+       inet_ntop(AF_INET6, &e->ipv6.dst, buf, sizeof buf);
+       puts(buf);
+       putchar('/');
+       len = ipv6_prefix_length(&e->ipv6.dmsk);
+       if (len != -1)
+               printf("%d", len);
+       else {
+               inet_ntop(AF_INET6, &e->ipv6.dmsk, buf, sizeof buf);
+               puts(buf);
+       }
+       putchar('\n');
+       
+       printf("Interface: `%s'/", e->ipv6.iniface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ipv6.iniface_mask[i] ? 'X' : '.');
+       printf("to `%s'/", e->ipv6.outiface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ipv6.outiface_mask[i] ? 'X' : '.');
+       printf("\nProtocol: %u\n", e->ipv6.proto);
+       if (e->ipv6.flags & IP6T_F_TOS)
+               printf("TOS: %u\n", e->ipv6.tos);
+       printf("Flags: %02X\n", e->ipv6.flags);
+       printf("Invflags: %02X\n", e->ipv6.invflags);
+       printf("Counters: %llu packets, %llu bytes\n",
+              (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+       printf("Cache: %08X ", e->nfcache);
+       if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
+       if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
+       printf("\n");
+       
+       IP6T_MATCH_ITERATE(e, print_match);
+
+       t = ip6t_get_target(e);
+       printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+       if (strcmp(t->u.user.name, IP6T_STANDARD_TARGET) == 0) {
+               int pos = *(int *)t->data;
+               if (pos < 0)
+                       printf("verdict=%s\n",
+                              pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+                              : pos == -NF_DROP-1 ? "NF_DROP"
+                              : pos == IP6T_RETURN ? "RETURN"
+                              : "UNKNOWN");
+               else
+                       printf("verdict=%u\n", pos);
+       } else if (strcmp(t->u.user.name, IP6T_ERROR_TARGET) == 0)
+               printf("error=`%s'\n", t->data);
+
+       printf("\n");
+       return 0;
+}
+
+static unsigned char *
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b,
+       unsigned char *matchmask)
+{
+       unsigned int i;
+       unsigned char *mptr;
+
+       /* Always compare head structures: ignore mask here. */
+       if (memcmp(&a->ipv6.src, &b->ipv6.src, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.dst, &b->ipv6.dst, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.smsk, &b->ipv6.smsk, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.dmsk, &b->ipv6.dmsk, sizeof(struct in6_addr))
+           || a->ipv6.proto != b->ipv6.proto
+           || a->ipv6.tos != b->ipv6.tos
+           || a->ipv6.flags != b->ipv6.flags
+           || a->ipv6.invflags != b->ipv6.invflags)
+               return NULL;
+
+       for (i = 0; i < IFNAMSIZ; i++) {
+               if (a->ipv6.iniface_mask[i] != b->ipv6.iniface_mask[i])
+                       return NULL;
+               if ((a->ipv6.iniface[i] & a->ipv6.iniface_mask[i])
+                   != (b->ipv6.iniface[i] & b->ipv6.iniface_mask[i]))
+                       return NULL;
+               if (a->ipv6.outiface_mask[i] != b->ipv6.outiface_mask[i])
+                       return NULL;
+               if ((a->ipv6.outiface[i] & a->ipv6.outiface_mask[i])
+                   != (b->ipv6.outiface[i] & b->ipv6.outiface_mask[i]))
+                       return NULL;
+       }
+
+       if (a->nfcache != b->nfcache
+           || a->target_offset != b->target_offset
+           || a->next_offset != b->next_offset)
+               return NULL;
+
+       mptr = matchmask + sizeof(STRUCT_ENTRY);
+       if (IP6T_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+               return NULL;
+       mptr += IP6T_ALIGN(sizeof(struct ip6t_entry_target));
+
+       return mptr;
+}
+
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ip6t_ip6 *ipv6)
+{
+       unsigned int i;
+
+       for (i = 0; i < sizeof(*ipv6); i++)
+               if (((char *)ipv6)[i])
+                       break;
+
+       return (i == sizeof(*ipv6));
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+       unsigned int i, n;
+       unsigned int user_offset; /* Offset of first user chain */
+       int was_return;
+
+       assert(h->changed == 0 || h->changed == 1);
+       if (strcmp(h->info.name, "filter") == 0) {
+               assert(h->info.valid_hooks
+                      == (1 << NF_IP6_LOCAL_IN
+                          | 1 << NF_IP6_FORWARD
+                          | 1 << NF_IP6_LOCAL_OUT));
+
+               /* Hooks should be first three */
+               assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == 0);
+
+               n = get_chain_end(h, 0);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+
+               n = get_chain_end(h, n);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+       } else if (strcmp(h->info.name, "nat") == 0) {
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_IN
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)));
+
+               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+               n = get_chain_end(h, n);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+                       user_offset = h->info.hook_entry[NF_IP6_LOCAL_IN];
+               }
+
+       } else if (strcmp(h->info.name, "mangle") == 0) {
+               /* This code is getting ugly because linux < 2.4.18-pre6 had
+                * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+                * */
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_OUT)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_IN
+                           | 1 << NF_IP6_FORWARD
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)));
+
+               /* Hooks should be first five */
+               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               if (h->info.valid_hooks & (1 << NF_IP6_FORWARD)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP6_POST_ROUTING)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+                       user_offset = h->info.hook_entry[NF_IP6_POST_ROUTING];
+               }
+       } else if (strcmp(h->info.name, "raw") == 0) {
+               assert(h->info.valid_hooks
+                      == (1 << NF_IP6_PRE_ROUTING
+                          | 1 << NF_IP6_LOCAL_OUT));
+
+               /* Hooks should be first three */
+               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, n);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+       } else {
+                fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+               abort();
+       }
+
+       /* User chain == end of last builtin + policy entry */
+       user_offset = get_chain_end(h, user_offset);
+       user_offset += get_entry(h, user_offset)->next_offset;
+
+       /* Overflows should be end of entry chains, and unconditional
+           policy nodes. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               STRUCT_ENTRY *e;
+               STRUCT_STANDARD_TARGET *t;
+
+               if (!(h->info.valid_hooks & (1 << i)))
+                       continue;
+               assert(h->info.underflow[i]
+                      == get_chain_end(h, h->info.hook_entry[i]));
+
+               e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+               assert(unconditional(&e->ipv6));
+               assert(e->target_offset == sizeof(*e));
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+               printf("target_size=%u, align=%u\n",
+                       t->target.u.target_size, ALIGN(sizeof(*t)));
+               assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+               assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+               assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+               assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+               /* Hooks and underflows must be valid entries */
+               iptcb_entry2index(h, get_entry(h, h->info.hook_entry[i]));
+               iptcb_entry2index(h, get_entry(h, h->info.underflow[i]));
+       }
+
+       assert(h->info.size
+              >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+                                        +sizeof(STRUCT_STANDARD_TARGET)));
+
+       assert(h->entries.size
+              >= (h->new_number
+                  * (sizeof(STRUCT_ENTRY)
+                     + sizeof(STRUCT_STANDARD_TARGET))));
+       assert(strcmp(h->info.name, h->entries.name) == 0);
+
+       i = 0; n = 0;
+       was_return = 0;
+
+#if 0
+       /* Check all the entries. */
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     check_entry, &i, &n, user_offset, &was_return, h);
+
+       assert(i == h->new_number);
+       assert(n == h->entries.size);
+
+       /* Final entry must be error node */
+       assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+                     ->u.user.name,
+                     ERROR_TARGET) == 0);
+#endif
+}
+#endif /*IPTC_DEBUG*/
diff --git a/libiptc/libip6tc.c.orig b/libiptc/libip6tc.c.orig
new file mode 100644 (file)
index 0000000..9a78a5a
--- /dev/null
@@ -0,0 +1,449 @@
+/* Library which manipulates firewall rules.  Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+   COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libip6tc.h"
+
+#define HOOK_PRE_ROUTING       NF_IP6_PRE_ROUTING
+#define HOOK_LOCAL_IN          NF_IP6_LOCAL_IN
+#define HOOK_FORWARD           NF_IP6_FORWARD
+#define HOOK_LOCAL_OUT         NF_IP6_LOCAL_OUT
+#define HOOK_POST_ROUTING      NF_IP6_POST_ROUTING
+
+#define STRUCT_ENTRY_TARGET    struct ip6t_entry_target
+#define STRUCT_ENTRY           struct ip6t_entry
+#define STRUCT_ENTRY_MATCH     struct ip6t_entry_match
+#define STRUCT_GETINFO         struct ip6t_getinfo
+#define STRUCT_GET_ENTRIES     struct ip6t_get_entries
+#define STRUCT_COUNTERS                struct ip6t_counters
+#define STRUCT_COUNTERS_INFO   struct ip6t_counters_info
+#define STRUCT_STANDARD_TARGET struct ip6t_standard_target
+#define STRUCT_REPLACE         struct ip6t_replace
+
+#define STRUCT_TC_HANDLE       struct ip6tc_handle
+#define TC_HANDLE_T            ip6tc_handle_t
+
+#define ENTRY_ITERATE          IP6T_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN       IP6T_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN    IP6T_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET             ip6t_get_target
+
+#define ERROR_TARGET           IP6T_ERROR_TARGET
+#define NUMHOOKS               NF_IP6_NUMHOOKS
+
+#define IPT_CHAINLABEL         ip6t_chainlabel
+
+#define TC_DUMP_ENTRIES                dump_entries6
+#define TC_IS_CHAIN            ip6tc_is_chain
+#define TC_FIRST_CHAIN         ip6tc_first_chain
+#define TC_NEXT_CHAIN          ip6tc_next_chain
+#define TC_FIRST_RULE          ip6tc_first_rule
+#define TC_NEXT_RULE           ip6tc_next_rule
+#define TC_GET_TARGET          ip6tc_get_target
+#define TC_BUILTIN             ip6tc_builtin
+#define TC_GET_POLICY          ip6tc_get_policy
+#define TC_INSERT_ENTRY                ip6tc_insert_entry
+#define TC_REPLACE_ENTRY       ip6tc_replace_entry
+#define TC_APPEND_ENTRY                ip6tc_append_entry
+#define TC_DELETE_ENTRY                ip6tc_delete_entry
+#define TC_DELETE_NUM_ENTRY    ip6tc_delete_num_entry
+#define TC_CHECK_PACKET                ip6tc_check_packet
+#define TC_FLUSH_ENTRIES       ip6tc_flush_entries
+#define TC_ZERO_ENTRIES                ip6tc_zero_entries
+#define TC_ZERO_COUNTER                ip6tc_zero_counter
+#define TC_READ_COUNTER                ip6tc_read_counter
+#define TC_SET_COUNTER         ip6tc_set_counter
+#define TC_CREATE_CHAIN                ip6tc_create_chain
+#define TC_GET_REFERENCES      ip6tc_get_references
+#define TC_DELETE_CHAIN                ip6tc_delete_chain
+#define TC_RENAME_CHAIN                ip6tc_rename_chain
+#define TC_SET_POLICY          ip6tc_set_policy
+#define TC_GET_RAW_SOCKET      ip6tc_get_raw_socket
+#define TC_INIT                        ip6tc_init
+#define TC_FREE                        ip6tc_free
+#define TC_COMMIT              ip6tc_commit
+#define TC_STRERROR            ip6tc_strerror
+
+#define TC_AF                  AF_INET6
+#define TC_IPPROTO             IPPROTO_IPV6
+
+#define SO_SET_REPLACE         IP6T_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS    IP6T_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO            IP6T_SO_GET_INFO
+#define SO_GET_ENTRIES         IP6T_SO_GET_ENTRIES
+#define SO_GET_VERSION         IP6T_SO_GET_VERSION
+
+#define STANDARD_TARGET                IP6T_STANDARD_TARGET
+#define LABEL_RETURN           IP6TC_LABEL_RETURN
+#define LABEL_ACCEPT           IP6TC_LABEL_ACCEPT
+#define LABEL_DROP             IP6TC_LABEL_DROP
+#define LABEL_QUEUE            IP6TC_LABEL_QUEUE
+
+#define ALIGN                  IP6T_ALIGN
+#define RETURN                 IP6T_RETURN
+
+#include "libiptc.c"
+
+#define BIT6(a, l) \
+ ((ntohl(a->in6_u.u6_addr32[(l) / 32]) >> (31 - ((l) & 31))) & 1)
+
+int
+ipv6_prefix_length(const struct in6_addr *a)
+{
+       int l, i;
+       for (l = 0; l < 128; l++) {
+               if (BIT6(a, l) == 0)
+                       break;
+       }
+       for (i = l + 1; i < 128; i++) {
+               if (BIT6(a, i) == 1)
+                       return -1;
+       }
+       return l;
+}
+
+static int
+dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
+{
+       size_t i;
+       char buf[40];
+       int len;
+       struct ip6t_entry_target *t;
+       
+       printf("Entry %u (%lu):\n", entry2index(handle, e),
+              entry2offset(handle, e));
+       puts("SRC IP: ");
+       inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof buf);
+       puts(buf);
+       putchar('/');
+       len = ipv6_prefix_length(&e->ipv6.smsk);
+       if (len != -1)
+               printf("%d", len);
+       else {
+               inet_ntop(AF_INET6, &e->ipv6.smsk, buf, sizeof buf);
+               puts(buf);
+       }
+       putchar('\n');
+       
+       puts("DST IP: ");
+       inet_ntop(AF_INET6, &e->ipv6.dst, buf, sizeof buf);
+       puts(buf);
+       putchar('/');
+       len = ipv6_prefix_length(&e->ipv6.dmsk);
+       if (len != -1)
+               printf("%d", len);
+       else {
+               inet_ntop(AF_INET6, &e->ipv6.dmsk, buf, sizeof buf);
+               puts(buf);
+       }
+       putchar('\n');
+       
+       printf("Interface: `%s'/", e->ipv6.iniface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ipv6.iniface_mask[i] ? 'X' : '.');
+       printf("to `%s'/", e->ipv6.outiface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ipv6.outiface_mask[i] ? 'X' : '.');
+       printf("\nProtocol: %u\n", e->ipv6.proto);
+       if (e->ipv6.flags & IP6T_F_TOS)
+               printf("TOS: %u\n", e->ipv6.tos);
+       printf("Flags: %02X\n", e->ipv6.flags);
+       printf("Invflags: %02X\n", e->ipv6.invflags);
+       printf("Counters: %llu packets, %llu bytes\n",
+              e->counters.pcnt, e->counters.bcnt);
+       printf("Cache: %08X ", e->nfcache);
+       if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
+       if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
+       if (e->nfcache & NFC_IP6_SRC) printf("IP6_SRC ");
+       if (e->nfcache & NFC_IP6_DST) printf("IP6_DST ");
+       if (e->nfcache & NFC_IP6_IF_IN) printf("IP6_IF_IN ");
+       if (e->nfcache & NFC_IP6_IF_OUT) printf("IP6_IF_OUT ");
+       if (e->nfcache & NFC_IP6_TOS) printf("IP6_TOS ");
+       if (e->nfcache & NFC_IP6_PROTO) printf("IP6_PROTO ");
+       if (e->nfcache & NFC_IP6_OPTIONS) printf("IP6_OPTIONS ");
+       if (e->nfcache & NFC_IP6_TCPFLAGS) printf("IP6_TCPFLAGS ");
+       if (e->nfcache & NFC_IP6_SRC_PT) printf("IP6_SRC_PT ");
+       if (e->nfcache & NFC_IP6_DST_PT) printf("IP6_DST_PT ");
+       if (e->nfcache & NFC_IP6_PROTO_UNKNOWN) printf("IP6_PROTO_UNKNOWN ");
+       printf("\n");
+       
+       IP6T_MATCH_ITERATE(e, print_match);
+
+       t = ip6t_get_target(e);
+       printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+       if (strcmp(t->u.user.name, IP6T_STANDARD_TARGET) == 0) {
+               int pos = *(int *)t->data;
+               if (pos < 0)
+                       printf("verdict=%s\n",
+                              pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+                              : pos == -NF_DROP-1 ? "NF_DROP"
+                              : pos == IP6T_RETURN ? "RETURN"
+                              : "UNKNOWN");
+               else
+                       printf("verdict=%u\n", pos);
+       } else if (strcmp(t->u.user.name, IP6T_ERROR_TARGET) == 0)
+               printf("error=`%s'\n", t->data);
+
+       printf("\n");
+       return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b,
+       unsigned char *matchmask)
+{
+       unsigned int i;
+       STRUCT_ENTRY_TARGET *ta, *tb;
+       unsigned char *mptr;
+
+       /* Always compare head structures: ignore mask here. */
+       if (memcmp(&a->ipv6.src, &b->ipv6.src, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.dst, &b->ipv6.dst, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.smsk, &b->ipv6.smsk, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.dmsk, &b->ipv6.dmsk, sizeof(struct in6_addr))
+           || a->ipv6.proto != b->ipv6.proto
+           || a->ipv6.tos != b->ipv6.tos
+           || a->ipv6.flags != b->ipv6.flags
+           || a->ipv6.invflags != b->ipv6.invflags)
+               return 0;
+
+       for (i = 0; i < IFNAMSIZ; i++) {
+               if (a->ipv6.iniface_mask[i] != b->ipv6.iniface_mask[i])
+                       return 0;
+               if ((a->ipv6.iniface[i] & a->ipv6.iniface_mask[i])
+                   != (b->ipv6.iniface[i] & b->ipv6.iniface_mask[i]))
+                       return 0;
+               if (a->ipv6.outiface_mask[i] != b->ipv6.outiface_mask[i])
+                       return 0;
+               if ((a->ipv6.outiface[i] & a->ipv6.outiface_mask[i])
+                   != (b->ipv6.outiface[i] & b->ipv6.outiface_mask[i]))
+                       return 0;
+       }
+
+       if (a->nfcache != b->nfcache
+           || a->target_offset != b->target_offset
+           || a->next_offset != b->next_offset)
+               return 0;
+
+       mptr = matchmask + sizeof(STRUCT_ENTRY);
+       if (IP6T_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+               return 0;
+
+       ta = GET_TARGET((STRUCT_ENTRY *)a);
+       tb = GET_TARGET((STRUCT_ENTRY *)b);
+       if (ta->u.target_size != tb->u.target_size)
+               return 0;
+       if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
+               return 0;
+       mptr += sizeof(*ta);
+
+       if (target_different(ta->data, tb->data,
+                            ta->u.target_size - sizeof(*ta), mptr))
+               return 0;
+
+       return 1;
+}
+
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ip6t_ip6 *ipv6)
+{
+       unsigned int i;
+
+       for (i = 0; i < sizeof(*ipv6); i++)
+               if (((char *)ipv6)[i])
+                       break;
+
+       return (i == sizeof(*ipv6));
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+       unsigned int i, n;
+       unsigned int user_offset; /* Offset of first user chain */
+       int was_return;
+
+       assert(h->changed == 0 || h->changed == 1);
+       if (strcmp(h->info.name, "filter") == 0) {
+               assert(h->info.valid_hooks
+                      == (1 << NF_IP6_LOCAL_IN
+                          | 1 << NF_IP6_FORWARD
+                          | 1 << NF_IP6_LOCAL_OUT));
+
+               /* Hooks should be first three */
+               assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == 0);
+
+               n = get_chain_end(h, 0);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+
+               n = get_chain_end(h, n);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+       } else if (strcmp(h->info.name, "nat") == 0) {
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_IN
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)));
+
+               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+               n = get_chain_end(h, n);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+                       user_offset = h->info.hook_entry[NF_IP6_LOCAL_IN];
+               }
+
+       } else if (strcmp(h->info.name, "mangle") == 0) {
+               /* This code is getting ugly because linux < 2.4.18-pre6 had
+                * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+                * */
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_OUT)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_IN
+                           | 1 << NF_IP6_FORWARD
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)));
+
+               /* Hooks should be first five */
+               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               if (h->info.valid_hooks & (1 << NF_IP6_FORWARD)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP6_POST_ROUTING)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+                       user_offset = h->info.hook_entry[NF_IP6_POST_ROUTING];
+               }
+       } else {
+                fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+               abort();
+       }
+
+       /* User chain == end of last builtin + policy entry */
+       user_offset = get_chain_end(h, user_offset);
+       user_offset += get_entry(h, user_offset)->next_offset;
+
+       /* Overflows should be end of entry chains, and unconditional
+           policy nodes. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               STRUCT_ENTRY *e;
+               STRUCT_STANDARD_TARGET *t;
+
+               if (!(h->info.valid_hooks & (1 << i)))
+                       continue;
+               assert(h->info.underflow[i]
+                      == get_chain_end(h, h->info.hook_entry[i]));
+
+               e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+               assert(unconditional(&e->ipv6));
+               assert(e->target_offset == sizeof(*e));
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+               printf("target_size=%u, align=%u\n",
+                       t->target.u.target_size, ALIGN(sizeof(*t)));
+               assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+               assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+               assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+               assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+               /* Hooks and underflows must be valid entries */
+               entry2index(h, get_entry(h, h->info.hook_entry[i]));
+               entry2index(h, get_entry(h, h->info.underflow[i]));
+       }
+
+       assert(h->info.size
+              >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+                                        +sizeof(STRUCT_STANDARD_TARGET)));
+
+       assert(h->entries.size
+              >= (h->new_number
+                  * (sizeof(STRUCT_ENTRY)
+                     + sizeof(STRUCT_STANDARD_TARGET))));
+       assert(strcmp(h->info.name, h->entries.name) == 0);
+
+       i = 0; n = 0;
+       was_return = 0;
+
+#if 0
+       /* Check all the entries. */
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     check_entry, &i, &n, user_offset, &was_return, h);
+
+       assert(i == h->new_number);
+       assert(n == h->entries.size);
+
+       /* Final entry must be error node */
+       assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+                     ->u.user.name,
+                     ERROR_TARGET) == 0);
+#endif
+}
+#endif /*IPTC_DEBUG*/
diff --git a/libiptc/libiptc.c b/libiptc/libiptc.c
new file mode 100644 (file)
index 0000000..1c17480
--- /dev/null
@@ -0,0 +1,2283 @@
+/* Library which manipulates firewall rules.  Version $Revision: 6665 $ */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+ * COPYING for details). 
+ * (C) 2000-2004 by the Netfilter Core Team <coreteam@netfilter.org>
+ *
+ * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
+ *     - Reimplementation of chain cache to use offsets instead of entries
+ * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
+ *     - performance optimization, sponsored by Astaro AG (http://www.astaro.com/)
+ *       don't rebuild the chain cache after every operation, instead fix it
+ *       up after a ruleset change.  
+ * 2004-Aug-18: Harald Welte <laforge@netfilter.org>:
+ *     - futher performance work: total reimplementation of libiptc.
+ *     - libiptc now has a real internal (linked-list) represntation of the
+ *       ruleset and a parser/compiler from/to this internal representation
+ *     - again sponsored by Astaro AG (http://www.astaro.com/)
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "linux_list.h"
+
+//#define IPTC_DEBUG2 1
+
+#ifdef IPTC_DEBUG2
+#include <fcntl.h>
+#define DEBUGP(x, args...)     fprintf(stderr, "%s: " x, __FUNCTION__, ## args)
+#define DEBUGP_C(x, args...)   fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...)
+#define DEBUGP_C(x, args...)
+#endif
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/local/lib/iptables"
+#endif
+
+static int sockfd = -1;
+static int sockfd_use = 0;
+static void *iptc_fn = NULL;
+
+static const char *hooknames[]
+= { [HOOK_PRE_ROUTING]  "PREROUTING",
+    [HOOK_LOCAL_IN]     "INPUT",
+    [HOOK_FORWARD]      "FORWARD",
+    [HOOK_LOCAL_OUT]    "OUTPUT",
+    [HOOK_POST_ROUTING] "POSTROUTING",
+#ifdef HOOK_DROPPING
+    [HOOK_DROPPING]    "DROPPING"
+#endif
+};
+
+/* Convenience structures */
+struct ipt_error_target
+{
+       STRUCT_ENTRY_TARGET t;
+       char error[TABLE_MAXNAMELEN];
+};
+
+struct chain_head;
+struct rule_head;
+
+struct counter_map
+{
+       enum {
+               COUNTER_MAP_NOMAP,
+               COUNTER_MAP_NORMAL_MAP,
+               COUNTER_MAP_ZEROED,
+               COUNTER_MAP_SET
+       } maptype;
+       unsigned int mappos;
+};
+
+enum iptcc_rule_type {
+       IPTCC_R_STANDARD,               /* standard target (ACCEPT, ...) */
+       IPTCC_R_MODULE,                 /* extension module (SNAT, ...) */
+       IPTCC_R_FALLTHROUGH,            /* fallthrough rule */
+       IPTCC_R_JUMP,                   /* jump to other chain */
+};
+
+struct rule_head
+{
+       struct list_head list;
+       struct chain_head *chain;
+       struct counter_map counter_map;
+
+       unsigned int index;             /* index (needed for counter_map) */
+       unsigned int offset;            /* offset in rule blob */
+
+       enum iptcc_rule_type type;
+       struct chain_head *jump;        /* jump target, if IPTCC_R_JUMP */
+
+       unsigned int size;              /* size of entry data */
+       STRUCT_ENTRY entry[0];
+};
+
+struct chain_head
+{
+       struct list_head list;
+       char name[TABLE_MAXNAMELEN];
+       unsigned int hooknum;           /* hook number+1 if builtin */
+       unsigned int references;        /* how many jumps reference us */
+       int verdict;                    /* verdict if builtin */
+
+       STRUCT_COUNTERS counters;       /* per-chain counters */
+       struct counter_map counter_map;
+
+       unsigned int num_rules;         /* number of rules in list */
+       struct list_head rules;         /* list of rules */
+
+       unsigned int index;             /* index (needed for jump resolval) */
+       unsigned int head_offset;       /* offset in rule blob */
+       unsigned int foot_index;        /* index (needed for counter_map) */
+       unsigned int foot_offset;       /* offset in rule blob */
+};
+
+STRUCT_TC_HANDLE
+{
+       int changed;                     /* Have changes been made? */
+
+       struct list_head chains;
+       
+       struct chain_head *chain_iterator_cur;
+       struct rule_head *rule_iterator_cur;
+
+       STRUCT_GETINFO info;
+       STRUCT_GET_ENTRIES *entries;
+};
+
+/* allocate a new chain head for the cache */
+static struct chain_head *iptcc_alloc_chain_head(const char *name, int hooknum)
+{
+       struct chain_head *c = malloc(sizeof(*c));
+       if (!c)
+               return NULL;
+       memset(c, 0, sizeof(*c));
+
+       strncpy(c->name, name, TABLE_MAXNAMELEN);
+       c->hooknum = hooknum;
+       INIT_LIST_HEAD(&c->rules);
+
+       return c;
+}
+
+/* allocate and initialize a new rule for the cache */
+static struct rule_head *iptcc_alloc_rule(struct chain_head *c, unsigned int size)
+{
+       struct rule_head *r = malloc(sizeof(*r)+size);
+       if (!r)
+               return NULL;
+       memset(r, 0, sizeof(*r));
+
+       r->chain = c;
+       r->size = size;
+
+       return r;
+}
+
+/* notify us that the ruleset has been modified by the user */
+static void
+set_changed(TC_HANDLE_T h)
+{
+       h->changed = 1;
+}
+
+#ifdef IPTC_DEBUG
+static void do_check(TC_HANDLE_T h, unsigned int line);
+#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
+#else
+#define CHECK(h)
+#endif
+
+
+/**********************************************************************
+ * iptc blob utility functions (iptcb_*)
+ **********************************************************************/
+
+static inline int
+iptcb_get_number(const STRUCT_ENTRY *i,
+          const STRUCT_ENTRY *seek,
+          unsigned int *pos)
+{
+       if (i == seek)
+               return 1;
+       (*pos)++;
+       return 0;
+}
+
+static inline int
+iptcb_get_entry_n(STRUCT_ENTRY *i,
+           unsigned int number,
+           unsigned int *pos,
+           STRUCT_ENTRY **pe)
+{
+       if (*pos == number) {
+               *pe = i;
+               return 1;
+       }
+       (*pos)++;
+       return 0;
+}
+
+static inline STRUCT_ENTRY *
+iptcb_get_entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *)((char *)h->entries->entrytable + offset);
+}
+
+static unsigned int
+iptcb_entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
+{
+       unsigned int pos = 0;
+
+       if (ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
+                         iptcb_get_number, seek, &pos) == 0) {
+               fprintf(stderr, "ERROR: offset %u not an entry!\n",
+                       (unsigned int)((char *)seek - (char *)h->entries->entrytable));
+               abort();
+       }
+       return pos;
+}
+
+static inline STRUCT_ENTRY *
+iptcb_offset2entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *) ((void *)h->entries->entrytable+offset);
+}
+
+
+static inline unsigned long
+iptcb_entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+       return (void *)e - (void *)h->entries->entrytable;
+}
+
+static inline unsigned int
+iptcb_offset2index(const TC_HANDLE_T h, unsigned int offset)
+{
+       return iptcb_entry2index(h, iptcb_offset2entry(h, offset));
+}
+
+/* Returns 0 if not hook entry, else hooknumber + 1 */
+static inline unsigned int
+iptcb_ent_is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
+{
+       unsigned int i;
+
+       for (i = 0; i < NUMHOOKS; i++) {
+               if ((h->info.valid_hooks & (1 << i))
+                   && iptcb_get_entry(h, h->info.hook_entry[i]) == e)
+                       return i+1;
+       }
+       return 0;
+}
+
+
+/**********************************************************************
+ * iptc cache utility functions (iptcc_*)
+ **********************************************************************/
+
+/* Is the given chain builtin (1) or user-defined (0) */
+static unsigned int iptcc_is_builtin(struct chain_head *c)
+{
+       return (c->hooknum ? 1 : 0);
+}
+
+/* Get a specific rule within a chain */
+static struct rule_head *iptcc_get_rule_num(struct chain_head *c,
+                                           unsigned int rulenum)
+{
+       struct rule_head *r;
+       unsigned int num = 0;
+
+       list_for_each_entry(r, &c->rules, list) {
+               num++;
+               if (num == rulenum)
+                       return r;
+       }
+       return NULL;
+}
+
+/* Get a specific rule within a chain backwards */
+static struct rule_head *iptcc_get_rule_num_reverse(struct chain_head *c,
+                                           unsigned int rulenum)
+{
+       struct rule_head *r;
+       unsigned int num = 0;
+
+       list_for_each_entry_reverse(r, &c->rules, list) {
+               num++;
+               if (num == rulenum)
+                       return r;
+       }
+       return NULL;
+}
+
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+iptcc_find_chain_by_offset(TC_HANDLE_T handle, unsigned int offset)
+{
+       struct list_head *pos;
+
+       if (list_empty(&handle->chains))
+               return NULL;
+
+       list_for_each(pos, &handle->chains) {
+               struct chain_head *c = list_entry(pos, struct chain_head, list);
+               if (offset >= c->head_offset && offset <= c->foot_offset)
+                       return c;
+       }
+
+       return NULL;
+}
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+iptcc_find_label(const char *name, TC_HANDLE_T handle)
+{
+       struct list_head *pos;
+
+       if (list_empty(&handle->chains))
+               return NULL;
+
+       list_for_each(pos, &handle->chains) {
+               struct chain_head *c = list_entry(pos, struct chain_head, list);
+               if (!strcmp(c->name, name))
+                       return c;
+       }
+
+       return NULL;
+}
+
+/* called when rule is to be removed from cache */
+static void iptcc_delete_rule(struct rule_head *r)
+{
+       DEBUGP("deleting rule %p (offset %u)\n", r, r->offset);
+       /* clean up reference count of called chain */
+       if (r->type == IPTCC_R_JUMP
+           && r->jump)
+               r->jump->references--;
+
+       list_del(&r->list);
+       free(r);
+}
+
+
+/**********************************************************************
+ * RULESET PARSER (blob -> cache)
+ **********************************************************************/
+
+/* Delete policy rule of previous chain, since cache doesn't contain
+ * chain policy rules.
+ * WARNING: This function has ugly design and relies on a lot of context, only
+ * to be called from specific places within the parser */
+static int __iptcc_p_del_policy(TC_HANDLE_T h, unsigned int num)
+{
+       if (h->chain_iterator_cur) {
+               /* policy rule is last rule */
+               struct rule_head *pr = (struct rule_head *)
+                       h->chain_iterator_cur->rules.prev;
+
+               /* save verdict */
+               h->chain_iterator_cur->verdict = 
+                       *(int *)GET_TARGET(pr->entry)->data;
+
+               /* save counter and counter_map information */
+               h->chain_iterator_cur->counter_map.maptype = 
+                                               COUNTER_MAP_NORMAL_MAP;
+               h->chain_iterator_cur->counter_map.mappos = num-1;
+               memcpy(&h->chain_iterator_cur->counters, &pr->entry->counters, 
+                       sizeof(h->chain_iterator_cur->counters));
+
+               /* foot_offset points to verdict rule */
+               h->chain_iterator_cur->foot_index = num;
+               h->chain_iterator_cur->foot_offset = pr->offset;
+
+               /* delete rule from cache */
+               iptcc_delete_rule(pr);
+               h->chain_iterator_cur->num_rules--;
+
+               return 1;
+       }
+       return 0;
+}
+
+/* alphabetically insert a chain into the list */
+static inline void iptc_insert_chain(TC_HANDLE_T h, struct chain_head *c)
+{
+       struct chain_head *tmp;
+
+       /* sort only user defined chains */
+       if (!c->hooknum) {
+               list_for_each_entry(tmp, &h->chains, list) {
+                       if (!tmp->hooknum && strcmp(c->name, tmp->name) <= 0) {
+                               list_add(&c->list, tmp->list.prev);
+                               return;
+                       }
+               }
+       }
+
+       /* survived till end of list: add at tail */
+       list_add_tail(&c->list, &h->chains);
+}
+
+/* Another ugly helper function split out of cache_add_entry to make it less
+ * spaghetti code */
+static void __iptcc_p_add_chain(TC_HANDLE_T h, struct chain_head *c,
+                               unsigned int offset, unsigned int *num)
+{
+       __iptcc_p_del_policy(h, *num);
+
+       c->head_offset = offset;
+       c->index = *num;
+
+       iptc_insert_chain(h, c);
+       
+       h->chain_iterator_cur = c;
+}
+
+/* main parser function: add an entry from the blob to the cache */
+static int cache_add_entry(STRUCT_ENTRY *e, 
+                          TC_HANDLE_T h, 
+                          STRUCT_ENTRY **prev,
+                          unsigned int *num)
+{
+       unsigned int builtin;
+       unsigned int offset = (char *)e - (char *)h->entries->entrytable;
+
+       DEBUGP("entering...");
+
+       /* Last entry ("policy rule"). End it.*/
+       if (iptcb_entry2offset(h,e) + e->next_offset == h->entries->size) {
+               /* This is the ERROR node at the end of the chain */
+               DEBUGP_C("%u:%u: end of table:\n", *num, offset);
+
+               __iptcc_p_del_policy(h, *num);
+
+               h->chain_iterator_cur = NULL;
+               goto out_inc;
+       }
+
+       /* We know this is the start of a new chain if it's an ERROR
+        * target, or a hook entry point */
+
+       if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
+               struct chain_head *c = 
+                       iptcc_alloc_chain_head((const char *)GET_TARGET(e)->data, 0);
+               DEBUGP_C("%u:%u:new userdefined chain %s: %p\n", *num, offset, 
+                       (char *)c->name, c);
+               if (!c) {
+                       errno = -ENOMEM;
+                       return -1;
+               }
+
+               __iptcc_p_add_chain(h, c, offset, num);
+
+       } else if ((builtin = iptcb_ent_is_hook_entry(e, h)) != 0) {
+               struct chain_head *c =
+                       iptcc_alloc_chain_head((char *)hooknames[builtin-1], 
+                                               builtin);
+               DEBUGP_C("%u:%u new builtin chain: %p (rules=%p)\n", 
+                       *num, offset, c, &c->rules);
+               if (!c) {
+                       errno = -ENOMEM;
+                       return -1;
+               }
+
+               c->hooknum = builtin;
+
+               __iptcc_p_add_chain(h, c, offset, num);
+
+               /* FIXME: this is ugly. */
+               goto new_rule;
+       } else {
+               /* has to be normal rule */
+               struct rule_head *r;
+new_rule:
+
+               if (!(r = iptcc_alloc_rule(h->chain_iterator_cur, 
+                                          e->next_offset))) {
+                       errno = ENOMEM;
+                       return -1;
+               }
+               DEBUGP_C("%u:%u normal rule: %p: ", *num, offset, r);
+
+               r->index = *num;
+               r->offset = offset;
+               memcpy(r->entry, e, e->next_offset);
+               r->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
+               r->counter_map.mappos = r->index;
+
+               /* handling of jumps, etc. */
+               if (!strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET)) {
+                       STRUCT_STANDARD_TARGET *t;
+
+                       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+                       if (t->target.u.target_size
+                           != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
+                               errno = EINVAL;
+                               return -1;
+                       }
+
+                       if (t->verdict < 0) {
+                               DEBUGP_C("standard, verdict=%d\n", t->verdict);
+                               r->type = IPTCC_R_STANDARD;
+                       } else if (t->verdict == r->offset+e->next_offset) {
+                               DEBUGP_C("fallthrough\n");
+                               r->type = IPTCC_R_FALLTHROUGH;
+                       } else {
+                               DEBUGP_C("jump, target=%u\n", t->verdict);
+                               r->type = IPTCC_R_JUMP;
+                               /* Jump target fixup has to be deferred
+                                * until second pass, since we migh not
+                                * yet have parsed the target */
+                       }
+               } else {
+                       DEBUGP_C("module, target=%s\n", GET_TARGET(e)->u.user.name);
+                       r->type = IPTCC_R_MODULE;
+               }
+
+               list_add_tail(&r->list, &h->chain_iterator_cur->rules);
+               h->chain_iterator_cur->num_rules++;
+       }
+out_inc:
+       (*num)++;
+       return 0;
+}
+
+
+/* parse an iptables blob into it's pieces */
+static int parse_table(TC_HANDLE_T h)
+{
+       STRUCT_ENTRY *prev;
+       unsigned int num = 0;
+       struct chain_head *c;
+
+       /* First pass: over ruleset blob */
+       ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
+                       cache_add_entry, h, &prev, &num);
+
+       /* Second pass: fixup parsed data from first pass */
+       list_for_each_entry(c, &h->chains, list) {
+               struct rule_head *r;
+               list_for_each_entry(r, &c->rules, list) {
+                       struct chain_head *c;
+                       STRUCT_STANDARD_TARGET *t;
+
+                       if (r->type != IPTCC_R_JUMP)
+                               continue;
+
+                       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+                       c = iptcc_find_chain_by_offset(h, t->verdict);
+                       if (!c)
+                               return -1;
+                       r->jump = c;
+                       c->references++;
+               }
+       }
+
+       /* FIXME: sort chains */
+
+       return 1;
+}
+
+
+/**********************************************************************
+ * RULESET COMPILATION (cache -> blob)
+ **********************************************************************/
+
+/* Convenience structures */
+struct iptcb_chain_start{
+       STRUCT_ENTRY e;
+       struct ipt_error_target name;
+};
+#define IPTCB_CHAIN_START_SIZE (sizeof(STRUCT_ENTRY) +                 \
+                                ALIGN(sizeof(struct ipt_error_target)))
+
+struct iptcb_chain_foot {
+       STRUCT_ENTRY e;
+       STRUCT_STANDARD_TARGET target;
+};
+#define IPTCB_CHAIN_FOOT_SIZE  (sizeof(STRUCT_ENTRY) +                 \
+                                ALIGN(sizeof(STRUCT_STANDARD_TARGET)))
+
+struct iptcb_chain_error {
+       STRUCT_ENTRY entry;
+       struct ipt_error_target target;
+};
+#define IPTCB_CHAIN_ERROR_SIZE (sizeof(STRUCT_ENTRY) +                 \
+                                ALIGN(sizeof(struct ipt_error_target)))
+
+
+
+/* compile rule from cache into blob */
+static inline int iptcc_compile_rule (TC_HANDLE_T h, STRUCT_REPLACE *repl, struct rule_head *r)
+{
+       /* handle jumps */
+       if (r->type == IPTCC_R_JUMP) {
+               STRUCT_STANDARD_TARGET *t;
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+               /* memset for memcmp convenience on delete/replace */
+               memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+               strcpy(t->target.u.user.name, STANDARD_TARGET);
+               /* Jumps can only happen to builtin chains, so we
+                * can safely assume that they always have a header */
+               t->verdict = r->jump->head_offset + IPTCB_CHAIN_START_SIZE;
+       } else if (r->type == IPTCC_R_FALLTHROUGH) {
+               STRUCT_STANDARD_TARGET *t;
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+               t->verdict = r->offset + r->size;
+       }
+       
+       /* copy entry from cache to blob */
+       memcpy((char *)repl->entries+r->offset, r->entry, r->size);
+
+       return 1;
+}
+
+/* compile chain from cache into blob */
+static int iptcc_compile_chain(TC_HANDLE_T h, STRUCT_REPLACE *repl, struct chain_head *c)
+{
+       int ret;
+       struct rule_head *r;
+       struct iptcb_chain_start *head;
+       struct iptcb_chain_foot *foot;
+
+       /* only user-defined chains have heaer */
+       if (!iptcc_is_builtin(c)) {
+               /* put chain header in place */
+               head = (void *)repl->entries + c->head_offset;
+               head->e.target_offset = sizeof(STRUCT_ENTRY);
+               head->e.next_offset = IPTCB_CHAIN_START_SIZE;
+               strcpy(head->name.t.u.user.name, ERROR_TARGET);
+               head->name.t.u.target_size = 
+                               ALIGN(sizeof(struct ipt_error_target));
+               strcpy(head->name.error, c->name);
+       } else {
+               repl->hook_entry[c->hooknum-1] = c->head_offset;        
+               repl->underflow[c->hooknum-1] = c->foot_offset;
+       }
+
+       /* iterate over rules */
+       list_for_each_entry(r, &c->rules, list) {
+               ret = iptcc_compile_rule(h, repl, r);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* put chain footer in place */
+       foot = (void *)repl->entries + c->foot_offset;
+       foot->e.target_offset = sizeof(STRUCT_ENTRY);
+       foot->e.next_offset = IPTCB_CHAIN_FOOT_SIZE;
+       strcpy(foot->target.target.u.user.name, STANDARD_TARGET);
+       foot->target.target.u.target_size =
+                               ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       /* builtin targets have verdict, others return */
+       if (iptcc_is_builtin(c))
+               foot->target.verdict = c->verdict;
+       else
+               foot->target.verdict = RETURN;
+       /* set policy-counters */
+       memcpy(&foot->e.counters, &c->counters, sizeof(STRUCT_COUNTERS));
+
+       return 0;
+}
+
+/* calculate offset and number for every rule in the cache */
+static int iptcc_compile_chain_offsets(TC_HANDLE_T h, struct chain_head *c,
+                                      unsigned int *offset, unsigned int *num)
+{
+       struct rule_head *r;
+
+       c->head_offset = *offset;
+       DEBUGP("%s: chain_head %u, offset=%u\n", c->name, *num, *offset);
+
+       if (!iptcc_is_builtin(c))  {
+               /* Chain has header */
+               *offset += sizeof(STRUCT_ENTRY) 
+                            + ALIGN(sizeof(struct ipt_error_target));
+               (*num)++;
+       }
+
+       list_for_each_entry(r, &c->rules, list) {
+               DEBUGP("rule %u, offset=%u, index=%u\n", *num, *offset, *num);
+               r->offset = *offset;
+               r->index = *num;
+               *offset += r->size;
+               (*num)++;
+       }
+
+       DEBUGP("%s; chain_foot %u, offset=%u, index=%u\n", c->name, *num, 
+               *offset, *num);
+       c->foot_offset = *offset;
+       c->foot_index = *num;
+       *offset += sizeof(STRUCT_ENTRY)
+                  + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       (*num)++;
+
+       return 1;
+}
+
+/* put the pieces back together again */
+static int iptcc_compile_table_prep(TC_HANDLE_T h, unsigned int *size)
+{
+       struct chain_head *c;
+       unsigned int offset = 0, num = 0;
+       int ret = 0;
+
+       /* First pass: calculate offset for every rule */
+       list_for_each_entry(c, &h->chains, list) {
+               ret = iptcc_compile_chain_offsets(h, c, &offset, &num);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* Append one error rule at end of chain */
+       num++;
+       offset += sizeof(STRUCT_ENTRY)
+                 + ALIGN(sizeof(struct ipt_error_target));
+
+       /* ruleset size is now in offset */
+       *size = offset;
+       return num;
+}
+
+static int iptcc_compile_table(TC_HANDLE_T h, STRUCT_REPLACE *repl)
+{
+       struct chain_head *c;
+       struct iptcb_chain_error *error;
+
+       /* Second pass: copy from cache to offsets, fill in jumps */
+       list_for_each_entry(c, &h->chains, list) {
+               int ret = iptcc_compile_chain(h, repl, c);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* Append error rule at end of chain */
+       error = (void *)repl->entries + repl->size - IPTCB_CHAIN_ERROR_SIZE;
+       error->entry.target_offset = sizeof(STRUCT_ENTRY);
+       error->entry.next_offset = IPTCB_CHAIN_ERROR_SIZE;
+       error->target.t.u.user.target_size = 
+               ALIGN(sizeof(struct ipt_error_target));
+       strcpy((char *)&error->target.t.u.user.name, ERROR_TARGET);
+       strcpy((char *)&error->target.error, "ERROR");
+
+       return 1;
+}
+
+/**********************************************************************
+ * EXTERNAL API (operates on cache only)
+ **********************************************************************/
+
+/* Allocate handle of given size */
+static TC_HANDLE_T
+alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
+{
+       size_t len;
+       TC_HANDLE_T h;
+
+       len = sizeof(STRUCT_TC_HANDLE) + size;
+
+       h = malloc(sizeof(STRUCT_TC_HANDLE));
+       if (!h) {
+               errno = ENOMEM;
+               return NULL;
+       }
+       memset(h, 0, sizeof(*h));
+       INIT_LIST_HEAD(&h->chains);
+       strcpy(h->info.name, tablename);
+
+       h->entries = malloc(sizeof(STRUCT_GET_ENTRIES) + size);
+       if (!h->entries)
+               goto out_free_handle;
+
+       strcpy(h->entries->name, tablename);
+       h->entries->size = size;
+
+       return h;
+
+out_free_handle:
+       free(h);
+
+       return NULL;
+}
+
+
+TC_HANDLE_T
+TC_INIT(const char *tablename)
+{
+       TC_HANDLE_T h;
+       STRUCT_GETINFO info;
+       unsigned int tmp;
+       socklen_t s;
+
+       iptc_fn = TC_INIT;
+
+       if (strlen(tablename) >= TABLE_MAXNAMELEN) {
+               errno = EINVAL;
+               return NULL;
+       }
+       
+       if (sockfd_use == 0) {
+               sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
+               if (sockfd < 0)
+                       return NULL;
+       }
+       sockfd_use++;
+
+       s = sizeof(info);
+
+       strcpy(info.name, tablename);
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0) {
+               if (--sockfd_use == 0) {
+                       close(sockfd);
+                       sockfd = -1;
+               }
+               return NULL;
+       }
+
+       DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n",
+               info.valid_hooks, info.num_entries, info.size);
+
+       if ((h = alloc_handle(info.name, info.size, info.num_entries))
+           == NULL) {
+               if (--sockfd_use == 0) {
+                       close(sockfd);
+                       sockfd = -1;
+               }
+               return NULL;
+       }
+
+       /* Initialize current state */
+       h->info = info;
+
+       h->entries->size = h->info.size;
+
+       tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
+
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries,
+                      &tmp) < 0)
+               goto error;
+
+#ifdef IPTC_DEBUG2
+       {
+               int fd = open("/tmp/libiptc-so_get_entries.blob", 
+                               O_CREAT|O_WRONLY);
+               if (fd >= 0) {
+                       write(fd, h->entries, tmp);
+                       close(fd);
+               }
+       }
+#endif
+
+       if (parse_table(h) < 0)
+               goto error;
+
+       CHECK(h);
+       return h;
+error:
+       if (--sockfd_use == 0) {
+               close(sockfd);
+               sockfd = -1;
+       }
+       TC_FREE(&h);
+       return NULL;
+}
+
+void
+TC_FREE(TC_HANDLE_T *h)
+{
+       struct chain_head *c, *tmp;
+
+       iptc_fn = TC_FREE;
+       if (--sockfd_use == 0) {
+               close(sockfd);
+               sockfd = -1;
+       }
+
+       list_for_each_entry_safe(c, tmp, &(*h)->chains, list) {
+               struct rule_head *r, *rtmp;
+
+               list_for_each_entry_safe(r, rtmp, &c->rules, list) {
+                       free(r);
+               }
+
+               free(c);
+       }
+
+       free((*h)->entries);
+       free(*h);
+
+       *h = NULL;
+}
+
+static inline int
+print_match(const STRUCT_ENTRY_MATCH *m)
+{
+       printf("Match name: `%s'\n", m->u.user.name);
+       return 0;
+}
+
+static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
+void
+TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
+{
+       iptc_fn = TC_DUMP_ENTRIES;
+       CHECK(handle);
+#if 0
+       printf("libiptc v%s. %u bytes.\n",
+              IPTABLES_VERSION, handle->entries->size);
+       printf("Table `%s'\n", handle->info.name);
+       printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+              handle->info.hook_entry[HOOK_PRE_ROUTING],
+              handle->info.hook_entry[HOOK_LOCAL_IN],
+              handle->info.hook_entry[HOOK_FORWARD],
+              handle->info.hook_entry[HOOK_LOCAL_OUT],
+              handle->info.hook_entry[HOOK_POST_ROUTING]);
+       printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+              handle->info.underflow[HOOK_PRE_ROUTING],
+              handle->info.underflow[HOOK_LOCAL_IN],
+              handle->info.underflow[HOOK_FORWARD],
+              handle->info.underflow[HOOK_LOCAL_OUT],
+              handle->info.underflow[HOOK_POST_ROUTING]);
+
+       ENTRY_ITERATE(handle->entries->entrytable, handle->entries->size,
+                     dump_entry, handle);
+#endif
+}
+
+/* Does this chain exist? */
+int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
+{
+       iptc_fn = TC_IS_CHAIN;
+       return iptcc_find_label(chain, handle) != NULL;
+}
+
+static void iptcc_chain_iterator_advance(TC_HANDLE_T handle)
+{
+       struct chain_head *c = handle->chain_iterator_cur;
+
+       if (c->list.next == &handle->chains)
+               handle->chain_iterator_cur = NULL;
+       else
+               handle->chain_iterator_cur = 
+                       list_entry(c->list.next, struct chain_head, list);
+}
+
+/* Iterator functions to run through the chains. */
+const char *
+TC_FIRST_CHAIN(TC_HANDLE_T *handle)
+{
+       struct chain_head *c = list_entry((*handle)->chains.next,
+                                         struct chain_head, list);
+
+       iptc_fn = TC_FIRST_CHAIN;
+
+
+       if (list_empty(&(*handle)->chains)) {
+               DEBUGP(": no chains\n");
+               return NULL;
+       }
+
+       (*handle)->chain_iterator_cur = c;
+       iptcc_chain_iterator_advance(*handle);
+
+       DEBUGP(": returning `%s'\n", c->name);
+       return c->name;
+}
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *
+TC_NEXT_CHAIN(TC_HANDLE_T *handle)
+{
+       struct chain_head *c = (*handle)->chain_iterator_cur;
+
+       iptc_fn = TC_NEXT_CHAIN;
+
+       if (!c) {
+               DEBUGP(": no more chains\n");
+               return NULL;
+       }
+
+       iptcc_chain_iterator_advance(*handle);
+       
+       DEBUGP(": returning `%s'\n", c->name);
+       return c->name;
+}
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const STRUCT_ENTRY *
+TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r;
+
+       iptc_fn = TC_FIRST_RULE;
+
+       DEBUGP("first rule(%s): ", chain);
+
+       c = iptcc_find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       /* Empty chain: single return/policy rule */
+       if (list_empty(&c->rules)) {
+               DEBUGP_C("no rules, returning NULL\n");
+               return NULL;
+       }
+
+       r = list_entry(c->rules.next, struct rule_head, list);
+       (*handle)->rule_iterator_cur = r;
+       DEBUGP_C("%p\n", r);
+
+       return r->entry;
+}
+
+/* Returns NULL when rules run out. */
+const STRUCT_ENTRY *
+TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
+{
+       struct rule_head *r;
+
+       iptc_fn = TC_NEXT_RULE;
+       DEBUGP("rule_iterator_cur=%p...", (*handle)->rule_iterator_cur);
+
+       if (!(*handle)->rule_iterator_cur) {
+               DEBUGP_C("returning NULL\n");
+               return NULL;
+       }
+       
+       r = list_entry((*handle)->rule_iterator_cur->list.next, 
+                       struct rule_head, list);
+
+       iptc_fn = TC_NEXT_RULE;
+
+       DEBUGP_C("next=%p, head=%p...", &r->list, 
+               &(*handle)->rule_iterator_cur->chain->rules);
+
+       if (&r->list == &(*handle)->rule_iterator_cur->chain->rules) {
+               (*handle)->rule_iterator_cur = NULL;
+               DEBUGP_C("finished, returning NULL\n");
+               return NULL;
+       }
+
+       (*handle)->rule_iterator_cur = r;
+
+       /* NOTE: prev is without any influence ! */
+       DEBUGP_C("returning rule %p\n", r);
+       return r->entry;
+}
+
+/* How many rules in this chain? */
+unsigned int
+TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       iptc_fn = TC_NUM_RULES;
+       CHECK(*handle);
+
+       c = iptcc_find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return (unsigned int)-1;
+       }
+       
+       return c->num_rules;
+}
+
+const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
+                               unsigned int n,
+                               TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r;
+       
+       iptc_fn = TC_GET_RULE;
+
+       CHECK(*handle);
+
+       c = iptcc_find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       r = iptcc_get_rule_num(c, n);
+       if (!r)
+               return NULL;
+       return r->entry;
+}
+
+/* Returns a pointer to the target name of this position. */
+const char *standard_target_map(int verdict)
+{
+       switch (verdict) {
+               case RETURN:
+                       return LABEL_RETURN;
+                       break;
+               case -NF_ACCEPT-1:
+                       return LABEL_ACCEPT;
+                       break;
+               case -NF_DROP-1:
+                       return LABEL_DROP;
+                       break;
+               case -NF_QUEUE-1:
+                       return LABEL_QUEUE;
+                       break;
+               default:
+                       fprintf(stderr, "ERROR: %d not a valid target)\n",
+                               verdict);
+                       abort();
+                       break;
+       }
+       /* not reached */
+       return NULL;
+}
+
+/* Returns a pointer to the target name of this position. */
+const char *TC_GET_TARGET(const STRUCT_ENTRY *ce,
+                         TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
+       struct rule_head *r = container_of(e, struct rule_head, entry[0]);
+
+       iptc_fn = TC_GET_TARGET;
+
+       switch(r->type) {
+               int spos;
+               case IPTCC_R_FALLTHROUGH:
+                       return "";
+                       break;
+               case IPTCC_R_JUMP:
+                       DEBUGP("r=%p, jump=%p, name=`%s'\n", r, r->jump, r->jump->name);
+                       return r->jump->name;
+                       break;
+               case IPTCC_R_STANDARD:
+                       spos = *(int *)GET_TARGET(e)->data;
+                       DEBUGP("r=%p, spos=%d'\n", r, spos);
+                       return standard_target_map(spos);
+                       break;
+               case IPTCC_R_MODULE:
+                       return GET_TARGET(e)->u.user.name;
+                       break;
+       }
+       return NULL;
+}
+/* Is this a built-in chain?  Actually returns hook + 1. */
+int
+TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
+{
+       struct chain_head *c;
+       
+       iptc_fn = TC_BUILTIN;
+
+       c = iptcc_find_label(chain, handle);
+       if (!c) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       return iptcc_is_builtin(c);
+}
+
+/* Get the policy of a given built-in chain */
+const char *
+TC_GET_POLICY(const char *chain,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+
+       iptc_fn = TC_GET_POLICY;
+
+       DEBUGP("called for chain %s\n", chain);
+
+       c = iptcc_find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       if (!iptcc_is_builtin(c))
+               return NULL;
+
+       *counters = c->counters;
+
+       return standard_target_map(c->verdict);
+}
+
+static int
+iptcc_standard_map(struct rule_head *r, int verdict)
+{
+       STRUCT_ENTRY *e = r->entry;
+       STRUCT_STANDARD_TARGET *t;
+
+       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+       if (t->target.u.target_size
+           != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
+               errno = EINVAL;
+               return 0;
+       }
+       /* memset for memcmp convenience on delete/replace */
+       memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+       strcpy(t->target.u.user.name, STANDARD_TARGET);
+       t->verdict = verdict;
+
+       r->type = IPTCC_R_STANDARD;
+
+       return 1;
+}
+
+static int
+iptcc_map_target(const TC_HANDLE_T handle,
+          struct rule_head *r)
+{
+       STRUCT_ENTRY *e = r->entry;
+       STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
+
+       /* Maybe it's empty (=> fall through) */
+       if (strcmp(t->u.user.name, "") == 0) {
+               r->type = IPTCC_R_FALLTHROUGH;
+               return 1;
+       }
+       /* Maybe it's a standard target name... */
+       else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
+               return iptcc_standard_map(r, -NF_ACCEPT - 1);
+       else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
+               return iptcc_standard_map(r, -NF_DROP - 1);
+       else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
+               return iptcc_standard_map(r, -NF_QUEUE - 1);
+       else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
+               return iptcc_standard_map(r, RETURN);
+       else if (TC_BUILTIN(t->u.user.name, handle)) {
+               /* Can't jump to builtins. */
+               errno = EINVAL;
+               return 0;
+       } else {
+               /* Maybe it's an existing chain name. */
+               struct chain_head *c;
+               DEBUGP("trying to find chain `%s': ", t->u.user.name);
+
+               c = iptcc_find_label(t->u.user.name, handle);
+               if (c) {
+                       DEBUGP_C("found!\n");
+                       r->type = IPTCC_R_JUMP;
+                       r->jump = c;
+                       c->references++;
+                       return 1;
+               }
+               DEBUGP_C("not found :(\n");
+       }
+
+       /* Must be a module?  If not, kernel will reject... */
+       /* memset to all 0 for your memcmp convenience: don't clear version */
+       memset(t->u.user.name + strlen(t->u.user.name),
+              0,
+              FUNCTION_MAXNAMELEN - 1 - strlen(t->u.user.name));
+       r->type = IPTCC_R_MODULE;
+       set_changed(handle);
+       return 1;
+}
+
+/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
+int
+TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *e,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r;
+       struct list_head *prev;
+
+       iptc_fn = TC_INSERT_ENTRY;
+
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       /* first rulenum index = 0
+          first c->num_rules index = 1 */
+       if (rulenum > c->num_rules) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       /* If we are inserting at the end just take advantage of the
+          double linked list, insert will happen before the entry
+          prev points to. */
+       if (rulenum == c->num_rules) {
+               prev = &c->rules;
+       } else if (rulenum + 1 <= c->num_rules/2) {
+               r = iptcc_get_rule_num(c, rulenum + 1);
+               prev = &r->list;
+       } else {
+               r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
+               prev = &r->list;
+       }
+
+       if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       memcpy(r->entry, e, e->next_offset);
+       r->counter_map.maptype = COUNTER_MAP_SET;
+
+       if (!iptcc_map_target(*handle, r)) {
+               free(r);
+               return 0;
+       }
+
+       list_add_tail(&r->list, prev);
+       c->num_rules++;
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int
+TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
+                const STRUCT_ENTRY *e,
+                unsigned int rulenum,
+                TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r, *old;
+
+       iptc_fn = TC_REPLACE_ENTRY;
+
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (rulenum >= c->num_rules) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       /* Take advantage of the double linked list if possible. */
+       if (rulenum + 1 <= c->num_rules/2) {
+               old = iptcc_get_rule_num(c, rulenum + 1);
+       } else {
+               old = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
+       }
+
+       if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       memcpy(r->entry, e, e->next_offset);
+       r->counter_map.maptype = COUNTER_MAP_SET;
+
+       if (!iptcc_map_target(*handle, r)) {
+               free(r);
+               return 0;
+       }
+
+       list_add(&r->list, &old->list);
+       iptcc_delete_rule(old);
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Append entry `fw' to chain `chain'.  Equivalent to insert with
+   rulenum = length of chain. */
+int
+TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *e,
+               TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r;
+
+       iptc_fn = TC_APPEND_ENTRY;
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               DEBUGP("unable to find chain `%s'\n", chain);
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+               DEBUGP("unable to allocate rule for chain `%s'\n", chain);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       memcpy(r->entry, e, e->next_offset);
+       r->counter_map.maptype = COUNTER_MAP_SET;
+
+       if (!iptcc_map_target(*handle, r)) {
+               DEBUGP("unable to map target of rule for chain `%s'\n", chain);
+               free(r);
+               return 0;
+       }
+
+       list_add_tail(&r->list, &c->rules);
+       c->num_rules++;
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+static inline int
+match_different(const STRUCT_ENTRY_MATCH *a,
+               const unsigned char *a_elems,
+               const unsigned char *b_elems,
+               unsigned char **maskptr)
+{
+       const STRUCT_ENTRY_MATCH *b;
+       unsigned int i;
+
+       /* Offset of b is the same as a. */
+       b = (void *)b_elems + ((unsigned char *)a - a_elems);
+
+       if (a->u.match_size != b->u.match_size)
+               return 1;
+
+       if (strcmp(a->u.user.name, b->u.user.name) != 0)
+               return 1;
+
+       *maskptr += ALIGN(sizeof(*a));
+
+       for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
+               if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
+                       return 1;
+       *maskptr += i;
+       return 0;
+}
+
+static inline int
+target_same(struct rule_head *a, struct rule_head *b,const unsigned char *mask)
+{
+       unsigned int i;
+       STRUCT_ENTRY_TARGET *ta, *tb;
+
+       if (a->type != b->type)
+               return 0;
+
+       ta = GET_TARGET(a->entry);
+       tb = GET_TARGET(b->entry);
+
+       switch (a->type) {
+       case IPTCC_R_FALLTHROUGH:
+               return 1;
+       case IPTCC_R_JUMP:
+               return a->jump == b->jump;
+       case IPTCC_R_STANDARD:
+               return ((STRUCT_STANDARD_TARGET *)ta)->verdict
+                       == ((STRUCT_STANDARD_TARGET *)tb)->verdict;
+       case IPTCC_R_MODULE:
+               if (ta->u.target_size != tb->u.target_size)
+                       return 0;
+               if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
+                       return 0;
+
+               for (i = 0; i < ta->u.target_size - sizeof(*ta); i++)
+                       if (((ta->data[i] ^ tb->data[i]) & mask[i]) != 0)
+                               return 0;
+               return 1;
+       default:
+               fprintf(stderr, "ERROR: bad type %i\n", a->type);
+               abort();
+       }
+}
+
+static unsigned char *
+is_same(const STRUCT_ENTRY *a,
+       const STRUCT_ENTRY *b,
+       unsigned char *matchmask);
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int
+TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *origfw,
+               unsigned char *matchmask,
+               TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r, *i;
+
+       iptc_fn = TC_DELETE_ENTRY;
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       /* Create a rule_head from origfw. */
+       r = iptcc_alloc_rule(c, origfw->next_offset);
+       if (!r) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       memcpy(r->entry, origfw, origfw->next_offset);
+       r->counter_map.maptype = COUNTER_MAP_NOMAP;
+       if (!iptcc_map_target(*handle, r)) {
+               DEBUGP("unable to map target of rule for chain `%s'\n", chain);
+               free(r);
+               return 0;
+       } else {
+               /* iptcc_map_target increment target chain references
+                * since this is a fake rule only used for matching
+                * the chain references count is decremented again. 
+                */
+               if (r->type == IPTCC_R_JUMP
+                   && r->jump)
+                       r->jump->references--;
+       }
+
+       list_for_each_entry(i, &c->rules, list) {
+               unsigned char *mask;
+
+               mask = is_same(r->entry, i->entry, matchmask);
+               if (!mask)
+                       continue;
+
+               if (!target_same(r, i, mask))
+                       continue;
+
+               /* If we are about to delete the rule that is the
+                * current iterator, move rule iterator back.  next
+                * pointer will then point to real next node */
+               if (i == (*handle)->rule_iterator_cur) {
+                       (*handle)->rule_iterator_cur = 
+                               list_entry((*handle)->rule_iterator_cur->list.prev,
+                                          struct rule_head, list);
+               }
+
+               c->num_rules--;
+               iptcc_delete_rule(i);
+
+               set_changed(*handle);
+               free(r);
+               return 1;
+       }
+
+       free(r);
+       errno = ENOENT;
+       return 0;
+}
+
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int
+TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
+                   unsigned int rulenum,
+                   TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r;
+
+       iptc_fn = TC_DELETE_NUM_ENTRY;
+
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (rulenum >= c->num_rules) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       /* Take advantage of the double linked list if possible. */
+       if (rulenum + 1 <= c->num_rules/2) {
+               r = iptcc_get_rule_num(c, rulenum + 1);
+       } else {
+               r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
+       }
+
+       /* If we are about to delete the rule that is the current
+        * iterator, move rule iterator back.  next pointer will then
+        * point to real next node */
+       if (r == (*handle)->rule_iterator_cur) {
+               (*handle)->rule_iterator_cur = 
+                       list_entry((*handle)->rule_iterator_cur->list.prev,
+                                  struct rule_head, list);
+       }
+
+       c->num_rules--;
+       iptcc_delete_rule(r);
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Check the packet `fw' on chain `chain'.  Returns the verdict, or
+   NULL and sets errno. */
+const char *
+TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
+               STRUCT_ENTRY *entry,
+               TC_HANDLE_T *handle)
+{
+       iptc_fn = TC_CHECK_PACKET;
+       errno = ENOSYS;
+       return NULL;
+}
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int
+TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r, *tmp;
+
+       iptc_fn = TC_FLUSH_ENTRIES;
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       list_for_each_entry_safe(r, tmp, &c->rules, list) {
+               iptcc_delete_rule(r);
+       }
+
+       c->num_rules = 0;
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Zeroes the counters in a chain. */
+int
+TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r;
+
+       iptc_fn = TC_ZERO_ENTRIES;
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (c->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+               c->counter_map.maptype = COUNTER_MAP_ZEROED;
+
+       list_for_each_entry(r, &c->rules, list) {
+               if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+                       r->counter_map.maptype = COUNTER_MAP_ZEROED;
+       }
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+STRUCT_COUNTERS *
+TC_READ_COUNTER(const IPT_CHAINLABEL chain,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r;
+
+       iptc_fn = TC_READ_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       if (!(r = iptcc_get_rule_num(c, rulenum))) {
+               errno = E2BIG;
+               return NULL;
+       }
+
+       return &r->entry[0].counters;
+}
+
+int
+TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r;
+       
+       iptc_fn = TC_ZERO_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (!(r = iptcc_get_rule_num(c, rulenum))) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+               r->counter_map.maptype = COUNTER_MAP_ZEROED;
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+int 
+TC_SET_COUNTER(const IPT_CHAINLABEL chain,
+              unsigned int rulenum,
+              STRUCT_COUNTERS *counters,
+              TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r;
+       STRUCT_ENTRY *e;
+
+       iptc_fn = TC_SET_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (!(r = iptcc_get_rule_num(c, rulenum))) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       e = r->entry;
+       r->counter_map.maptype = COUNTER_MAP_SET;
+
+       memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Creates a new chain. */
+/* To create a chain, create two rules: error node and unconditional
+ * return. */
+int
+TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       static struct chain_head *c;
+
+       iptc_fn = TC_CREATE_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (iptcc_find_label(chain, *handle)
+           || strcmp(chain, LABEL_DROP) == 0
+           || strcmp(chain, LABEL_ACCEPT) == 0
+           || strcmp(chain, LABEL_QUEUE) == 0
+           || strcmp(chain, LABEL_RETURN) == 0) {
+               DEBUGP("Chain `%s' already exists\n", chain);
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
+               DEBUGP("Chain name `%s' too long\n", chain);
+               errno = EINVAL;
+               return 0;
+       }
+
+       c = iptcc_alloc_chain_head(chain, 0);
+       if (!c) {
+               DEBUGP("Cannot allocate memory for chain `%s'\n", chain);
+               errno = ENOMEM;
+               return 0;
+
+       }
+
+       DEBUGP("Creating chain `%s'\n", chain);
+       list_add_tail(&c->list, &(*handle)->chains);
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Get the number of references to this chain. */
+int
+TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
+                 TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+
+       iptc_fn = TC_GET_REFERENCES;
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       *ref = c->references;
+
+       return 1;
+}
+
+/* Deletes a chain. */
+int
+TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       unsigned int references;
+       struct chain_head *c;
+
+       iptc_fn = TC_DELETE_CHAIN;
+
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               DEBUGP("cannot find chain `%s'\n", chain);
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (TC_BUILTIN(chain, *handle)) {
+               DEBUGP("cannot remove builtin chain `%s'\n", chain);
+               errno = EINVAL;
+               return 0;
+       }
+
+       if (!TC_GET_REFERENCES(&references, chain, handle)) {
+               DEBUGP("cannot get references on chain `%s'\n", chain);
+               return 0;
+       }
+
+       if (references > 0) {
+               DEBUGP("chain `%s' still has references\n", chain);
+               errno = EMLINK;
+               return 0;
+       }
+
+       if (c->num_rules) {
+               DEBUGP("chain `%s' is not empty\n", chain);
+               errno = ENOTEMPTY;
+               return 0;
+       }
+
+       /* If we are about to delete the chain that is the current
+        * iterator, move chain iterator firward. */
+       if (c == (*handle)->chain_iterator_cur)
+               iptcc_chain_iterator_advance(*handle);
+
+       list_del(&c->list);
+       free(c);
+
+       DEBUGP("chain `%s' deleted\n", chain);
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Renames a chain. */
+int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
+                   const IPT_CHAINLABEL newname,
+                   TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       iptc_fn = TC_RENAME_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (iptcc_find_label(newname, *handle)
+           || strcmp(newname, LABEL_DROP) == 0
+           || strcmp(newname, LABEL_ACCEPT) == 0
+           || strcmp(newname, LABEL_QUEUE) == 0
+           || strcmp(newname, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (!(c = iptcc_find_label(oldname, *handle))
+           || TC_BUILTIN(oldname, *handle)) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       strncpy(c->name, newname, sizeof(IPT_CHAINLABEL));
+       
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Sets the policy on a built-in chain. */
+int
+TC_SET_POLICY(const IPT_CHAINLABEL chain,
+             const IPT_CHAINLABEL policy,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+
+       iptc_fn = TC_SET_POLICY;
+
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               DEBUGP("cannot find chain `%s'\n", chain);
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (!iptcc_is_builtin(c)) {
+               DEBUGP("cannot set policy of userdefinedchain `%s'\n", chain);
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (strcmp(policy, LABEL_ACCEPT) == 0)
+               c->verdict = -NF_ACCEPT - 1;
+       else if (strcmp(policy, LABEL_DROP) == 0)
+               c->verdict = -NF_DROP - 1;
+       else {
+               errno = EINVAL;
+               return 0;
+       }
+
+       if (counters) {
+               /* set byte and packet counters */
+               memcpy(&c->counters, counters, sizeof(STRUCT_COUNTERS));
+               c->counter_map.maptype = COUNTER_MAP_SET;
+       } else {
+               c->counter_map.maptype = COUNTER_MAP_NOMAP;
+       }
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Without this, on gcc 2.7.2.3, we get:
+   libiptc.c: In function `TC_COMMIT':
+   libiptc.c:833: fixed or forbidden register was spilled.
+   This may be due to a compiler bug or to impossible asm
+   statements or clauses.
+*/
+static void
+subtract_counters(STRUCT_COUNTERS *answer,
+                 const STRUCT_COUNTERS *a,
+                 const STRUCT_COUNTERS *b)
+{
+       answer->pcnt = a->pcnt - b->pcnt;
+       answer->bcnt = a->bcnt - b->bcnt;
+}
+
+
+static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters,
+                          unsigned int index)
+{
+       newcounters->counters[index] = ((STRUCT_COUNTERS) { 0, 0});
+       DEBUGP_C("NOMAP => zero\n");
+}
+
+static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters,
+                               STRUCT_REPLACE *repl,
+                               unsigned int index,
+                               unsigned int mappos)
+{
+       /* Original read: X.
+        * Atomic read on replacement: X + Y.
+        * Currently in kernel: Z.
+        * Want in kernel: X + Y + Z.
+        * => Add in X + Y
+        * => Add in replacement read.
+        */
+       newcounters->counters[index] = repl->counters[mappos];
+       DEBUGP_C("NORMAL_MAP => mappos %u \n", mappos);
+}
+
+static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters,
+                               STRUCT_REPLACE *repl,
+                               unsigned int index,
+                               unsigned int mappos,
+                               STRUCT_COUNTERS *counters)
+{
+       /* Original read: X.
+        * Atomic read on replacement: X + Y.
+        * Currently in kernel: Z.
+        * Want in kernel: Y + Z.
+        * => Add in Y.
+        * => Add in (replacement read - original read).
+        */
+       subtract_counters(&newcounters->counters[index],
+                         &repl->counters[mappos],
+                         counters);
+       DEBUGP_C("ZEROED => mappos %u\n", mappos);
+}
+
+static void counters_map_set(STRUCT_COUNTERS_INFO *newcounters,
+                            unsigned int index,
+                            STRUCT_COUNTERS *counters)
+{
+       /* Want to set counter (iptables-restore) */
+
+       memcpy(&newcounters->counters[index], counters,
+               sizeof(STRUCT_COUNTERS));
+
+       DEBUGP_C("SET\n");
+}
+
+
+int
+TC_COMMIT(TC_HANDLE_T *handle)
+{
+       /* Replace, then map back the counters. */
+       STRUCT_REPLACE *repl;
+       STRUCT_COUNTERS_INFO *newcounters;
+       struct chain_head *c;
+       int ret;
+       size_t counterlen;
+       int new_number;
+       unsigned int new_size;
+
+       iptc_fn = TC_COMMIT;
+       CHECK(*handle);
+
+       /* Don't commit if nothing changed. */
+       if (!(*handle)->changed)
+               goto finished;
+
+       new_number = iptcc_compile_table_prep(*handle, &new_size);
+       if (new_number < 0) {
+               errno = ENOMEM;
+               goto out_zero;
+       }
+
+       repl = malloc(sizeof(*repl) + new_size);
+       if (!repl) {
+               errno = ENOMEM;
+               goto out_zero;
+       }
+       memset(repl, 0, sizeof(*repl) + new_size);
+
+#if 0
+       TC_DUMP_ENTRIES(*handle);
+#endif
+
+       counterlen = sizeof(STRUCT_COUNTERS_INFO)
+                       + sizeof(STRUCT_COUNTERS) * new_number;
+
+       /* These are the old counters we will get from kernel */
+       repl->counters = malloc(sizeof(STRUCT_COUNTERS)
+                               * (*handle)->info.num_entries);
+       if (!repl->counters) {
+               errno = ENOMEM;
+               goto out_free_repl;
+       }
+       /* These are the counters we're going to put back, later. */
+       newcounters = malloc(counterlen);
+       if (!newcounters) {
+               errno = ENOMEM;
+               goto out_free_repl_counters;
+       }
+       memset(newcounters, 0, counterlen);
+
+       strcpy(repl->name, (*handle)->info.name);
+       repl->num_entries = new_number;
+       repl->size = new_size;
+
+       repl->num_counters = (*handle)->info.num_entries;
+       repl->valid_hooks = (*handle)->info.valid_hooks;
+
+       DEBUGP("num_entries=%u, size=%u, num_counters=%u\n",
+               repl->num_entries, repl->size, repl->num_counters);
+
+       ret = iptcc_compile_table(*handle, repl);
+       if (ret < 0) {
+               errno = ret;
+               goto out_free_newcounters;
+       }
+
+
+#ifdef IPTC_DEBUG2
+       {
+               int fd = open("/tmp/libiptc-so_set_replace.blob", 
+                               O_CREAT|O_WRONLY);
+               if (fd >= 0) {
+                       write(fd, repl, sizeof(*repl) + repl->size);
+                       close(fd);
+               }
+       }
+#endif
+
+       ret = setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
+                        sizeof(*repl) + repl->size);
+       if (ret < 0)
+               goto out_free_newcounters;
+
+       /* Put counters back. */
+       strcpy(newcounters->name, (*handle)->info.name);
+       newcounters->num_counters = new_number;
+
+       list_for_each_entry(c, &(*handle)->chains, list) {
+               struct rule_head *r;
+
+               /* Builtin chains have their own counters */
+               if (iptcc_is_builtin(c)) {
+                       DEBUGP("counter for chain-index %u: ", c->foot_index);
+                       switch(c->counter_map.maptype) {
+                       case COUNTER_MAP_NOMAP:
+                               counters_nomap(newcounters, c->foot_index);
+                               break;
+                       case COUNTER_MAP_NORMAL_MAP:
+                               counters_normal_map(newcounters, repl,
+                                                   c->foot_index, 
+                                                   c->counter_map.mappos);
+                               break;
+                       case COUNTER_MAP_ZEROED:
+                               counters_map_zeroed(newcounters, repl,
+                                                   c->foot_index, 
+                                                   c->counter_map.mappos,
+                                                   &c->counters);
+                               break;
+                       case COUNTER_MAP_SET:
+                               counters_map_set(newcounters, c->foot_index,
+                                                &c->counters);
+                               break;
+                       }
+               }
+
+               list_for_each_entry(r, &c->rules, list) {
+                       DEBUGP("counter for index %u: ", r->index);
+                       switch (r->counter_map.maptype) {
+                       case COUNTER_MAP_NOMAP:
+                               counters_nomap(newcounters, r->index);
+                               break;
+
+                       case COUNTER_MAP_NORMAL_MAP:
+                               counters_normal_map(newcounters, repl,
+                                                   r->index, 
+                                                   r->counter_map.mappos);
+                               break;
+
+                       case COUNTER_MAP_ZEROED:
+                               counters_map_zeroed(newcounters, repl,
+                                                   r->index,
+                                                   r->counter_map.mappos,
+                                                   &r->entry->counters);
+                               break;
+
+                       case COUNTER_MAP_SET:
+                               counters_map_set(newcounters, r->index,
+                                                &r->entry->counters);
+                               break;
+                       }
+               }
+       }
+
+
+#ifdef KERNEL_64_USERSPACE_32
+       {
+               /* Kernel will think that pointer should be 64-bits, and get
+                  padding.  So we accomodate here (assumption: alignment of
+                  `counters' is on 64-bit boundary). */
+               u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
+               if ((unsigned long)&newcounters->counters % 8 != 0) {
+                       fprintf(stderr,
+                               "counters alignment incorrect! Mail rusty!\n");
+                       abort();
+               }
+               *kernptr = newcounters->counters;
+       }
+#endif /* KERNEL_64_USERSPACE_32 */
+
+#ifdef IPTC_DEBUG2
+       {
+               int fd = open("/tmp/libiptc-so_set_add_counters.blob", 
+                               O_CREAT|O_WRONLY);
+               if (fd >= 0) {
+                       write(fd, newcounters, counterlen);
+                       close(fd);
+               }
+       }
+#endif
+
+       ret = setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
+                        newcounters, counterlen);
+       if (ret < 0)
+               goto out_free_newcounters;
+
+       free(repl->counters);
+       free(repl);
+       free(newcounters);
+
+finished:
+       TC_FREE(handle);
+       return 1;
+
+out_free_newcounters:
+       free(newcounters);
+out_free_repl_counters:
+       free(repl->counters);
+out_free_repl:
+       free(repl);
+out_zero:
+       return 0;
+}
+
+/* Get raw socket. */
+int
+TC_GET_RAW_SOCKET()
+{
+       return sockfd;
+}
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *
+TC_STRERROR(int err)
+{
+       unsigned int i;
+       struct table_struct {
+               void *fn;
+               int err;
+               const char *message;
+       } table [] =
+         { { TC_INIT, EPERM, "Permission denied (you must be root)" },
+           { TC_INIT, EINVAL, "Module is wrong version" },
+           { TC_INIT, ENOENT, 
+                   "Table does not exist (do you need to insmod?)" },
+           { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
+           { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
+           { TC_DELETE_CHAIN, EMLINK,
+             "Can't delete chain with references left" },
+           { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
+           { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
+           { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
+           { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
+           { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
+           { TC_INSERT_ENTRY, EINVAL, "Target problem" },
+           /* EINVAL for CHECK probably means bad interface. */
+           { TC_CHECK_PACKET, EINVAL,
+             "Bad arguments (does that interface exist?)" },
+           { TC_CHECK_PACKET, ENOSYS,
+             "Checking will most likely never get implemented" },
+           /* ENOENT for DELETE probably means no matching rule */
+           { TC_DELETE_ENTRY, ENOENT,
+             "Bad rule (does a matching rule exist in that chain?)" },
+           { TC_SET_POLICY, ENOENT,
+             "Bad built-in chain name" },
+           { TC_SET_POLICY, EINVAL,
+             "Bad policy name" },
+
+           { NULL, 0, "Incompatible with this kernel" },
+           { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
+           { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
+           { NULL, ENOMEM, "Memory allocation problem" },
+           { NULL, ENOENT, "No chain/target/match by that name" },
+         };
+
+       for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+               if ((!table[i].fn || table[i].fn == iptc_fn)
+                   && table[i].err == err)
+                       return table[i].message;
+       }
+
+       return strerror(err);
+}
diff --git a/libiptc/linux_list.h b/libiptc/linux_list.h
new file mode 100644 (file)
index 0000000..abdcf88
--- /dev/null
@@ -0,0 +1,723 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:       the pointer to the member.
+ * @type:      the type of the container struct this is embedded in.
+ * @member:    the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({                     \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);   \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * Check at compile time that something is of a particular type.
+ * Always evaluates to 1 so you may use it easily in comparisons.
+ */
+#define typecheck(type,x) \
+({     type __dummy; \
+       typeof(x) __dummy2; \
+       (void)(&__dummy == &__dummy2); \
+       1; \
+})
+
+#define prefetch(x)            1
+
+/* empty define to make this work in userspace -HW */
+#define smp_wmb()
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1  ((void *) 0x00100100)
+#define LIST_POISON2  ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+       struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+                             struct list_head *prev,
+                             struct list_head *next)
+{
+       next->prev = new;
+       new->next = next;
+       new->prev = prev;
+       prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add_rcu(struct list_head * new,
+               struct list_head * prev, struct list_head * next)
+{
+       new->next = next;
+       new->prev = prev;
+       smp_wmb();
+       next->prev = new;
+       prev->next = new;
+}
+
+/**
+ * list_add_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_rcu(struct list_head *new, struct list_head *head)
+{
+       __list_add_rcu(new, head, head->next);
+}
+
+/**
+ * list_add_tail_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_tail_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_tail_rcu(struct list_head *new,
+                                       struct list_head *head)
+{
+       __list_add_rcu(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->next = LIST_POISON1;
+       entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_rcu - deletes entry from list without re-initialization
+ * @entry: the element to delete from the list.
+ *
+ * Note: list_empty on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_del_rcu()
+ * or list_add_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ *
+ * Note that the caller is not permitted to immediately free
+ * the newly deleted entry.  Instead, either synchronize_kernel()
+ * or call_rcu() must be used to defer freeing until an RCU
+ * grace period has elapsed.
+ */
+static inline void list_del_rcu(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+                                 struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+       return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is
+ * empty _and_ checks that no other CPU might be
+ * in the process of still modifying either member
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ *
+ * @head: the list to test.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+       struct list_head *next = head->next;
+       return (next == head) && (next == head->prev);
+}
+
+static inline void __list_splice(struct list_head *list,
+                                struct list_head *head)
+{
+       struct list_head *first = list->next;
+       struct list_head *last = list->prev;
+       struct list_head *at = head->next;
+
+       first->prev = head;
+       head->next = first;
+
+       last->next = at;
+       at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+       if (!list_empty(list))
+               __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+                                   struct list_head *head)
+{
+       if (!list_empty(list)) {
+               __list_splice(list, head);
+               INIT_LIST_HEAD(list);
+       }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:       the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+       container_of(ptr, type, member)
+
+/**
+ * list_for_each       -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each(pos, head) \
+       for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+               pos = pos->next, prefetch(pos->next))
+
+/**
+ * __list_for_each     -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev  -       iterate over a list backwards
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+       for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+               pos = pos->prev, prefetch(pos->prev))
+
+/**
+ * list_for_each_safe  -       iterate over a list safe against removal of list entry
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @n:         another &struct list_head to use as temporary storage
+ * @head:      the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+       for (pos = (head)->next, n = pos->next; pos != (head); \
+               pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry -       iterate over list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)                         \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+                    prefetch(pos->member.next);                        \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member),  \
+                    prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)                 \
+       for (pos = list_entry((head)->prev, typeof(*pos), member),      \
+                    prefetch(pos->member.prev);                        \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.prev, typeof(*pos), member),  \
+                    prefetch(pos->member.prev))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use as a start point in
+ *                     list_for_each_entry_continue
+ * @pos:       the type * to use as a start point
+ * @head:      the head of the list
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_prepare_entry(pos, head, member) \
+       ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue -      iterate over list of given type
+ *                     continuing after existing point
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_continue(pos, head, member)                \
+       for (pos = list_entry(pos->member.next, typeof(*pos), member),  \
+                    prefetch(pos->member.next);                        \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member),  \
+                    prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:       the type * to use as a loop counter.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)                 \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+               n = list_entry(pos->member.next, typeof(*pos), member); \
+            &pos->member != (head);                                    \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_rcu   -       iterate over an rcu-protected list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_rcu(pos, head) \
+       for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+               pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
+
+#define __list_for_each_rcu(pos, head) \
+       for (pos = (head)->next; pos != (head); \
+               pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
+
+/**
+ * list_for_each_safe_rcu      -       iterate over an rcu-protected list safe
+ *                                     against removal of list entry
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @n:         another &struct list_head to use as temporary storage
+ * @head:      the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_safe_rcu(pos, n, head) \
+       for (pos = (head)->next, n = pos->next; pos != (head); \
+               pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
+
+/**
+ * list_for_each_entry_rcu     -       iterate over rcu list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_entry_rcu(pos, head, member)                     \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+                    prefetch(pos->member.next);                        \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member),  \
+                    ({ smp_read_barrier_depends(); 0;}),               \
+                    prefetch(pos->member.next))
+
+
+/**
+ * list_for_each_continue_rcu  -       iterate over an rcu-protected list
+ *                     continuing after existing point.
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_continue_rcu(pos, head) \
+       for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
+               (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+       struct hlist_node *first;
+};
+
+struct hlist_node {
+       struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+       return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+       return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+       struct hlist_node *next = n->next;
+       struct hlist_node **pprev = n->pprev;
+       *pprev = next;
+       if (next)
+               next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+       __hlist_del(n);
+       n->next = LIST_POISON1;
+       n->pprev = LIST_POISON2;
+}
+
+/**
+ * hlist_del_rcu - deletes entry from hash list without re-initialization
+ * @n: the element to delete from the hash list.
+ *
+ * Note: list_unhashed() on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the hash list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry().
+ */
+static inline void hlist_del_rcu(struct hlist_node *n)
+{
+       __hlist_del(n);
+       n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+       if (n->pprev)  {
+               __hlist_del(n);
+               INIT_HLIST_NODE(n);
+       }
+}
+
+#define hlist_del_rcu_init hlist_del_init
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+       struct hlist_node *first = h->first;
+       n->next = first;
+       if (first)
+               first->pprev = &n->next;
+       h->first = n;
+       n->pprev = &h->first;
+}
+
+
+/**
+ * hlist_add_head_rcu - adds the specified element to the specified hlist,
+ * while permitting racing traversals.
+ * @n: the element to add to the hash list.
+ * @h: the list to add to.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry(), but only if smp_read_barrier_depends()
+ * is used to prevent memory-consistency problems on Alpha CPUs.
+ * Regardless of the type of CPU, the list-traversal primitive
+ * must be guarded by rcu_read_lock().
+ *
+ * OK, so why don't we have an hlist_for_each_entry_rcu()???
+ */
+static inline void hlist_add_head_rcu(struct hlist_node *n,
+                                       struct hlist_head *h)
+{
+       struct hlist_node *first = h->first;
+       n->next = first;
+       n->pprev = &h->first;
+       smp_wmb();
+       if (first)
+               first->pprev = &n->next;
+       h->first = n;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+                                       struct hlist_node *next)
+{
+       n->pprev = next->pprev;
+       n->next = next;
+       next->pprev = &n->next;
+       *(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+                                       struct hlist_node *next)
+{
+       next->next = n->next;
+       n->next = next;
+       next->pprev = &n->next;
+
+       if(next->next)
+               next->next->pprev  = &next->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+       for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+            pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+       for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+            pos = n)
+
+/**
+ * hlist_for_each_entry        - iterate over list of given type
+ * @tpos:      the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member)                   \
+       for (pos = (head)->first;                                        \
+            pos && ({ prefetch(pos->next); 1;}) &&                      \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
+ * @tpos:      the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member)                \
+       for (pos = (pos)->next;                                          \
+            pos && ({ prefetch(pos->next); 1;}) &&                      \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
+ * @tpos:      the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member)                    \
+       for (; pos && ({ prefetch(pos->next); 1;}) &&                    \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos:      the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @n:         another &struct hlist_node to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member)           \
+       for (pos = (head)->first;                                        \
+            pos && ({ n = pos->next; 1; }) &&                           \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = n)
+
+/**
+ * hlist_for_each_entry_rcu - iterate over rcu list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as hlist_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define hlist_for_each_entry_rcu(tpos, pos, head, member)               \
+       for (pos = (head)->first;                                        \
+            pos && ({ prefetch(pos->next); 1;}) &&                      \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next, ({ smp_read_barrier_depends(); 0; }) )
+
+#endif
diff --git a/libiptc/linux_stddef.h b/libiptc/linux_stddef.h
new file mode 100644 (file)
index 0000000..56416f1
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _LINUX_STDDEF_H
+#define _LINUX_STDDEF_H
+
+#undef NULL
+#if defined(__cplusplus)
+#define NULL 0
+#else
+#define NULL ((void *)0)
+#endif
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:       the pointer to the member.
+ * @type:      the type of the container struct this is embedded in.
+ * @member:    the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({                     \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);   \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * Check at compile time that something is of a particular type.
+ * Always evaluates to 1 so you may use it easily in comparisons.
+ */
+#define typecheck(type,x) \
+({     type __dummy; \
+       typeof(x) __dummy2; \
+       (void)(&__dummy == &__dummy2); \
+       1; \
+})
+
+
+#endif
diff --git a/libiptc2/foo.diff b/libiptc2/foo.diff
new file mode 100644 (file)
index 0000000..af69eb5
--- /dev/null
@@ -0,0 +1,391 @@
+--- libiptc.c  2003-06-30 18:26:59.000000000 +0200
++++ libiptc.c  2003-06-30 18:27:24.000000000 +0200
+@@ -64,19 +61,35 @@
+       char error[TABLE_MAXNAMELEN];
+ };
+-struct chain_cache
++struct rule_head
+ {
++      struct list_head list;
++      
++      struct chain_head *chain;
++
++      unsigned int size;
++      STRUCT_ENTRY entry[0];
++}
++
++struct chain_head
++{
++      struct list_head list;
++
+       char name[TABLE_MAXNAMELEN];
+-      /* This is the first rule in chain. */
+-      unsigned int start_off;
+-      /* Last rule in chain */
+-      unsigned int end_off;
++      unsigned int hooknum;
++      struct list_head rules;
+ };
+ STRUCT_TC_HANDLE
+ {
+       /* Have changes been made? */
+       int changed;
++
++      struct list_head chains;
++      
++      struct chain_head *chain_iterator_cur;
++
++#if 0
+       /* Size in here reflects original state. */
+       STRUCT_GETINFO info;
+@@ -98,6 +111,7 @@
+       /* Number in here reflects current state. */
+       unsigned int new_number;
+       STRUCT_GET_ENTRIES entries;
++#endif
+ };
+ static void
+@@ -375,173 +389,25 @@
+       }
+       return 0;
+ }
+-
+-static inline int
+-add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
+-{
+-      unsigned int builtin;
+-
+-      /* Last entry.  End it. */
+-      if (entry2offset(h, e) + e->next_offset == h->entries.size) {
+-              /* This is the ERROR node at end of the table */
+-              h->cache_chain_heads[h->cache_num_chains-1].end_off = 
+-                      entry2offset(h, *prev);
+-              return 0;
+-      }
+-
+-      /* We know this is the start of a new chain if it's an ERROR
+-         target, or a hook entry point */
+-      if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
+-              /* prev was last entry in previous chain */
+-              h->cache_chain_heads[h->cache_num_chains-1].end_off
+-                      = entry2offset(h, *prev);
+-
+-              strcpy(h->cache_chain_heads[h->cache_num_chains].name,
+-                     (const char *)GET_TARGET(e)->data);
+-              h->cache_chain_heads[h->cache_num_chains].start_off
+-                      = entry2offset(h, (void *)e + e->next_offset);
+-              h->cache_num_chains++;
+-      } else if ((builtin = is_hook_entry(e, h)) != 0) {
+-              if (h->cache_num_chains > 0)
+-                      /* prev was last entry in previous chain */
+-                      h->cache_chain_heads[h->cache_num_chains-1].end_off
+-                              = entry2offset(h, *prev);
+-
+-              strcpy(h->cache_chain_heads[h->cache_num_chains].name,
+-                     h->hooknames[builtin-1]);
+-              h->cache_chain_heads[h->cache_num_chains].start_off
+-                      = entry2offset(h, (void *)e);
+-              h->cache_num_chains++;
+-      }
+-
+-      *prev = e;
+-      return 0;
+-}
+-
+ static int alphasort(const void *a, const void *b)
+ {
+       return strcmp(((struct chain_cache *)a)->name,
+                     ((struct chain_cache *)b)->name);
+ }
+-static int populate_cache(TC_HANDLE_T h)
+-{
+-      unsigned int i;
+-      STRUCT_ENTRY *prev;
+-
+-      /* # chains < # rules / 2 + num builtins - 1 */
+-      h->cache_chain_heads = malloc((h->new_number / 2 + 4)
+-                                    * sizeof(struct chain_cache));
+-      if (!h->cache_chain_heads) {
+-              errno = ENOMEM;
+-              return 0;
+-      }
+-
+-      h->cache_num_chains = 0;
+-      h->cache_num_builtins = 0;
+-
+-      /* Count builtins */
+-      for (i = 0; i < NUMHOOKS; i++) {
+-              if (h->info.valid_hooks & (1 << i))
+-                      h->cache_num_builtins++;
+-      }
+-
+-      prev = NULL;
+-      ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+-                    add_chain, h, &prev);
+-
+-      qsort(h->cache_chain_heads + h->cache_num_builtins,
+-            h->cache_num_chains - h->cache_num_builtins,
+-            sizeof(struct chain_cache), alphasort);
+-
+-      return 1;
+-}
+-
+-static int 
+-correct_cache(TC_HANDLE_T h, unsigned int offset, int delta)
+-{
+-      int i;          /* needs to be signed because deleting first
+-                         chain can make it drop to -1 */
+-
+-      if (!delta)
+-              return 1;
+-
+-      for (i = 0; i < h->cache_num_chains; i++) {
+-              struct chain_cache *cc = &h->cache_chain_heads[i];
+-
+-              if (delta < 0) {
+-                      /* take care about deleted chains */
+-                      if (cc->start_off >= offset+delta
+-                          && cc->end_off <= offset) {
+-                              /* this chain is within the deleted range,
+-                               * let's remove it from the cache */
+-                              void *start;
+-                              unsigned int size;
+-
+-                              h->cache_num_chains--;
+-                              if (i+1 >= h->cache_num_chains)
+-                                      continue;
+-                              start = &h->cache_chain_heads[i+1];
+-                              size = (h->cache_num_chains-i)
+-                                      * sizeof(struct chain_cache);
+-                              memmove(cc, start, size);
+-
+-                              /* iterate over same index again, since
+-                               * it is now a different chain */
+-                              i--;
+-                              continue;
+-                      }
+-              }
+-
+-              if (cc->start_off > offset)
+-                      cc->start_off += delta;
+-
+-              if (cc->end_off >= offset)
+-                      cc->end_off += delta;
+-      }
+-      /* HW_FIXME: sorting might be needed, but just in case a new chain was
+-       * added */
+-
+-      return 1;
+-}
+-
+-static int
+-add_chain_cache(TC_HANDLE_T h, const char *name, unsigned int start_off,
+-              unsigned int end_off)
+-{
+-      struct chain_cache *ccs = realloc(h->cache_chain_heads, 
+-                                        (h->new_number / 2 + 4 + 1)
+-                                         * sizeof(struct chain_cache));
+-      struct chain_cache *newcc;
+-      
+-      if (!ccs)
+-              return 0;
+-
+-      h->cache_chain_heads = ccs;
+-      newcc = &h->cache_chain_heads[h->cache_num_chains];
+-      h->cache_num_chains++;
+-
+-      strncpy(newcc->name, name, TABLE_MAXNAMELEN-1);
+-      newcc->start_off = start_off;
+-      newcc->end_off = end_off;
+-
+-      return 1;
+-}
+-
+-/* Returns cache ptr if found, otherwise NULL. */
+-static struct chain_cache *
++/* Returns chain head if found, otherwise NULL. */
++static struct chain_head *
+ find_label(const char *name, TC_HANDLE_T handle)
+ {
+-      unsigned int i;
++      struct list_head *pos;
+-      if (handle->cache_chain_heads == NULL
+-          && !populate_cache(handle))
++      if (!handle->chains)
+               return NULL;
+-      /* FIXME: Linear search through builtins, then binary --RR */
+-      for (i = 0; i < handle->cache_num_chains; i++) {
+-              if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
+-                      return &handle->cache_chain_heads[i];
++      list_for_each(pos, &handle->chains) {
++              struct chain_head *c = list_entry(pos, struct chain_head, list);
++              if (!strcmp(c->name, name))
++                      return c;
+       }
+       return NULL;
+@@ -594,34 +460,30 @@
+ const char *
+ TC_FIRST_CHAIN(TC_HANDLE_T *handle)
+ {
+-      if ((*handle)->cache_chain_heads == NULL
+-          && !populate_cache(*handle))
+-              return NULL;
++      (*handle)->chain_iterator_cur = (*handle)->chains;
+-      (*handle)->cache_chain_iteration
+-              = &(*handle)->cache_chain_heads[0];
+-
+-      return (*handle)->cache_chain_iteration->name;
++      return (*handle)->chains.name;
+ }
+ /* Iterator functions to run through the chains.  Returns NULL at end. */
+ const char *
+ TC_NEXT_CHAIN(TC_HANDLE_T *handle)
+ {
+-      (*handle)->cache_chain_iteration++;
++      struct chain_head *next = list_entry(&(*handle)->chain_iterator_cur->list.next, struct chain_head, list);
++      (*handle)->chain_iterator_cur = next;
+-      if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
+-          == (*handle)->cache_num_chains)
++      if (next == (*handle)->chains)
+               return NULL;
+-      return (*handle)->cache_chain_iteration->name;
++      return next->name;
+ }
+ /* Get first rule in the given chain: NULL for empty chain. */
+ const STRUCT_ENTRY *
+ TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
+ {
+-      struct chain_cache *c;
++      struct chain_head *c;
++      struct rule_head *r;
+       c = find_label(chain, *handle);
+       if (!c) {
+@@ -630,22 +492,26 @@
+       }
+       /* Empty chain: single return/policy rule */
+-      if (c->start_off == c->end_off)
++      if (list_empty(c->rules))
+               return NULL;
+-      (*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
+-      return offset2entry(*handle, c->start_off);
++      r = list_entry(&c->rules.next, struct rule_head, list);
++      (*handle)->rule_iterator_cur = r;
++
++      return r->entry;
+ }
+ /* Returns NULL when rules run out. */
+ const STRUCT_ENTRY *
+ TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
+ {
+-      if ((void *)prev + prev->next_offset
+-          == (void *)(*handle)->cache_rule_end)
++      struct rule_head *r = list_entry((*handle)->rule_iterator_cur->list.next, struct rule_head, list);
++
++      if (r == r->chain)
+               return NULL;
+-      return (void *)prev + prev->next_offset;
++      /* NOTE: prev is without any influence ! */
++      return r->entry;
+ }
+ #if 0
+@@ -773,7 +639,7 @@
+       return target_name(*handle, e);
+ }
+-static inline int
++static int
+ correct_verdict(STRUCT_ENTRY *e,
+               char *base,
+               unsigned int offset, int delta_offset)
+@@ -874,16 +740,8 @@
+       newh->entries.size = (*handle)->entries.size + rules_size;
+       newh->hooknames = (*handle)->hooknames;
+-      newh->cache_chain_heads = (*handle)->cache_chain_heads;
+-      newh->cache_num_builtins = (*handle)->cache_num_builtins;
+-      newh->cache_num_chains = (*handle)->cache_num_chains;
+-      newh->cache_rule_end = (*handle)->cache_rule_end;
+-      newh->cache_chain_iteration = (*handle)->cache_chain_iteration;
+-      if (!correct_cache(newh, offset, rules_size)) {
+-              free(newh);
+-              return 0;
+-      }
+-
++      if ((*handle)->cache_chain_heads)
++              free((*handle)->cache_chain_heads);
+       free(*handle);
+       *handle = newh;
+@@ -942,10 +800,6 @@
+       (*handle)->new_number -= num_rules;
+       (*handle)->entries.size -= rules_size;
+-      /* Fix the chain cache */
+-      if (!correct_cache(*handle, offset, -(int)rules_size))
+-              return 0;
+-
+       return set_verdict(offset, -(int)rules_size, handle);
+ }
+@@ -1449,7 +1303,6 @@
+               STRUCT_ENTRY ret;
+               STRUCT_STANDARD_TARGET target;
+       } newc;
+-      unsigned int destination;
+       iptc_fn = TC_CREATE_CHAIN;
+@@ -1487,21 +1340,11 @@
+               = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       newc.target.verdict = RETURN;
+-      destination = index2offset(*handle, (*handle)->new_number -1);
+-
+       /* Add just before terminal entry */
+       ret = insert_rules(2, sizeof(newc), &newc.head,
+-                         destination,
++                         index2offset(*handle, (*handle)->new_number - 1),
+                          (*handle)->new_number - 1,
+                          0, handle);
+-
+-      set_changed(*handle);
+-
+-      /* add chain cache info for this chain */
+-      add_chain_cache(*handle, chain, 
+-                      destination+newc.head.next_offset, 
+-                      destination+newc.head.next_offset);
+-
+       return ret;
+ }
+@@ -1629,11 +1472,6 @@
+       memset(t->error, 0, sizeof(t->error));
+       strcpy(t->error, newname);
+-
+-      /* update chain cache */
+-      memset(c->name, 0, sizeof(c->name));
+-      strcpy(c->name, newname);
+-
+       set_changed(*handle);
+       return 1;
diff --git a/libiptc2/libip4tc.c b/libiptc2/libip4tc.c
new file mode 100644 (file)
index 0000000..e012c08
--- /dev/null
@@ -0,0 +1,507 @@
+/* Library which manipulates firewall rules.  Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+   COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libiptc.h"
+
+#define IP_VERSION     4
+#define IP_OFFSET      0x1FFF
+
+#define HOOK_PRE_ROUTING       NF_IP_PRE_ROUTING
+#define HOOK_LOCAL_IN          NF_IP_LOCAL_IN
+#define HOOK_FORWARD           NF_IP_FORWARD
+#define HOOK_LOCAL_OUT         NF_IP_LOCAL_OUT
+#define HOOK_POST_ROUTING      NF_IP_POST_ROUTING
+#ifdef NF_IP_DROPPING
+#define HOOK_DROPPING          NF_IP_DROPPING
+#endif
+
+#define STRUCT_ENTRY_TARGET    struct ipt_entry_target
+#define STRUCT_ENTRY           struct ipt_entry
+#define STRUCT_ENTRY_MATCH     struct ipt_entry_match
+#define STRUCT_GETINFO         struct ipt_getinfo
+#define STRUCT_GET_ENTRIES     struct ipt_get_entries
+#define STRUCT_COUNTERS                struct ipt_counters
+#define STRUCT_COUNTERS_INFO   struct ipt_counters_info
+#define STRUCT_STANDARD_TARGET struct ipt_standard_target
+#define STRUCT_REPLACE         struct ipt_replace
+
+#define STRUCT_TC_HANDLE       struct iptc_handle
+#define TC_HANDLE_T            iptc_handle_t
+
+#define ENTRY_ITERATE          IPT_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN       IPT_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN    IPT_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET             ipt_get_target
+
+#define ERROR_TARGET           IPT_ERROR_TARGET
+#define NUMHOOKS               NF_IP_NUMHOOKS
+
+#define IPT_CHAINLABEL         ipt_chainlabel
+
+#define TC_DUMP_ENTRIES                dump_entries
+#define TC_IS_CHAIN            iptc_is_chain
+#define TC_FIRST_CHAIN         iptc_first_chain
+#define TC_NEXT_CHAIN          iptc_next_chain
+#define TC_FIRST_RULE          iptc_first_rule
+#define TC_NEXT_RULE           iptc_next_rule
+#define TC_GET_TARGET          iptc_get_target
+#define TC_BUILTIN             iptc_builtin
+#define TC_GET_POLICY          iptc_get_policy
+#define TC_INSERT_ENTRY                iptc_insert_entry
+#define TC_REPLACE_ENTRY       iptc_replace_entry
+#define TC_APPEND_ENTRY                iptc_append_entry
+#define TC_DELETE_ENTRY                iptc_delete_entry
+#define TC_DELETE_NUM_ENTRY    iptc_delete_num_entry
+#define TC_CHECK_PACKET                iptc_check_packet
+#define TC_FLUSH_ENTRIES       iptc_flush_entries
+#define TC_ZERO_ENTRIES                iptc_zero_entries
+#define TC_READ_COUNTER                iptc_read_counter
+#define TC_ZERO_COUNTER                iptc_zero_counter
+#define TC_SET_COUNTER         iptc_set_counter
+#define TC_CREATE_CHAIN                iptc_create_chain
+#define TC_GET_REFERENCES      iptc_get_references
+#define TC_DELETE_CHAIN                iptc_delete_chain
+#define TC_RENAME_CHAIN                iptc_rename_chain
+#define TC_SET_POLICY          iptc_set_policy
+#define TC_GET_RAW_SOCKET      iptc_get_raw_socket
+#define TC_INIT                        iptc_init
+#define TC_FREE                        iptc_free
+#define TC_COMMIT              iptc_commit
+#define TC_STRERROR            iptc_strerror
+
+#define TC_AF                  AF_INET
+#define TC_IPPROTO             IPPROTO_IP
+
+#define SO_SET_REPLACE         IPT_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS    IPT_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO            IPT_SO_GET_INFO
+#define SO_GET_ENTRIES         IPT_SO_GET_ENTRIES
+#define SO_GET_VERSION         IPT_SO_GET_VERSION
+
+#define STANDARD_TARGET                IPT_STANDARD_TARGET
+#define LABEL_RETURN           IPTC_LABEL_RETURN
+#define LABEL_ACCEPT           IPTC_LABEL_ACCEPT
+#define LABEL_DROP             IPTC_LABEL_DROP
+#define LABEL_QUEUE            IPTC_LABEL_QUEUE
+
+#define ALIGN                  IPT_ALIGN
+#define RETURN                 IPT_RETURN
+
+#include "libiptc.c"
+
+#define IP_PARTS_NATIVE(n)                     \
+(unsigned int)((n)>>24)&0xFF,                  \
+(unsigned int)((n)>>16)&0xFF,                  \
+(unsigned int)((n)>>8)&0xFF,                   \
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+int
+dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle)
+{
+       size_t i;
+       STRUCT_ENTRY_TARGET *t;
+
+       printf("Entry %u (%lu):\n", entry2index(handle, e),
+              entry2offset(handle, e));
+       printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+              IP_PARTS(e->ip.src.s_addr),IP_PARTS(e->ip.smsk.s_addr));
+       printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+              IP_PARTS(e->ip.dst.s_addr),IP_PARTS(e->ip.dmsk.s_addr));
+       printf("Interface: `%s'/", e->ip.iniface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ip.iniface_mask[i] ? 'X' : '.');
+       printf("to `%s'/", e->ip.outiface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ip.outiface_mask[i] ? 'X' : '.');
+       printf("\nProtocol: %u\n", e->ip.proto);
+       printf("Flags: %02X\n", e->ip.flags);
+       printf("Invflags: %02X\n", e->ip.invflags);
+       printf("Counters: %llu packets, %llu bytes\n",
+              e->counters.pcnt, e->counters.bcnt);
+       printf("Cache: %08X ", e->nfcache);
+       if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
+       if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
+       if (e->nfcache & NFC_IP_SRC) printf("IP_SRC ");
+       if (e->nfcache & NFC_IP_DST) printf("IP_DST ");
+       if (e->nfcache & NFC_IP_IF_IN) printf("IP_IF_IN ");
+       if (e->nfcache & NFC_IP_IF_OUT) printf("IP_IF_OUT ");
+       if (e->nfcache & NFC_IP_TOS) printf("IP_TOS ");
+       if (e->nfcache & NFC_IP_PROTO) printf("IP_PROTO ");
+       if (e->nfcache & NFC_IP_OPTIONS) printf("IP_OPTIONS ");
+       if (e->nfcache & NFC_IP_TCPFLAGS) printf("IP_TCPFLAGS ");
+       if (e->nfcache & NFC_IP_SRC_PT) printf("IP_SRC_PT ");
+       if (e->nfcache & NFC_IP_DST_PT) printf("IP_DST_PT ");
+       if (e->nfcache & NFC_IP_PROTO_UNKNOWN) printf("IP_PROTO_UNKNOWN ");
+       printf("\n");
+
+       IPT_MATCH_ITERATE(e, print_match);
+
+       t = GET_TARGET(e);
+       printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+       if (strcmp(t->u.user.name, STANDARD_TARGET) == 0) {
+               int pos = *(int *)t->data;
+               if (pos < 0)
+                       printf("verdict=%s\n",
+                              pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+                              : pos == -NF_DROP-1 ? "NF_DROP"
+                              : pos == -NF_QUEUE-1 ? "NF_QUEUE"
+                              : pos == RETURN ? "RETURN"
+                              : "UNKNOWN");
+               else
+                       printf("verdict=%u\n", pos);
+       } else if (strcmp(t->u.user.name, IPT_ERROR_TARGET) == 0)
+               printf("error=`%s'\n", t->data);
+
+       printf("\n");
+       return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask)
+{
+       unsigned int i;
+       STRUCT_ENTRY_TARGET *ta, *tb;
+       unsigned char *mptr;
+
+       /* Always compare head structures: ignore mask here. */
+       if (a->ip.src.s_addr != b->ip.src.s_addr
+           || a->ip.dst.s_addr != b->ip.dst.s_addr
+           || a->ip.smsk.s_addr != b->ip.smsk.s_addr
+           || a->ip.dmsk.s_addr != b->ip.dmsk.s_addr
+           || a->ip.proto != b->ip.proto
+           || a->ip.flags != b->ip.flags
+           || a->ip.invflags != b->ip.invflags)
+               return 0;
+
+       for (i = 0; i < IFNAMSIZ; i++) {
+               if (a->ip.iniface_mask[i] != b->ip.iniface_mask[i])
+                       return 0;
+               if ((a->ip.iniface[i] & a->ip.iniface_mask[i])
+                   != (b->ip.iniface[i] & b->ip.iniface_mask[i]))
+                       return 0;
+               if (a->ip.outiface_mask[i] != b->ip.outiface_mask[i])
+                       return 0;
+               if ((a->ip.outiface[i] & a->ip.outiface_mask[i])
+                   != (b->ip.outiface[i] & b->ip.outiface_mask[i]))
+                       return 0;
+       }
+
+       if (a->nfcache != b->nfcache
+           || a->target_offset != b->target_offset
+           || a->next_offset != b->next_offset)
+               return 0;
+
+       mptr = matchmask + sizeof(STRUCT_ENTRY);
+       if (IPT_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+               return 0;
+
+       ta = GET_TARGET((STRUCT_ENTRY *)a);
+       tb = GET_TARGET((STRUCT_ENTRY *)b);
+       if (ta->u.target_size != tb->u.target_size)
+               return 0;
+       if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
+               return 0;
+
+       mptr += sizeof(*ta);
+       if (target_different(ta->data, tb->data,
+                            ta->u.target_size - sizeof(*ta), mptr))
+               return 0;
+
+       return 1;
+}
+
+/***************************** DEBUGGING ********************************/
+static inline int
+unconditional(const struct ipt_ip *ip)
+{
+       unsigned int i;
+
+       for (i = 0; i < sizeof(*ip)/sizeof(u_int32_t); i++)
+               if (((u_int32_t *)ip)[i])
+                       return 0;
+
+       return 1;
+}
+
+static inline int
+check_match(const STRUCT_ENTRY_MATCH *m, unsigned int *off)
+{
+       assert(m->u.match_size >= sizeof(STRUCT_ENTRY_MATCH));
+       assert(ALIGN(m->u.match_size) == m->u.match_size);
+
+       (*off) += m->u.match_size;
+       return 0;
+}
+
+static inline int
+check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off,
+           unsigned int user_offset, int *was_return,
+           TC_HANDLE_T h)
+{
+       unsigned int toff;
+       STRUCT_STANDARD_TARGET *t;
+
+       assert(e->target_offset >= sizeof(STRUCT_ENTRY));
+       assert(e->next_offset >= e->target_offset
+              + sizeof(STRUCT_ENTRY_TARGET));
+       toff = sizeof(STRUCT_ENTRY);
+       IPT_MATCH_ITERATE(e, check_match, &toff);
+
+       assert(toff == e->target_offset);
+
+       t = (STRUCT_STANDARD_TARGET *)
+               GET_TARGET((STRUCT_ENTRY *)e);
+       /* next_offset will have to be multiple of entry alignment. */
+       assert(e->next_offset == ALIGN(e->next_offset));
+       assert(e->target_offset == ALIGN(e->target_offset));
+       assert(t->target.u.target_size == ALIGN(t->target.u.target_size));
+       assert(!TC_IS_CHAIN(t->target.u.user.name, h));
+
+       if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0) {
+               assert(t->target.u.target_size
+                      == ALIGN(sizeof(STRUCT_STANDARD_TARGET)));
+
+               assert(t->verdict == -NF_DROP-1
+                      || t->verdict == -NF_ACCEPT-1
+                      || t->verdict == RETURN
+                      || t->verdict < (int)h->entries.size);
+
+               if (t->verdict >= 0) {
+                       STRUCT_ENTRY *te = get_entry(h, t->verdict);
+                       int idx;
+
+                       idx = entry2index(h, te);
+                       assert(strcmp(GET_TARGET(te)->u.user.name,
+                                     IPT_ERROR_TARGET)
+                              != 0);
+                       assert(te != e);
+
+                       /* Prior node must be error node, or this node. */
+                       assert(t->verdict == entry2offset(h, e)+e->next_offset
+                              || strcmp(GET_TARGET(index2entry(h, idx-1))
+                                        ->u.user.name, IPT_ERROR_TARGET)
+                              == 0);
+               }
+
+               if (t->verdict == RETURN
+                   && unconditional(&e->ip)
+                   && e->target_offset == sizeof(*e))
+                       *was_return = 1;
+               else
+                       *was_return = 0;
+       } else if (strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0) {
+               assert(t->target.u.target_size
+                      == ALIGN(sizeof(struct ipt_error_target)));
+
+               /* If this is in user area, previous must have been return */
+               if (*off > user_offset)
+                       assert(*was_return);
+
+               *was_return = 0;
+       }
+       else *was_return = 0;
+
+       if (*off == user_offset)
+               assert(strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0);
+
+       (*off) += e->next_offset;
+       (*i)++;
+       return 0;
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+       unsigned int i, n;
+       unsigned int user_offset; /* Offset of first user chain */
+       int was_return;
+
+       assert(h->changed == 0 || h->changed == 1);
+       if (strcmp(h->info.name, "filter") == 0) {
+               assert(h->info.valid_hooks
+                      == (1 << NF_IP_LOCAL_IN
+                          | 1 << NF_IP_FORWARD
+                          | 1 << NF_IP_LOCAL_OUT));
+
+               /* Hooks should be first three */
+               assert(h->info.hook_entry[NF_IP_LOCAL_IN] == 0);
+
+               n = get_chain_end(h, 0);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+
+               n = get_chain_end(h, n);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+
+               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+       } else if (strcmp(h->info.name, "nat") == 0) {
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_POST_ROUTING
+                           | 1 << NF_IP_LOCAL_OUT)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_LOCAL_IN
+                           | 1 << NF_IP_POST_ROUTING
+                           | 1 << NF_IP_LOCAL_OUT)));
+
+               assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+               n = get_chain_end(h, n);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+                       user_offset = h->info.hook_entry[NF_IP_LOCAL_IN];
+               }
+
+       } else if (strcmp(h->info.name, "mangle") == 0) {
+               /* This code is getting ugly because linux < 2.4.18-pre6 had
+                * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+                * */
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_LOCAL_OUT)) || 
+                      (h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_LOCAL_IN
+                           | 1 << NF_IP_FORWARD
+                           | 1 << NF_IP_LOCAL_OUT
+                           | 1 << NF_IP_POST_ROUTING)));
+
+               /* Hooks should be first five */
+               assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               if (h->info.valid_hooks & (1 << NF_IP_FORWARD)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP_POST_ROUTING)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+                       user_offset = h->info.hook_entry[NF_IP_POST_ROUTING];
+               }
+
+#ifdef NF_IP_DROPPING
+       } else if (strcmp(h->info.name, "drop") == 0) {
+               assert(h->info.valid_hooks == (1 << NF_IP_DROPPING));
+
+               /* Hook should be first */
+               assert(h->info.hook_entry[NF_IP_DROPPING] == 0);
+               user_offset = 0;
+#endif
+       } else {
+               fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+               abort();
+       }
+
+       /* User chain == end of last builtin + policy entry */
+       user_offset = get_chain_end(h, user_offset);
+       user_offset += get_entry(h, user_offset)->next_offset;
+
+       /* Overflows should be end of entry chains, and unconditional
+           policy nodes. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               STRUCT_ENTRY *e;
+               STRUCT_STANDARD_TARGET *t;
+
+               if (!(h->info.valid_hooks & (1 << i)))
+                       continue;
+               assert(h->info.underflow[i]
+                      == get_chain_end(h, h->info.hook_entry[i]));
+
+               e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+               assert(unconditional(&e->ip));
+               assert(e->target_offset == sizeof(*e));
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+               assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+               assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+               assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+               assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+               /* Hooks and underflows must be valid entries */
+               entry2index(h, get_entry(h, h->info.hook_entry[i]));
+               entry2index(h, get_entry(h, h->info.underflow[i]));
+       }
+
+       assert(h->info.size
+              >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+                                        +sizeof(STRUCT_STANDARD_TARGET)));
+
+       assert(h->entries.size
+              >= (h->new_number
+                  * (sizeof(STRUCT_ENTRY)
+                     + sizeof(STRUCT_STANDARD_TARGET))));
+       assert(strcmp(h->info.name, h->entries.name) == 0);
+
+       i = 0; n = 0;
+       was_return = 0;
+       /* Check all the entries. */
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     check_entry, &i, &n, user_offset, &was_return, h);
+
+       assert(i == h->new_number);
+       assert(n == h->entries.size);
+
+       /* Final entry must be error node */
+       assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+                     ->u.user.name,
+                     ERROR_TARGET) == 0);
+}
+#endif /*IPTC_DEBUG*/
diff --git a/libiptc2/libip6tc.c b/libiptc2/libip6tc.c
new file mode 100644 (file)
index 0000000..9a78a5a
--- /dev/null
@@ -0,0 +1,449 @@
+/* Library which manipulates firewall rules.  Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+   COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libip6tc.h"
+
+#define HOOK_PRE_ROUTING       NF_IP6_PRE_ROUTING
+#define HOOK_LOCAL_IN          NF_IP6_LOCAL_IN
+#define HOOK_FORWARD           NF_IP6_FORWARD
+#define HOOK_LOCAL_OUT         NF_IP6_LOCAL_OUT
+#define HOOK_POST_ROUTING      NF_IP6_POST_ROUTING
+
+#define STRUCT_ENTRY_TARGET    struct ip6t_entry_target
+#define STRUCT_ENTRY           struct ip6t_entry
+#define STRUCT_ENTRY_MATCH     struct ip6t_entry_match
+#define STRUCT_GETINFO         struct ip6t_getinfo
+#define STRUCT_GET_ENTRIES     struct ip6t_get_entries
+#define STRUCT_COUNTERS                struct ip6t_counters
+#define STRUCT_COUNTERS_INFO   struct ip6t_counters_info
+#define STRUCT_STANDARD_TARGET struct ip6t_standard_target
+#define STRUCT_REPLACE         struct ip6t_replace
+
+#define STRUCT_TC_HANDLE       struct ip6tc_handle
+#define TC_HANDLE_T            ip6tc_handle_t
+
+#define ENTRY_ITERATE          IP6T_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN       IP6T_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN    IP6T_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET             ip6t_get_target
+
+#define ERROR_TARGET           IP6T_ERROR_TARGET
+#define NUMHOOKS               NF_IP6_NUMHOOKS
+
+#define IPT_CHAINLABEL         ip6t_chainlabel
+
+#define TC_DUMP_ENTRIES                dump_entries6
+#define TC_IS_CHAIN            ip6tc_is_chain
+#define TC_FIRST_CHAIN         ip6tc_first_chain
+#define TC_NEXT_CHAIN          ip6tc_next_chain
+#define TC_FIRST_RULE          ip6tc_first_rule
+#define TC_NEXT_RULE           ip6tc_next_rule
+#define TC_GET_TARGET          ip6tc_get_target
+#define TC_BUILTIN             ip6tc_builtin
+#define TC_GET_POLICY          ip6tc_get_policy
+#define TC_INSERT_ENTRY                ip6tc_insert_entry
+#define TC_REPLACE_ENTRY       ip6tc_replace_entry
+#define TC_APPEND_ENTRY                ip6tc_append_entry
+#define TC_DELETE_ENTRY                ip6tc_delete_entry
+#define TC_DELETE_NUM_ENTRY    ip6tc_delete_num_entry
+#define TC_CHECK_PACKET                ip6tc_check_packet
+#define TC_FLUSH_ENTRIES       ip6tc_flush_entries
+#define TC_ZERO_ENTRIES                ip6tc_zero_entries
+#define TC_ZERO_COUNTER                ip6tc_zero_counter
+#define TC_READ_COUNTER                ip6tc_read_counter
+#define TC_SET_COUNTER         ip6tc_set_counter
+#define TC_CREATE_CHAIN                ip6tc_create_chain
+#define TC_GET_REFERENCES      ip6tc_get_references
+#define TC_DELETE_CHAIN                ip6tc_delete_chain
+#define TC_RENAME_CHAIN                ip6tc_rename_chain
+#define TC_SET_POLICY          ip6tc_set_policy
+#define TC_GET_RAW_SOCKET      ip6tc_get_raw_socket
+#define TC_INIT                        ip6tc_init
+#define TC_FREE                        ip6tc_free
+#define TC_COMMIT              ip6tc_commit
+#define TC_STRERROR            ip6tc_strerror
+
+#define TC_AF                  AF_INET6
+#define TC_IPPROTO             IPPROTO_IPV6
+
+#define SO_SET_REPLACE         IP6T_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS    IP6T_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO            IP6T_SO_GET_INFO
+#define SO_GET_ENTRIES         IP6T_SO_GET_ENTRIES
+#define SO_GET_VERSION         IP6T_SO_GET_VERSION
+
+#define STANDARD_TARGET                IP6T_STANDARD_TARGET
+#define LABEL_RETURN           IP6TC_LABEL_RETURN
+#define LABEL_ACCEPT           IP6TC_LABEL_ACCEPT
+#define LABEL_DROP             IP6TC_LABEL_DROP
+#define LABEL_QUEUE            IP6TC_LABEL_QUEUE
+
+#define ALIGN                  IP6T_ALIGN
+#define RETURN                 IP6T_RETURN
+
+#include "libiptc.c"
+
+#define BIT6(a, l) \
+ ((ntohl(a->in6_u.u6_addr32[(l) / 32]) >> (31 - ((l) & 31))) & 1)
+
+int
+ipv6_prefix_length(const struct in6_addr *a)
+{
+       int l, i;
+       for (l = 0; l < 128; l++) {
+               if (BIT6(a, l) == 0)
+                       break;
+       }
+       for (i = l + 1; i < 128; i++) {
+               if (BIT6(a, i) == 1)
+                       return -1;
+       }
+       return l;
+}
+
+static int
+dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
+{
+       size_t i;
+       char buf[40];
+       int len;
+       struct ip6t_entry_target *t;
+       
+       printf("Entry %u (%lu):\n", entry2index(handle, e),
+              entry2offset(handle, e));
+       puts("SRC IP: ");
+       inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof buf);
+       puts(buf);
+       putchar('/');
+       len = ipv6_prefix_length(&e->ipv6.smsk);
+       if (len != -1)
+               printf("%d", len);
+       else {
+               inet_ntop(AF_INET6, &e->ipv6.smsk, buf, sizeof buf);
+               puts(buf);
+       }
+       putchar('\n');
+       
+       puts("DST IP: ");
+       inet_ntop(AF_INET6, &e->ipv6.dst, buf, sizeof buf);
+       puts(buf);
+       putchar('/');
+       len = ipv6_prefix_length(&e->ipv6.dmsk);
+       if (len != -1)
+               printf("%d", len);
+       else {
+               inet_ntop(AF_INET6, &e->ipv6.dmsk, buf, sizeof buf);
+               puts(buf);
+       }
+       putchar('\n');
+       
+       printf("Interface: `%s'/", e->ipv6.iniface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ipv6.iniface_mask[i] ? 'X' : '.');
+       printf("to `%s'/", e->ipv6.outiface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ipv6.outiface_mask[i] ? 'X' : '.');
+       printf("\nProtocol: %u\n", e->ipv6.proto);
+       if (e->ipv6.flags & IP6T_F_TOS)
+               printf("TOS: %u\n", e->ipv6.tos);
+       printf("Flags: %02X\n", e->ipv6.flags);
+       printf("Invflags: %02X\n", e->ipv6.invflags);
+       printf("Counters: %llu packets, %llu bytes\n",
+              e->counters.pcnt, e->counters.bcnt);
+       printf("Cache: %08X ", e->nfcache);
+       if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
+       if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
+       if (e->nfcache & NFC_IP6_SRC) printf("IP6_SRC ");
+       if (e->nfcache & NFC_IP6_DST) printf("IP6_DST ");
+       if (e->nfcache & NFC_IP6_IF_IN) printf("IP6_IF_IN ");
+       if (e->nfcache & NFC_IP6_IF_OUT) printf("IP6_IF_OUT ");
+       if (e->nfcache & NFC_IP6_TOS) printf("IP6_TOS ");
+       if (e->nfcache & NFC_IP6_PROTO) printf("IP6_PROTO ");
+       if (e->nfcache & NFC_IP6_OPTIONS) printf("IP6_OPTIONS ");
+       if (e->nfcache & NFC_IP6_TCPFLAGS) printf("IP6_TCPFLAGS ");
+       if (e->nfcache & NFC_IP6_SRC_PT) printf("IP6_SRC_PT ");
+       if (e->nfcache & NFC_IP6_DST_PT) printf("IP6_DST_PT ");
+       if (e->nfcache & NFC_IP6_PROTO_UNKNOWN) printf("IP6_PROTO_UNKNOWN ");
+       printf("\n");
+       
+       IP6T_MATCH_ITERATE(e, print_match);
+
+       t = ip6t_get_target(e);
+       printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+       if (strcmp(t->u.user.name, IP6T_STANDARD_TARGET) == 0) {
+               int pos = *(int *)t->data;
+               if (pos < 0)
+                       printf("verdict=%s\n",
+                              pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+                              : pos == -NF_DROP-1 ? "NF_DROP"
+                              : pos == IP6T_RETURN ? "RETURN"
+                              : "UNKNOWN");
+               else
+                       printf("verdict=%u\n", pos);
+       } else if (strcmp(t->u.user.name, IP6T_ERROR_TARGET) == 0)
+               printf("error=`%s'\n", t->data);
+
+       printf("\n");
+       return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b,
+       unsigned char *matchmask)
+{
+       unsigned int i;
+       STRUCT_ENTRY_TARGET *ta, *tb;
+       unsigned char *mptr;
+
+       /* Always compare head structures: ignore mask here. */
+       if (memcmp(&a->ipv6.src, &b->ipv6.src, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.dst, &b->ipv6.dst, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.smsk, &b->ipv6.smsk, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.dmsk, &b->ipv6.dmsk, sizeof(struct in6_addr))
+           || a->ipv6.proto != b->ipv6.proto
+           || a->ipv6.tos != b->ipv6.tos
+           || a->ipv6.flags != b->ipv6.flags
+           || a->ipv6.invflags != b->ipv6.invflags)
+               return 0;
+
+       for (i = 0; i < IFNAMSIZ; i++) {
+               if (a->ipv6.iniface_mask[i] != b->ipv6.iniface_mask[i])
+                       return 0;
+               if ((a->ipv6.iniface[i] & a->ipv6.iniface_mask[i])
+                   != (b->ipv6.iniface[i] & b->ipv6.iniface_mask[i]))
+                       return 0;
+               if (a->ipv6.outiface_mask[i] != b->ipv6.outiface_mask[i])
+                       return 0;
+               if ((a->ipv6.outiface[i] & a->ipv6.outiface_mask[i])
+                   != (b->ipv6.outiface[i] & b->ipv6.outiface_mask[i]))
+                       return 0;
+       }
+
+       if (a->nfcache != b->nfcache
+           || a->target_offset != b->target_offset
+           || a->next_offset != b->next_offset)
+               return 0;
+
+       mptr = matchmask + sizeof(STRUCT_ENTRY);
+       if (IP6T_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+               return 0;
+
+       ta = GET_TARGET((STRUCT_ENTRY *)a);
+       tb = GET_TARGET((STRUCT_ENTRY *)b);
+       if (ta->u.target_size != tb->u.target_size)
+               return 0;
+       if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
+               return 0;
+       mptr += sizeof(*ta);
+
+       if (target_different(ta->data, tb->data,
+                            ta->u.target_size - sizeof(*ta), mptr))
+               return 0;
+
+       return 1;
+}
+
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ip6t_ip6 *ipv6)
+{
+       unsigned int i;
+
+       for (i = 0; i < sizeof(*ipv6); i++)
+               if (((char *)ipv6)[i])
+                       break;
+
+       return (i == sizeof(*ipv6));
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+       unsigned int i, n;
+       unsigned int user_offset; /* Offset of first user chain */
+       int was_return;
+
+       assert(h->changed == 0 || h->changed == 1);
+       if (strcmp(h->info.name, "filter") == 0) {
+               assert(h->info.valid_hooks
+                      == (1 << NF_IP6_LOCAL_IN
+                          | 1 << NF_IP6_FORWARD
+                          | 1 << NF_IP6_LOCAL_OUT));
+
+               /* Hooks should be first three */
+               assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == 0);
+
+               n = get_chain_end(h, 0);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+
+               n = get_chain_end(h, n);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+       } else if (strcmp(h->info.name, "nat") == 0) {
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_IN
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)));
+
+               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+               n = get_chain_end(h, n);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+                       user_offset = h->info.hook_entry[NF_IP6_LOCAL_IN];
+               }
+
+       } else if (strcmp(h->info.name, "mangle") == 0) {
+               /* This code is getting ugly because linux < 2.4.18-pre6 had
+                * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+                * */
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_OUT)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_IN
+                           | 1 << NF_IP6_FORWARD
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)));
+
+               /* Hooks should be first five */
+               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               if (h->info.valid_hooks & (1 << NF_IP6_FORWARD)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP6_POST_ROUTING)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+                       user_offset = h->info.hook_entry[NF_IP6_POST_ROUTING];
+               }
+       } else {
+                fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+               abort();
+       }
+
+       /* User chain == end of last builtin + policy entry */
+       user_offset = get_chain_end(h, user_offset);
+       user_offset += get_entry(h, user_offset)->next_offset;
+
+       /* Overflows should be end of entry chains, and unconditional
+           policy nodes. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               STRUCT_ENTRY *e;
+               STRUCT_STANDARD_TARGET *t;
+
+               if (!(h->info.valid_hooks & (1 << i)))
+                       continue;
+               assert(h->info.underflow[i]
+                      == get_chain_end(h, h->info.hook_entry[i]));
+
+               e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+               assert(unconditional(&e->ipv6));
+               assert(e->target_offset == sizeof(*e));
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+               printf("target_size=%u, align=%u\n",
+                       t->target.u.target_size, ALIGN(sizeof(*t)));
+               assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+               assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+               assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+               assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+               /* Hooks and underflows must be valid entries */
+               entry2index(h, get_entry(h, h->info.hook_entry[i]));
+               entry2index(h, get_entry(h, h->info.underflow[i]));
+       }
+
+       assert(h->info.size
+              >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+                                        +sizeof(STRUCT_STANDARD_TARGET)));
+
+       assert(h->entries.size
+              >= (h->new_number
+                  * (sizeof(STRUCT_ENTRY)
+                     + sizeof(STRUCT_STANDARD_TARGET))));
+       assert(strcmp(h->info.name, h->entries.name) == 0);
+
+       i = 0; n = 0;
+       was_return = 0;
+
+#if 0
+       /* Check all the entries. */
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     check_entry, &i, &n, user_offset, &was_return, h);
+
+       assert(i == h->new_number);
+       assert(n == h->entries.size);
+
+       /* Final entry must be error node */
+       assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+                     ->u.user.name,
+                     ERROR_TARGET) == 0);
+#endif
+}
+#endif /*IPTC_DEBUG*/
diff --git a/libiptc2/libiptc.c b/libiptc2/libiptc.c
new file mode 100644 (file)
index 0000000..3c3c76a
--- /dev/null
@@ -0,0 +1,1830 @@
+/* Library which manipulates firewall rules.  Version $Revision: 1.41 $ */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+ * COPYING for details). 
+ * (C) 2000-2003 by the Netfilter Core Team <coreteam@netfilter.org>
+ *
+ * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
+ *     - Reimplementation of chain cache to use offsets instead of entries
+ * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
+ *     - speed optimization, sponsored by Astaro AG (http://www.astaro.com/)
+ *       don't rebuild the chain cache after every operation, instead fix it
+ *       up after a ruleset change.  
+ * 2003-Jun-30: Harald Welte <laforge@netfilter.org>:
+ *     - reimplementation from scratch. *sigh*.  I hope nobody has to touch 
+ *       this code ever again.
+ */
+#include "linux_listhelp.h"
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/lib/iptables"
+#endif
+
+static int sockfd = -1;
+static void *iptc_fn = NULL;
+
+static const char *hooknames[]
+= { [HOOK_PRE_ROUTING]  "PREROUTING",
+    [HOOK_LOCAL_IN]     "INPUT",
+    [HOOK_FORWARD]      "FORWARD",
+    [HOOK_LOCAL_OUT]    "OUTPUT",
+    [HOOK_POST_ROUTING] "POSTROUTING",
+#ifdef HOOK_DROPPING
+    [HOOK_DROPPING]    "DROPPING"
+#endif
+};
+
+struct counter_map
+{
+       enum {
+               COUNTER_MAP_NOMAP,
+               COUNTER_MAP_NORMAL_MAP,
+               COUNTER_MAP_ZEROED,
+               COUNTER_MAP_SET
+       } maptype;
+       unsigned int mappos;
+};
+
+/* Convenience structures */
+struct ipt_error_target
+{
+       STRUCT_ENTRY_TARGET t;
+       char error[TABLE_MAXNAMELEN];
+};
+
+struct rule_head
+{
+       struct list_head list;          /* list of rules in chain */
+       
+       struct chain_head *chain;       /* we're part of this chain */
+
+       struct chain_head *jumpto;      /* target of this rule, in case
+                                          it is a jump rule */
+
+       struct counter_map counter_map;
+
+       unsigned int size;              /* size of rule */
+       STRUCT_ENTRY *entry_blob;       /* pointer to entry in blob */
+       STRUCT_ENTRY entry[0];
+};
+
+struct chain_head
+{
+       struct list_head list;
+
+       char name[TABLE_MAXNAMELEN];
+       unsigned int hooknum;
+       struct list_head rules;
+       struct rule_head *firstrule;    /* first (ERROR) rule */
+       struct rule_head *lastrule;     /* last (RETURN) rule */
+};
+
+STRUCT_TC_HANDLE
+{
+       /* Have changes been made? */
+       int changed;
+
+       /* linked list of chains in this table */
+       struct list_head chains;
+       
+       /* current position of first_chain() / next_chain() */
+       struct chain_head *chain_iterator_cur;
+
+       /* current position of first_rule() / next_rule() */
+       struct rule_head *rule_iterator_cur;
+
+       /* the structure we receive from getsockopt() */
+       STRUCT_GETINFO info;
+
+       /* Array of hook names */
+       const char **hooknames;
+#if 0
+       /* Size in here reflects original state. */
+
+
+       /* Cached position of chain heads (NULL = no cache). */
+       unsigned int cache_num_chains;
+       unsigned int cache_num_builtins;
+       struct chain_cache *cache_chain_heads;
+
+       /* Chain iterator: current chain cache entry. */
+       struct chain_cache *cache_chain_iteration;
+
+       /* Rule iterator: terminal rule */
+       STRUCT_ENTRY *cache_rule_end;
+
+       /* Number in here reflects current state. */
+       unsigned int new_number;
+#endif
+       STRUCT_GET_ENTRIES entries;
+};
+
+static void
+set_changed(TC_HANDLE_T h)
+{
+       h->changed = 1;
+}
+
+#ifdef IPTC_DEBUG
+static void do_check(TC_HANDLE_T h, unsigned int line);
+#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
+#else
+#define CHECK(h)
+#endif
+
+static struct rule_head *ruleh_alloc(unsigned int size)
+{
+       struct rule_head *ruleh = malloc(sizeof(*ruleh)+size);
+       if (!ruleh)
+               return NULL;
+       
+       memset(ruleh, 0, sizeof(*ruleh)+size);
+       ruleh->size = size;
+
+       return ruleh;
+}
+
+static void ruleh_free(struct rule_head *ruleh)
+{
+       list_del(&ruleh->list);
+       free(ruleh);
+}
+
+static struct chain_head *chainh_alloc(TC_HANDLE_T h, const char *name)
+{
+       struct chain_head *chainh = malloc(sizeof(*chainh));
+       if (!chainh)
+               return NULL;
+
+       memset(chainh, 0, sizeof(*chainh));
+       strncpy(chainh->name, name, sizeof(&chainh->name));
+       list_append(&chainh->list, &h->chains);
+
+       return chainh;
+}
+
+static void
+chainh_clean(struct chain_head *chainh)
+{
+       /* FIXME */
+       struct list_head *cur_item, *item2;
+
+       list_for_each_safe(cur_item, item2, &chainh->rules) {
+               struct rule_head *ruleh = list_entry(cur_item, 
+                                                    struct rule_head,
+                                                   list);
+               ruleh_free(ruleh);
+       }
+}
+
+static void 
+chainh_free(struct chain_head *chainh)
+{
+       chainh_clean(chainh);
+       list_del(&chainh->list);
+}
+
+static struct chain_head *
+chainh_find(TC_HANDLE_T h, const IPT_CHAINLABEL name)
+{
+       struct list_head *cur;
+
+       list_for_each(cur, &h->chains) {
+               struct chain_head *ch = list_entry(cur, struct chain_head, 
+                                                  list);
+               if (!strcmp(name, ch->name))
+                       return ch;
+       }
+       return NULL;
+}
+
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+find_label(const char *name, TC_HANDLE_T handle)
+{
+       return chainh_find(handle, name);
+}
+
+
+/* 
+ * functions that directly operate on the blob 
+ */
+
+static inline unsigned long
+entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+       return (void *)e - (void *)h->entries.entrytable;
+}
+
+static inline STRUCT_ENTRY *
+get_entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
+}
+
+/* needed by entry2index */
+static inline int
+get_number(const STRUCT_ENTRY *i,
+          const STRUCT_ENTRY *seek,
+          unsigned int *pos)
+{
+       if (i == seek)
+               return 1;
+       (*pos)++;
+       return 0;
+}
+
+static unsigned int
+entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
+{
+       unsigned int pos = 0;
+
+       if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                         get_number, seek, &pos) == 0) {
+               fprintf(stderr, "ERROR: offset %i not an entry!\n",
+                       (char *)seek - (char *)h->entries.entrytable);
+               abort();
+       }
+       return pos;
+}
+
+static inline int
+get_entry_n(STRUCT_ENTRY *i,
+           unsigned int number,
+           unsigned int *pos,
+           STRUCT_ENTRY **pe)
+{
+       if (*pos == number) {
+               *pe = i;
+               return 1;
+       }
+       (*pos)++;
+       return 0;
+}
+
+static STRUCT_ENTRY *
+index2entry(TC_HANDLE_T h, unsigned int index)
+{
+       unsigned int pos = 0;
+       STRUCT_ENTRY *ret = NULL;
+
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     get_entry_n, index, &pos, &ret);
+
+       return ret;
+}
+
+static inline unsigned long
+index2offset(TC_HANDLE_T h, unsigned int index)
+{
+       return entry2offset(h, index2entry(h, index));
+}
+
+static char *
+get_errorlabel(TC_HANDLE_T h, unsigned int offset)
+{
+       STRUCT_ENTRY *e;
+
+       e = get_entry(h, offset);
+       if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) {
+               fprintf(stderr, "ERROR: offset %u not an error node!\n",
+                       offset);
+               abort();
+       }
+
+       return (char *)GET_TARGET(e)->data;
+}
+
+#if 0
+static inline STRUCT_ENTRY *
+offset2entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *) ((void *)h->entries.entrytable+offset);
+}
+
+static inline unsigned int
+offset2index(const TC_HANDLE_T h, unsigned int offset)
+{
+       return entry2index(h, offset2entry(h, offset));
+}
+
+
+#endif
+
+/* Allocate handle of given size */
+static TC_HANDLE_T
+alloc_tc_handle(const char *tablename, unsigned int size, 
+               unsigned int num_rules)
+{
+       size_t len;
+       TC_HANDLE_T h;
+
+       len = sizeof(STRUCT_TC_HANDLE)
+               + size
+               + num_rules * sizeof(struct counter_map);
+
+       if ((h = malloc(len)) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       h->changed = 0;
+
+       strcpy(h->info.name, tablename);
+       strcpy(h->entries.name, tablename);
+       INIT_LIST_HEAD(&h->chains);
+
+       return h;
+}
+
+/* get the name of the chain that we jump to */
+static char *
+parse_jumptarget(const STRUCT_ENTRY *e, TC_HANDLE_T h)
+{
+       STRUCT_ENTRY *jumpto;
+       int spos, labelidx;
+
+       if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0) {
+               /* called for non-standard target */
+               return "__FIXME";
+       }
+       /* Standard target: evaluate */
+       spos = *(int *)GET_TARGET(e)->data;
+       if (spos < 0) {
+               return "__FIXME";
+       }
+
+       jumpto = get_entry(h, spos);
+
+       /* Fall through rule */
+       if (jumpto == (void *)e + e->next_offset)
+               return "";
+
+       /* Must point to head of a chain: ie. after error rule */
+       /* FIXME: this needs to deal with internal jump targets */
+       labelidx = entry2index(h, jumpto) - 1;
+       return get_errorlabel(h, index2offset(h, labelidx));
+}
+
+/* parser functions */
+
+struct rule_head *
+append_entrycopy(const STRUCT_ENTRY *e, struct rule_head *prev)
+{
+       struct rule_head *ruleh = ruleh_alloc(e->next_offset);
+       if (!ruleh)
+               return NULL;
+       
+       memcpy(&ruleh->entry, e, e->next_offset);
+       ruleh->chain = prev->chain;
+       ruleh->entry_blob = e;
+       list_append(&ruleh->list, &prev->list);
+
+       return ruleh;
+}
+
+/* have to return 0 on success, bcf ENTRY_ITERATE */
+static inline int 
+parse_entry(const STRUCT_ENTRY *e, TC_HANDLE_T h, struct chain_head **curchain)
+{
+       int i;
+       union tgt_u {
+               STRUCT_ENTRY_TARGET ent;
+               STRUCT_STANDARD_TARGET std;
+               struct ipt_error_target err;
+       } *tgt;
+
+       struct rule_head *lastrule = list_entry((*curchain)->rules.prev,
+                                                struct rule_head, list);
+       struct rule_head *newrule;
+
+       tgt = (union tgt_u *) GET_TARGET(e);
+
+       if (e->target_offset == sizeof(STRUCT_ENTRY)
+           && (strcmp(tgt->ent.u.user.name, IPT_STANDARD_TARGET) == 0)) {
+               /* jump to somewhere else */
+               char *targname;
+               struct chain_head *chainh;
+
+               newrule = append_entrycopy(e, lastrule);
+
+               targname = parse_jumptarget(e, h);
+               if (!(chainh = find_label(targname, h))) {
+                       chainh = chainh_alloc(h, targname);
+               }
+               if (!chainh) {
+                       errno = ENOMEM;
+                       return 1;
+               }
+               newrule->jumpto = chainh;
+
+       } else if (e->target_offset == sizeof(STRUCT_ENTRY)
+                  && e->next_offset == sizeof(STRUCT_ENTRY)
+                                       + ALIGN(sizeof(struct ipt_error_target))
+                  && !strcmp(tgt->ent.u.user.name, ERROR_TARGET)) {
+               /* chain head */
+               *curchain = chainh_find(h, tgt->err.error);
+               if (!(*curchain)) {
+                       *curchain = chainh_alloc(h, tgt->err.error);
+                       /* FIXME: error handling */
+               }
+               newrule = append_entrycopy(e, lastrule);
+               (*curchain)->firstrule = newrule;
+
+       } else if (e->target_offset == sizeof(STRUCT_ENTRY)
+                  && e->next_offset == sizeof(STRUCT_ENTRY)
+                                       + ALIGN(sizeof(STRUCT_STANDARD_TARGET))
+                  && tgt->std.verdict == RETURN) {
+               /* chain end */
+               newrule = append_entrycopy(e, lastrule);
+               (*curchain)->lastrule = newrule;
+               *curchain = NULL;
+       } else {
+               /* normal rule */
+               newrule = append_entrycopy(e, lastrule);
+       }
+
+       /* create counter map entry */
+       newrule->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
+       newrule->counter_map.mappos = entry2index(h, e);
+
+       /* iterate over hook_entries, needed to connect builtin
+        * chains with hook numbers */
+       for (i = 0; i < NUMHOOKS; i++) {
+               if (!(h->info.valid_hooks & (1 << i)))
+                       continue;
+               if (h->info.hook_entry[i] == entry2offset(h, e)) {
+                       /* found hook entry point */
+                       if (*curchain)
+                               (*curchain)->hooknum = i;
+               }
+               if (h->info.underflow[i] == entry2offset(h, e)) {
+                       /* found underflow point */
+               }
+       }
+
+       return 0;
+}
+
+static int parse_ruleset(TC_HANDLE_T h)
+{
+       struct chain_head *curchain;
+       
+       /* iterate over ruleset; create linked list of rule_head/chain_head */
+       if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size, 
+                     parse_entry, h, &curchain)) {
+               /* some error happened while iterating */
+               return 0;
+       }
+
+       return 1;
+}
+
+TC_HANDLE_T
+TC_INIT(const char *tablename)
+{
+       TC_HANDLE_T h;
+       STRUCT_GETINFO info;
+       int tmp;
+       socklen_t s;
+
+       iptc_fn = TC_INIT;
+
+       if (sockfd != -1) {
+               close(sockfd);
+               sockfd = -1;
+       }
+
+       if (strlen(tablename) >= TABLE_MAXNAMELEN) {
+               errno = EINVAL;
+               return NULL;
+       }
+       
+       sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0)
+               return NULL;
+
+       s = sizeof(info);
+
+       strcpy(info.name, tablename);
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
+               return NULL;
+
+       if ((h = alloc_tc_handle(info.name, info.size, info.num_entries))
+           == NULL) {
+               close(sockfd);
+               sockfd = -1;
+               return NULL;
+       }
+
+/* Too hard --RR */
+#if 0
+       sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
+       dynlib = dlopen(pathname, RTLD_NOW);
+       if (!dynlib) {
+               errno = ENOENT;
+               return NULL;
+       }
+       h->hooknames = dlsym(dynlib, "hooknames");
+       if (!h->hooknames) {
+               errno = ENOENT;
+               return NULL;
+       }
+#else
+       h->hooknames = hooknames;
+#endif
+
+       /* Initialize current state */
+       h->info = info;
+       //h->new_number = h->info.num_entries;
+       //
+       h->entries.size = h->info.size;
+
+       tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
+
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
+                      &tmp) < 0) {
+               close(sockfd);
+               sockfd = -1;
+               free(h);
+               return NULL;
+       }
+
+       CHECK(h);
+       parse_ruleset(h);
+
+       return h;
+}
+
+void
+TC_FREE(TC_HANDLE_T *h)
+{
+       struct list_head *cur_item, *item2;
+
+       close(sockfd);
+       sockfd = -1;
+
+       /* free all chains */
+       list_for_each_safe(cur_item, item2, &(*h)->chains) {
+               struct chain_head *chead = list_entry(cur_item,
+                                                     struct chain_head,
+                                                     list);
+               chainh_free(chead);
+       }
+
+       /* FIXME: free all other ressources we might be using */
+
+       free(*h);
+       *h = NULL;
+}
+
+static inline int
+print_match(const STRUCT_ENTRY_MATCH *m)
+{
+       printf("Match name: `%s'\n", m->u.user.name);
+       return 0;
+}
+
+static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
+#if 0
+void
+TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
+{
+       CHECK(handle);
+
+       printf("libiptc v%s.  %u entries, %u bytes.\n",
+              IPTABLES_VERSION,
+              handle->new_number, handle->entries.size);
+       printf("Table `%s'\n", handle->info.name);
+       printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+              handle->info.hook_entry[HOOK_PRE_ROUTING],
+              handle->info.hook_entry[HOOK_LOCAL_IN],
+              handle->info.hook_entry[HOOK_FORWARD],
+              handle->info.hook_entry[HOOK_LOCAL_OUT],
+              handle->info.hook_entry[HOOK_POST_ROUTING]);
+       printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+              handle->info.underflow[HOOK_PRE_ROUTING],
+              handle->info.underflow[HOOK_LOCAL_IN],
+              handle->info.underflow[HOOK_FORWARD],
+              handle->info.underflow[HOOK_LOCAL_OUT],
+              handle->info.underflow[HOOK_POST_ROUTING]);
+
+       ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
+                     dump_entry, handle);
+}
+
+/* Returns 0 if not hook entry, else hooknumber + 1 */
+static inline unsigned int
+is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
+{
+       unsigned int i;
+
+       for (i = 0; i < NUMHOOKS; i++) {
+               if ((h->info.valid_hooks & (1 << i))
+                   && get_entry(h, h->info.hook_entry[i]) == e)
+                       return i+1;
+       }
+       return 0;
+}
+
+
+static int alphasort(const void *a, const void *b)
+{
+       return strcmp(((struct chain_cache *)a)->name,
+                     ((struct chain_cache *)b)->name);
+}
+#endif
+
+/* Does this chain exist? */
+int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
+{
+       return find_label(chain, handle) != NULL;
+}
+
+#if 0
+/* Returns the position of the final (ie. unconditional) element. */
+static unsigned int
+get_chain_end(const TC_HANDLE_T handle, unsigned int start)
+{
+       unsigned int last_off, off;
+       STRUCT_ENTRY *e;
+
+       last_off = start;
+       e = get_entry(handle, start);
+
+       /* Terminate when we meet a error label or a hook entry. */
+       for (off = start + e->next_offset;
+            off < handle->entries.size;
+            last_off = off, off += e->next_offset) {
+               STRUCT_ENTRY_TARGET *t;
+               unsigned int i;
+
+               e = get_entry(handle, off);
+
+               /* We hit an entry point. */
+               for (i = 0; i < NUMHOOKS; i++) {
+                       if ((handle->info.valid_hooks & (1 << i))
+                           && off == handle->info.hook_entry[i])
+                               return last_off;
+               }
+
+               /* We hit a user chain label */
+               t = GET_TARGET(e);
+               if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
+                       return last_off;
+       }
+       /* SHOULD NEVER HAPPEN */
+       fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
+               handle->entries.size, off);
+       abort();
+}
+#endif
+
+/* Iterator functions to run through the chains. */
+const char *
+TC_FIRST_CHAIN(TC_HANDLE_T *handle)
+{
+       struct chain_head *firsthead = list_entry((*handle)->chains.next,
+                                                  struct chain_head, list);
+       (*handle)->chain_iterator_cur = firsthead;
+
+       return firsthead->name;
+}
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *
+TC_NEXT_CHAIN(TC_HANDLE_T *handle)
+{
+       struct chain_head *next = list_entry(&(*handle)->chain_iterator_cur->list.next, struct chain_head, list);
+       (*handle)->chain_iterator_cur = next;
+
+       if (&next->list == &(*handle)->chains)
+               return NULL;
+
+       return next->name;
+}
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const STRUCT_ENTRY *
+TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r;
+
+       c = find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       /* Empty chain: single return/policy rule */
+       if (list_empty(&c->rules))
+               return NULL;
+
+       r = list_entry(c->rules.next, struct rule_head, list);
+       (*handle)->rule_iterator_cur = r;
+
+       return r->entry;
+}
+
+/* Returns NULL when rules run out. */
+const STRUCT_ENTRY *
+TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
+{
+       struct rule_head *r = list_entry((*handle)->rule_iterator_cur->list.next, struct rule_head, list);
+
+       if (&r->list == &r->chain->rules)
+               return NULL;
+
+       /* NOTE: prev is without any influence ! */
+       return r->entry;
+}
+
+#if 0
+/* How many rules in this chain? */
+unsigned int
+TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
+{
+       unsigned int off = 0;
+       STRUCT_ENTRY *start, *end;
+
+       CHECK(*handle);
+       if (!find_label(&off, chain, *handle)) {
+               errno = ENOENT;
+               return (unsigned int)-1;
+       }
+
+       start = get_entry(*handle, off);
+       end = get_entry(*handle, get_chain_end(*handle, off));
+
+       return entry2index(*handle, end) - entry2index(*handle, start);
+}
+
+/* Get n'th rule in this chain. */
+const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
+                               unsigned int n,
+                               TC_HANDLE_T *handle)
+{
+       unsigned int pos = 0, chainindex;
+
+       CHECK(*handle);
+       if (!find_label(&pos, chain, *handle)) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       chainindex = entry2index(*handle, get_entry(*handle, pos));
+
+       return index2entry(*handle, chainindex + n);
+}
+#endif
+
+static const char *
+target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
+{
+       int spos;
+
+       /* To avoid const warnings */
+       STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
+
+       if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
+               return GET_TARGET(e)->u.user.name;
+
+       /* Standard target: evaluate */
+       spos = *(int *)GET_TARGET(e)->data;
+       if (spos < 0) {
+               if (spos == RETURN)
+                       return LABEL_RETURN;
+               else if (spos == -NF_ACCEPT-1)
+                       return LABEL_ACCEPT;
+               else if (spos == -NF_DROP-1)
+                       return LABEL_DROP;
+               else if (spos == -NF_QUEUE-1)
+                       return LABEL_QUEUE;
+
+               fprintf(stderr, "ERROR: entry %p not a valid target (%d)\n",
+                       e, spos);
+               abort();
+       }
+
+#if 0
+//     jumpto = get_entry(handle, spos);
+
+       /* Fall through rule */
+       if (jumpto == (void *)e + e->next_offset)
+               return "";
+
+       /* Must point to head of a chain: ie. after error rule */
+       /* FIXME: this needs to deal with internal jump targets */
+       labelidx = entry2index(handle, jumpto) - 1;
+       return get_errorlabel(handle, index2offset(handle, labelidx));
+#endif
+       return "";
+}
+
+/* Returns a pointer to the target name of this position. */
+const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
+                         TC_HANDLE_T *handle)
+{
+       return target_name(*handle, e);
+}
+
+/* Is this a built-in chain?  Actually returns hook + 1. */
+int
+TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
+{
+       unsigned int i;
+
+       for (i = 0; i < NUMHOOKS; i++) {
+               if ((handle->info.valid_hooks & (1 << i))
+                   && handle->hooknames[i]
+                   && strcmp(handle->hooknames[i], chain) == 0)
+                       return i+1;
+       }
+       return 0;
+}
+
+/* Get the policy of a given built-in chain */
+const char *
+TC_GET_POLICY(const char *chain,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_head *chainh;
+       struct rule_head *ruleh;
+       int hook;
+
+       hook = TC_BUILTIN(chain, *handle);
+       if (hook == 0)
+               return NULL;
+
+       chainh = find_label(chain, *handle);
+       if (!chainh) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       ruleh = chainh->lastrule;
+
+       e = ruleh->entry;
+       *counters = e->counters;
+
+       return target_name(*handle, e);
+}
+
+#if 0
+static int
+correct_verdict(STRUCT_ENTRY *e,
+               char *base,
+               unsigned int offset, int delta_offset)
+{
+       STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
+       unsigned int curr = (char *)e - base;
+
+       /* Trap: insert of fall-through rule.  Don't change fall-through
+          verdict to jump-over-next-rule. */
+       if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
+           && t->verdict > (int)offset
+           && !(curr == offset &&
+                t->verdict == curr + e->next_offset)) {
+               t->verdict += delta_offset;
+       }
+
+       return 0;
+}
+
+/* Adjusts standard verdict jump positions after an insertion/deletion. */
+static int
+set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
+{
+       ENTRY_ITERATE((*handle)->entries.entrytable,
+                     (*handle)->entries.size,
+                     correct_verdict, (char *)(*handle)->entries.entrytable,
+                     offset, delta_offset);
+
+       set_changed(*handle);
+       return 1;
+}
+#endif
+
+
+
+static int
+standard_map(STRUCT_ENTRY *e, int verdict)
+{
+       STRUCT_STANDARD_TARGET *t;
+
+       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+       if (t->target.u.target_size
+           != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
+               errno = EINVAL;
+               return 0;
+       }
+       /* memset for memcmp convenience on delete/replace */
+       memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+       strcpy(t->target.u.user.name, STANDARD_TARGET);
+       t->verdict = verdict;
+
+       return 1;
+}
+
+static int
+map_target(const TC_HANDLE_T handle,
+          STRUCT_ENTRY *e,
+          unsigned int offset,
+          STRUCT_ENTRY_TARGET *old)
+{
+       STRUCT_ENTRY_TARGET *t = (STRUCT_ENTRY_TARGET *)GET_TARGET(e);
+
+       /* Save old target (except data, which we don't change, except for
+          standard case, where we don't care). */
+       *old = *t;
+
+       /* Maybe it's empty (=> fall through) */
+       if (strcmp(t->u.user.name, "") == 0)
+               return standard_map(e, offset + e->next_offset);
+       /* Maybe it's a standard target name... */
+       else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
+               return standard_map(e, -NF_ACCEPT - 1);
+       else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
+               return standard_map(e, -NF_DROP - 1);
+       else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
+               return standard_map(e, -NF_QUEUE - 1);
+       else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
+               return standard_map(e, RETURN);
+       else if (TC_BUILTIN(t->u.user.name, handle)) {
+               /* Can't jump to builtins. */
+               errno = EINVAL;
+               return 0;
+       } else {
+               /* Maybe it's an existing chain name. */
+               struct chain_head *c;
+
+#if 0
+               /* FIXME */
+               c = find_label(t->u.user.name, handle);
+               if (c)
+                       return standard_map(e, c->start_off);
+#endif
+       }
+
+       /* Must be a module?  If not, kernel will reject... */
+       /* memset to all 0 for your memcmp convenience. */
+       memset(t->u.user.name + strlen(t->u.user.name),
+              0,
+              FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
+       return 1;
+}
+
+static void
+unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
+{
+       STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
+
+       /* Save old target (except data, which we don't change, except for
+          standard case, where we don't care). */
+       *t = *old;
+}
+
+static struct rule_head *
+ruleh_get_n(struct chain_head *chead, int rulenum) 
+{
+       int i = 0;
+       struct list_head *list;
+
+       
+       list_for_each(list, &chead->rules) {
+               struct rule_head *rhead = list_entry(list, struct rule_head, 
+                                                       list);
+               i++;
+               if (i == rulenum)
+                       return rhead;
+       }
+       return NULL;
+}
+
+/* Insert the entry `e' in chain `chain' into position `rulenum'. */
+int
+TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *e,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *prev;
+
+       iptc_fn = TC_INSERT_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       prev = ruleh_get_n(c, rulenum-1);
+       if (!prev) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       if (append_entrycopy(e, prev))
+               return 1;
+
+       return 0;
+}
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int
+TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
+                const STRUCT_ENTRY *e,
+                unsigned int rulenum,
+                TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *repl;
+
+       iptc_fn = TC_REPLACE_ENTRY;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       repl = ruleh_get_n(c, rulenum);
+       if (!repl) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       if (!append_entrycopy(e, repl)) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       ruleh_free(repl);
+       return 1;
+}
+
+/* Append entry `e' to chain `chain'.  Equivalent to insert with
+   rulenum = length of chain. */
+int
+TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *e,
+               TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *rhead;
+
+       iptc_fn = TC_APPEND_ENTRY;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       rhead = list_entry(c->rules.prev, struct rule_head, list);
+       if(append_entrycopy(e, rhead))
+               return 1;
+       
+       return 0;
+}
+
+static inline int
+match_different(const STRUCT_ENTRY_MATCH *a,
+               const unsigned char *a_elems,
+               const unsigned char *b_elems,
+               unsigned char **maskptr)
+{
+       const STRUCT_ENTRY_MATCH *b;
+       unsigned int i;
+
+       /* Offset of b is the same as a. */
+       b = (void *)b_elems + ((unsigned char *)a - a_elems);
+
+       if (a->u.match_size != b->u.match_size)
+               return 1;
+
+       if (strcmp(a->u.user.name, b->u.user.name) != 0)
+               return 1;
+
+       *maskptr += ALIGN(sizeof(*a));
+
+       for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
+               if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
+                       return 1;
+       *maskptr += i;
+       return 0;
+}
+
+static inline int
+target_different(const unsigned char *a_targdata,
+                const unsigned char *b_targdata,
+                unsigned int tdatasize,
+                const unsigned char *mask)
+{
+       unsigned int i;
+       for (i = 0; i < tdatasize; i++)
+               if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
+                       return 1;
+
+       return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a,
+       const STRUCT_ENTRY *b,
+       unsigned char *matchmask);
+
+/* Delete the first rule in `chain' which matches `origfw'. */
+int
+TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *origfw,
+               unsigned char *matchmask,
+               TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct list_head *cur, *cur2;
+
+       iptc_fn = TC_DELETE_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       list_for_each_safe(cur, cur2, &c->rules) {
+               struct rule_head *rhead = list_entry(cur, struct rule_head, 
+                                                       list);
+               if (is_same(rhead->entry, origfw, matchmask)) {
+                       ruleh_free(rhead);
+                       return 1;
+               }
+       }
+
+       errno = ENOENT;
+       return 0;
+}
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int
+TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
+                   unsigned int rulenum,
+                   TC_HANDLE_T *handle)
+{
+       struct chain_head *chainh;
+       struct rule_head *rhead;
+
+       iptc_fn = TC_DELETE_NUM_ENTRY;
+
+       if (!(chainh = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       rhead = ruleh_get_n(chainh, rulenum);
+       if (!rhead) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       ruleh_free(rhead);
+
+       return 1;
+}
+
+/* Check the packet `fw' on chain `chain'.  Returns the verdict, or
+   NULL and sets errno. */
+const char *
+TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
+               STRUCT_ENTRY *entry,
+               TC_HANDLE_T *handle)
+{
+       errno = ENOSYS;
+       return NULL;
+}
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int
+TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       struct list_head *cur, *cur2;
+       struct chain_head *chainh;
+
+       if (!(chainh = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       list_for_each_safe(cur, cur2, &chainh->rules) {
+               struct rule_head *ruleh = list_entry(cur, struct rule_head, 
+                                                       list);
+               /* don't free the entry and policy/return entries */
+               if (ruleh != chainh->firstrule && ruleh != chainh->lastrule)
+                       ruleh_free(ruleh);
+       }
+       return 1;
+}
+
+/* Zeroes the counters in a chain. */
+int
+TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct list_head *cur;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       list_for_each(cur, c->rules.next) {
+               struct rule_head *r = list_entry(cur, struct rule_head, list);
+               if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+                       r->counter_map.maptype = COUNTER_MAP_ZEROED;
+       }
+       set_changed(*handle);
+
+       return 1;
+}
+
+STRUCT_COUNTERS *
+TC_READ_COUNTER(const IPT_CHAINLABEL chain,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_head *c;
+       struct rule_head *r;
+
+       iptc_fn = TC_READ_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle) )
+             || !(r = ruleh_get_n(c, rulenum))) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       return &r->entry->counters;
+}
+
+int
+TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_head *c;
+       struct rule_head *r;
+       
+       iptc_fn = TC_ZERO_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))
+             || !(r = ruleh_get_n(c, rulenum))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+               r->counter_map.maptype = COUNTER_MAP_ZEROED;
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+int 
+TC_SET_COUNTER(const IPT_CHAINLABEL chain,
+              unsigned int rulenum,
+              STRUCT_COUNTERS *counters,
+              TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_head *c;
+       struct rule_head *r;
+
+       iptc_fn = TC_SET_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))
+             || !(r = ruleh_get_n(c, rulenum))) {
+               errno = ENOENT;
+               return 0;
+       }
+       
+       r->counter_map.maptype = COUNTER_MAP_SET;
+       memcpy(&r->entry->counters, counters, sizeof(STRUCT_COUNTERS));
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Creates a new chain. */
+/* To create a chain, create two rules: error node and unconditional
+ * return. */
+int
+TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       int ret;
+       struct chainstart {
+               STRUCT_ENTRY head;
+               struct ipt_error_target name;
+       } *newc1;
+       struct chainend {
+               STRUCT_ENTRY ret;
+               STRUCT_STANDARD_TARGET target;
+       } *newc2;
+       struct rule_head *newr1, *newr2;
+       struct chain_head *chead;
+
+       iptc_fn = TC_CREATE_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (find_label(chain, *handle)
+           || strcmp(chain, LABEL_DROP) == 0
+           || strcmp(chain, LABEL_ACCEPT) == 0
+           || strcmp(chain, LABEL_QUEUE) == 0
+           || strcmp(chain, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       chead = chainh_alloc(*handle, chain);
+       if (!chead) {
+               errno = ENOMEM;
+               return 0;
+       }
+       
+       newr1 = ruleh_alloc(sizeof(*newc1));
+       if (!newr1) {
+               chainh_free(chead);
+               return 0;
+       }
+       newc1 = (struct chainstart *) newr1->entry;
+
+       newr2 = ruleh_alloc(sizeof(*newc2));
+       if (!newr2) {
+               chainh_free(chead);
+               ruleh_free(newr1);
+               return 0;
+       }
+       newc2 = (struct chainend *) newr2->entry;
+
+       newc1->head.target_offset = sizeof(STRUCT_ENTRY);
+       newc1->head.next_offset
+               = sizeof(STRUCT_ENTRY)
+               + ALIGN(sizeof(struct ipt_error_target));
+       strcpy(newc1->name.t.u.user.name, ERROR_TARGET);
+       newc1->name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
+       strcpy(newc1->name.error, chain);
+
+       newc2->ret.target_offset = sizeof(STRUCT_ENTRY);
+       newc2->ret.next_offset
+               = sizeof(STRUCT_ENTRY)
+               + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       strcpy(newc2->target.target.u.user.name, STANDARD_TARGET);
+       newc2->target.target.u.target_size
+               = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       newc2->target.verdict = RETURN;
+
+       list_prepend(&newr1->list, &chead->rules);
+       chead->firstrule = newr1;
+       list_append(&newr2->list, &chead->rules);
+       chead->lastrule = newr2;
+
+       return 1;
+}
+
+#if 0
+static int
+count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
+{
+       STRUCT_STANDARD_TARGET *t;
+
+       if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+               if (t->verdict == offset)
+                       (*ref)++;
+       }
+
+       return 0;
+}
+
+/* Get the number of references to this chain. */
+int
+TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
+                 TC_HANDLE_T *handle)
+{
+       struct chain_cache *c;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       *ref = 0;
+       ENTRY_ITERATE((*handle)->entries.entrytable,
+                     (*handle)->entries.size,
+                     count_ref, c->start_off, ref);
+       return 1;
+}
+#endif
+
+static unsigned int
+count_rules(struct chain_head *chainh)
+{
+       unsigned int numrules = 0;
+       struct list_head *cur;
+
+       list_for_each(cur, &chainh->rules) {
+               numrules++;
+       }
+
+       if (numrules <=2)
+               return 0;
+       else
+               return numrules-2;
+}
+
+/* Deletes a chain. */
+int
+TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       unsigned int references;
+       struct chain_head *chainh;
+
+#if 0
+       if (!TC_GET_REFERENCES(&references, chain, handle))
+               return 0;
+
+       iptc_fn = TC_DELETE_CHAIN;
+
+       if (TC_BUILTIN(chain, *handle)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       if (references > 0) {
+               errno = EMLINK;
+               return 0;
+       }
+#endif 
+
+       if (!(chainh = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (!(count_rules(chainh) == 0)) {
+               errno = ENOTEMPTY;
+               return 0;
+       }
+
+       chainh_free(chainh);
+       return 1;
+}
+
+/* Renames a chain. */
+int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
+                   const IPT_CHAINLABEL newname,
+                   TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *ruleh;
+       struct ipt_error_target *t;
+
+       iptc_fn = TC_RENAME_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (find_label(newname, *handle)
+           || strcmp(newname, LABEL_DROP) == 0
+           || strcmp(newname, LABEL_ACCEPT) == 0
+           || strcmp(newname, LABEL_QUEUE) == 0
+           || strcmp(newname, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (!(c = find_label(oldname, *handle))
+           || TC_BUILTIN(oldname, *handle)) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       ruleh = list_entry(&c->rules.next, struct rule_head, list);
+
+       t = (struct ipt_error_target *)
+               GET_TARGET(ruleh->entry);
+
+       memset(t->error, 0, sizeof(t->error));
+       strcpy(t->error, newname);
+
+       return 1;
+}
+
+/* Sets the policy on a built-in chain. */
+int
+TC_SET_POLICY(const IPT_CHAINLABEL chain,
+             const IPT_CHAINLABEL policy,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
+{
+       int ctrindex;
+       unsigned int hook;
+       struct chain_head *chainh;
+       struct rule_head *policyrh;
+       STRUCT_ENTRY *e;
+       STRUCT_STANDARD_TARGET *t;
+
+       iptc_fn = TC_SET_POLICY;
+       /* Figure out which chain. */
+       hook = TC_BUILTIN(chain, *handle);
+       if (hook == 0) {
+               errno = ENOENT;
+               return 0;
+       } else
+               hook--;
+
+       if (!(chainh = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       policyrh = chainh->lastrule;
+       if (policyrh) {
+               printf("ERROR: Policy for `%s' non-existant", chain);
+               return 0;
+       }
+
+       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(policyrh->entry);
+
+       if (strcmp(policy, LABEL_ACCEPT) == 0)
+               t->verdict = -NF_ACCEPT - 1;
+       else if (strcmp(policy, LABEL_DROP) == 0)
+               t->verdict = -NF_DROP - 1;
+       else {
+               errno = EINVAL;
+               return 0;
+       }
+
+       ctrindex = entry2index(*handle, e);
+
+       if (counters) {
+               /* set byte and packet counters */
+               memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+
+               policyrh->counter_map.maptype = COUNTER_MAP_SET;
+
+       } else {
+               policyrh->counter_map.maptype = COUNTER_MAP_NOMAP;
+               policyrh->counter_map.mappos = 0;
+       }
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Without this, on gcc 2.7.2.3, we get:
+   libiptc.c: In function `TC_COMMIT':
+   libiptc.c:833: fixed or forbidden register was spilled.
+   This may be due to a compiler bug or to impossible asm
+   statements or clauses.
+*/
+static void
+subtract_counters(STRUCT_COUNTERS *answer,
+                 const STRUCT_COUNTERS *a,
+                 const STRUCT_COUNTERS *b)
+{
+       answer->pcnt = a->pcnt - b->pcnt;
+       answer->bcnt = a->bcnt - b->bcnt;
+}
+
+int
+TC_COMMIT(TC_HANDLE_T *handle)
+{
+       /* Replace, then map back the counters. */
+       STRUCT_REPLACE *repl;
+       STRUCT_COUNTERS_INFO *newcounters;
+       unsigned int i;
+       size_t counterlen;
+
+       CHECK(*handle);
+
+       counterlen = sizeof(STRUCT_COUNTERS_INFO)
+                       + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
+
+#if 0
+       TC_DUMP_ENTRIES(*handle);
+#endif
+
+       /* Don't commit if nothing changed. */
+       if (!(*handle)->changed)
+               goto finished;
+
+       repl = malloc(sizeof(*repl) + (*handle)->entries.size);
+       if (!repl) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* These are the old counters we will get from kernel */
+       repl->counters = malloc(sizeof(STRUCT_COUNTERS)
+                               * (*handle)->info.num_entries);
+       if (!repl->counters) {
+               free(repl);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* These are the counters we're going to put back, later. */
+       newcounters = malloc(counterlen);
+       if (!newcounters) {
+               free(repl->counters);
+               free(repl);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       strcpy(repl->name, (*handle)->info.name);
+       repl->num_entries = (*handle)->new_number;
+       repl->size = (*handle)->entries.size;
+       memcpy(repl->hook_entry, (*handle)->info.hook_entry,
+              sizeof(repl->hook_entry));
+       memcpy(repl->underflow, (*handle)->info.underflow,
+              sizeof(repl->underflow));
+       repl->num_counters = (*handle)->info.num_entries;
+       repl->valid_hooks = (*handle)->info.valid_hooks;
+       memcpy(repl->entries, (*handle)->entries.entrytable,
+              (*handle)->entries.size);
+
+       if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
+                      sizeof(*repl) + (*handle)->entries.size) < 0) {
+               free(repl->counters);
+               free(repl);
+               free(newcounters);
+               return 0;
+       }
+
+       /* Put counters back. */
+       strcpy(newcounters->name, (*handle)->info.name);
+       newcounters->num_counters = (*handle)->new_number;
+       for (i = 0; i < (*handle)->new_number; i++) {
+               unsigned int mappos = (*handle)->counter_map[i].mappos;
+               switch ((*handle)->counter_map[i].maptype) {
+               case COUNTER_MAP_NOMAP:
+                       newcounters->counters[i]
+                               = ((STRUCT_COUNTERS){ 0, 0 });
+                       break;
+
+               case COUNTER_MAP_NORMAL_MAP:
+                       /* Original read: X.
+                        * Atomic read on replacement: X + Y.
+                        * Currently in kernel: Z.
+                        * Want in kernel: X + Y + Z.
+                        * => Add in X + Y
+                        * => Add in replacement read.
+                        */
+                       newcounters->counters[i] = repl->counters[mappos];
+                       break;
+
+               case COUNTER_MAP_ZEROED:
+                       /* Original read: X.
+                        * Atomic read on replacement: X + Y.
+                        * Currently in kernel: Z.
+                        * Want in kernel: Y + Z.
+                        * => Add in Y.
+                        * => Add in (replacement read - original read).
+                        */
+                       subtract_counters(&newcounters->counters[i],
+                                         &repl->counters[mappos],
+                                         &index2entry(*handle, i)->counters);
+                       break;
+
+               case COUNTER_MAP_SET:
+                       /* Want to set counter (iptables-restore) */
+
+                       memcpy(&newcounters->counters[i],
+                              &index2entry(*handle, i)->counters,
+                              sizeof(STRUCT_COUNTERS));
+
+                       break;
+               }
+       }
+
+#ifdef KERNEL_64_USERSPACE_32
+       {
+               /* Kernel will think that pointer should be 64-bits, and get
+                  padding.  So we accomodate here (assumption: alignment of
+                  `counters' is on 64-bit boundary). */
+               u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
+               if ((unsigned long)&newcounters->counters % 8 != 0) {
+                       fprintf(stderr,
+                               "counters alignment incorrect! Mail rusty!\n");
+                       abort();
+               }
+               *kernptr = newcounters->counters;
+       }
+#endif /* KERNEL_64_USERSPACE_32 */
+
+       if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
+                      newcounters, counterlen) < 0) {
+               free(repl->counters);
+               free(repl);
+               free(newcounters);
+               return 0;
+       }
+
+       free(repl->counters);
+       free(repl);
+       free(newcounters);
+
+ finished:
+       TC_FREE(handle);
+       return 1;
+}
+
+/* Get raw socket. */
+int
+TC_GET_RAW_SOCKET()
+{
+       return sockfd;
+}
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *
+TC_STRERROR(int err)
+{
+       unsigned int i;
+       struct table_struct {
+               void *fn;
+               int err;
+               const char *message;
+       } table [] =
+         { { TC_INIT, EPERM, "Permission denied (you must be root)" },
+           { TC_INIT, EINVAL, "Module is wrong version" },
+           { TC_INIT, ENOENT, 
+                   "Table does not exist (do you need to insmod?)" },
+           { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
+           { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
+           { TC_DELETE_CHAIN, EMLINK,
+             "Can't delete chain with references left" },
+           { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
+           { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
+           { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
+           { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
+           { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
+           { TC_INSERT_ENTRY, EINVAL, "Target problem" },
+           /* EINVAL for CHECK probably means bad interface. */
+           { TC_CHECK_PACKET, EINVAL,
+             "Bad arguments (does that interface exist?)" },
+           { TC_CHECK_PACKET, ENOSYS,
+             "Checking will most likely never get implemented" },
+           /* ENOENT for DELETE probably means no matching rule */
+           { TC_DELETE_ENTRY, ENOENT,
+             "Bad rule (does a matching rule exist in that chain?)" },
+           { TC_SET_POLICY, ENOENT,
+             "Bad built-in chain name" },
+           { TC_SET_POLICY, EINVAL,
+             "Bad policy name" },
+
+           { NULL, 0, "Incompatible with this kernel" },
+           { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
+           { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
+           { NULL, ENOMEM, "Memory allocation problem" },
+           { NULL, ENOENT, "No chain/target/match by that name" },
+         };
+
+       for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+               if ((!table[i].fn || table[i].fn == iptc_fn)
+                   && table[i].err == err)
+                       return table[i].message;
+       }
+
+       return strerror(err);
+}
diff --git a/libiptc2/libiptc.cvs.c b/libiptc2/libiptc.cvs.c
new file mode 100644 (file)
index 0000000..3f03593
--- /dev/null
@@ -0,0 +1,1920 @@
+/* Library which manipulates firewall rules.  Version $Revision: 1.40 $ */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+ * COPYING for details). 
+ * (C) 2000-2003 by the Netfilter Core Team <coreteam@netfilter.org>
+ *
+ * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
+ *     - Reimplementation of chain cache to use offsets instead of entries
+ * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
+ *     - performance optimization, sponsored by Astaro AG (http://www.astaro.com/)
+ *       don't rebuild the chain cache after every operation, instead fix it
+ *       up after a ruleset change.  
+ */
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/lib/iptables"
+#endif
+
+#ifndef __OPTIMIZE__
+STRUCT_ENTRY_TARGET *
+GET_TARGET(STRUCT_ENTRY *e)
+{
+       return (void *)e + e->target_offset;
+}
+#endif
+
+static int sockfd = -1;
+static void *iptc_fn = NULL;
+
+static const char *hooknames[]
+= { [HOOK_PRE_ROUTING]  "PREROUTING",
+    [HOOK_LOCAL_IN]     "INPUT",
+    [HOOK_FORWARD]      "FORWARD",
+    [HOOK_LOCAL_OUT]    "OUTPUT",
+    [HOOK_POST_ROUTING] "POSTROUTING",
+#ifdef HOOK_DROPPING
+    [HOOK_DROPPING]    "DROPPING"
+#endif
+};
+
+struct counter_map
+{
+       enum {
+               COUNTER_MAP_NOMAP,
+               COUNTER_MAP_NORMAL_MAP,
+               COUNTER_MAP_ZEROED,
+               COUNTER_MAP_SET
+       } maptype;
+       unsigned int mappos;
+};
+
+/* Convenience structures */
+struct ipt_error_target
+{
+       STRUCT_ENTRY_TARGET t;
+       char error[TABLE_MAXNAMELEN];
+};
+
+struct chain_cache
+{
+       char name[TABLE_MAXNAMELEN];
+       /* This is the first rule in chain. */
+       unsigned int start_off;
+       /* Last rule in chain */
+       unsigned int end_off;
+};
+
+STRUCT_TC_HANDLE
+{
+       /* Have changes been made? */
+       int changed;
+       /* Size in here reflects original state. */
+       STRUCT_GETINFO info;
+
+       struct counter_map *counter_map;
+       /* Array of hook names */
+       const char **hooknames;
+
+       /* Cached position of chain heads (NULL = no cache). */
+       unsigned int cache_num_chains;
+       unsigned int cache_num_builtins;
+       struct chain_cache *cache_chain_heads;
+
+       /* Chain iterator: current chain cache entry. */
+       struct chain_cache *cache_chain_iteration;
+
+       /* Rule iterator: terminal rule */
+       STRUCT_ENTRY *cache_rule_end;
+
+       /* Number in here reflects current state. */
+       unsigned int new_number;
+       STRUCT_GET_ENTRIES entries;
+};
+
+static void
+set_changed(TC_HANDLE_T h)
+{
+       h->changed = 1;
+}
+
+#ifdef IPTC_DEBUG
+static void do_check(TC_HANDLE_T h, unsigned int line);
+#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
+#else
+#define CHECK(h)
+#endif
+
+static inline int
+get_number(const STRUCT_ENTRY *i,
+          const STRUCT_ENTRY *seek,
+          unsigned int *pos)
+{
+       if (i == seek)
+               return 1;
+       (*pos)++;
+       return 0;
+}
+
+static unsigned int
+entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
+{
+       unsigned int pos = 0;
+
+       if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                         get_number, seek, &pos) == 0) {
+               fprintf(stderr, "ERROR: offset %i not an entry!\n",
+                       (char *)seek - (char *)h->entries.entrytable);
+               abort();
+       }
+       return pos;
+}
+
+static inline int
+get_entry_n(STRUCT_ENTRY *i,
+           unsigned int number,
+           unsigned int *pos,
+           STRUCT_ENTRY **pe)
+{
+       if (*pos == number) {
+               *pe = i;
+               return 1;
+       }
+       (*pos)++;
+       return 0;
+}
+
+static STRUCT_ENTRY *
+index2entry(TC_HANDLE_T h, unsigned int index)
+{
+       unsigned int pos = 0;
+       STRUCT_ENTRY *ret = NULL;
+
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     get_entry_n, index, &pos, &ret);
+
+       return ret;
+}
+
+static inline STRUCT_ENTRY *
+get_entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
+}
+
+static inline unsigned long
+entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+       return (void *)e - (void *)h->entries.entrytable;
+}
+
+static inline unsigned long
+index2offset(TC_HANDLE_T h, unsigned int index)
+{
+       return entry2offset(h, index2entry(h, index));
+}
+
+static inline STRUCT_ENTRY *
+offset2entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *) ((void *)h->entries.entrytable+offset);
+}
+
+static inline unsigned int
+offset2index(const TC_HANDLE_T h, unsigned int offset)
+{
+       return entry2index(h, offset2entry(h, offset));
+}
+
+
+static const char *
+get_errorlabel(TC_HANDLE_T h, unsigned int offset)
+{
+       STRUCT_ENTRY *e;
+
+       e = get_entry(h, offset);
+       if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) {
+               fprintf(stderr, "ERROR: offset %u not an error node!\n",
+                       offset);
+               abort();
+       }
+
+       return (const char *)GET_TARGET(e)->data;
+}
+
+/* Allocate handle of given size */
+static TC_HANDLE_T
+alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
+{
+       size_t len;
+       TC_HANDLE_T h;
+
+       len = sizeof(STRUCT_TC_HANDLE)
+               + size
+               + num_rules * sizeof(struct counter_map);
+
+       if ((h = malloc(len)) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       h->changed = 0;
+       h->cache_num_chains = 0;
+       h->cache_chain_heads = NULL;
+       h->counter_map = (void *)h
+               + sizeof(STRUCT_TC_HANDLE)
+               + size;
+       strcpy(h->info.name, tablename);
+       strcpy(h->entries.name, tablename);
+
+       return h;
+}
+
+TC_HANDLE_T
+TC_INIT(const char *tablename)
+{
+       TC_HANDLE_T h;
+       STRUCT_GETINFO info;
+       unsigned int i;
+       int tmp;
+       socklen_t s;
+
+       iptc_fn = TC_INIT;
+
+       if (sockfd != -1) {
+               close(sockfd);
+               sockfd = -1;
+       }
+
+       if (strlen(tablename) >= TABLE_MAXNAMELEN) {
+               errno = EINVAL;
+               return NULL;
+       }
+       
+       sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0)
+               return NULL;
+
+       s = sizeof(info);
+
+       strcpy(info.name, tablename);
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
+               return NULL;
+
+       if ((h = alloc_handle(info.name, info.size, info.num_entries))
+           == NULL) {
+               close(sockfd);
+               sockfd = -1;
+               return NULL;
+       }
+
+/* Too hard --RR */
+#if 0
+       sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
+       dynlib = dlopen(pathname, RTLD_NOW);
+       if (!dynlib) {
+               errno = ENOENT;
+               return NULL;
+       }
+       h->hooknames = dlsym(dynlib, "hooknames");
+       if (!h->hooknames) {
+               errno = ENOENT;
+               return NULL;
+       }
+#else
+       h->hooknames = hooknames;
+#endif
+
+       /* Initialize current state */
+       h->info = info;
+       h->new_number = h->info.num_entries;
+       for (i = 0; i < h->info.num_entries; i++)
+               h->counter_map[i]
+                       = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
+
+       h->entries.size = h->info.size;
+
+       tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
+
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
+                      &tmp) < 0) {
+               close(sockfd);
+               sockfd = -1;
+               free(h);
+               return NULL;
+       }
+
+       CHECK(h);
+       return h;
+}
+
+void
+TC_FREE(TC_HANDLE_T *h)
+{
+       close(sockfd);
+       sockfd = -1;
+       if ((*h)->cache_chain_heads)
+               free((*h)->cache_chain_heads);
+       free(*h);
+       *h = NULL;
+}
+
+static inline int
+print_match(const STRUCT_ENTRY_MATCH *m)
+{
+       printf("Match name: `%s'\n", m->u.user.name);
+       return 0;
+}
+
+static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
+void
+TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
+{
+       CHECK(handle);
+
+       printf("libiptc v%s.  %u entries, %u bytes.\n",
+              IPTABLES_VERSION,
+              handle->new_number, handle->entries.size);
+       printf("Table `%s'\n", handle->info.name);
+       printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+              handle->info.hook_entry[HOOK_PRE_ROUTING],
+              handle->info.hook_entry[HOOK_LOCAL_IN],
+              handle->info.hook_entry[HOOK_FORWARD],
+              handle->info.hook_entry[HOOK_LOCAL_OUT],
+              handle->info.hook_entry[HOOK_POST_ROUTING]);
+       printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+              handle->info.underflow[HOOK_PRE_ROUTING],
+              handle->info.underflow[HOOK_LOCAL_IN],
+              handle->info.underflow[HOOK_FORWARD],
+              handle->info.underflow[HOOK_LOCAL_OUT],
+              handle->info.underflow[HOOK_POST_ROUTING]);
+
+       ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
+                     dump_entry, handle);
+}
+
+/* Returns 0 if not hook entry, else hooknumber + 1 */
+static inline unsigned int
+is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
+{
+       unsigned int i;
+
+       for (i = 0; i < NUMHOOKS; i++) {
+               if ((h->info.valid_hooks & (1 << i))
+                   && get_entry(h, h->info.hook_entry[i]) == e)
+                       return i+1;
+       }
+       return 0;
+}
+
+static inline int
+add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
+{
+       unsigned int builtin;
+
+       /* Last entry.  End it. */
+       if (entry2offset(h, e) + e->next_offset == h->entries.size) {
+               /* This is the ERROR node at end of the table */
+               h->cache_chain_heads[h->cache_num_chains-1].end_off = 
+                       entry2offset(h, *prev);
+               return 0;
+       }
+
+       /* We know this is the start of a new chain if it's an ERROR
+          target, or a hook entry point */
+       if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
+               /* prev was last entry in previous chain */
+               h->cache_chain_heads[h->cache_num_chains-1].end_off
+                       = entry2offset(h, *prev);
+
+               strcpy(h->cache_chain_heads[h->cache_num_chains].name,
+                      (const char *)GET_TARGET(e)->data);
+               h->cache_chain_heads[h->cache_num_chains].start_off
+                       = entry2offset(h, (void *)e + e->next_offset);
+               h->cache_num_chains++;
+       } else if ((builtin = is_hook_entry(e, h)) != 0) {
+               if (h->cache_num_chains > 0)
+                       /* prev was last entry in previous chain */
+                       h->cache_chain_heads[h->cache_num_chains-1].end_off
+                               = entry2offset(h, *prev);
+
+               strcpy(h->cache_chain_heads[h->cache_num_chains].name,
+                      h->hooknames[builtin-1]);
+               h->cache_chain_heads[h->cache_num_chains].start_off
+                       = entry2offset(h, (void *)e);
+               h->cache_num_chains++;
+       }
+
+       *prev = e;
+       return 0;
+}
+
+static int alphasort(const void *a, const void *b)
+{
+       return strcmp(((struct chain_cache *)a)->name,
+                     ((struct chain_cache *)b)->name);
+}
+
+static int populate_cache(TC_HANDLE_T h)
+{
+       unsigned int i;
+       STRUCT_ENTRY *prev;
+
+       /* # chains < # rules / 2 + num builtins - 1 */
+       h->cache_chain_heads = malloc((h->new_number / 2 + 4)
+                                     * sizeof(struct chain_cache));
+       if (!h->cache_chain_heads) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       h->cache_num_chains = 0;
+       h->cache_num_builtins = 0;
+
+       /* Count builtins */
+       for (i = 0; i < NUMHOOKS; i++) {
+               if (h->info.valid_hooks & (1 << i))
+                       h->cache_num_builtins++;
+       }
+
+       prev = NULL;
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     add_chain, h, &prev);
+
+       qsort(h->cache_chain_heads + h->cache_num_builtins,
+             h->cache_num_chains - h->cache_num_builtins,
+             sizeof(struct chain_cache), alphasort);
+
+       return 1;
+}
+
+static int 
+correct_cache(TC_HANDLE_T h, unsigned int offset, int delta)
+{
+       int i;          /* needs to be signed because deleting first
+                          chain can make it drop to -1 */
+
+       if (!delta)
+               return 1;
+
+       for (i = 0; i < h->cache_num_chains; i++) {
+               struct chain_cache *cc = &h->cache_chain_heads[i];
+
+               if (delta < 0) {
+                       /* take care about deleted chains */
+                       if (cc->start_off >= offset+delta
+                           && cc->end_off <= offset) {
+                               /* this chain is within the deleted range,
+                                * let's remove it from the cache */
+                               void *start;
+                               unsigned int size;
+
+                               h->cache_num_chains--;
+                               if (i+1 >= h->cache_num_chains)
+                                       continue;
+                               start = &h->cache_chain_heads[i+1];
+                               size = (h->cache_num_chains-i)
+                                       * sizeof(struct chain_cache);
+                               memmove(cc, start, size);
+
+                               /* iterate over same index again, since
+                                * it is now a different chain */
+                               i--;
+                               continue;
+                       }
+               }
+
+               if (cc->start_off > offset)
+                       cc->start_off += delta;
+
+               if (cc->end_off >= offset)
+                       cc->end_off += delta;
+       }
+       /* HW_FIXME: sorting might be needed, but just in case a new chain was
+        * added */
+
+       return 1;
+}
+
+static int
+add_chain_cache(TC_HANDLE_T h, const char *name, unsigned int start_off,
+               unsigned int end_off)
+{
+       struct chain_cache *ccs = realloc(h->cache_chain_heads, 
+                                         (h->new_number / 2 + 4 + 1)
+                                          * sizeof(struct chain_cache));
+       struct chain_cache *newcc;
+       
+       if (!ccs)
+               return 0;
+
+       h->cache_chain_heads = ccs;
+       newcc = &h->cache_chain_heads[h->cache_num_chains];
+       h->cache_num_chains++;
+
+       strncpy(newcc->name, name, TABLE_MAXNAMELEN-1);
+       newcc->start_off = start_off;
+       newcc->end_off = end_off;
+
+       return 1;
+}
+
+/* Returns cache ptr if found, otherwise NULL. */
+static struct chain_cache *
+find_label(const char *name, TC_HANDLE_T handle)
+{
+       unsigned int i;
+
+       if (handle->cache_chain_heads == NULL
+           && !populate_cache(handle))
+               return NULL;
+
+       /* FIXME: Linear search through builtins, then binary --RR */
+       for (i = 0; i < handle->cache_num_chains; i++) {
+               if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
+                       return &handle->cache_chain_heads[i];
+       }
+
+       return NULL;
+}
+
+/* Does this chain exist? */
+int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
+{
+       return find_label(chain, handle) != NULL;
+}
+
+/* Returns the position of the final (ie. unconditional) element. */
+static unsigned int
+get_chain_end(const TC_HANDLE_T handle, unsigned int start)
+{
+       unsigned int last_off, off;
+       STRUCT_ENTRY *e;
+
+       last_off = start;
+       e = get_entry(handle, start);
+
+       /* Terminate when we meet a error label or a hook entry. */
+       for (off = start + e->next_offset;
+            off < handle->entries.size;
+            last_off = off, off += e->next_offset) {
+               STRUCT_ENTRY_TARGET *t;
+               unsigned int i;
+
+               e = get_entry(handle, off);
+
+               /* We hit an entry point. */
+               for (i = 0; i < NUMHOOKS; i++) {
+                       if ((handle->info.valid_hooks & (1 << i))
+                           && off == handle->info.hook_entry[i])
+                               return last_off;
+               }
+
+               /* We hit a user chain label */
+               t = GET_TARGET(e);
+               if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
+                       return last_off;
+       }
+       /* SHOULD NEVER HAPPEN */
+       fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
+               handle->entries.size, off);
+       abort();
+}
+
+/* Iterator functions to run through the chains. */
+const char *
+TC_FIRST_CHAIN(TC_HANDLE_T *handle)
+{
+       if ((*handle)->cache_chain_heads == NULL
+           && !populate_cache(*handle))
+               return NULL;
+
+       (*handle)->cache_chain_iteration
+               = &(*handle)->cache_chain_heads[0];
+
+       return (*handle)->cache_chain_iteration->name;
+}
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *
+TC_NEXT_CHAIN(TC_HANDLE_T *handle)
+{
+       (*handle)->cache_chain_iteration++;
+
+       if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
+           == (*handle)->cache_num_chains)
+               return NULL;
+
+       return (*handle)->cache_chain_iteration->name;
+}
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const STRUCT_ENTRY *
+TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
+{
+       struct chain_cache *c;
+
+       c = find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       /* Empty chain: single return/policy rule */
+       if (c->start_off == c->end_off)
+               return NULL;
+
+       (*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
+       return offset2entry(*handle, c->start_off);
+}
+
+/* Returns NULL when rules run out. */
+const STRUCT_ENTRY *
+TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
+{
+       if ((void *)prev + prev->next_offset
+           == (void *)(*handle)->cache_rule_end)
+               return NULL;
+
+       return (void *)prev + prev->next_offset;
+}
+
+#if 0
+/* How many rules in this chain? */
+unsigned int
+TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
+{
+       unsigned int off = 0;
+       STRUCT_ENTRY *start, *end;
+
+       CHECK(*handle);
+       if (!find_label(&off, chain, *handle)) {
+               errno = ENOENT;
+               return (unsigned int)-1;
+       }
+
+       start = get_entry(*handle, off);
+       end = get_entry(*handle, get_chain_end(*handle, off));
+
+       return entry2index(*handle, end) - entry2index(*handle, start);
+}
+
+/* Get n'th rule in this chain. */
+const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
+                               unsigned int n,
+                               TC_HANDLE_T *handle)
+{
+       unsigned int pos = 0, chainindex;
+
+       CHECK(*handle);
+       if (!find_label(&pos, chain, *handle)) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       chainindex = entry2index(*handle, get_entry(*handle, pos));
+
+       return index2entry(*handle, chainindex + n);
+}
+#endif
+
+static const char *
+target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
+{
+       int spos;
+       unsigned int labelidx;
+       STRUCT_ENTRY *jumpto;
+
+       /* To avoid const warnings */
+       STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
+
+       if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
+               return GET_TARGET(e)->u.user.name;
+
+       /* Standard target: evaluate */
+       spos = *(int *)GET_TARGET(e)->data;
+       if (spos < 0) {
+               if (spos == RETURN)
+                       return LABEL_RETURN;
+               else if (spos == -NF_ACCEPT-1)
+                       return LABEL_ACCEPT;
+               else if (spos == -NF_DROP-1)
+                       return LABEL_DROP;
+               else if (spos == -NF_QUEUE-1)
+                       return LABEL_QUEUE;
+
+               fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
+                       entry2offset(handle, e), handle->entries.size,
+                       spos);
+               abort();
+       }
+
+       jumpto = get_entry(handle, spos);
+
+       /* Fall through rule */
+       if (jumpto == (void *)e + e->next_offset)
+               return "";
+
+       /* Must point to head of a chain: ie. after error rule */
+       labelidx = entry2index(handle, jumpto) - 1;
+       return get_errorlabel(handle, index2offset(handle, labelidx));
+}
+
+/* Returns a pointer to the target name of this position. */
+const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
+                         TC_HANDLE_T *handle)
+{
+       return target_name(*handle, e);
+}
+
+/* Is this a built-in chain?  Actually returns hook + 1. */
+int
+TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
+{
+       unsigned int i;
+
+       for (i = 0; i < NUMHOOKS; i++) {
+               if ((handle->info.valid_hooks & (1 << i))
+                   && handle->hooknames[i]
+                   && strcmp(handle->hooknames[i], chain) == 0)
+                       return i+1;
+       }
+       return 0;
+}
+
+/* Get the policy of a given built-in chain */
+const char *
+TC_GET_POLICY(const char *chain,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
+{
+       unsigned int start;
+       STRUCT_ENTRY *e;
+       int hook;
+
+       hook = TC_BUILTIN(chain, *handle);
+       if (hook != 0)
+               start = (*handle)->info.hook_entry[hook-1];
+       else
+               return NULL;
+
+       e = get_entry(*handle, get_chain_end(*handle, start));
+       *counters = e->counters;
+
+       return target_name(*handle, e);
+}
+
+static inline int
+correct_verdict(STRUCT_ENTRY *e,
+               char *base,
+               unsigned int offset, int delta_offset)
+{
+       STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
+       unsigned int curr = (char *)e - base;
+
+       /* Trap: insert of fall-through rule.  Don't change fall-through
+          verdict to jump-over-next-rule. */
+       if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
+           && t->verdict > (int)offset
+           && !(curr == offset &&
+                t->verdict == curr + e->next_offset)) {
+               t->verdict += delta_offset;
+       }
+
+       return 0;
+}
+
+/* Adjusts standard verdict jump positions after an insertion/deletion. */
+static int
+set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
+{
+       ENTRY_ITERATE((*handle)->entries.entrytable,
+                     (*handle)->entries.size,
+                     correct_verdict, (char *)(*handle)->entries.entrytable,
+                     offset, delta_offset);
+
+       set_changed(*handle);
+       return 1;
+}
+
+/* If prepend is set, then we are prepending to a chain: if the
+ * insertion position is an entry point, keep the entry point. */
+static int
+insert_rules(unsigned int num_rules, unsigned int rules_size,
+            const STRUCT_ENTRY *insert,
+            unsigned int offset, unsigned int num_rules_offset,
+            int prepend,
+            TC_HANDLE_T *handle)
+{
+       TC_HANDLE_T newh;
+       STRUCT_GETINFO newinfo;
+       unsigned int i;
+
+       if (offset >= (*handle)->entries.size) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       newinfo = (*handle)->info;
+
+       /* Fix up entry points. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               /* Entry points to START of chain, so keep same if
+                   inserting on at that point. */
+               if ((*handle)->info.hook_entry[i] > offset)
+                       newinfo.hook_entry[i] += rules_size;
+
+               /* Underflow always points to END of chain (policy),
+                  so if something is inserted at same point, it
+                  should be advanced. */
+               if ((*handle)->info.underflow[i] >= offset)
+                       newinfo.underflow[i] += rules_size;
+       }
+
+       newh = alloc_handle((*handle)->info.name,
+                           (*handle)->entries.size + rules_size,
+                           (*handle)->new_number + num_rules);
+       if (!newh)
+               return 0;
+       newh->info = newinfo;
+
+       /* Copy pre... */
+       memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
+       /* ... Insert new ... */
+       memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
+       /* ... copy post */
+       memcpy((char *)newh->entries.entrytable + offset + rules_size,
+              (char *)(*handle)->entries.entrytable + offset,
+              (*handle)->entries.size - offset);
+
+       /* Move counter map. */
+       /* Copy pre... */
+       memcpy(newh->counter_map, (*handle)->counter_map,
+              sizeof(struct counter_map) * num_rules_offset);
+       /* ... copy post */
+       memcpy(newh->counter_map + num_rules_offset + num_rules,
+              (*handle)->counter_map + num_rules_offset,
+              sizeof(struct counter_map) * ((*handle)->new_number
+                                            - num_rules_offset));
+       /* Set intermediates to no counter copy */
+       for (i = 0; i < num_rules; i++)
+               newh->counter_map[num_rules_offset+i]
+                       = ((struct counter_map){ COUNTER_MAP_SET, 0 });
+
+       newh->new_number = (*handle)->new_number + num_rules;
+       newh->entries.size = (*handle)->entries.size + rules_size;
+       newh->hooknames = (*handle)->hooknames;
+
+       newh->cache_chain_heads = (*handle)->cache_chain_heads;
+       newh->cache_num_builtins = (*handle)->cache_num_builtins;
+       newh->cache_num_chains = (*handle)->cache_num_chains;
+       newh->cache_rule_end = (*handle)->cache_rule_end;
+       newh->cache_chain_iteration = (*handle)->cache_chain_iteration;
+       if (!correct_cache(newh, offset, rules_size)) {
+               free(newh);
+               return 0;
+       }
+
+       free(*handle);
+       *handle = newh;
+
+       return set_verdict(offset, rules_size, handle);
+}
+
+static int
+delete_rules(unsigned int num_rules, unsigned int rules_size,
+            unsigned int offset, unsigned int num_rules_offset,
+            TC_HANDLE_T *handle)
+{
+       unsigned int i;
+
+       if (offset + rules_size > (*handle)->entries.size) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       /* Fix up entry points. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               /* In practice, we never delete up to a hook entry,
+                  since the built-in chains are always first,
+                  so these two are never equal */
+               if ((*handle)->info.hook_entry[i] >= offset + rules_size)
+                       (*handle)->info.hook_entry[i] -= rules_size;
+               else if ((*handle)->info.hook_entry[i] > offset) {
+                       fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
+                               i, (*handle)->info.hook_entry[i], offset);
+                       abort();
+               }
+
+               /* Underflow points to policy (terminal) rule in
+                   built-in, so sequality is valid here (when deleting
+                   the last rule). */
+               if ((*handle)->info.underflow[i] >= offset + rules_size)
+                       (*handle)->info.underflow[i] -= rules_size;
+               else if ((*handle)->info.underflow[i] > offset) {
+                       fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
+                               i, (*handle)->info.underflow[i], offset);
+                       abort();
+               }
+       }
+
+       /* Move the rules down. */
+       memmove((char *)(*handle)->entries.entrytable + offset,
+               (char *)(*handle)->entries.entrytable + offset + rules_size,
+               (*handle)->entries.size - (offset + rules_size));
+
+       /* Move the counter map down. */
+       memmove(&(*handle)->counter_map[num_rules_offset],
+               &(*handle)->counter_map[num_rules_offset + num_rules],
+               sizeof(struct counter_map)
+               * ((*handle)->new_number - (num_rules + num_rules_offset)));
+
+       /* Fix numbers */
+       (*handle)->new_number -= num_rules;
+       (*handle)->entries.size -= rules_size;
+
+       /* Fix the chain cache */
+       if (!correct_cache(*handle, offset, -(int)rules_size))
+               return 0;
+
+       return set_verdict(offset, -(int)rules_size, handle);
+}
+
+static int
+standard_map(STRUCT_ENTRY *e, int verdict)
+{
+       STRUCT_STANDARD_TARGET *t;
+
+       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+       if (t->target.u.target_size
+           != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
+               errno = EINVAL;
+               return 0;
+       }
+       /* memset for memcmp convenience on delete/replace */
+       memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+       strcpy(t->target.u.user.name, STANDARD_TARGET);
+       t->verdict = verdict;
+
+       return 1;
+}
+
+static int
+map_target(const TC_HANDLE_T handle,
+          STRUCT_ENTRY *e,
+          unsigned int offset,
+          STRUCT_ENTRY_TARGET *old)
+{
+       STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
+
+       /* Save old target (except data, which we don't change, except for
+          standard case, where we don't care). */
+       *old = *t;
+
+       /* Maybe it's empty (=> fall through) */
+       if (strcmp(t->u.user.name, "") == 0)
+               return standard_map(e, offset + e->next_offset);
+       /* Maybe it's a standard target name... */
+       else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
+               return standard_map(e, -NF_ACCEPT - 1);
+       else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
+               return standard_map(e, -NF_DROP - 1);
+       else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
+               return standard_map(e, -NF_QUEUE - 1);
+       else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
+               return standard_map(e, RETURN);
+       else if (TC_BUILTIN(t->u.user.name, handle)) {
+               /* Can't jump to builtins. */
+               errno = EINVAL;
+               return 0;
+       } else {
+               /* Maybe it's an existing chain name. */
+               struct chain_cache *c;
+
+               c = find_label(t->u.user.name, handle);
+               if (c)
+                       return standard_map(e, c->start_off);
+       }
+
+       /* Must be a module?  If not, kernel will reject... */
+       /* memset to all 0 for your memcmp convenience. */
+       memset(t->u.user.name + strlen(t->u.user.name),
+              0,
+              FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
+       return 1;
+}
+
+static void
+unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
+{
+       STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
+
+       /* Save old target (except data, which we don't change, except for
+          standard case, where we don't care). */
+       *t = *old;
+}
+
+/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
+int
+TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *e,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       unsigned int chainindex, offset;
+       STRUCT_ENTRY_TARGET old;
+       struct chain_cache *c;
+       STRUCT_ENTRY *tmp;
+       int ret;
+
+       iptc_fn = TC_INSERT_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+
+       tmp = index2entry(*handle, chainindex + rulenum);
+       if (!tmp || tmp > offset2entry(*handle, c->end_off)) {
+               errno = E2BIG;
+               return 0;
+       }
+       offset = index2offset(*handle, chainindex + rulenum);
+
+       /* Mapping target actually alters entry, but that's
+           transparent to the caller. */
+       if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
+               return 0;
+
+       ret = insert_rules(1, e->next_offset, e, offset,
+                          chainindex + rulenum, rulenum == 0, handle);
+       unmap_target((STRUCT_ENTRY *)e, &old);
+       return ret;
+}
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int
+TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
+                const STRUCT_ENTRY *e,
+                unsigned int rulenum,
+                TC_HANDLE_T *handle)
+{
+       unsigned int chainindex, offset;
+       STRUCT_ENTRY_TARGET old;
+       struct chain_cache *c;
+       STRUCT_ENTRY *tmp;
+       int ret;
+
+       iptc_fn = TC_REPLACE_ENTRY;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+
+       tmp = index2entry(*handle, chainindex + rulenum);
+       if (!tmp || tmp >= offset2entry(*handle, c->end_off)) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       offset = index2offset(*handle, chainindex + rulenum);
+       /* Replace = delete and insert. */
+       if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
+                         offset, chainindex + rulenum, handle))
+               return 0;
+
+       if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
+               return 0;
+
+       ret = insert_rules(1, e->next_offset, e, offset,
+                          chainindex + rulenum, 1, handle);
+       unmap_target((STRUCT_ENTRY *)e, &old);
+       return ret;
+}
+
+/* Append entry `fw' to chain `chain'.  Equivalent to insert with
+   rulenum = length of chain. */
+int
+TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *e,
+               TC_HANDLE_T *handle)
+{
+       struct chain_cache *c;
+       STRUCT_ENTRY_TARGET old;
+       int ret;
+
+       iptc_fn = TC_APPEND_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (!map_target(*handle, (STRUCT_ENTRY *)e,
+                       c->end_off, &old))
+               return 0;
+
+       ret = insert_rules(1, e->next_offset, e, c->end_off, 
+                          offset2index(*handle, c->end_off), 0, handle);
+       unmap_target((STRUCT_ENTRY *)e, &old);
+       return ret;
+}
+
+static inline int
+match_different(const STRUCT_ENTRY_MATCH *a,
+               const unsigned char *a_elems,
+               const unsigned char *b_elems,
+               unsigned char **maskptr)
+{
+       const STRUCT_ENTRY_MATCH *b;
+       unsigned int i;
+
+       /* Offset of b is the same as a. */
+       b = (void *)b_elems + ((unsigned char *)a - a_elems);
+
+       if (a->u.match_size != b->u.match_size)
+               return 1;
+
+       if (strcmp(a->u.user.name, b->u.user.name) != 0)
+               return 1;
+
+       *maskptr += ALIGN(sizeof(*a));
+
+       for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
+               if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
+                       return 1;
+       *maskptr += i;
+       return 0;
+}
+
+static inline int
+target_different(const unsigned char *a_targdata,
+                const unsigned char *b_targdata,
+                unsigned int tdatasize,
+                const unsigned char *mask)
+{
+       unsigned int i;
+       for (i = 0; i < tdatasize; i++)
+               if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
+                       return 1;
+
+       return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a,
+       const STRUCT_ENTRY *b,
+       unsigned char *matchmask);
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int
+TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *origfw,
+               unsigned char *matchmask,
+               TC_HANDLE_T *handle)
+{
+       unsigned int offset;
+       struct chain_cache *c;
+       STRUCT_ENTRY *e, *fw;
+
+       iptc_fn = TC_DELETE_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       fw = malloc(origfw->next_offset);
+       if (fw == NULL) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       for (offset = c->start_off; offset < c->end_off;
+            offset += e->next_offset) {
+               STRUCT_ENTRY_TARGET discard;
+
+               memcpy(fw, origfw, origfw->next_offset);
+
+               /* FIXME: handle this in is_same --RR */
+               if (!map_target(*handle, fw, offset, &discard)) {
+                       free(fw);
+                       return 0;
+               }
+               e = get_entry(*handle, offset);
+
+#if 0
+               printf("Deleting:\n");
+               dump_entry(newe);
+#endif
+               if (is_same(e, fw, matchmask)) {
+                       int ret;
+                       ret = delete_rules(1, e->next_offset,
+                                          offset, entry2index(*handle, e),
+                                          handle);
+                       free(fw);
+                       return ret;
+               }
+       }
+
+       free(fw);
+       errno = ENOENT;
+       return 0;
+}
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int
+TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
+                   unsigned int rulenum,
+                   TC_HANDLE_T *handle)
+{
+       unsigned int index;
+       int ret;
+       STRUCT_ENTRY *e;
+       struct chain_cache *c;
+
+       iptc_fn = TC_DELETE_NUM_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       index = offset2index(*handle, c->start_off) + rulenum;
+
+       if (index >= offset2index(*handle, c->end_off)) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       e = index2entry(*handle, index);
+       if (e == NULL) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
+                          index, handle);
+       return ret;
+}
+
+/* Check the packet `fw' on chain `chain'.  Returns the verdict, or
+   NULL and sets errno. */
+const char *
+TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
+               STRUCT_ENTRY *entry,
+               TC_HANDLE_T *handle)
+{
+       errno = ENOSYS;
+       return NULL;
+}
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int
+TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       unsigned int startindex, endindex;
+       STRUCT_ENTRY *startentry, *endentry;
+       struct chain_cache *c;
+       int ret;
+
+       iptc_fn = TC_FLUSH_ENTRIES;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+       startindex = offset2index(*handle, c->start_off);
+       endindex = offset2index(*handle, c->end_off);
+       startentry = offset2entry(*handle, c->start_off);
+       endentry = offset2entry(*handle, c->end_off);
+
+       ret = delete_rules(endindex - startindex,
+                          (char *)endentry - (char *)startentry,
+                          c->start_off, startindex,
+                          handle);
+       return ret;
+}
+
+/* Zeroes the counters in a chain. */
+int
+TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       unsigned int i, end;
+       struct chain_cache *c;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       i = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       for (; i <= end; i++) {
+               if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
+                       (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
+       }
+       set_changed(*handle);
+
+       return 1;
+}
+
+STRUCT_COUNTERS *
+TC_READ_COUNTER(const IPT_CHAINLABEL chain,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_cache *c;
+       unsigned int chainindex, end;
+
+       iptc_fn = TC_READ_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               errno = E2BIG;
+               return NULL;
+       }
+
+       e = index2entry(*handle, chainindex + rulenum);
+
+       return &e->counters;
+}
+
+int
+TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_cache *c;
+       unsigned int chainindex, end;
+       
+       iptc_fn = TC_ZERO_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       e = index2entry(*handle, chainindex + rulenum);
+
+       if ((*handle)->counter_map[chainindex + rulenum].maptype
+                       == COUNTER_MAP_NORMAL_MAP) {
+               (*handle)->counter_map[chainindex + rulenum].maptype
+                        = COUNTER_MAP_ZEROED;
+       }
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+int 
+TC_SET_COUNTER(const IPT_CHAINLABEL chain,
+              unsigned int rulenum,
+              STRUCT_COUNTERS *counters,
+              TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_cache *c;
+       unsigned int chainindex, end;
+
+       iptc_fn = TC_SET_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       e = index2entry(*handle, chainindex + rulenum);
+
+       (*handle)->counter_map[chainindex + rulenum].maptype
+               = COUNTER_MAP_SET;
+
+       memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Creates a new chain. */
+/* To create a chain, create two rules: error node and unconditional
+ * return. */
+int
+TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       int ret;
+       struct {
+               STRUCT_ENTRY head;
+               struct ipt_error_target name;
+               STRUCT_ENTRY ret;
+               STRUCT_STANDARD_TARGET target;
+       } newc;
+       unsigned int destination;
+
+       iptc_fn = TC_CREATE_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (find_label(chain, *handle)
+           || strcmp(chain, LABEL_DROP) == 0
+           || strcmp(chain, LABEL_ACCEPT) == 0
+           || strcmp(chain, LABEL_QUEUE) == 0
+           || strcmp(chain, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       memset(&newc, 0, sizeof(newc));
+       newc.head.target_offset = sizeof(STRUCT_ENTRY);
+       newc.head.next_offset
+               = sizeof(STRUCT_ENTRY)
+               + ALIGN(sizeof(struct ipt_error_target));
+       strcpy(newc.name.t.u.user.name, ERROR_TARGET);
+       newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
+       strcpy(newc.name.error, chain);
+
+       newc.ret.target_offset = sizeof(STRUCT_ENTRY);
+       newc.ret.next_offset
+               = sizeof(STRUCT_ENTRY)
+               + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
+       newc.target.target.u.target_size
+               = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       newc.target.verdict = RETURN;
+
+       destination = index2offset(*handle, (*handle)->new_number -1);
+
+       /* Add just before terminal entry */
+       ret = insert_rules(2, sizeof(newc), &newc.head,
+                          destination,
+                          (*handle)->new_number - 1,
+                          0, handle);
+
+       set_changed(*handle);
+
+       /* add chain cache info for this chain */
+       add_chain_cache(*handle, chain, 
+                       destination+newc.head.next_offset, 
+                       destination+newc.head.next_offset);
+
+       return ret;
+}
+
+static int
+count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
+{
+       STRUCT_STANDARD_TARGET *t;
+
+       if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+               if (t->verdict == offset)
+                       (*ref)++;
+       }
+
+       return 0;
+}
+
+/* Get the number of references to this chain. */
+int
+TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
+                 TC_HANDLE_T *handle)
+{
+       struct chain_cache *c;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       *ref = 0;
+       ENTRY_ITERATE((*handle)->entries.entrytable,
+                     (*handle)->entries.size,
+                     count_ref, c->start_off, ref);
+       return 1;
+}
+
+/* Deletes a chain. */
+int
+TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       unsigned int labelidx, labeloff;
+       unsigned int references;
+       struct chain_cache *c;
+       int ret;
+       STRUCT_ENTRY *start;
+
+       if (!TC_GET_REFERENCES(&references, chain, handle))
+               return 0;
+
+       iptc_fn = TC_DELETE_CHAIN;
+
+       if (TC_BUILTIN(chain, *handle)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       if (references > 0) {
+               errno = EMLINK;
+               return 0;
+       }
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (c->start_off != c->end_off) {
+               errno = ENOTEMPTY;
+               return 0;
+       }
+
+       /* Need label index: preceeds chain start */
+       labelidx = offset2index(*handle, c->start_off) - 1;
+       labeloff = index2offset(*handle, labelidx);
+
+       start = offset2entry(*handle, c->start_off);
+
+       ret = delete_rules(2,
+                          get_entry(*handle, labeloff)->next_offset
+                          + start->next_offset,
+                          labeloff, labelidx, handle);
+       return ret;
+}
+
+/* Renames a chain. */
+int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
+                   const IPT_CHAINLABEL newname,
+                   TC_HANDLE_T *handle)
+{
+       unsigned int labeloff, labelidx;
+       struct chain_cache *c;
+       struct ipt_error_target *t;
+
+       iptc_fn = TC_RENAME_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (find_label(newname, *handle)
+           || strcmp(newname, LABEL_DROP) == 0
+           || strcmp(newname, LABEL_ACCEPT) == 0
+           || strcmp(newname, LABEL_QUEUE) == 0
+           || strcmp(newname, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (!(c = find_label(oldname, *handle))
+           || TC_BUILTIN(oldname, *handle)) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       /* Need label index: preceeds chain start */
+       labelidx = offset2index(*handle, c->start_off) - 1;
+       labeloff = index2offset(*handle, labelidx);
+
+       t = (struct ipt_error_target *)
+               GET_TARGET(get_entry(*handle, labeloff));
+
+       memset(t->error, 0, sizeof(t->error));
+       strcpy(t->error, newname);
+
+       /* update chain cache */
+       memset(c->name, 0, sizeof(c->name));
+       strcpy(c->name, newname);
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Sets the policy on a built-in chain. */
+int
+TC_SET_POLICY(const IPT_CHAINLABEL chain,
+             const IPT_CHAINLABEL policy,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
+{
+       unsigned int hook;
+       unsigned int policyoff, ctrindex;
+       STRUCT_ENTRY *e;
+       STRUCT_STANDARD_TARGET *t;
+
+       iptc_fn = TC_SET_POLICY;
+       /* Figure out which chain. */
+       hook = TC_BUILTIN(chain, *handle);
+       if (hook == 0) {
+               errno = ENOENT;
+               return 0;
+       } else
+               hook--;
+
+       policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
+       if (policyoff != (*handle)->info.underflow[hook]) {
+               printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
+                      chain, policyoff, (*handle)->info.underflow[hook]);
+               return 0;
+       }
+
+       e = get_entry(*handle, policyoff);
+       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+       if (strcmp(policy, LABEL_ACCEPT) == 0)
+               t->verdict = -NF_ACCEPT - 1;
+       else if (strcmp(policy, LABEL_DROP) == 0)
+               t->verdict = -NF_DROP - 1;
+       else {
+               errno = EINVAL;
+               return 0;
+       }
+
+       ctrindex = entry2index(*handle, e);
+
+       if (counters) {
+               /* set byte and packet counters */
+               memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+
+               (*handle)->counter_map[ctrindex].maptype
+                       = COUNTER_MAP_SET;
+
+       } else {
+               (*handle)->counter_map[ctrindex]
+                       = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
+       }
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Without this, on gcc 2.7.2.3, we get:
+   libiptc.c: In function `TC_COMMIT':
+   libiptc.c:833: fixed or forbidden register was spilled.
+   This may be due to a compiler bug or to impossible asm
+   statements or clauses.
+*/
+static void
+subtract_counters(STRUCT_COUNTERS *answer,
+                 const STRUCT_COUNTERS *a,
+                 const STRUCT_COUNTERS *b)
+{
+       answer->pcnt = a->pcnt - b->pcnt;
+       answer->bcnt = a->bcnt - b->bcnt;
+}
+
+int
+TC_COMMIT(TC_HANDLE_T *handle)
+{
+       /* Replace, then map back the counters. */
+       STRUCT_REPLACE *repl;
+       STRUCT_COUNTERS_INFO *newcounters;
+       unsigned int i;
+       size_t counterlen;
+
+       CHECK(*handle);
+
+       counterlen = sizeof(STRUCT_COUNTERS_INFO)
+                       + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
+
+#if 0
+       TC_DUMP_ENTRIES(*handle);
+#endif
+
+       /* Don't commit if nothing changed. */
+       if (!(*handle)->changed)
+               goto finished;
+
+       repl = malloc(sizeof(*repl) + (*handle)->entries.size);
+       if (!repl) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* These are the old counters we will get from kernel */
+       repl->counters = malloc(sizeof(STRUCT_COUNTERS)
+                               * (*handle)->info.num_entries);
+       if (!repl->counters) {
+               free(repl);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* These are the counters we're going to put back, later. */
+       newcounters = malloc(counterlen);
+       if (!newcounters) {
+               free(repl->counters);
+               free(repl);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       strcpy(repl->name, (*handle)->info.name);
+       repl->num_entries = (*handle)->new_number;
+       repl->size = (*handle)->entries.size;
+       memcpy(repl->hook_entry, (*handle)->info.hook_entry,
+              sizeof(repl->hook_entry));
+       memcpy(repl->underflow, (*handle)->info.underflow,
+              sizeof(repl->underflow));
+       repl->num_counters = (*handle)->info.num_entries;
+       repl->valid_hooks = (*handle)->info.valid_hooks;
+       memcpy(repl->entries, (*handle)->entries.entrytable,
+              (*handle)->entries.size);
+
+       if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
+                      sizeof(*repl) + (*handle)->entries.size) < 0) {
+               free(repl->counters);
+               free(repl);
+               free(newcounters);
+               return 0;
+       }
+
+       /* Put counters back. */
+       strcpy(newcounters->name, (*handle)->info.name);
+       newcounters->num_counters = (*handle)->new_number;
+       for (i = 0; i < (*handle)->new_number; i++) {
+               unsigned int mappos = (*handle)->counter_map[i].mappos;
+               switch ((*handle)->counter_map[i].maptype) {
+               case COUNTER_MAP_NOMAP:
+                       newcounters->counters[i]
+                               = ((STRUCT_COUNTERS){ 0, 0 });
+                       break;
+
+               case COUNTER_MAP_NORMAL_MAP:
+                       /* Original read: X.
+                        * Atomic read on replacement: X + Y.
+                        * Currently in kernel: Z.
+                        * Want in kernel: X + Y + Z.
+                        * => Add in X + Y
+                        * => Add in replacement read.
+                        */
+                       newcounters->counters[i] = repl->counters[mappos];
+                       break;
+
+               case COUNTER_MAP_ZEROED:
+                       /* Original read: X.
+                        * Atomic read on replacement: X + Y.
+                        * Currently in kernel: Z.
+                        * Want in kernel: Y + Z.
+                        * => Add in Y.
+                        * => Add in (replacement read - original read).
+                        */
+                       subtract_counters(&newcounters->counters[i],
+                                         &repl->counters[mappos],
+                                         &index2entry(*handle, i)->counters);
+                       break;
+
+               case COUNTER_MAP_SET:
+                       /* Want to set counter (iptables-restore) */
+
+                       memcpy(&newcounters->counters[i],
+                              &index2entry(*handle, i)->counters,
+                              sizeof(STRUCT_COUNTERS));
+
+                       break;
+               }
+       }
+
+#ifdef KERNEL_64_USERSPACE_32
+       {
+               /* Kernel will think that pointer should be 64-bits, and get
+                  padding.  So we accomodate here (assumption: alignment of
+                  `counters' is on 64-bit boundary). */
+               u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
+               if ((unsigned long)&newcounters->counters % 8 != 0) {
+                       fprintf(stderr,
+                               "counters alignment incorrect! Mail rusty!\n");
+                       abort();
+               }
+               *kernptr = newcounters->counters;
+       }
+#endif /* KERNEL_64_USERSPACE_32 */
+
+       if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
+                      newcounters, counterlen) < 0) {
+               free(repl->counters);
+               free(repl);
+               free(newcounters);
+               return 0;
+       }
+
+       free(repl->counters);
+       free(repl);
+       free(newcounters);
+
+ finished:
+       TC_FREE(handle);
+       return 1;
+}
+
+/* Get raw socket. */
+int
+TC_GET_RAW_SOCKET()
+{
+       return sockfd;
+}
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *
+TC_STRERROR(int err)
+{
+       unsigned int i;
+       struct table_struct {
+               void *fn;
+               int err;
+               const char *message;
+       } table [] =
+         { { TC_INIT, EPERM, "Permission denied (you must be root)" },
+           { TC_INIT, EINVAL, "Module is wrong version" },
+           { TC_INIT, ENOENT, 
+                   "Table does not exist (do you need to insmod?)" },
+           { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
+           { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
+           { TC_DELETE_CHAIN, EMLINK,
+             "Can't delete chain with references left" },
+           { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
+           { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
+           { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
+           { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
+           { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
+           { TC_INSERT_ENTRY, EINVAL, "Target problem" },
+           /* EINVAL for CHECK probably means bad interface. */
+           { TC_CHECK_PACKET, EINVAL,
+             "Bad arguments (does that interface exist?)" },
+           { TC_CHECK_PACKET, ENOSYS,
+             "Checking will most likely never get implemented" },
+           /* ENOENT for DELETE probably means no matching rule */
+           { TC_DELETE_ENTRY, ENOENT,
+             "Bad rule (does a matching rule exist in that chain?)" },
+           { TC_SET_POLICY, ENOENT,
+             "Bad built-in chain name" },
+           { TC_SET_POLICY, EINVAL,
+             "Bad policy name" },
+
+           { NULL, 0, "Incompatible with this kernel" },
+           { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
+           { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
+           { NULL, ENOMEM, "Memory allocation problem" },
+           { NULL, ENOENT, "No chain/target/match by that name" },
+         };
+
+       for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+               if ((!table[i].fn || table[i].fn == iptc_fn)
+                   && table[i].err == err)
+                       return table[i].message;
+       }
+
+       return strerror(err);
+}
diff --git a/libiptc2/libiptc2.c b/libiptc2/libiptc2.c
new file mode 100644 (file)
index 0000000..a7d7915
--- /dev/null
@@ -0,0 +1,1758 @@
+/* Library which manipulates firewall rules.  Version $Revision: 1.39 $ */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+ * COPYING for details). 
+ * (C) 2000-2003 by the Netfilter Core Team <coreteam@netfilter.org>
+ *
+ * 2003-Jun-20: Harald Welte <laforge@netfilter.org:
+ *     - Reimplementation of chain cache to use offsets instead of entries
+ * 
+ */
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/lib/iptables"
+#endif
+
+#ifndef __OPTIMIZE__
+STRUCT_ENTRY_TARGET *
+GET_TARGET(STRUCT_ENTRY *e)
+{
+       return (void *)e + e->target_offset;
+}
+#endif
+
+static int sockfd = -1;
+static void *iptc_fn = NULL;
+
+static const char *hooknames[]
+= { [HOOK_PRE_ROUTING]  "PREROUTING",
+    [HOOK_LOCAL_IN]     "INPUT",
+    [HOOK_FORWARD]      "FORWARD",
+    [HOOK_LOCAL_OUT]    "OUTPUT",
+    [HOOK_POST_ROUTING] "POSTROUTING",
+#ifdef HOOK_DROPPING
+    [HOOK_DROPPING]    "DROPPING"
+#endif
+};
+
+struct counter_map
+{
+       enum {
+               COUNTER_MAP_NOMAP,
+               COUNTER_MAP_NORMAL_MAP,
+               COUNTER_MAP_ZEROED,
+               COUNTER_MAP_SET
+       } maptype;
+       unsigned int mappos;
+};
+
+/* Convenience structures */
+struct ipt_error_target
+{
+       STRUCT_ENTRY_TARGET t;
+       char error[TABLE_MAXNAMELEN];
+};
+
+struct rule_head
+{
+       struct list_head list;
+       
+       struct chain_head *chain;
+
+       unsigned int size;
+       STRUCT_ENTRY entry[0];
+}
+
+struct chain_head
+{
+       struct list_head list;
+
+       char name[TABLE_MAXNAMELEN];
+       unsigned int hooknum;
+       struct list_head rules;
+};
+
+STRUCT_TC_HANDLE
+{
+       /* Have changes been made? */
+       int changed;
+
+       struct list_head chains;
+       
+       struct chain_head *chain_iterator_cur;
+
+#if 0
+       /* Size in here reflects original state. */
+       STRUCT_GETINFO info;
+
+       struct counter_map *counter_map;
+       /* Array of hook names */
+       const char **hooknames;
+
+       /* Cached position of chain heads (NULL = no cache). */
+       unsigned int cache_num_chains;
+       unsigned int cache_num_builtins;
+       struct chain_cache *cache_chain_heads;
+
+       /* Chain iterator: current chain cache entry. */
+       struct chain_cache *cache_chain_iteration;
+
+       /* Rule iterator: terminal rule */
+       STRUCT_ENTRY *cache_rule_end;
+
+       /* Number in here reflects current state. */
+       unsigned int new_number;
+       STRUCT_GET_ENTRIES entries;
+#endif
+};
+
+static void
+set_changed(TC_HANDLE_T h)
+{
+       h->changed = 1;
+}
+
+#ifdef IPTC_DEBUG
+static void do_check(TC_HANDLE_T h, unsigned int line);
+#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
+#else
+#define CHECK(h)
+#endif
+
+static inline int
+get_number(const STRUCT_ENTRY *i,
+          const STRUCT_ENTRY *seek,
+          unsigned int *pos)
+{
+       if (i == seek)
+               return 1;
+       (*pos)++;
+       return 0;
+}
+
+static unsigned int
+entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
+{
+       unsigned int pos = 0;
+
+       if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                         get_number, seek, &pos) == 0) {
+               fprintf(stderr, "ERROR: offset %i not an entry!\n",
+                       (char *)seek - (char *)h->entries.entrytable);
+               abort();
+       }
+       return pos;
+}
+
+static inline int
+get_entry_n(STRUCT_ENTRY *i,
+           unsigned int number,
+           unsigned int *pos,
+           STRUCT_ENTRY **pe)
+{
+       if (*pos == number) {
+               *pe = i;
+               return 1;
+       }
+       (*pos)++;
+       return 0;
+}
+
+static STRUCT_ENTRY *
+index2entry(TC_HANDLE_T h, unsigned int index)
+{
+       unsigned int pos = 0;
+       STRUCT_ENTRY *ret = NULL;
+
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     get_entry_n, index, &pos, &ret);
+
+       return ret;
+}
+
+static inline STRUCT_ENTRY *
+get_entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
+}
+
+static inline unsigned long
+entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+       return (void *)e - (void *)h->entries.entrytable;
+}
+
+static inline unsigned long
+index2offset(TC_HANDLE_T h, unsigned int index)
+{
+       return entry2offset(h, index2entry(h, index));
+}
+
+static inline STRUCT_ENTRY *
+offset2entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *) ((void *)h->entries.entrytable+offset);
+}
+
+static inline unsigned int
+offset2index(const TC_HANDLE_T h, unsigned int offset)
+{
+       return entry2index(h, offset2entry(h, offset));
+}
+
+
+static const char *
+get_errorlabel(TC_HANDLE_T h, unsigned int offset)
+{
+       STRUCT_ENTRY *e;
+
+       e = get_entry(h, offset);
+       if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) {
+               fprintf(stderr, "ERROR: offset %u not an error node!\n",
+                       offset);
+               abort();
+       }
+
+       return (const char *)GET_TARGET(e)->data;
+}
+
+/* Allocate handle of given size */
+static TC_HANDLE_T
+alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
+{
+       size_t len;
+       TC_HANDLE_T h;
+
+       len = sizeof(STRUCT_TC_HANDLE)
+               + size
+               + num_rules * sizeof(struct counter_map);
+
+       if ((h = malloc(len)) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       h->changed = 0;
+       h->cache_num_chains = 0;
+       h->cache_chain_heads = NULL;
+       h->counter_map = (void *)h
+               + sizeof(STRUCT_TC_HANDLE)
+               + size;
+       strcpy(h->info.name, tablename);
+       strcpy(h->entries.name, tablename);
+
+       return h;
+}
+
+TC_HANDLE_T
+TC_INIT(const char *tablename)
+{
+       TC_HANDLE_T h;
+       STRUCT_GETINFO info;
+       unsigned int i;
+       int tmp;
+       socklen_t s;
+
+       iptc_fn = TC_INIT;
+
+       if (sockfd != -1) {
+               close(sockfd);
+               sockfd = -1;
+       }
+
+       if (strlen(tablename) >= TABLE_MAXNAMELEN) {
+               errno = EINVAL;
+               return NULL;
+       }
+       
+       sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0)
+               return NULL;
+
+       s = sizeof(info);
+
+       strcpy(info.name, tablename);
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
+               return NULL;
+
+       if ((h = alloc_handle(info.name, info.size, info.num_entries))
+           == NULL) {
+               close(sockfd);
+               sockfd = -1;
+               return NULL;
+       }
+
+/* Too hard --RR */
+#if 0
+       sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
+       dynlib = dlopen(pathname, RTLD_NOW);
+       if (!dynlib) {
+               errno = ENOENT;
+               return NULL;
+       }
+       h->hooknames = dlsym(dynlib, "hooknames");
+       if (!h->hooknames) {
+               errno = ENOENT;
+               return NULL;
+       }
+#else
+       h->hooknames = hooknames;
+#endif
+
+       /* Initialize current state */
+       h->info = info;
+       h->new_number = h->info.num_entries;
+       for (i = 0; i < h->info.num_entries; i++)
+               h->counter_map[i]
+                       = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
+
+       h->entries.size = h->info.size;
+
+       tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
+
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
+                      &tmp) < 0) {
+               close(sockfd);
+               sockfd = -1;
+               free(h);
+               return NULL;
+       }
+
+       CHECK(h);
+       return h;
+}
+
+void
+TC_FREE(TC_HANDLE_T *h)
+{
+       close(sockfd);
+       sockfd = -1;
+       if ((*h)->cache_chain_heads)
+               free((*h)->cache_chain_heads);
+       free(*h);
+       *h = NULL;
+}
+
+static inline int
+print_match(const STRUCT_ENTRY_MATCH *m)
+{
+       printf("Match name: `%s'\n", m->u.user.name);
+       return 0;
+}
+
+static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
+void
+TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
+{
+       CHECK(handle);
+
+       printf("libiptc v%s.  %u entries, %u bytes.\n",
+              IPTABLES_VERSION,
+              handle->new_number, handle->entries.size);
+       printf("Table `%s'\n", handle->info.name);
+       printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+              handle->info.hook_entry[HOOK_PRE_ROUTING],
+              handle->info.hook_entry[HOOK_LOCAL_IN],
+              handle->info.hook_entry[HOOK_FORWARD],
+              handle->info.hook_entry[HOOK_LOCAL_OUT],
+              handle->info.hook_entry[HOOK_POST_ROUTING]);
+       printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+              handle->info.underflow[HOOK_PRE_ROUTING],
+              handle->info.underflow[HOOK_LOCAL_IN],
+              handle->info.underflow[HOOK_FORWARD],
+              handle->info.underflow[HOOK_LOCAL_OUT],
+              handle->info.underflow[HOOK_POST_ROUTING]);
+
+       ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
+                     dump_entry, handle);
+}
+
+/* Returns 0 if not hook entry, else hooknumber + 1 */
+static inline unsigned int
+is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
+{
+       unsigned int i;
+
+       for (i = 0; i < NUMHOOKS; i++) {
+               if ((h->info.valid_hooks & (1 << i))
+                   && get_entry(h, h->info.hook_entry[i]) == e)
+                       return i+1;
+       }
+       return 0;
+}
+static int alphasort(const void *a, const void *b)
+{
+       return strcmp(((struct chain_cache *)a)->name,
+                     ((struct chain_cache *)b)->name);
+}
+
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+find_label(const char *name, TC_HANDLE_T handle)
+{
+       struct list_head *pos;
+
+       if (!handle->chains)
+               return NULL;
+
+       list_for_each(pos, &handle->chains) {
+               struct chain_head *c = list_entry(pos, struct chain_head, list);
+               if (!strcmp(c->name, name))
+                       return c;
+       }
+
+       return NULL;
+}
+
+/* Does this chain exist? */
+int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
+{
+       return find_label(chain, handle) != NULL;
+}
+
+/* Returns the position of the final (ie. unconditional) element. */
+static unsigned int
+get_chain_end(const TC_HANDLE_T handle, unsigned int start)
+{
+       unsigned int last_off, off;
+       STRUCT_ENTRY *e;
+
+       last_off = start;
+       e = get_entry(handle, start);
+
+       /* Terminate when we meet a error label or a hook entry. */
+       for (off = start + e->next_offset;
+            off < handle->entries.size;
+            last_off = off, off += e->next_offset) {
+               STRUCT_ENTRY_TARGET *t;
+               unsigned int i;
+
+               e = get_entry(handle, off);
+
+               /* We hit an entry point. */
+               for (i = 0; i < NUMHOOKS; i++) {
+                       if ((handle->info.valid_hooks & (1 << i))
+                           && off == handle->info.hook_entry[i])
+                               return last_off;
+               }
+
+               /* We hit a user chain label */
+               t = GET_TARGET(e);
+               if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
+                       return last_off;
+       }
+       /* SHOULD NEVER HAPPEN */
+       fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
+               handle->entries.size, off);
+       abort();
+}
+
+/* Iterator functions to run through the chains. */
+const char *
+TC_FIRST_CHAIN(TC_HANDLE_T *handle)
+{
+       (*handle)->chain_iterator_cur = (*handle)->chains;
+
+       return (*handle)->chains.name;
+}
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *
+TC_NEXT_CHAIN(TC_HANDLE_T *handle)
+{
+       struct chain_head *next = list_entry(&(*handle)->chain_iterator_cur->list.next, struct chain_head, list);
+       (*handle)->chain_iterator_cur = next;
+
+       if (next == (*handle)->chains)
+               return NULL;
+
+       return next->name;
+}
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const STRUCT_ENTRY *
+TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r;
+
+       c = find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       /* Empty chain: single return/policy rule */
+       if (list_empty(c->rules))
+               return NULL;
+
+       r = list_entry(&c->rules.next, struct rule_head, list);
+       (*handle)->rule_iterator_cur = r;
+
+       return r->entry;
+}
+
+/* Returns NULL when rules run out. */
+const STRUCT_ENTRY *
+TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
+{
+       struct rule_head *r = list_entry((*handle)->rule_iterator_cur->list.next, struct rule_head, list);
+
+       if (r == r->chain)
+               return NULL;
+
+       /* NOTE: prev is without any influence ! */
+       return r->entry;
+}
+
+#if 0
+/* How many rules in this chain? */
+unsigned int
+TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
+{
+       unsigned int off = 0;
+       STRUCT_ENTRY *start, *end;
+
+       CHECK(*handle);
+       if (!find_label(&off, chain, *handle)) {
+               errno = ENOENT;
+               return (unsigned int)-1;
+       }
+
+       start = get_entry(*handle, off);
+       end = get_entry(*handle, get_chain_end(*handle, off));
+
+       return entry2index(*handle, end) - entry2index(*handle, start);
+}
+
+/* Get n'th rule in this chain. */
+const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
+                               unsigned int n,
+                               TC_HANDLE_T *handle)
+{
+       unsigned int pos = 0, chainindex;
+
+       CHECK(*handle);
+       if (!find_label(&pos, chain, *handle)) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       chainindex = entry2index(*handle, get_entry(*handle, pos));
+
+       return index2entry(*handle, chainindex + n);
+}
+#endif
+
+static const char *
+target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
+{
+       int spos;
+       unsigned int labelidx;
+       STRUCT_ENTRY *jumpto;
+
+       /* To avoid const warnings */
+       STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
+
+       if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
+               return GET_TARGET(e)->u.user.name;
+
+       /* Standard target: evaluate */
+       spos = *(int *)GET_TARGET(e)->data;
+       if (spos < 0) {
+               if (spos == RETURN)
+                       return LABEL_RETURN;
+               else if (spos == -NF_ACCEPT-1)
+                       return LABEL_ACCEPT;
+               else if (spos == -NF_DROP-1)
+                       return LABEL_DROP;
+               else if (spos == -NF_QUEUE-1)
+                       return LABEL_QUEUE;
+
+               fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
+                       entry2offset(handle, e), handle->entries.size,
+                       spos);
+               abort();
+       }
+
+       jumpto = get_entry(handle, spos);
+
+       /* Fall through rule */
+       if (jumpto == (void *)e + e->next_offset)
+               return "";
+
+       /* Must point to head of a chain: ie. after error rule */
+       labelidx = entry2index(handle, jumpto) - 1;
+       return get_errorlabel(handle, index2offset(handle, labelidx));
+}
+
+/* Returns a pointer to the target name of this position. */
+const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
+                         TC_HANDLE_T *handle)
+{
+       return target_name(*handle, e);
+}
+
+/* Is this a built-in chain?  Actually returns hook + 1. */
+int
+TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
+{
+       unsigned int i;
+
+       for (i = 0; i < NUMHOOKS; i++) {
+               if ((handle->info.valid_hooks & (1 << i))
+                   && handle->hooknames[i]
+                   && strcmp(handle->hooknames[i], chain) == 0)
+                       return i+1;
+       }
+       return 0;
+}
+
+/* Get the policy of a given built-in chain */
+const char *
+TC_GET_POLICY(const char *chain,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
+{
+       unsigned int start;
+       STRUCT_ENTRY *e;
+       int hook;
+
+       hook = TC_BUILTIN(chain, *handle);
+       if (hook != 0)
+               start = (*handle)->info.hook_entry[hook-1];
+       else
+               return NULL;
+
+       e = get_entry(*handle, get_chain_end(*handle, start));
+       *counters = e->counters;
+
+       return target_name(*handle, e);
+}
+
+static int
+correct_verdict(STRUCT_ENTRY *e,
+               char *base,
+               unsigned int offset, int delta_offset)
+{
+       STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
+       unsigned int curr = (char *)e - base;
+
+       /* Trap: insert of fall-through rule.  Don't change fall-through
+          verdict to jump-over-next-rule. */
+       if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
+           && t->verdict > (int)offset
+           && !(curr == offset &&
+                t->verdict == curr + e->next_offset)) {
+               t->verdict += delta_offset;
+       }
+
+       return 0;
+}
+
+/* Adjusts standard verdict jump positions after an insertion/deletion. */
+static int
+set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
+{
+       ENTRY_ITERATE((*handle)->entries.entrytable,
+                     (*handle)->entries.size,
+                     correct_verdict, (char *)(*handle)->entries.entrytable,
+                     offset, delta_offset);
+
+       set_changed(*handle);
+       return 1;
+}
+
+/* If prepend is set, then we are prepending to a chain: if the
+ * insertion position is an entry point, keep the entry point. */
+static int
+insert_rules(unsigned int num_rules, unsigned int rules_size,
+            const STRUCT_ENTRY *insert,
+            unsigned int offset, unsigned int num_rules_offset,
+            int prepend,
+            TC_HANDLE_T *handle)
+{
+       TC_HANDLE_T newh;
+       STRUCT_GETINFO newinfo;
+       unsigned int i;
+
+       if (offset >= (*handle)->entries.size) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       newinfo = (*handle)->info;
+
+       /* Fix up entry points. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               /* Entry points to START of chain, so keep same if
+                   inserting on at that point. */
+               if ((*handle)->info.hook_entry[i] > offset)
+                       newinfo.hook_entry[i] += rules_size;
+
+               /* Underflow always points to END of chain (policy),
+                  so if something is inserted at same point, it
+                  should be advanced. */
+               if ((*handle)->info.underflow[i] >= offset)
+                       newinfo.underflow[i] += rules_size;
+       }
+
+       newh = alloc_handle((*handle)->info.name,
+                           (*handle)->entries.size + rules_size,
+                           (*handle)->new_number + num_rules);
+       if (!newh)
+               return 0;
+       newh->info = newinfo;
+
+       /* Copy pre... */
+       memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
+       /* ... Insert new ... */
+       memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
+       /* ... copy post */
+       memcpy((char *)newh->entries.entrytable + offset + rules_size,
+              (char *)(*handle)->entries.entrytable + offset,
+              (*handle)->entries.size - offset);
+
+       /* Move counter map. */
+       /* Copy pre... */
+       memcpy(newh->counter_map, (*handle)->counter_map,
+              sizeof(struct counter_map) * num_rules_offset);
+       /* ... copy post */
+       memcpy(newh->counter_map + num_rules_offset + num_rules,
+              (*handle)->counter_map + num_rules_offset,
+              sizeof(struct counter_map) * ((*handle)->new_number
+                                            - num_rules_offset));
+       /* Set intermediates to no counter copy */
+       for (i = 0; i < num_rules; i++)
+               newh->counter_map[num_rules_offset+i]
+                       = ((struct counter_map){ COUNTER_MAP_SET, 0 });
+
+       newh->new_number = (*handle)->new_number + num_rules;
+       newh->entries.size = (*handle)->entries.size + rules_size;
+       newh->hooknames = (*handle)->hooknames;
+
+       if ((*handle)->cache_chain_heads)
+               free((*handle)->cache_chain_heads);
+       free(*handle);
+       *handle = newh;
+
+       return set_verdict(offset, rules_size, handle);
+}
+
+static int
+delete_rules(unsigned int num_rules, unsigned int rules_size,
+            unsigned int offset, unsigned int num_rules_offset,
+            TC_HANDLE_T *handle)
+{
+       unsigned int i;
+
+       if (offset + rules_size > (*handle)->entries.size) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       /* Fix up entry points. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               /* In practice, we never delete up to a hook entry,
+                  since the built-in chains are always first,
+                  so these two are never equal */
+               if ((*handle)->info.hook_entry[i] >= offset + rules_size)
+                       (*handle)->info.hook_entry[i] -= rules_size;
+               else if ((*handle)->info.hook_entry[i] > offset) {
+                       fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
+                               i, (*handle)->info.hook_entry[i], offset);
+                       abort();
+               }
+
+               /* Underflow points to policy (terminal) rule in
+                   built-in, so sequality is valid here (when deleting
+                   the last rule). */
+               if ((*handle)->info.underflow[i] >= offset + rules_size)
+                       (*handle)->info.underflow[i] -= rules_size;
+               else if ((*handle)->info.underflow[i] > offset) {
+                       fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
+                               i, (*handle)->info.underflow[i], offset);
+                       abort();
+               }
+       }
+
+       /* Move the rules down. */
+       memmove((char *)(*handle)->entries.entrytable + offset,
+               (char *)(*handle)->entries.entrytable + offset + rules_size,
+               (*handle)->entries.size - (offset + rules_size));
+
+       /* Move the counter map down. */
+       memmove(&(*handle)->counter_map[num_rules_offset],
+               &(*handle)->counter_map[num_rules_offset + num_rules],
+               sizeof(struct counter_map)
+               * ((*handle)->new_number - (num_rules + num_rules_offset)));
+
+       /* Fix numbers */
+       (*handle)->new_number -= num_rules;
+       (*handle)->entries.size -= rules_size;
+
+       return set_verdict(offset, -(int)rules_size, handle);
+}
+
+static int
+standard_map(STRUCT_ENTRY *e, int verdict)
+{
+       STRUCT_STANDARD_TARGET *t;
+
+       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+       if (t->target.u.target_size
+           != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
+               errno = EINVAL;
+               return 0;
+       }
+       /* memset for memcmp convenience on delete/replace */
+       memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+       strcpy(t->target.u.user.name, STANDARD_TARGET);
+       t->verdict = verdict;
+
+       return 1;
+}
+
+static int
+map_target(const TC_HANDLE_T handle,
+          STRUCT_ENTRY *e,
+          unsigned int offset,
+          STRUCT_ENTRY_TARGET *old)
+{
+       STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
+
+       /* Save old target (except data, which we don't change, except for
+          standard case, where we don't care). */
+       *old = *t;
+
+       /* Maybe it's empty (=> fall through) */
+       if (strcmp(t->u.user.name, "") == 0)
+               return standard_map(e, offset + e->next_offset);
+       /* Maybe it's a standard target name... */
+       else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
+               return standard_map(e, -NF_ACCEPT - 1);
+       else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
+               return standard_map(e, -NF_DROP - 1);
+       else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
+               return standard_map(e, -NF_QUEUE - 1);
+       else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
+               return standard_map(e, RETURN);
+       else if (TC_BUILTIN(t->u.user.name, handle)) {
+               /* Can't jump to builtins. */
+               errno = EINVAL;
+               return 0;
+       } else {
+               /* Maybe it's an existing chain name. */
+               struct chain_cache *c;
+
+               c = find_label(t->u.user.name, handle);
+               if (c)
+                       return standard_map(e, c->start_off);
+       }
+
+       /* Must be a module?  If not, kernel will reject... */
+       /* memset to all 0 for your memcmp convenience. */
+       memset(t->u.user.name + strlen(t->u.user.name),
+              0,
+              FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
+       return 1;
+}
+
+static void
+unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
+{
+       STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
+
+       /* Save old target (except data, which we don't change, except for
+          standard case, where we don't care). */
+       *t = *old;
+}
+
+/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
+int
+TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *e,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       unsigned int chainindex, offset;
+       STRUCT_ENTRY_TARGET old;
+       struct chain_cache *c;
+       STRUCT_ENTRY *tmp;
+       int ret;
+
+       iptc_fn = TC_INSERT_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+
+       tmp = index2entry(*handle, chainindex + rulenum);
+       if (!tmp || tmp > offset2entry(*handle, c->end_off)) {
+               errno = E2BIG;
+               return 0;
+       }
+       offset = index2offset(*handle, chainindex + rulenum);
+
+       /* Mapping target actually alters entry, but that's
+           transparent to the caller. */
+       if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
+               return 0;
+
+       ret = insert_rules(1, e->next_offset, e, offset,
+                          chainindex + rulenum, rulenum == 0, handle);
+       unmap_target((STRUCT_ENTRY *)e, &old);
+       return ret;
+}
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int
+TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
+                const STRUCT_ENTRY *e,
+                unsigned int rulenum,
+                TC_HANDLE_T *handle)
+{
+       unsigned int chainindex, offset;
+       STRUCT_ENTRY_TARGET old;
+       struct chain_cache *c;
+       STRUCT_ENTRY *tmp;
+       int ret;
+
+       iptc_fn = TC_REPLACE_ENTRY;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+
+       tmp = index2entry(*handle, chainindex + rulenum);
+       if (!tmp || tmp >= offset2entry(*handle, c->end_off)) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       offset = index2offset(*handle, chainindex + rulenum);
+       /* Replace = delete and insert. */
+       if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
+                         offset, chainindex + rulenum, handle))
+               return 0;
+
+       if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
+               return 0;
+
+       ret = insert_rules(1, e->next_offset, e, offset,
+                          chainindex + rulenum, 1, handle);
+       unmap_target((STRUCT_ENTRY *)e, &old);
+       return ret;
+}
+
+/* Append entry `fw' to chain `chain'.  Equivalent to insert with
+   rulenum = length of chain. */
+int
+TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *e,
+               TC_HANDLE_T *handle)
+{
+       struct chain_cache *c;
+       STRUCT_ENTRY_TARGET old;
+       int ret;
+
+       iptc_fn = TC_APPEND_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (!map_target(*handle, (STRUCT_ENTRY *)e,
+                       c->end_off, &old))
+               return 0;
+
+       ret = insert_rules(1, e->next_offset, e, c->end_off, 
+                          offset2index(*handle, c->end_off), 0, handle);
+       unmap_target((STRUCT_ENTRY *)e, &old);
+       return ret;
+}
+
+static inline int
+match_different(const STRUCT_ENTRY_MATCH *a,
+               const unsigned char *a_elems,
+               const unsigned char *b_elems,
+               unsigned char **maskptr)
+{
+       const STRUCT_ENTRY_MATCH *b;
+       unsigned int i;
+
+       /* Offset of b is the same as a. */
+       b = (void *)b_elems + ((unsigned char *)a - a_elems);
+
+       if (a->u.match_size != b->u.match_size)
+               return 1;
+
+       if (strcmp(a->u.user.name, b->u.user.name) != 0)
+               return 1;
+
+       *maskptr += ALIGN(sizeof(*a));
+
+       for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
+               if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
+                       return 1;
+       *maskptr += i;
+       return 0;
+}
+
+static inline int
+target_different(const unsigned char *a_targdata,
+                const unsigned char *b_targdata,
+                unsigned int tdatasize,
+                const unsigned char *mask)
+{
+       unsigned int i;
+       for (i = 0; i < tdatasize; i++)
+               if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
+                       return 1;
+
+       return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a,
+       const STRUCT_ENTRY *b,
+       unsigned char *matchmask);
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int
+TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *origfw,
+               unsigned char *matchmask,
+               TC_HANDLE_T *handle)
+{
+       unsigned int offset;
+       struct chain_cache *c;
+       STRUCT_ENTRY *e, *fw;
+
+       iptc_fn = TC_DELETE_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       fw = malloc(origfw->next_offset);
+       if (fw == NULL) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       for (offset = c->start_off; offset < c->end_off;
+            offset += e->next_offset) {
+               STRUCT_ENTRY_TARGET discard;
+
+               memcpy(fw, origfw, origfw->next_offset);
+
+               /* FIXME: handle this in is_same --RR */
+               if (!map_target(*handle, fw, offset, &discard)) {
+                       free(fw);
+                       return 0;
+               }
+               e = get_entry(*handle, offset);
+
+#if 0
+               printf("Deleting:\n");
+               dump_entry(newe);
+#endif
+               if (is_same(e, fw, matchmask)) {
+                       int ret;
+                       ret = delete_rules(1, e->next_offset,
+                                          offset, entry2index(*handle, e),
+                                          handle);
+                       free(fw);
+                       return ret;
+               }
+       }
+
+       free(fw);
+       errno = ENOENT;
+       return 0;
+}
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int
+TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
+                   unsigned int rulenum,
+                   TC_HANDLE_T *handle)
+{
+       unsigned int index;
+       int ret;
+       STRUCT_ENTRY *e;
+       struct chain_cache *c;
+
+       iptc_fn = TC_DELETE_NUM_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       index = offset2index(*handle, c->start_off) + rulenum;
+
+       if (index >= offset2index(*handle, c->end_off)) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       e = index2entry(*handle, index);
+       if (e == NULL) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
+                          index, handle);
+       return ret;
+}
+
+/* Check the packet `fw' on chain `chain'.  Returns the verdict, or
+   NULL and sets errno. */
+const char *
+TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
+               STRUCT_ENTRY *entry,
+               TC_HANDLE_T *handle)
+{
+       errno = ENOSYS;
+       return NULL;
+}
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int
+TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       unsigned int startindex, endindex;
+       STRUCT_ENTRY *startentry, *endentry;
+       struct chain_cache *c;
+       int ret;
+
+       iptc_fn = TC_FLUSH_ENTRIES;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+       startindex = offset2index(*handle, c->start_off);
+       endindex = offset2index(*handle, c->end_off);
+       startentry = offset2entry(*handle, c->start_off);
+       endentry = offset2entry(*handle, c->end_off);
+
+       ret = delete_rules(endindex - startindex,
+                          (char *)endentry - (char *)startentry,
+                          c->start_off, startindex,
+                          handle);
+       return ret;
+}
+
+/* Zeroes the counters in a chain. */
+int
+TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       unsigned int i, end;
+       struct chain_cache *c;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       i = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       for (; i <= end; i++) {
+               if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
+                       (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
+       }
+       set_changed(*handle);
+
+       return 1;
+}
+
+STRUCT_COUNTERS *
+TC_READ_COUNTER(const IPT_CHAINLABEL chain,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_cache *c;
+       unsigned int chainindex, end;
+
+       iptc_fn = TC_READ_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               errno = E2BIG;
+               return NULL;
+       }
+
+       e = index2entry(*handle, chainindex + rulenum);
+
+       return &e->counters;
+}
+
+int
+TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_cache *c;
+       unsigned int chainindex, end;
+       
+       iptc_fn = TC_ZERO_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       e = index2entry(*handle, chainindex + rulenum);
+
+       if ((*handle)->counter_map[chainindex + rulenum].maptype
+                       == COUNTER_MAP_NORMAL_MAP) {
+               (*handle)->counter_map[chainindex + rulenum].maptype
+                        = COUNTER_MAP_ZEROED;
+       }
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+int 
+TC_SET_COUNTER(const IPT_CHAINLABEL chain,
+              unsigned int rulenum,
+              STRUCT_COUNTERS *counters,
+              TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_cache *c;
+       unsigned int chainindex, end;
+
+       iptc_fn = TC_SET_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       e = index2entry(*handle, chainindex + rulenum);
+
+       (*handle)->counter_map[chainindex + rulenum].maptype
+               = COUNTER_MAP_SET;
+
+       memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Creates a new chain. */
+/* To create a chain, create two rules: error node and unconditional
+ * return. */
+int
+TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       int ret;
+       struct {
+               STRUCT_ENTRY head;
+               struct ipt_error_target name;
+               STRUCT_ENTRY ret;
+               STRUCT_STANDARD_TARGET target;
+       } newc;
+
+       iptc_fn = TC_CREATE_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (find_label(chain, *handle)
+           || strcmp(chain, LABEL_DROP) == 0
+           || strcmp(chain, LABEL_ACCEPT) == 0
+           || strcmp(chain, LABEL_QUEUE) == 0
+           || strcmp(chain, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       memset(&newc, 0, sizeof(newc));
+       newc.head.target_offset = sizeof(STRUCT_ENTRY);
+       newc.head.next_offset
+               = sizeof(STRUCT_ENTRY)
+               + ALIGN(sizeof(struct ipt_error_target));
+       strcpy(newc.name.t.u.user.name, ERROR_TARGET);
+       newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
+       strcpy(newc.name.error, chain);
+
+       newc.ret.target_offset = sizeof(STRUCT_ENTRY);
+       newc.ret.next_offset
+               = sizeof(STRUCT_ENTRY)
+               + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
+       newc.target.target.u.target_size
+               = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       newc.target.verdict = RETURN;
+
+       /* Add just before terminal entry */
+       ret = insert_rules(2, sizeof(newc), &newc.head,
+                          index2offset(*handle, (*handle)->new_number - 1),
+                          (*handle)->new_number - 1,
+                          0, handle);
+       return ret;
+}
+
+static int
+count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
+{
+       STRUCT_STANDARD_TARGET *t;
+
+       if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+               if (t->verdict == offset)
+                       (*ref)++;
+       }
+
+       return 0;
+}
+
+/* Get the number of references to this chain. */
+int
+TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
+                 TC_HANDLE_T *handle)
+{
+       struct chain_cache *c;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       *ref = 0;
+       ENTRY_ITERATE((*handle)->entries.entrytable,
+                     (*handle)->entries.size,
+                     count_ref, c->start_off, ref);
+       return 1;
+}
+
+/* Deletes a chain. */
+int
+TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       unsigned int labelidx, labeloff;
+       unsigned int references;
+       struct chain_cache *c;
+       int ret;
+       STRUCT_ENTRY *start;
+
+       if (!TC_GET_REFERENCES(&references, chain, handle))
+               return 0;
+
+       iptc_fn = TC_DELETE_CHAIN;
+
+       if (TC_BUILTIN(chain, *handle)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       if (references > 0) {
+               errno = EMLINK;
+               return 0;
+       }
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (c->start_off != c->end_off) {
+               errno = ENOTEMPTY;
+               return 0;
+       }
+
+       /* Need label index: preceeds chain start */
+       labelidx = offset2index(*handle, c->start_off) - 1;
+       labeloff = index2offset(*handle, labelidx);
+
+       start = offset2entry(*handle, c->start_off);
+
+       ret = delete_rules(2,
+                          get_entry(*handle, labeloff)->next_offset
+                          + start->next_offset,
+                          labeloff, labelidx, handle);
+       return ret;
+}
+
+/* Renames a chain. */
+int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
+                   const IPT_CHAINLABEL newname,
+                   TC_HANDLE_T *handle)
+{
+       unsigned int labeloff, labelidx;
+       struct chain_cache *c;
+       struct ipt_error_target *t;
+
+       iptc_fn = TC_RENAME_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (find_label(newname, *handle)
+           || strcmp(newname, LABEL_DROP) == 0
+           || strcmp(newname, LABEL_ACCEPT) == 0
+           || strcmp(newname, LABEL_QUEUE) == 0
+           || strcmp(newname, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (!(c = find_label(oldname, *handle))
+           || TC_BUILTIN(oldname, *handle)) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       /* Need label index: preceeds chain start */
+       labelidx = offset2index(*handle, c->start_off) - 1;
+       labeloff = index2offset(*handle, labelidx);
+
+       t = (struct ipt_error_target *)
+               GET_TARGET(get_entry(*handle, labeloff));
+
+       memset(t->error, 0, sizeof(t->error));
+       strcpy(t->error, newname);
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Sets the policy on a built-in chain. */
+int
+TC_SET_POLICY(const IPT_CHAINLABEL chain,
+             const IPT_CHAINLABEL policy,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
+{
+       unsigned int hook;
+       unsigned int policyoff, ctrindex;
+       STRUCT_ENTRY *e;
+       STRUCT_STANDARD_TARGET *t;
+
+       iptc_fn = TC_SET_POLICY;
+       /* Figure out which chain. */
+       hook = TC_BUILTIN(chain, *handle);
+       if (hook == 0) {
+               errno = ENOENT;
+               return 0;
+       } else
+               hook--;
+
+       policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
+       if (policyoff != (*handle)->info.underflow[hook]) {
+               printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
+                      chain, policyoff, (*handle)->info.underflow[hook]);
+               return 0;
+       }
+
+       e = get_entry(*handle, policyoff);
+       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+       if (strcmp(policy, LABEL_ACCEPT) == 0)
+               t->verdict = -NF_ACCEPT - 1;
+       else if (strcmp(policy, LABEL_DROP) == 0)
+               t->verdict = -NF_DROP - 1;
+       else {
+               errno = EINVAL;
+               return 0;
+       }
+
+       ctrindex = entry2index(*handle, e);
+
+       if (counters) {
+               /* set byte and packet counters */
+               memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+
+               (*handle)->counter_map[ctrindex].maptype
+                       = COUNTER_MAP_SET;
+
+       } else {
+               (*handle)->counter_map[ctrindex]
+                       = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
+       }
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Without this, on gcc 2.7.2.3, we get:
+   libiptc.c: In function `TC_COMMIT':
+   libiptc.c:833: fixed or forbidden register was spilled.
+   This may be due to a compiler bug or to impossible asm
+   statements or clauses.
+*/
+static void
+subtract_counters(STRUCT_COUNTERS *answer,
+                 const STRUCT_COUNTERS *a,
+                 const STRUCT_COUNTERS *b)
+{
+       answer->pcnt = a->pcnt - b->pcnt;
+       answer->bcnt = a->bcnt - b->bcnt;
+}
+
+int
+TC_COMMIT(TC_HANDLE_T *handle)
+{
+       /* Replace, then map back the counters. */
+       STRUCT_REPLACE *repl;
+       STRUCT_COUNTERS_INFO *newcounters;
+       unsigned int i;
+       size_t counterlen;
+
+       CHECK(*handle);
+
+       counterlen = sizeof(STRUCT_COUNTERS_INFO)
+                       + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
+
+#if 0
+       TC_DUMP_ENTRIES(*handle);
+#endif
+
+       /* Don't commit if nothing changed. */
+       if (!(*handle)->changed)
+               goto finished;
+
+       repl = malloc(sizeof(*repl) + (*handle)->entries.size);
+       if (!repl) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* These are the old counters we will get from kernel */
+       repl->counters = malloc(sizeof(STRUCT_COUNTERS)
+                               * (*handle)->info.num_entries);
+       if (!repl->counters) {
+               free(repl);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* These are the counters we're going to put back, later. */
+       newcounters = malloc(counterlen);
+       if (!newcounters) {
+               free(repl->counters);
+               free(repl);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       strcpy(repl->name, (*handle)->info.name);
+       repl->num_entries = (*handle)->new_number;
+       repl->size = (*handle)->entries.size;
+       memcpy(repl->hook_entry, (*handle)->info.hook_entry,
+              sizeof(repl->hook_entry));
+       memcpy(repl->underflow, (*handle)->info.underflow,
+              sizeof(repl->underflow));
+       repl->num_counters = (*handle)->info.num_entries;
+       repl->valid_hooks = (*handle)->info.valid_hooks;
+       memcpy(repl->entries, (*handle)->entries.entrytable,
+              (*handle)->entries.size);
+
+       if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
+                      sizeof(*repl) + (*handle)->entries.size) < 0) {
+               free(repl->counters);
+               free(repl);
+               free(newcounters);
+               return 0;
+       }
+
+       /* Put counters back. */
+       strcpy(newcounters->name, (*handle)->info.name);
+       newcounters->num_counters = (*handle)->new_number;
+       for (i = 0; i < (*handle)->new_number; i++) {
+               unsigned int mappos = (*handle)->counter_map[i].mappos;
+               switch ((*handle)->counter_map[i].maptype) {
+               case COUNTER_MAP_NOMAP:
+                       newcounters->counters[i]
+                               = ((STRUCT_COUNTERS){ 0, 0 });
+                       break;
+
+               case COUNTER_MAP_NORMAL_MAP:
+                       /* Original read: X.
+                        * Atomic read on replacement: X + Y.
+                        * Currently in kernel: Z.
+                        * Want in kernel: X + Y + Z.
+                        * => Add in X + Y
+                        * => Add in replacement read.
+                        */
+                       newcounters->counters[i] = repl->counters[mappos];
+                       break;
+
+               case COUNTER_MAP_ZEROED:
+                       /* Original read: X.
+                        * Atomic read on replacement: X + Y.
+                        * Currently in kernel: Z.
+                        * Want in kernel: Y + Z.
+                        * => Add in Y.
+                        * => Add in (replacement read - original read).
+                        */
+                       subtract_counters(&newcounters->counters[i],
+                                         &repl->counters[mappos],
+                                         &index2entry(*handle, i)->counters);
+                       break;
+
+               case COUNTER_MAP_SET:
+                       /* Want to set counter (iptables-restore) */
+
+                       memcpy(&newcounters->counters[i],
+                              &index2entry(*handle, i)->counters,
+                              sizeof(STRUCT_COUNTERS));
+
+                       break;
+               }
+       }
+
+#ifdef KERNEL_64_USERSPACE_32
+       {
+               /* Kernel will think that pointer should be 64-bits, and get
+                  padding.  So we accomodate here (assumption: alignment of
+                  `counters' is on 64-bit boundary). */
+               u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
+               if ((unsigned long)&newcounters->counters % 8 != 0) {
+                       fprintf(stderr,
+                               "counters alignment incorrect! Mail rusty!\n");
+                       abort();
+               }
+               *kernptr = newcounters->counters;
+       }
+#endif /* KERNEL_64_USERSPACE_32 */
+
+       if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
+                      newcounters, counterlen) < 0) {
+               free(repl->counters);
+               free(repl);
+               free(newcounters);
+               return 0;
+       }
+
+       free(repl->counters);
+       free(repl);
+       free(newcounters);
+
+ finished:
+       TC_FREE(handle);
+       return 1;
+}
+
+/* Get raw socket. */
+int
+TC_GET_RAW_SOCKET()
+{
+       return sockfd;
+}
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *
+TC_STRERROR(int err)
+{
+       unsigned int i;
+       struct table_struct {
+               void *fn;
+               int err;
+               const char *message;
+       } table [] =
+         { { TC_INIT, EPERM, "Permission denied (you must be root)" },
+           { TC_INIT, EINVAL, "Module is wrong version" },
+           { TC_INIT, ENOENT, 
+                   "Table does not exist (do you need to insmod?)" },
+           { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
+           { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
+           { TC_DELETE_CHAIN, EMLINK,
+             "Can't delete chain with references left" },
+           { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
+           { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
+           { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
+           { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
+           { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
+           { TC_INSERT_ENTRY, EINVAL, "Target problem" },
+           /* EINVAL for CHECK probably means bad interface. */
+           { TC_CHECK_PACKET, EINVAL,
+             "Bad arguments (does that interface exist?)" },
+           { TC_CHECK_PACKET, ENOSYS,
+             "Checking will most likely never get implemented" },
+           /* ENOENT for DELETE probably means no matching rule */
+           { TC_DELETE_ENTRY, ENOENT,
+             "Bad rule (does a matching rule exist in that chain?)" },
+           { TC_SET_POLICY, ENOENT,
+             "Bad built-in chain name" },
+           { TC_SET_POLICY, EINVAL,
+             "Bad policy name" },
+
+           { NULL, 0, "Incompatible with this kernel" },
+           { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
+           { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
+           { NULL, ENOMEM, "Memory allocation problem" },
+           { NULL, ENOENT, "No chain/target/match by that name" },
+         };
+
+       for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+               if ((!table[i].fn || table[i].fn == iptc_fn)
+                   && table[i].err == err)
+                       return table[i].message;
+       }
+
+       return strerror(err);
+}
diff --git a/libiptc2/linux_list.h b/libiptc2/linux_list.h
new file mode 100644 (file)
index 0000000..41de482
--- /dev/null
@@ -0,0 +1,161 @@
+#ifndef _LINUXLIST_H
+#define _LINUXLIST_H
+
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+       struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries. 
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * new,
+       struct list_head * prev,
+       struct list_head * next)
+{
+       next->prev = new;
+       new->next = next;
+       new->prev = prev;
+       prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static __inline__ void list_add(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+                                 struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
+ */
+static __inline__ void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static __inline__ void list_del_init(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       INIT_LIST_HEAD(entry); 
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static __inline__ int list_empty(struct list_head *head)
+{
+       return head->next == head;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+       struct list_head *first = list->next;
+
+       if (first != list) {
+               struct list_head *last = list->prev;
+               struct list_head *at = head->next;
+
+               first->prev = head;
+               head->next = first;
+
+               last->next = at;
+               at->prev = last;
+       }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:       the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+       ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each       -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); pos = pos->next)
+               
+/**
+ * list_for_each_safe  -       iterate over a list safe against removal of list entry
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @n:         another &struct list_head to use as temporary storage
+ * @head:      the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+       for (pos = (head)->next, n = pos->next; pos != (head); \
+               pos = n, n = pos->next)
+
+#endif
diff --git a/libiptc2/linux_listhelp.h b/libiptc2/linux_listhelp.h
new file mode 100644 (file)
index 0000000..466d921
--- /dev/null
@@ -0,0 +1,99 @@
+#ifndef _LINUX_LISTHELP_H
+#define _LINUX_LISTHELP_H
+#include <stdlib.h>
+#include <string.h>
+#include "linux_list.h"
+
+/* Header to do more comprehensive job than linux/list.h; assume list
+   is first entry in structure. */
+
+/* Return pointer to first true entry, if any, or NULL.  A macro
+   required to allow inlining of cmpfn. */
+#define LIST_FIND(head, cmpfn, type, args...)          \
+({                                                     \
+       const struct list_head *__i = (head);           \
+                                                       \
+       do {                                            \
+               __i = __i->next;                        \
+               if (__i == (head)) {                    \
+                       __i = NULL;                     \
+                       break;                          \
+               }                                       \
+       } while (!cmpfn((const type)__i , ## args));    \
+       (type)__i;                                      \
+})
+
+#define LIST_FIND_W(head, cmpfn, type, args...)        \
+({                                             \
+       const struct list_head *__i = (head);   \
+                                               \
+       do {                                    \
+               __i = __i->next;                \
+               if (__i == (head)) {            \
+                       __i = NULL;             \
+                       break;                  \
+               }                               \
+       } while (!cmpfn((type)__i , ## args));  \
+       (type)__i;                              \
+})
+
+static inline int
+__list_cmp_same(const void *p1, const void *p2) { return p1 == p2; }
+
+/* Is this entry in the list? */
+static inline int
+list_inlist(struct list_head *head, const void *entry)
+{
+       return LIST_FIND(head, __list_cmp_same, void *, entry) != NULL;
+}
+
+/* Delete from list. */
+#define LIST_DELETE(head, oldentry) list_del((struct list_head *)oldentry)
+
+/* Append. */
+static inline void
+list_append(struct list_head *head, void *new)
+{
+       list_add((new), (head)->prev);
+}
+
+/* Prepend. */
+static inline void
+list_prepend(struct list_head *head, void *new)
+{
+       list_add(new, head);
+}
+
+/* Insert according to ordering function; insert before first true. */
+#define LIST_INSERT(head, new, cmpfn)                          \
+do {                                                           \
+       struct list_head *__i;                                  \
+       for (__i = (head)->next;                                \
+            !cmpfn((new), (typeof (new))__i) && __i != (head); \
+            __i = __i->next);                                  \
+       list_add((struct list_head *)(new), __i->prev);         \
+} while(0)
+
+/* If the field after the list_head is a nul-terminated string, you
+   can use these functions. */
+static inline int __list_cmp_name(const void *i, const char *name)
+{
+       return strcmp(name, i+sizeof(struct list_head)) == 0;
+}
+
+/* Returns false if same name already in list, otherwise does insert. */
+static inline int
+list_named_insert(struct list_head *head, void *new)
+{
+       if (LIST_FIND(head, __list_cmp_name, void *,
+                     new + sizeof(struct list_head)))
+               return 0;
+       list_prepend(head, new);
+       return 1;
+}
+
+/* Find this named element in the list. */
+#define list_named_find(head, name)                    \
+LIST_FIND(head, __list_cmp_name, void *, name)
+
+#endif /*_LISTHELP_H*/
diff --git a/planetlab-config b/planetlab-config
new file mode 100644 (file)
index 0000000..b5954cf
--- /dev/null
@@ -0,0 +1,22 @@
+*filter
+:INPUT ACCEPT 
+:FORWARD ACCEPT
+:OUTPUT ACCEPT 
+:BLACKLIST - 
+:LOGDROP - 
+-A OUTPUT -j BLACKLIST
+-A LOGDROP -j LOG
+-A LOGDROP -j DROP
+COMMIT
+
+*mangle
+:PREROUTING ACCEPT 
+:INPUT ACCEPT 
+:FORWARD ACCEPT 
+:OUTPUT ACCEPT 
+:POSTROUTING ACCEPT 
+-A INPUT -j MARK --copy-xid 0x0
+-A POSTROUTING -j MARK --copy-xid 0x0 
+-A POSTROUTING -j CLASSIFY --set-class 0001:1000 --add-mark
+-A POSTROUTING -o eth0 -j ULOG --ulog-cprange 54 --ulog-qthreshold 16
+COMMIT