[libvirt] [RFC PATCHv2 0/9] DHCP snooping support for libvirt.

This series of patches adds DHCP snooping support to libvirt. This version saves leases on disk for restoration after a libvirtd restart and allows selection of different ip_learning methods by setting filter parameter "ip_learning" to one of "any" (existing IP learning code) "none" (static only addresses) or "DHCP" (DHCP Snooping). This code does not (yet) support passing lease information across a migration. A migrated guest requires a DHCP ACK (e.g., via ifdown/ifup on the guest) to send/receive traffic for DHCP-learned addresses after a migration. David L Stevens (9): support continue/return allow required ARP packets reverse sense of address matching make default chain policy "DROP" allow chain modification support addRules support variable value changing add DHCP snooping add leasefile support examples/xml/nwfilter/Makefile.am | 5 +- examples/xml/nwfilter/allow-arp.xml | 5 +- examples/xml/nwfilter/allow-arpip.xml | 3 + examples/xml/nwfilter/allow-arpmac.xml | 3 + examples/xml/nwfilter/clean-traffic.xml | 6 +- examples/xml/nwfilter/no-arp-spoofing.xml | 38 +- examples/xml/nwfilter/no-arpip-spoofing.xml | 10 + examples/xml/nwfilter/no-arpmac-spoofing.xml | 5 + examples/xml/nwfilter/no-ip-spoofing.xml | 9 +- examples/xml/nwfilter/no-mac-spoofing.xml | 10 +- examples/xml/nwfilter/no-other-l2-traffic.xml | 13 +- examples/xml/nwfilter/no-other-rarp-traffic.xml | 3 - examples/xml/nwfilter/qemu-announce-self.xml | 1 - src/Makefile.am | 2 + src/conf/nwfilter_conf.c | 12 +- src/conf/nwfilter_conf.h | 16 +- src/nwfilter/nwfilter_dhcpsnoop.c | 938 +++++++++++++++++++++++ src/nwfilter/nwfilter_dhcpsnoop.h | 36 + src/nwfilter/nwfilter_driver.c | 5 + src/nwfilter/nwfilter_ebiptables_driver.c | 225 +++++-- src/nwfilter/nwfilter_gentech_driver.c | 199 ++++- src/nwfilter/nwfilter_gentech_driver.h | 11 + 22 files changed, 1419 insertions(+), 136 deletions(-) create mode 100644 examples/xml/nwfilter/allow-arpip.xml create mode 100644 examples/xml/nwfilter/allow-arpmac.xml create mode 100644 examples/xml/nwfilter/no-arpip-spoofing.xml create mode 100644 examples/xml/nwfilter/no-arpmac-spoofing.xml delete mode 100644 examples/xml/nwfilter/no-other-rarp-traffic.xml create mode 100644 src/nwfilter/nwfilter_dhcpsnoop.c create mode 100644 src/nwfilter/nwfilter_dhcpsnoop.h -- 1.7.6.4

This patch adds support for "continue" and "return" actions in filter rules. Signed-off-by: David L Stevens <dlstevens@us.ibm.com> --- src/conf/nwfilter_conf.c | 8 ++++++-- src/conf/nwfilter_conf.h | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/conf/nwfilter_conf.c b/src/conf/nwfilter_conf.c index 08ede48..e0c2fb6 100644 --- a/src/conf/nwfilter_conf.c +++ b/src/conf/nwfilter_conf.c @@ -55,12 +55,16 @@ VIR_ENUM_IMPL(virNWFilterRuleAction, VIR_NWFILTER_RULE_ACTION_LAST, "drop", "accept", - "reject"); + "reject", + "return", + "continue"); VIR_ENUM_IMPL(virNWFilterJumpTarget, VIR_NWFILTER_RULE_ACTION_LAST, "DROP", "ACCEPT", - "REJECT"); + "REJECT", + "RETURN", + "CONTINUE"); VIR_ENUM_IMPL(virNWFilterRuleDirection, VIR_NWFILTER_RULE_DIRECTION_LAST, "in", diff --git a/src/conf/nwfilter_conf.h b/src/conf/nwfilter_conf.h index 5306403..c96851a 100644 --- a/src/conf/nwfilter_conf.h +++ b/src/conf/nwfilter_conf.h @@ -299,6 +299,8 @@ enum virNWFilterRuleActionType { VIR_NWFILTER_RULE_ACTION_DROP = 0, VIR_NWFILTER_RULE_ACTION_ACCEPT, VIR_NWFILTER_RULE_ACTION_REJECT, + VIR_NWFILTER_RULE_ACTION_RETURN, + VIR_NWFILTER_RULE_ACTION_CONTINUE, VIR_NWFILTER_RULE_ACTION_LAST, }; -- 1.7.6.4

The ARP protocol requires processing of packets that may not be explicitly addressed to a host and only defines request and reply. This patch removes the filtering of ARP requests not explicitly addressed to a VM to allow for proper ARP cache updates for entries based on any traffic and removes the unnecessary check for arpop of request or reply. Signed-off-by: David L Stevens <dlstevens@us.ibm.com> --- examples/xml/nwfilter/no-arp-spoofing.xml | 23 ++--------------------- 1 files changed, 2 insertions(+), 21 deletions(-) diff --git a/examples/xml/nwfilter/no-arp-spoofing.xml b/examples/xml/nwfilter/no-arp-spoofing.xml index 96c58c1..3c83acd 100644 --- a/examples/xml/nwfilter/no-arp-spoofing.xml +++ b/examples/xml/nwfilter/no-arp-spoofing.xml @@ -12,25 +12,6 @@ <rule action='drop' direction='out' priority='400' > <arp match='no' arpsrcipaddr='$IP' /> </rule> - <!-- allow gratuitous arp --> - <rule action='accept' direction='in' priority='425'> - <arp gratuitous='true'/> - </rule> - <!-- drop if ipaddr or macaddr does not belong to guest --> - <rule action='drop' direction='in' priority='450' > - <arp match='no' arpdstmacaddr='$MAC'/> - <arp opcode='reply'/> - </rule> - <rule action='drop' direction='in' priority='500' > - <arp match='no' arpdstipaddr='$IP' /> - </rule> - <!-- accept only request or reply packets --> - <rule action='accept' direction='inout' priority='600' > - <arp opcode='request'/> - </rule> - <rule action='accept' direction='inout' priority='650' > - <arp opcode='reply'/> - </rule> - <!-- drop everything else --> - <rule action='drop' direction='inout' priority='1000' /> + <!-- allow everything else --> + <rule action='accept' direction='in' priority='425' /> </filter> -- 1.7.6.4

This patch changes rules of the form: if ! addr drop accept to: if addr return ... drop The patch adds a "mac" chain to do a mac address list and separates the "arp" chain into separate "arpmac" and "arpip" chains that can check multiple MAC or IP addresses in any combination. This patch itself does not support multiple addresses via the MAC and IP variables, but only changes the form of the rules to allow multiple addresses in the future. Signed-off-by: David L Stevens <dlstevens@us.ibm.com> --- examples/xml/nwfilter/Makefile.am | 4 ++ examples/xml/nwfilter/allow-arp.xml | 5 ++- examples/xml/nwfilter/allow-arpip.xml | 3 ++ examples/xml/nwfilter/allow-arpmac.xml | 3 ++ examples/xml/nwfilter/clean-traffic.xml | 6 ++-- examples/xml/nwfilter/no-arp-spoofing.xml | 19 ++-------- examples/xml/nwfilter/no-arpip-spoofing.xml | 12 ++++++ examples/xml/nwfilter/no-arpmac-spoofing.xml | 7 ++++ examples/xml/nwfilter/no-ip-spoofing.xml | 6 ++- examples/xml/nwfilter/no-mac-spoofing.xml | 12 ++++-- examples/xml/nwfilter/no-other-l2-traffic.xml | 13 +++++-- src/conf/nwfilter_conf.c | 4 ++- src/conf/nwfilter_conf.h | 4 ++- src/nwfilter/nwfilter_ebiptables_driver.c | 48 +++++++++++++++++------- 14 files changed, 99 insertions(+), 47 deletions(-) create mode 100644 examples/xml/nwfilter/allow-arpip.xml create mode 100644 examples/xml/nwfilter/allow-arpmac.xml create mode 100644 examples/xml/nwfilter/no-arpip-spoofing.xml create mode 100644 examples/xml/nwfilter/no-arpmac-spoofing.xml diff --git a/examples/xml/nwfilter/Makefile.am b/examples/xml/nwfilter/Makefile.am index 23fd753..84aaa3c 100644 --- a/examples/xml/nwfilter/Makefile.am +++ b/examples/xml/nwfilter/Makefile.am @@ -3,12 +3,16 @@ FILTERS = \ allow-arp.xml \ + allow-arpip.xml \ + allow-arpmac.xml \ allow-dhcp-server.xml \ allow-dhcp.xml \ allow-incoming-ipv4.xml \ allow-ipv4.xml \ clean-traffic.xml \ no-arp-spoofing.xml \ + no-arpmac-spoofing.xml \ + no-arpip-spoofing.xml \ no-ip-multicast.xml \ no-ip-spoofing.xml \ no-mac-broadcast.xml \ diff --git a/examples/xml/nwfilter/allow-arp.xml b/examples/xml/nwfilter/allow-arp.xml index 63a92b2..6271ae4 100644 --- a/examples/xml/nwfilter/allow-arp.xml +++ b/examples/xml/nwfilter/allow-arp.xml @@ -1,3 +1,4 @@ -<filter name='allow-arp' chain='arp'> - <rule direction='inout' action='accept'/> +<filter name='allow-arp' chain='arpmac'> + <filterref filter='allow-arpmac.xml'/> + <filterref filter='allow-arpip.xml'/> </filter> diff --git a/examples/xml/nwfilter/allow-arpip.xml b/examples/xml/nwfilter/allow-arpip.xml new file mode 100644 index 0000000..6ddb6fe --- /dev/null +++ b/examples/xml/nwfilter/allow-arpip.xml @@ -0,0 +1,3 @@ +<filter name='allow-arpip' chain='arpip'> + <rule direction='inout' action='accept'/> +</filter> diff --git a/examples/xml/nwfilter/allow-arpmac.xml b/examples/xml/nwfilter/allow-arpmac.xml new file mode 100644 index 0000000..54f6714 --- /dev/null +++ b/examples/xml/nwfilter/allow-arpmac.xml @@ -0,0 +1,3 @@ +<filter name='allow-arpmac' chain='arpmac'> + <rule direction='inout' action='accept'/> +</filter> diff --git a/examples/xml/nwfilter/clean-traffic.xml b/examples/xml/nwfilter/clean-traffic.xml index 40f0ecb..9cee799 100644 --- a/examples/xml/nwfilter/clean-traffic.xml +++ b/examples/xml/nwfilter/clean-traffic.xml @@ -11,10 +11,10 @@ <!-- preventing ARP spoofing/poisoning --> <filterref filter='no-arp-spoofing'/> - <!-- preventing any other traffic than IPv4 and ARP --> - <filterref filter='no-other-l2-traffic'/> - <!-- allow qemu to send a self-announce upon migration end --> <filterref filter='qemu-announce-self'/> + <!-- preventing any other traffic than IPv4 and ARP --> + <filterref filter='no-other-l2-traffic'/> + </filter> diff --git a/examples/xml/nwfilter/no-arp-spoofing.xml b/examples/xml/nwfilter/no-arp-spoofing.xml index 3c83acd..1979b20 100644 --- a/examples/xml/nwfilter/no-arp-spoofing.xml +++ b/examples/xml/nwfilter/no-arp-spoofing.xml @@ -1,17 +1,4 @@ -<filter name='no-arp-spoofing' chain='arp'> - <uuid>f88f1932-debf-4aa1-9fbe-f10d3aa4bc95</uuid> - <rule action='drop' direction='out' priority='300' > - <mac match='no' srcmacaddr='$MAC'/> - </rule> - - <!-- no arp spoofing --> - <!-- drop if ipaddr or macaddr does not belong to guest --> - <rule action='drop' direction='out' priority='350' > - <arp match='no' arpsrcmacaddr='$MAC'/> - </rule> - <rule action='drop' direction='out' priority='400' > - <arp match='no' arpsrcipaddr='$IP' /> - </rule> - <!-- allow everything else --> - <rule action='accept' direction='in' priority='425' /> +<filter name='no-arp-spoofing'> + <filterref filter='no-arpmac-spoofing' /> + <filterref filter='no-arpip-spoofing' /> </filter> diff --git a/examples/xml/nwfilter/no-arpip-spoofing.xml b/examples/xml/nwfilter/no-arpip-spoofing.xml new file mode 100644 index 0000000..ee42d40 --- /dev/null +++ b/examples/xml/nwfilter/no-arpip-spoofing.xml @@ -0,0 +1,12 @@ +<filter name='no-arpip-spoofing' chain='arpip'> + <!-- no arp spoofing --> + <!-- drop if ipaddr does not belong to guest --> + <rule action='return' direction='out' priority='400' > + <arp match='yes' arpsrcipaddr='$IP' /> + </rule> + <rule action='return' direction='out' priority='410' > + <arp match='yes' arpsrcipaddr='0.0.0.0' /> + </rule> + <!-- drop everything else --> + <rule action='drop' direction='out' priority='1000' /> +</filter> diff --git a/examples/xml/nwfilter/no-arpmac-spoofing.xml b/examples/xml/nwfilter/no-arpmac-spoofing.xml new file mode 100644 index 0000000..90499d3 --- /dev/null +++ b/examples/xml/nwfilter/no-arpmac-spoofing.xml @@ -0,0 +1,7 @@ +<filter name='no-arpmac-spoofing' chain='arpmac'> + <rule action='return' direction='out' priority='350' > + <arp match='yes' arpsrcmacaddr='$MAC'/> + </rule> + <!-- drop everything else --> + <rule action='drop' direction='out' priority='1000' /> +</filter> diff --git a/examples/xml/nwfilter/no-ip-spoofing.xml b/examples/xml/nwfilter/no-ip-spoofing.xml index b8c94c8..84e8a5e 100644 --- a/examples/xml/nwfilter/no-ip-spoofing.xml +++ b/examples/xml/nwfilter/no-ip-spoofing.xml @@ -1,7 +1,9 @@ <filter name='no-ip-spoofing' chain='ipv4'> <!-- drop if srcipaddr is not the IP address of the guest --> - <rule action='drop' direction='out'> - <ip match='no' srcipaddr='$IP' /> + <rule action='return' direction='out'> + <ip match='yes' srcipaddr='$IP' /> </rule> + <!-- drop any that don't match the source IP list --> + <rule action='drop' direction='out' /> </filter> diff --git a/examples/xml/nwfilter/no-mac-spoofing.xml b/examples/xml/nwfilter/no-mac-spoofing.xml index f210623..aee56c7 100644 --- a/examples/xml/nwfilter/no-mac-spoofing.xml +++ b/examples/xml/nwfilter/no-mac-spoofing.xml @@ -1,5 +1,9 @@ -<filter name='no-mac-spoofing' chain='ipv4'> - <rule action='drop' direction='out' priority='10'> - <mac match='no' srcmacaddr='$MAC' /> - </rule> +<filter name='no-mac-spoofing' chain='mac'> + <!-- no mac spoofing --> + <!-- drop if macaddr does not belong to guest --> + <rule action='return' direction='out' priority='350' > + <mac match='yes' srcmacaddr='$MAC'/> + </rule> + <!-- drop everything else --> + <rule action='drop' direction='out' priority='1000' /> </filter> diff --git a/examples/xml/nwfilter/no-other-l2-traffic.xml b/examples/xml/nwfilter/no-other-l2-traffic.xml index 8bad86e..0501b1a 100644 --- a/examples/xml/nwfilter/no-other-l2-traffic.xml +++ b/examples/xml/nwfilter/no-other-l2-traffic.xml @@ -1,7 +1,12 @@ -<filter name='no-other-l2-traffic'> +<filter name='no-other-l2-traffic' chain='root'> - <!-- drop all other l2 traffic than for which rules have been - written for; i.e., drop all other than arp and ipv4 traffic --> - <rule action='drop' direction='inout' priority='1000'/> + <!-- drop all other than arp and ipv4 traffic --> + <rule action='accept' direction='inout'> + <mac protocolid='0x800' /> + </rule> + <rule action='accept' direction='inout'> + <mac protocolid='0x806' /> + </rule> + <rule action='drop' direction='inout' priority='1000' /> </filter> diff --git a/src/conf/nwfilter_conf.c b/src/conf/nwfilter_conf.c index e0c2fb6..31199cb 100644 --- a/src/conf/nwfilter_conf.c +++ b/src/conf/nwfilter_conf.c @@ -82,7 +82,9 @@ VIR_ENUM_IMPL(virNWFilterEbtablesTable, VIR_NWFILTER_EBTABLES_TABLE_LAST, VIR_ENUM_IMPL(virNWFilterChainSuffix, VIR_NWFILTER_CHAINSUFFIX_LAST, "root", - "arp", + "mac", + "arpmac", + "arpip", "rarp", "ipv4", "ipv6"); diff --git a/src/conf/nwfilter_conf.h b/src/conf/nwfilter_conf.h index c96851a..17e954e 100644 --- a/src/conf/nwfilter_conf.h +++ b/src/conf/nwfilter_conf.h @@ -428,7 +428,9 @@ struct _virNWFilterEntry { enum virNWFilterChainSuffixType { VIR_NWFILTER_CHAINSUFFIX_ROOT = 0, - VIR_NWFILTER_CHAINSUFFIX_ARP, + VIR_NWFILTER_CHAINSUFFIX_MAC, + VIR_NWFILTER_CHAINSUFFIX_ARPMAC, + VIR_NWFILTER_CHAINSUFFIX_ARPIP, VIR_NWFILTER_CHAINSUFFIX_RARP, VIR_NWFILTER_CHAINSUFFIX_IPv4, VIR_NWFILTER_CHAINSUFFIX_IPv6, diff --git a/src/nwfilter/nwfilter_ebiptables_driver.c b/src/nwfilter/nwfilter_ebiptables_driver.c index f87cfa1..3c6fca7 100644 --- a/src/nwfilter/nwfilter_ebiptables_driver.c +++ b/src/nwfilter/nwfilter_ebiptables_driver.c @@ -130,20 +130,24 @@ struct ushort_map { enum l3_proto_idx { - L3_PROTO_IPV4_IDX = 0, - L3_PROTO_IPV6_IDX, - L3_PROTO_ARP_IDX, + L3_PROTO_MAC_IDX = 0, + L3_PROTO_ARPMAC_IDX, + L3_PROTO_ARPIP_IDX, L3_PROTO_RARP_IDX, + L3_PROTO_IPV4_IDX, + L3_PROTO_IPV6_IDX, L3_PROTO_LAST_IDX }; #define USHORTMAP_ENTRY_IDX(IDX, ATT, VAL) [IDX] = { .attr = ATT, .val = VAL } static const struct ushort_map l3_protocols[] = { - USHORTMAP_ENTRY_IDX(L3_PROTO_IPV4_IDX, ETHERTYPE_IP , "ipv4"), - USHORTMAP_ENTRY_IDX(L3_PROTO_IPV6_IDX, ETHERTYPE_IPV6 , "ipv6"), - USHORTMAP_ENTRY_IDX(L3_PROTO_ARP_IDX , ETHERTYPE_ARP , "arp"), - USHORTMAP_ENTRY_IDX(L3_PROTO_RARP_IDX, ETHERTYPE_REVARP, "rarp"), + USHORTMAP_ENTRY_IDX(L3_PROTO_MAC_IDX, 0 , "mac"), + USHORTMAP_ENTRY_IDX(L3_PROTO_IPV4_IDX, ETHERTYPE_IP , "ipv4"), + USHORTMAP_ENTRY_IDX(L3_PROTO_IPV6_IDX, ETHERTYPE_IPV6 , "ipv6"), + USHORTMAP_ENTRY_IDX(L3_PROTO_ARPMAC_IDX,ETHERTYPE_ARP , "arpmac"), + USHORTMAP_ENTRY_IDX(L3_PROTO_ARPIP_IDX, ETHERTYPE_ARP , "arpip"), + USHORTMAP_ENTRY_IDX(L3_PROTO_RARP_IDX, ETHERTYPE_REVARP, "rarp"), USHORTMAP_ENTRY_IDX(L3_PROTO_LAST_IDX, 0 , NULL), }; @@ -1947,7 +1951,7 @@ ebtablesCreateRuleInstance(char chainPrefix, virBufferAsprintf(&buf, " -p 0x%x", (rule->prtclType == VIR_NWFILTER_RULE_PROTOCOL_ARP) - ? l3_protocols[L3_PROTO_ARP_IDX].attr + ? l3_protocols[L3_PROTO_ARPMAC_IDX].attr : l3_protocols[L3_PROTO_RARP_IDX].attr); if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataHWType)) { @@ -2775,15 +2779,22 @@ ebtablesCreateTmpSubChain(virBufferPtr buf, char rootchain[MAX_CHAINNAME_LENGTH], chain[MAX_CHAINNAME_LENGTH]; char chainPrefix = (incoming) ? CHAINPREFIX_HOST_IN_TEMP : CHAINPREFIX_HOST_OUT_TEMP; + char protostr[16]; PRINT_ROOT_CHAIN(rootchain, chainPrefix, ifname); PRINT_CHAIN(chain, chainPrefix, ifname, l3_protocols[protoidx].val); + if (l3_protocols[protoidx].attr) + snprintf(protostr, sizeof(protostr), "-p 0x%04x ", + l3_protocols[protoidx].attr); + else + protostr[0] = '\0'; + virBufferAsprintf(buf, CMD_DEF("%s -t %s -N %s") CMD_SEPARATOR CMD_EXEC "%s" - CMD_DEF("%s -t %s -A %s -p 0x%x -j %s") CMD_SEPARATOR + CMD_DEF("%s -t %s -A %s %s -j %s") CMD_SEPARATOR CMD_EXEC "%s", @@ -2792,7 +2803,7 @@ ebtablesCreateTmpSubChain(virBufferPtr buf, CMD_STOPONERR(stopOnError), ebtables_cmd_path, EBTABLES_DEFAULT_TABLE, - rootchain, l3_protocols[protoidx].attr, chain, + rootchain, protostr, chain, CMD_STOPONERR(stopOnError)); @@ -3365,6 +3376,11 @@ ebiptablesApplyNewRules(virConnectPtr conn ATTRIBUTE_UNUSED, if (chains_out != 0) ebtablesCreateTmpRootChain(&buf, 0, ifname, 1); + if (chains_in & (1 << VIR_NWFILTER_CHAINSUFFIX_MAC)) + ebtablesCreateTmpSubChain(&buf, 1, ifname, L3_PROTO_MAC_IDX, 1); + if (chains_out & (1 << VIR_NWFILTER_CHAINSUFFIX_MAC)) + ebtablesCreateTmpSubChain(&buf, 0, ifname, L3_PROTO_MAC_IDX, 1); + if (chains_in & (1 << VIR_NWFILTER_CHAINSUFFIX_IPv4)) ebtablesCreateTmpSubChain(&buf, 1, ifname, L3_PROTO_IPV4_IDX, 1); if (chains_out & (1 << VIR_NWFILTER_CHAINSUFFIX_IPv4)) @@ -3376,10 +3392,14 @@ ebiptablesApplyNewRules(virConnectPtr conn ATTRIBUTE_UNUSED, ebtablesCreateTmpSubChain(&buf, 0, ifname, L3_PROTO_IPV6_IDX, 1); /* keep arp,rarp as last */ - if (chains_in & (1 << VIR_NWFILTER_CHAINSUFFIX_ARP)) - ebtablesCreateTmpSubChain(&buf, 1, ifname, L3_PROTO_ARP_IDX, 1); - if (chains_out & (1 << VIR_NWFILTER_CHAINSUFFIX_ARP)) - ebtablesCreateTmpSubChain(&buf, 0, ifname, L3_PROTO_ARP_IDX, 1); + if (chains_in & (1 << VIR_NWFILTER_CHAINSUFFIX_ARPMAC)) + ebtablesCreateTmpSubChain(&buf, 1, ifname, L3_PROTO_ARPMAC_IDX, 1); + if (chains_in & (1 << VIR_NWFILTER_CHAINSUFFIX_ARPIP)) + ebtablesCreateTmpSubChain(&buf, 1, ifname, L3_PROTO_ARPIP_IDX, 1); + if (chains_out & (1 << VIR_NWFILTER_CHAINSUFFIX_ARPMAC)) + ebtablesCreateTmpSubChain(&buf, 0, ifname, L3_PROTO_ARPMAC_IDX, 1); + if (chains_out & (1 << VIR_NWFILTER_CHAINSUFFIX_ARPIP)) + ebtablesCreateTmpSubChain(&buf, 0, ifname, L3_PROTO_ARPIP_IDX, 1); if (chains_in & (1 << VIR_NWFILTER_CHAINSUFFIX_RARP)) ebtablesCreateTmpSubChain(&buf, 1, ifname, L3_PROTO_RARP_IDX, 1); if (chains_out & (1 << VIR_NWFILTER_CHAINSUFFIX_RARP)) -- 1.7.6.4

On Wed, Oct 05, 2011 at 08:08:48AM -0700, David L Stevens wrote:
This patch changes rules of the form:
if ! addr drop accept to: if addr return ... drop
The patch adds a "mac" chain to do a mac address list and separates the "arp" chain into separate "arpmac" and "arpip" chains that can check multiple MAC or IP addresses in any combination. This patch itself does not support multiple addresses via the MAC and IP variables, but only changes the form of the rules to allow multiple addresses in the future.
Signed-off-by: David L Stevens <dlstevens@us.ibm.com> diff --git a/src/conf/nwfilter_conf.c b/src/conf/nwfilter_conf.c index e0c2fb6..31199cb 100644 --- a/src/conf/nwfilter_conf.c +++ b/src/conf/nwfilter_conf.c @@ -82,7 +82,9 @@ VIR_ENUM_IMPL(virNWFilterEbtablesTable, VIR_NWFILTER_EBTABLES_TABLE_LAST,
VIR_ENUM_IMPL(virNWFilterChainSuffix, VIR_NWFILTER_CHAINSUFFIX_LAST, "root", - "arp", + "mac", + "arpmac", + "arpip", "rarp", "ipv4", "ipv6"); diff --git a/src/conf/nwfilter_conf.h b/src/conf/nwfilter_conf.h index c96851a..17e954e 100644 --- a/src/conf/nwfilter_conf.h +++ b/src/conf/nwfilter_conf.h @@ -428,7 +428,9 @@ struct _virNWFilterEntry {
enum virNWFilterChainSuffixType { VIR_NWFILTER_CHAINSUFFIX_ROOT = 0, - VIR_NWFILTER_CHAINSUFFIX_ARP, + VIR_NWFILTER_CHAINSUFFIX_MAC, + VIR_NWFILTER_CHAINSUFFIX_ARPMAC, + VIR_NWFILTER_CHAINSUFFIX_ARPIP, VIR_NWFILTER_CHAINSUFFIX_RARP, VIR_NWFILTER_CHAINSUFFIX_IPv4, VIR_NWFILTER_CHAINSUFFIX_IPv6,
This looks like it is breaking compatibility of NWFilter XML with previously deployed libvirt releases. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

"Daniel P. Berrange" <berrange@redhat.com> wrote on 10/05/2011 08:41:56 AM:
This looks like it is breaking compatibility of NWFilter XML with previously deployed libvirt releases.
I think only if someone has modified the standard filters. Then they would have to apply those modifications to the new set. These have to be split into separate filters to support multiple MAC and multiple IP addresses, or you'd need nMAC X nIP rules to match all combinations. This way needs only nMAC + nIP. +-DLS

This patch simplifies the table rules by setting the protocol chains policy to be "DROP" and removes the explicit "-j DROP" entries that the protocol rules had previously. It also makes "no-other-rarp-traffic.xml" obsolete. Signed-off-by: David L Stevens <dlstevens@us.ibm.com> --- examples/xml/nwfilter/Makefile.am | 1 - examples/xml/nwfilter/no-arpip-spoofing.xml | 2 -- examples/xml/nwfilter/no-arpmac-spoofing.xml | 2 -- examples/xml/nwfilter/no-ip-spoofing.xml | 2 -- examples/xml/nwfilter/no-mac-spoofing.xml | 2 -- examples/xml/nwfilter/no-other-rarp-traffic.xml | 3 --- examples/xml/nwfilter/qemu-announce-self.xml | 1 - src/nwfilter/nwfilter_ebiptables_driver.c | 11 +---------- 8 files changed, 1 insertions(+), 23 deletions(-) delete mode 100644 examples/xml/nwfilter/no-other-rarp-traffic.xml diff --git a/examples/xml/nwfilter/Makefile.am b/examples/xml/nwfilter/Makefile.am index 84aaa3c..67085fa 100644 --- a/examples/xml/nwfilter/Makefile.am +++ b/examples/xml/nwfilter/Makefile.am @@ -18,7 +18,6 @@ FILTERS = \ no-mac-broadcast.xml \ no-mac-spoofing.xml \ no-other-l2-traffic.xml \ - no-other-rarp-traffic.xml \ qemu-announce-self.xml \ qemu-announce-self-rarp.xml diff --git a/examples/xml/nwfilter/no-arpip-spoofing.xml b/examples/xml/nwfilter/no-arpip-spoofing.xml index ee42d40..7ef6f0f 100644 --- a/examples/xml/nwfilter/no-arpip-spoofing.xml +++ b/examples/xml/nwfilter/no-arpip-spoofing.xml @@ -7,6 +7,4 @@ <rule action='return' direction='out' priority='410' > <arp match='yes' arpsrcipaddr='0.0.0.0' /> </rule> - <!-- drop everything else --> - <rule action='drop' direction='out' priority='1000' /> </filter> diff --git a/examples/xml/nwfilter/no-arpmac-spoofing.xml b/examples/xml/nwfilter/no-arpmac-spoofing.xml index 90499d3..3834047 100644 --- a/examples/xml/nwfilter/no-arpmac-spoofing.xml +++ b/examples/xml/nwfilter/no-arpmac-spoofing.xml @@ -2,6 +2,4 @@ <rule action='return' direction='out' priority='350' > <arp match='yes' arpsrcmacaddr='$MAC'/> </rule> - <!-- drop everything else --> - <rule action='drop' direction='out' priority='1000' /> </filter> diff --git a/examples/xml/nwfilter/no-ip-spoofing.xml b/examples/xml/nwfilter/no-ip-spoofing.xml index 84e8a5e..2fccd12 100644 --- a/examples/xml/nwfilter/no-ip-spoofing.xml +++ b/examples/xml/nwfilter/no-ip-spoofing.xml @@ -4,6 +4,4 @@ <rule action='return' direction='out'> <ip match='yes' srcipaddr='$IP' /> </rule> - <!-- drop any that don't match the source IP list --> - <rule action='drop' direction='out' /> </filter> diff --git a/examples/xml/nwfilter/no-mac-spoofing.xml b/examples/xml/nwfilter/no-mac-spoofing.xml index aee56c7..e2e8c03 100644 --- a/examples/xml/nwfilter/no-mac-spoofing.xml +++ b/examples/xml/nwfilter/no-mac-spoofing.xml @@ -4,6 +4,4 @@ <rule action='return' direction='out' priority='350' > <mac match='yes' srcmacaddr='$MAC'/> </rule> - <!-- drop everything else --> - <rule action='drop' direction='out' priority='1000' /> </filter> diff --git a/examples/xml/nwfilter/no-other-rarp-traffic.xml b/examples/xml/nwfilter/no-other-rarp-traffic.xml deleted file mode 100644 index 7729996..0000000 --- a/examples/xml/nwfilter/no-other-rarp-traffic.xml +++ /dev/null @@ -1,3 +0,0 @@ -<filter name='no-other-rarp-traffic' chain='rarp'> - <rule action='drop' direction='inout' priority='1000'/> -</filter> diff --git a/examples/xml/nwfilter/qemu-announce-self.xml b/examples/xml/nwfilter/qemu-announce-self.xml index 352db50..12957b5 100644 --- a/examples/xml/nwfilter/qemu-announce-self.xml +++ b/examples/xml/nwfilter/qemu-announce-self.xml @@ -8,6 +8,5 @@ <!-- accept if it was changed to rarp --> <filterref filter='qemu-announce-self-rarp'/> - <filterref filter='no-other-rarp-traffic'/> </filter> diff --git a/src/nwfilter/nwfilter_ebiptables_driver.c b/src/nwfilter/nwfilter_ebiptables_driver.c index 3c6fca7..e6a4880 100644 --- a/src/nwfilter/nwfilter_ebiptables_driver.c +++ b/src/nwfilter/nwfilter_ebiptables_driver.c @@ -2791,7 +2791,7 @@ ebtablesCreateTmpSubChain(virBufferPtr buf, protostr[0] = '\0'; virBufferAsprintf(buf, - CMD_DEF("%s -t %s -N %s") CMD_SEPARATOR + CMD_DEF("%s -t %s -N %s -P DROP") CMD_SEPARATOR CMD_EXEC "%s" CMD_DEF("%s -t %s -A %s %s -j %s") CMD_SEPARATOR @@ -3015,15 +3015,6 @@ ebtablesApplyBasicRules(const char *ifname, PRINT_ROOT_CHAIN(chain, chainPrefix, ifname); virBufferAsprintf(&buf, - CMD_DEF("%s -t %s -A %s -s ! %s -j DROP") CMD_SEPARATOR - CMD_EXEC - "%s", - - ebtables_cmd_path, EBTABLES_DEFAULT_TABLE, - chain, macaddr_str, - CMD_STOPONERR(1)); - - virBufferAsprintf(&buf, CMD_DEF("%s -t %s -A %s -p IPv4 -j ACCEPT") CMD_SEPARATOR CMD_EXEC "%s", -- 1.7.6.4

On Wed, Oct 05, 2011 at 08:08:49AM -0700, David L Stevens wrote:
This patch simplifies the table rules by setting the protocol chains policy to be "DROP" and removes the explicit "-j DROP" entries that the protocol rules had previously. It also makes "no-other-rarp-traffic.xml" obsolete.
This sounds like it is introducing a backwards compatibility problem wrt older libvirt deployments using NW Filters. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

"Daniel P. Berrange" <berrange@redhat.com> wrote on 10/05/2011 08:43:45 AM:
This sounds like it is introducing a backwards compatibility problem wrt older libvirt deployments using NW Filters.
I don't think so. Again, only if someone has modified a standard filter would they have to make those same modifications to the new set. The default value wouldn't need to change, but they are all ending with "-j DROP" otherwise. +-DLS

On Wed, Oct 05, 2011 at 09:04:11AM -0700, David Stevens wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote on 10/05/2011 08:43:45 AM:
This sounds like it is introducing a backwards compatibility problem wrt older libvirt deployments using NW Filters.
I don't think so. Again, only if someone has modified a standard filter would they have to make those same modifications to the new set. The default value wouldn't need to change, but they are all ending with "-j DROP" otherwise.
What if they have created their own custom filters and written their filter on the assumption that the default policy was ACCEPT ? Surely this change will break their filter ? Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

"Daniel P. Berrange" <berrange@redhat.com> wrote on 10/05/2011 09:15:08 AM:
What if they have created their own custom filters and written their filter on the assumption that the default policy was ACCEPT ? Surely this change will break their filter ?
If their filter has "ACCEPT" or "DROP", that will be the action for any matching packets -- the policy only applies when there is no match. So filters that explicitly handle the disposition of a particular packet have no change. The set of packets allowed from the standard filters is identical, so additional filters that explicitly allow or deny some packets should have no problem, and because "RETURN" and "CONTINUE" were not previously supported before my patch to add them in this set, a subchain can't directly rely on the default behavior of its parent chain in existing custom filters. Certainly it is possible that some filters would need to change, and especially those that modify the standard chains, but support for multiple addresses without n^2 rules requires that. Whether it is by changing the default policy or adding "-j DROP" at the end of every chain, exactly the same set of existing filters would break -- those that do not explicitly dispose of matching packets via ACCEPT or DROP. I think that is the price for supporting multiple MAC and IP addresses without exploding the number of rules to do the matching. +-DLS

This patch adds the internal capability to add rules to existing chains instead of using temporary chains and to generate placeholders for chains that are referenced without generating a rule for them immediately. Finally, it includes variable matching for filter instantiation (i.e., instantiate only when a given variable is present in a filter, or only when it is not). Signed-off-by: David L Stevens <dlstevens@us.ibm.com> --- src/conf/nwfilter_conf.h | 4 +- src/nwfilter/nwfilter_ebiptables_driver.c | 93 +++++++++++++++++++++-------- src/nwfilter/nwfilter_gentech_driver.c | 32 +++++++++- 3 files changed, 100 insertions(+), 29 deletions(-) diff --git a/src/conf/nwfilter_conf.h b/src/conf/nwfilter_conf.h index 17e954e..4348378 100644 --- a/src/conf/nwfilter_conf.h +++ b/src/conf/nwfilter_conf.h @@ -525,7 +525,9 @@ typedef int (*virNWFilterRuleCreateInstance)(virConnectPtr conn, virNWFilterRuleDefPtr rule, const char *ifname, virNWFilterHashTablePtr vars, - virNWFilterRuleInstPtr res); + virNWFilterRuleInstPtr res, + bool usetemp, + bool dummy); typedef int (*virNWFilterRuleApplyNewRules)(virConnectPtr conn, const char *ifname, diff --git a/src/nwfilter/nwfilter_ebiptables_driver.c b/src/nwfilter/nwfilter_ebiptables_driver.c index e6a4880..918625c 100644 --- a/src/nwfilter/nwfilter_ebiptables_driver.c +++ b/src/nwfilter/nwfilter_ebiptables_driver.c @@ -1136,6 +1136,7 @@ iptablesEnforceDirection(int directionIn, * @isIPv6 : Whether this is an IPv6 rule * @maySkipICMP : whether this rule may under certain circumstances skip * the ICMP rule from being created + * @dummy : generate rule placeholder without installing * * Convert a single rule into its representation for later instantiation * @@ -1154,7 +1155,8 @@ _iptablesCreateRuleInstance(int directionIn, const char *match, bool defMatch, const char *accept_target, bool isIPv6, - bool maySkipICMP) + bool maySkipICMP, + bool dummy) { char chain[MAX_CHAINNAME_LENGTH]; char number[20]; @@ -1181,6 +1183,13 @@ _iptablesCreateRuleInstance(int directionIn, PRINT_IPT_ROOT_CHAIN(chain, chainPrefix, ifname); + if (dummy) { + virBufferAsprintf(&buf, CMD_DEF_PRE "%s -- %s -%%c %s %%s", + "echo", iptables_cmd, chain); + bufUsed = virBufferUse(&buf); + goto prskip; + } + switch (rule->prtclType) { case VIR_NWFILTER_RULE_PROTOCOL_TCP: case VIR_NWFILTER_RULE_PROTOCOL_TCPoIPV6: @@ -1521,6 +1530,8 @@ _iptablesCreateRuleInstance(int directionIn, return -1; } +prskip: + if ((srcMacSkipped && bufUsed == virBufferUse(&buf)) || skipRule) { virBufferFreeAndReset(&buf); @@ -1636,7 +1647,9 @@ iptablesCreateRuleInstanceStateCtrl(virNWFilterDefPtr nwfilter, const char *ifname, virNWFilterHashTablePtr vars, virNWFilterRuleInstPtr res, - bool isIPv6) + bool isIPv6, + bool usetemp, + bool dummy) { int rc; int directionIn = 0; @@ -1668,7 +1681,7 @@ iptablesCreateRuleInstanceStateCtrl(virNWFilterDefPtr nwfilter, return 1; } - chainPrefix[1] = CHAINPREFIX_HOST_IN_TEMP; + chainPrefix[1] = usetemp ? CHAINPREFIX_HOST_IN_TEMP : CHAINPREFIX_HOST_IN; if (create) { rc = _iptablesCreateRuleInstance(directionIn, chainPrefix, @@ -1680,7 +1693,8 @@ iptablesCreateRuleInstanceStateCtrl(virNWFilterDefPtr nwfilter, matchState, false, "RETURN", isIPv6, - maySkipICMP); + maySkipICMP, + dummy); VIR_FREE(matchState); if (rc) @@ -1700,7 +1714,8 @@ iptablesCreateRuleInstanceStateCtrl(virNWFilterDefPtr nwfilter, return 1; } - chainPrefix[1] = CHAINPREFIX_HOST_OUT_TEMP; + chainPrefix[1] = usetemp ? CHAINPREFIX_HOST_OUT_TEMP : + CHAINPREFIX_HOST_OUT; if (create) { rc = _iptablesCreateRuleInstance(!directionIn, chainPrefix, @@ -1712,7 +1727,8 @@ iptablesCreateRuleInstanceStateCtrl(virNWFilterDefPtr nwfilter, matchState, false, "ACCEPT", isIPv6, - maySkipICMP); + maySkipICMP, + dummy); VIR_FREE(matchState); @@ -1736,7 +1752,8 @@ iptablesCreateRuleInstanceStateCtrl(virNWFilterDefPtr nwfilter, if (create) { chainPrefix[0] = 'H'; - chainPrefix[1] = CHAINPREFIX_HOST_IN_TEMP; + chainPrefix[1] = usetemp ? CHAINPREFIX_HOST_IN_TEMP : + CHAINPREFIX_HOST_IN; rc = _iptablesCreateRuleInstance(directionIn, chainPrefix, nwfilter, @@ -1747,7 +1764,8 @@ iptablesCreateRuleInstanceStateCtrl(virNWFilterDefPtr nwfilter, matchState, false, "RETURN", isIPv6, - maySkipICMP); + maySkipICMP, + dummy); VIR_FREE(matchState); } @@ -1761,7 +1779,9 @@ iptablesCreateRuleInstance(virNWFilterDefPtr nwfilter, const char *ifname, virNWFilterHashTablePtr vars, virNWFilterRuleInstPtr res, - bool isIPv6) + bool isIPv6, + bool usetemp, + bool dummy) { int rc; int directionIn = 0; @@ -1777,7 +1797,9 @@ iptablesCreateRuleInstance(virNWFilterDefPtr nwfilter, ifname, vars, res, - isIPv6); + isIPv6, + usetemp, + dummy); } if ((rule->tt == VIR_NWFILTER_RULE_DIRECTION_IN) || @@ -1800,7 +1822,7 @@ iptablesCreateRuleInstance(virNWFilterDefPtr nwfilter, else matchState = NULL; - chainPrefix[1] = CHAINPREFIX_HOST_IN_TEMP; + chainPrefix[1] = usetemp ? CHAINPREFIX_HOST_IN_TEMP : CHAINPREFIX_HOST_IN; rc = _iptablesCreateRuleInstance(directionIn, chainPrefix, nwfilter, @@ -1811,7 +1833,8 @@ iptablesCreateRuleInstance(virNWFilterDefPtr nwfilter, matchState, true, "RETURN", isIPv6, - maySkipICMP); + maySkipICMP, + dummy); if (rc) return rc; @@ -1822,7 +1845,7 @@ iptablesCreateRuleInstance(virNWFilterDefPtr nwfilter, else matchState = NULL; - chainPrefix[1] = CHAINPREFIX_HOST_OUT_TEMP; + chainPrefix[1] = usetemp ? CHAINPREFIX_HOST_OUT_TEMP : CHAINPREFIX_HOST_OUT; rc = _iptablesCreateRuleInstance(!directionIn, chainPrefix, nwfilter, @@ -1833,7 +1856,8 @@ iptablesCreateRuleInstance(virNWFilterDefPtr nwfilter, matchState, true, "ACCEPT", isIPv6, - maySkipICMP); + maySkipICMP, + dummy); if (rc) return rc; @@ -1844,7 +1868,7 @@ iptablesCreateRuleInstance(virNWFilterDefPtr nwfilter, matchState = NULL; chainPrefix[0] = 'H'; - chainPrefix[1] = CHAINPREFIX_HOST_IN_TEMP; + chainPrefix[1] = usetemp ? CHAINPREFIX_HOST_IN_TEMP : CHAINPREFIX_HOST_IN; rc = _iptablesCreateRuleInstance(directionIn, chainPrefix, nwfilter, @@ -1855,7 +1879,8 @@ iptablesCreateRuleInstance(virNWFilterDefPtr nwfilter, matchState, true, "RETURN", isIPv6, - maySkipICMP); + maySkipICMP, + dummy); return rc; } @@ -1886,7 +1911,8 @@ ebtablesCreateRuleInstance(char chainPrefix, const char *ifname, virNWFilterHashTablePtr vars, virNWFilterRuleInstPtr res, - bool reverse) + bool reverse, + bool dummy) { char macaddr[VIR_MAC_STRING_BUFLEN], ipaddr[INET_ADDRSTRLEN], @@ -1909,6 +1935,11 @@ ebtablesCreateRuleInstance(char chainPrefix, PRINT_CHAIN(chain, chainPrefix, ifname, virNWFilterChainSuffixTypeToString(nwfilter->chainsuffix)); + if (dummy) { + virBufferAsprintf(&buf, CMD_DEF_PRE "%s -- -t %s -%%c %s %%s", + "echo", EBTABLES_DEFAULT_TABLE, chain); + goto prskip; + } switch (rule->prtclType) { case VIR_NWFILTER_RULE_PROTOCOL_MAC: @@ -2317,6 +2348,8 @@ ebtablesCreateRuleInstance(char chainPrefix, return -1; } +prskip: + switch (rule->action) { case VIR_NWFILTER_RULE_ACTION_REJECT: /* REJECT not supported */ @@ -2374,11 +2407,15 @@ ebiptablesCreateRuleInstance(virConnectPtr conn ATTRIBUTE_UNUSED, virNWFilterRuleDefPtr rule, const char *ifname, virNWFilterHashTablePtr vars, - virNWFilterRuleInstPtr res) + virNWFilterRuleInstPtr res, + bool usetemp, + bool dummy) { int rc = 0; bool isIPv6; + char chainPrefix; + chainPrefix = usetemp ? CHAINPREFIX_HOST_IN_TEMP : CHAINPREFIX_HOST_IN; switch (rule->prtclType) { case VIR_NWFILTER_RULE_PROTOCOL_IP: case VIR_NWFILTER_RULE_PROTOCOL_MAC: @@ -2389,26 +2426,30 @@ ebiptablesCreateRuleInstance(virConnectPtr conn ATTRIBUTE_UNUSED, if (rule->tt == VIR_NWFILTER_RULE_DIRECTION_OUT || rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT) { - rc = ebtablesCreateRuleInstance(CHAINPREFIX_HOST_IN_TEMP, + rc = ebtablesCreateRuleInstance(chainPrefix, nwfilter, rule, ifname, vars, res, - rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT); + rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT, + dummy); if (rc) return rc; } + chainPrefix = usetemp ? CHAINPREFIX_HOST_OUT_TEMP : + CHAINPREFIX_HOST_OUT; if (rule->tt == VIR_NWFILTER_RULE_DIRECTION_IN || rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT) { - rc = ebtablesCreateRuleInstance(CHAINPREFIX_HOST_OUT_TEMP, + rc = ebtablesCreateRuleInstance(chainPrefix, nwfilter, rule, ifname, vars, res, - false); + false, + dummy); } break; @@ -2427,7 +2468,9 @@ ebiptablesCreateRuleInstance(virConnectPtr conn ATTRIBUTE_UNUSED, ifname, vars, res, - isIPv6); + isIPv6, + usetemp, + dummy); break; case VIR_NWFILTER_RULE_PROTOCOL_TCPoIPV6: @@ -2444,7 +2487,9 @@ ebiptablesCreateRuleInstance(virConnectPtr conn ATTRIBUTE_UNUSED, ifname, vars, res, - isIPv6); + isIPv6, + usetemp, + dummy); break; case VIR_NWFILTER_RULE_PROTOCOL_LAST: diff --git a/src/nwfilter/nwfilter_gentech_driver.c b/src/nwfilter/nwfilter_gentech_driver.c index 7891983..79350ac 100644 --- a/src/nwfilter/nwfilter_gentech_driver.c +++ b/src/nwfilter/nwfilter_gentech_driver.c @@ -284,7 +284,9 @@ virNWFilterRuleInstantiate(virConnectPtr conn, virNWFilterDefPtr filter, virNWFilterRuleDefPtr rule, const char *ifname, - virNWFilterHashTablePtr vars) + virNWFilterHashTablePtr vars, + bool usetemp, + bool dummy) { int rc; int i; @@ -297,8 +299,8 @@ virNWFilterRuleInstantiate(virConnectPtr conn, ret->techdriver = techdriver; - rc = techdriver->createRuleInstance(conn, nettype, filter, - rule, ifname, vars, ret); + rc = techdriver->createRuleInstance(conn, nettype, filter, rule, ifname, + vars, ret, usetemp, dummy); if (rc) { for (i = 0; i < ret->ndata; i++) @@ -354,6 +356,8 @@ err_exit: * @ifname: The name of the interface to apply the rules to * @vars: A map holding variable names and values used for instantiating * the filter and its subfilters. + * @matchvar: if non-null, variable name to match + * @notmatch: if matchvar set, match filters that do not reference matchvar * @nEntries: number of virNWFilterInst objects collected * @insts: pointer to array for virNWFilterIns object pointers * @useNewFilter: instruct whether to use a newDef pointer rather than a @@ -377,6 +381,8 @@ _virNWFilterInstantiateRec(virConnectPtr conn, virNWFilterDefPtr filter, const char *ifname, virNWFilterHashTablePtr vars, + const char *matchvar, + bool notmatch, int *nEntries, virNWFilterRuleInstPtr **insts, enum instCase useNewFilter, bool *foundNewFilter, @@ -387,18 +393,34 @@ _virNWFilterInstantiateRec(virConnectPtr conn, int i; virNWFilterRuleInstPtr inst; virNWFilterDefPtr next_filter; + bool usetemp, dummy; for (i = 0; i < filter->nentries; i++) { virNWFilterRuleDefPtr rule = filter->filterEntries[i]->rule; virNWFilterIncludeDefPtr inc = filter->filterEntries[i]->include; if (rule) { + usetemp = 1; + dummy = 0; + if (matchvar) { + int j; + + for (j = 0; j < rule->nvars; ++j) + if (strcmp(rule->vars[j], matchvar) == 0) + break; + /* use temp chains only on base rule install */ + usetemp = notmatch; + /* skip if not found; notmatch reverses the sense */ + dummy = (j == rule->nvars) ^ notmatch; + } inst = virNWFilterRuleInstantiate(conn, techdriver, nettype, filter, rule, ifname, - vars); + vars, + usetemp, + dummy); if (!inst) { rc = 1; break; @@ -456,6 +478,7 @@ _virNWFilterInstantiateRec(virConnectPtr conn, next_filter, ifname, tmpvars, + matchvar, notmatch, nEntries, insts, useNewFilter, foundNewFilter, @@ -684,6 +707,7 @@ virNWFilterInstantiate(virConnectPtr conn, filter, ifname, vars, + 0, 0, &nEntries, &insts, useNewFilter, foundNewFilter, driver); -- 1.7.6.4

This patch adds the capability of adding individual rules to existing chains. Signed-off-by: David L Stevens <dlstevens@us.ibm.com> --- src/conf/nwfilter_conf.h | 6 ++ src/nwfilter/nwfilter_ebiptables_driver.c | 73 +++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 0 deletions(-) diff --git a/src/conf/nwfilter_conf.h b/src/conf/nwfilter_conf.h index 4348378..12d1e0f 100644 --- a/src/conf/nwfilter_conf.h +++ b/src/conf/nwfilter_conf.h @@ -540,6 +540,11 @@ typedef int (*virNWFilterRuleTeardownNewRules)(virConnectPtr conn, typedef int (*virNWFilterRuleTeardownOldRules)(virConnectPtr conn, const char *ifname); +typedef int (*virNWFilterRuleAddRules)(virConnectPtr conn, + const char *ifname, + int nruleInstances, + void **_inst); + typedef int (*virNWFilterRuleRemoveRules)(virConnectPtr conn, const char *ifname, int nruleInstances, @@ -580,6 +585,7 @@ struct _virNWFilterTechDriver { virNWFilterRuleApplyNewRules applyNewRules; virNWFilterRuleTeardownNewRules tearNewRules; virNWFilterRuleTeardownOldRules tearOldRules; + virNWFilterRuleAddRules addRules; virNWFilterRuleRemoveRules removeRules; virNWFilterRuleAllTeardown allTeardown; virNWFilterRuleFreeInstanceData freeRuleInstance; diff --git a/src/nwfilter/nwfilter_ebiptables_driver.c b/src/nwfilter/nwfilter_ebiptables_driver.c index 918625c..1169e5a 100644 --- a/src/nwfilter/nwfilter_ebiptables_driver.c +++ b/src/nwfilter/nwfilter_ebiptables_driver.c @@ -3695,6 +3695,78 @@ err_exit: return rc; } +/** + * ebiptablesAddRules: + * @conn : pointer to virConnect object + * @ifname : the name of the interface to which the rules apply + * @nRuleInstance : the number of given rules + * @_inst : array of rule instantiation data + * + * Add all rules one after the other + * + * Return 0 on success, 1 if execution of one or more cleanup + * commands failed. + */ +static int +ebiptablesAddRules(virConnectPtr conn, + const char *ifname, + int nruleInstances, + void **_inst) +{ + int i; + int cli_status; + ebiptablesRuleInstPtr *inst = (ebiptablesRuleInstPtr *)_inst; + virBuffer buf = VIR_BUFFER_INITIALIZER; + bool haveIptables = false; + bool haveIp6tables = false; + + for (i = 0; i < nruleInstances; i++) { + sa_assert (inst); + switch (inst[i]->ruleType) { + case RT_EBTABLES: + ebiptablesInstCommand(&buf, + inst[i]->commandTemplate, + 'A', -1, 1); + break; + case RT_IPTABLES: + if (inst[i]->ruleType == RT_IPTABLES) + iptablesInstCommand(&buf, + inst[i]->commandTemplate, + 'A', -1, 1); + haveIptables = true; + break; + case RT_IP6TABLES: + if (inst[i]->ruleType == RT_IP6TABLES) + iptablesInstCommand(&buf, + inst[i]->commandTemplate, + 'A', -1, 1); + haveIp6tables = true; + break; + } + } + + if (ebiptablesExecCLI(&buf, &cli_status) || cli_status != 0) + goto err_exit; + + if (haveIptables) + iptablesCheckBridgeNFCallEnabled(false); + + if (haveIp6tables) + iptablesCheckBridgeNFCallEnabled(true); + + return 0; + +err_exit: + (void) ebiptablesRemoveRules(conn, ifname, nruleInstances, _inst); + + virNWFilterReportError(VIR_ERR_BUILD_FIREWALL, + _("Some rules could not be created for " + "interface %s."), + ifname); + + return 1; +} + /** * ebiptablesAllTeardown: @@ -3751,6 +3823,7 @@ virNWFilterTechDriver ebiptables_driver = { .tearNewRules = ebiptablesTearNewRules, .tearOldRules = ebiptablesTearOldRules, .allTeardown = ebiptablesAllTeardown, + .addRules = ebiptablesAddRules, .removeRules = ebiptablesRemoveRules, .freeRuleInstance = ebiptablesFreeRuleInstance, .displayRuleInstance = ebiptablesDisplayRuleInstance, -- 1.7.6.4

This patch adds a function that applies or deletes filter rules to existing chains. Rules referencing the given variable are instantiated with the given value, or optionally deleted. For example, passing variable "IP" with different values will install rules using the IP variable with each of the different values. These rules can later be removed by calling this function with the same variable and value and "delete" argument set to "1". Signed-off-by: David L Stevens <dlstevens@us.ibm.com> --- src/nwfilter/nwfilter_gentech_driver.c | 86 ++++++++++++++++++++++++++++++++ src/nwfilter/nwfilter_gentech_driver.h | 11 ++++ 2 files changed, 97 insertions(+), 0 deletions(-) diff --git a/src/nwfilter/nwfilter_gentech_driver.c b/src/nwfilter/nwfilter_gentech_driver.c index 79350ac..563a1f3 100644 --- a/src/nwfilter/nwfilter_gentech_driver.c +++ b/src/nwfilter/nwfilter_gentech_driver.c @@ -620,6 +620,92 @@ virNWFilterRuleInstancesToArray(int nEntries, /** + * virNWFilterChangeVar: + * @conn: pointer to virConnect object + * @techdriver: The driver to use for instantiation + * @filter: The filter to instantiate + * @ifname: The name of the interface to apply the rules to + * @vars: A map holding variable names and values used for instantiating + * the filter and its subfilters. + * @var: name of variable to change + * @value: value of variable to change + * @delete: =0 to create or =1 to delete the rules + * + * Returns 0 on success, a value otherwise. + * + * Instantiate or delete a filter and all subfilters with variable "var" + * set to value "value". + * The name of the interface to which the rules belong must be + * provided. + * + * Call this function while holding the NWFilter filter update lock + */ +int +virNWFilterChangeVar(virConnectPtr conn, + virNWFilterTechDriverPtr techdriver, + enum virDomainNetType nettype, + virNWFilterDefPtr filter, + const char *ifname, + virNWFilterHashTablePtr vars, + virNWFilterDriverStatePtr driver, + const char *var, + char *value, + bool delete) +{ + int rc; + int j, nptrs; + int nEntries = 0; + virNWFilterRuleInstPtr *insts = NULL; + void **ptrs = NULL; + bool foundNewFilter = 0; + + if (virNWFilterHashTablePut(vars, var, value, 1)) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, _("Cound not add " + "variable \"%s\" to hashmap"), var); + return 1; + } + rc = _virNWFilterInstantiateRec(conn, + techdriver, + nettype, + filter, + ifname, + vars, + NWFILTER_STD_VAR_IP, 0, + &nEntries, &insts, + INSTANTIATE_ALWAYS, &foundNewFilter, + driver); + if (rc) + goto err_exit; + rc = virNWFilterRuleInstancesToArray(nEntries, insts, &ptrs, &nptrs); + if (rc) + goto err_exit; + + if (virNWFilterHashTableRemoveEntry(vars, var) < 0) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, _("Could not remove " + "variable \"%s\" from hashmap"), var); + return 1; + } + + if (virNWFilterLockIface(ifname)) + goto err_exit; + + if (delete) + rc = techdriver->removeRules(conn, ifname, nptrs, ptrs); + else + rc = techdriver->addRules(conn, ifname, nptrs, ptrs); + virNWFilterUnlockIface(ifname); + VIR_FREE(ptrs); + +err_exit: + + for (j = 0; j < nEntries; j++) + virNWFilterRuleInstFree(insts[j]); + VIR_FREE(insts); + return rc; +} + + +/** * virNWFilterInstantiate: * @conn: pointer to virConnect object * @techdriver: The driver to use for instantiation diff --git a/src/nwfilter/nwfilter_gentech_driver.h b/src/nwfilter/nwfilter_gentech_driver.h index fa86030..34e95c7 100644 --- a/src/nwfilter/nwfilter_gentech_driver.h +++ b/src/nwfilter/nwfilter_gentech_driver.h @@ -48,6 +48,17 @@ int virNWFilterRollbackUpdateFilter(virConnectPtr conn, int virNWFilterTearOldFilter(virConnectPtr conn, const virDomainNetDefPtr net); +int virNWFilterChangeVar(virConnectPtr conn, + virNWFilterTechDriverPtr techdriver, + enum virDomainNetType nettype, + virNWFilterDefPtr filter, + const char *ifname, + virNWFilterHashTablePtr vars, + virNWFilterDriverStatePtr driver, + const char *var, + char *value, + bool delete); + int virNWFilterInstantiateFilterLate(virConnectPtr conn, const char *ifname, int ifindex, -- 1.7.6.4

This patch adds DHCP Snooping support to libvirt. Signed-off-by: David L Stevens <dlstevens@us.ibm.com> --- examples/xml/nwfilter/no-ip-spoofing.xml | 5 + src/Makefile.am | 2 + src/nwfilter/nwfilter_dhcpsnoop.c | 602 ++++++++++++++++++++++++++++++ src/nwfilter/nwfilter_dhcpsnoop.h | 36 ++ src/nwfilter/nwfilter_driver.c | 5 + src/nwfilter/nwfilter_gentech_driver.c | 83 +++-- 6 files changed, 708 insertions(+), 25 deletions(-) create mode 100644 src/nwfilter/nwfilter_dhcpsnoop.c create mode 100644 src/nwfilter/nwfilter_dhcpsnoop.h diff --git a/examples/xml/nwfilter/no-ip-spoofing.xml b/examples/xml/nwfilter/no-ip-spoofing.xml index 2fccd12..2ae9500 100644 --- a/examples/xml/nwfilter/no-ip-spoofing.xml +++ b/examples/xml/nwfilter/no-ip-spoofing.xml @@ -4,4 +4,9 @@ <rule action='return' direction='out'> <ip match='yes' srcipaddr='$IP' /> </rule> + <!-- allow DHCP requests --> + <rule action='return' direction='out'> + <ip match='yes' srcipaddr='0.0.0.0' protocol='udp' srcportstart='68' + srcportend='68' /> + </rule> </filter> diff --git a/src/Makefile.am b/src/Makefile.am index 738ee91..f6d3fdd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -473,6 +473,8 @@ NWFILTER_DRIVER_SOURCES = \ nwfilter/nwfilter_driver.h nwfilter/nwfilter_driver.c \ nwfilter/nwfilter_gentech_driver.c \ nwfilter/nwfilter_gentech_driver.h \ + nwfilter/nwfilter_dhcpsnoop.c \ + nwfilter/nwfilter_dhcpsnoop.h \ nwfilter/nwfilter_ebiptables_driver.c \ nwfilter/nwfilter_ebiptables_driver.h \ nwfilter/nwfilter_learnipaddr.c \ diff --git a/src/nwfilter/nwfilter_dhcpsnoop.c b/src/nwfilter/nwfilter_dhcpsnoop.c new file mode 100644 index 0000000..f784a29 --- /dev/null +++ b/src/nwfilter/nwfilter_dhcpsnoop.c @@ -0,0 +1,602 @@ + +/* + * nwfilter_dhcpsnoop.c: support for DHCP snooping used by a VM + * on an interface + * + * Copyright (C) 2011 IBM Corp. + * Copyright (C) 2011 David L Stevens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: David L Stevens <dlstevens@us.ibm.com> + * Based in part on work by Stefan Berger <stefanb@us.ibm.com> + */ + +#include <config.h> + +#ifdef HAVE_LIBPCAP +#include <pcap.h> +#endif + +#include <fcntl.h> +#include <sys/ioctl.h> +#include <signal.h> + +#include <arpa/inet.h> +#include <net/ethernet.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <intprops.h> + +#include "internal.h" + +#include "buf.h" +#include "memory.h" +#include "logging.h" +#include "datatypes.h" +#include "interface.h" +#include "virterror_internal.h" +#include "threads.h" +#include "conf/nwfilter_params.h" +#include "conf/domain_conf.h" +#include "nwfilter_gentech_driver.h" +#include "nwfilter_ebiptables_driver.h" +#include "nwfilter_dhcpsnoop.h" + +#define VIR_FROM_THIS VIR_FROM_NWFILTER + +static virHashTablePtr SnoopReqs; + +struct virNWFilterSnoopReq { + virConnectPtr conn; + virNWFilterTechDriverPtr techdriver; + enum virDomainNetType nettype; + virNWFilterDefPtr filter; + const char *ifname; + virNWFilterHashTablePtr vars; + virNWFilterDriverStatePtr driver; + pthread_t thread; + /* start and end of lease list, ordered by lease time */ + struct iplease *start; + struct iplease *end; + bool die; +}; + +#define POLL_INTERVAL 10*1000 /* 10 secs */ + +struct iplease { + uint32_t ipl_ipaddr; + uint32_t ipl_server; + struct virNWFilterSnoopReq *ipl_req; + unsigned int ipl_timeout; + /* timer list */ + struct iplease *ipl_prev; + struct iplease *ipl_next; +}; + +static struct iplease *ipl_getbyip(struct iplease *start, uint32_t ipaddr); +static void ipl_update(struct iplease *pl, uint32_t timeout); + + +/* + * ipl_ladd - add an IP lease to a list + */ +static void +ipl_ladd(struct iplease *plnew, struct iplease **start, struct iplease **end) +{ + struct iplease *pl; + + plnew->ipl_next = plnew->ipl_prev = 0; + if (!*start) { + plnew->ipl_prev = plnew->ipl_next = 0; + *start = *end = plnew; + return; + } + for (pl = *end; pl && plnew->ipl_timeout < pl->ipl_timeout; + pl = pl->ipl_prev) + /* empty */ ; + if (!pl) { + plnew->ipl_next = *start; + *start = plnew; + } else { + plnew->ipl_next = pl->ipl_next; + pl->ipl_next = plnew; + } + plnew->ipl_prev = pl; + if (plnew->ipl_next) + plnew->ipl_next->ipl_prev = plnew; + else + *end = plnew; +} + +/* + * ipl_tadd - add an IP lease to the timer list + */ +static void +ipl_tadd(struct iplease *plnew) +{ + struct virNWFilterSnoopReq *req = plnew->ipl_req; + + ipl_ladd(plnew, &req->start, &req->end); +} + +/* + * ipl_add - create or update an IP lease + */ +static void +ipl_add(struct iplease *plnew) +{ + struct iplease *pl; + int rc; + char ipbuf[20]; /* dotted decimal IP addr string */ + char *ipstr; + + pl = ipl_getbyip(plnew->ipl_req->start, plnew->ipl_ipaddr); + if (pl) { + ipl_update(pl, plnew->ipl_timeout); + return; + } + if (VIR_ALLOC(pl) < 0) { + virReportOOMError(); + return; + } + *pl = *plnew; + + if (!inet_ntop(AF_INET, &pl->ipl_ipaddr, ipbuf, sizeof(ipbuf))) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("ipl_add inet_ntop " "failed (0x%08X)"), + pl->ipl_ipaddr); + VIR_FREE(pl); + return; + + } + + ipstr = strdup(ipbuf); + if (!ipstr) { + VIR_FREE(pl); + virReportOOMError(); + return; + } + rc = virNWFilterChangeVar(pl->ipl_req->conn, + pl->ipl_req->techdriver, + pl->ipl_req->nettype, + pl->ipl_req->filter, + pl->ipl_req->ifname, + pl->ipl_req->vars, + pl->ipl_req->driver, "IP", ipstr, 0); + if (rc) { + VIR_FREE(ipstr); + VIR_FREE(pl); + return; + } + ipl_tadd(pl); +} + +/* + * ipl_tdel - remove an IP lease from a list + */ +static void +ipl_ldel(struct iplease *ipl, struct iplease **start, struct iplease **end) +{ + if (ipl->ipl_prev) + ipl->ipl_prev->ipl_next = ipl->ipl_next; + else + *start = ipl->ipl_next; + if (ipl->ipl_next) + ipl->ipl_next->ipl_prev = ipl->ipl_prev; + else + *end = ipl->ipl_prev; + ipl->ipl_next = ipl->ipl_prev = 0; +} + +/* + * ipl_tdel - remove an IP lease from the timer list + */ +static void +ipl_tdel(struct iplease *ipl) +{ + struct virNWFilterSnoopReq *req = ipl->ipl_req; + + ipl_ldel(ipl, &req->start, &req->end); + ipl->ipl_timeout = 0; +} + +/* + * ipl_del - delete an IP lease + */ +static void +ipl_del(struct iplease *ipl) +{ + struct virNWFilterSnoopReq *req; + char ipbuf[20]; /* dotted decimal IP addr string */ + char *ipstr; + + if (!ipl) + return; + + req = ipl->ipl_req; + + ipl_tdel(ipl); + + if (inet_ntop(AF_INET, &ipl->ipl_ipaddr, ipbuf, sizeof(ipbuf))) { + ipstr = strdup(ipbuf); + if (virNWFilterChangeVar(req->conn, + req->techdriver, + req->nettype, + req->filter, + req->ifname, + req->vars, req->driver, "IP", ipstr, 1)) + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("ipl_del virNWFilterChangeVar failed; " + "filter not deleted")); + } else + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("ipl_del inet_ntop " "failed (0x%08X)"), + ipl->ipl_ipaddr); + VIR_FREE(ipl); +} + +/* + * ipl_update - update the timeout on an IP lease + */ +static void +ipl_update(struct iplease *ipl, uint32_t timeout) +{ + ipl_tdel(ipl); + ipl->ipl_timeout = timeout; + ipl_tadd(ipl); + return; +} + +/* + * ipl_getbyip - lookup IP lease by IP address + */ +static struct iplease * +ipl_getbyip(struct iplease *start, uint32_t ipaddr) +{ + struct iplease *pl; + + for (pl = start; pl && pl->ipl_ipaddr != ipaddr; pl = pl->ipl_next) + /* empty */ ; + return pl; +} + +#define GRACE 5 + +/* + * ipl_trun - run the IP lease timeout list + */ +static unsigned int +ipl_trun(struct virNWFilterSnoopReq *req) +{ + uint32_t now; + + now = time(0); + while (req->start && req->start->ipl_timeout <= now) + ipl_del(req->start); + return 0; +} + +typedef unsigned char Eaddr[6]; + +struct eth { + Eaddr eh_dst; + Eaddr eh_src; + unsigned short eh_type; + unsigned char eh_data[0]; +}; + +struct dhcp { + unsigned char d_op; + unsigned char d_htype; + unsigned char d_hlen; + unsigned char d_hops; + unsigned int d_xid; + unsigned short d_secs; + unsigned short d_flags; + unsigned int d_ciaddr; + unsigned int d_yiaddr; + unsigned int d_siaddr; + unsigned int d_giaddr; + unsigned char d_chaddr[16]; + char d_sname[64]; + char d_file[128]; + unsigned char d_opts[0]; +}; + +/* DHCP options */ + +#define DHCPO_PAD 0 +#define DHCPO_LEASE 51 /* lease time in secs */ +#define DHCPO_MTYPE 53 /* message type */ +#define DHCPO_END 255 /* end of options */ + +/* DHCP message types */ +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPRELEASE 7 + +unsigned char dhcp_magic[4] = { 99, 130, 83, 99 }; + +static int +dhcp_getopt(struct dhcp *pd, int len, int *pmtype, int *pleasetime) +{ + int oind, olen; + int oend; + + olen = len - sizeof *pd; + oind = 0; + + if (olen < 4) /* bad magic */ + return -1; + for (oind = 0; oind < sizeof dhcp_magic; ++oind) + if (pd->d_opts[oind] != dhcp_magic[oind]) + return -1; /* bad magic */ + + oend = 0; + + *pmtype = *pleasetime = 0; + + while (oind < olen) { + switch (pd->d_opts[oind]) { + case DHCPO_LEASE: + if (olen - oind < 6) + goto malformed; + if (*pleasetime) + return -1; /* duplicate lease time */ + *pleasetime = + ntohl(*(unsigned int *) (pd->d_opts + oind + 2)); + break; + case DHCPO_MTYPE: + if (olen - oind < 3) + goto malformed; + if (*pmtype) + return -1; /* duplicate message type */ + *pmtype = pd->d_opts[oind + 2]; + break; + case DHCPO_PAD: + oind++; + continue; + + case DHCPO_END: + oend = 1; + break; + default: + if (olen - oind < 2) + goto malformed; + } + if (oend) + break; + oind += pd->d_opts[oind + 1] + 2; + } + return 0; + malformed: + VIR_WARN("got lost in the options!"); + return -1; +} + +static void +dhcpdecode(struct virNWFilterSnoopReq *req, struct eth *pep, int len) +{ + struct iphdr *pip; + struct udphdr *pup; + struct dhcp *pd; + struct iplease ipl, *pipl; + int mtype, leasetime; + + /* go through the protocol headers */ + pip = (struct iphdr *) pep->eh_data; + len -= sizeof(*pep); + pup = (struct udphdr *) ((char *) pip + (pip->ihl << 2)); + len -= pip->ihl << 2; + pd = (struct dhcp *) ((char *) pup + sizeof(*pup)); + len -= sizeof(*pup); + if (len < 0) + return; /* dhcpdecode: invalid packet length */ + if (dhcp_getopt(pd, len, &mtype, &leasetime) < 0) + return; + + memset(&ipl, 0, sizeof(ipl)); + ipl.ipl_ipaddr = pd->d_yiaddr; + ipl.ipl_server = pd->d_siaddr; + if (leasetime == ~0) + ipl.ipl_timeout = ~0; + else + ipl.ipl_timeout = time(0) + leasetime; + ipl.ipl_req = req; + + switch (mtype) { + case DHCPACK: + ipl_add(&ipl); + break; + case DHCPDECLINE: + case DHCPRELEASE: + pipl = ipl_getbyip(req->start, ipl.ipl_ipaddr); + ipl_del(pipl); + break; + default: + break; + } +} + +#define PBUFSIZE 576 /* >= IP/TCP/DHCP headers */ + +static pcap_t * +dhcpopen(const char *intf) +{ + pcap_t *handle; + struct bpf_program fp; + char filter[64]; + char pcap_errbuf[PCAP_ERRBUF_SIZE]; + + handle = pcap_open_live(intf, PBUFSIZE, 0, POLL_INTERVAL, pcap_errbuf); + if (handle == NULL) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("pcap_open_live: %s"), pcap_errbuf); + return 0; + } + + sprintf(filter, "port 67 or dst port 68"); + if (pcap_compile(handle, &fp, filter, 1, PCAP_NETMASK_UNKNOWN) != 0) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("pcap_compile: %s"), pcap_geterr(handle)); + return 0; + } + if (pcap_setfilter(handle, &fp) != 0) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("pcap_setfilter: %s"), pcap_geterr(handle)); + return 0; + } + pcap_freecode(&fp); + return handle; +} + +static void * +virNWFilterDHCPSnoop(void *req0) +{ + struct virNWFilterSnoopReq *req = req0; + pcap_t *handle; + struct pcap_pkthdr hdr; + struct eth *packet; + struct iplease *ipl; + int ifindex; + + handle = dhcpopen(req->ifname); + if (!handle) + return 0; + + ifindex = if_nametoindex(req->ifname); + + while (1) { + if (req->die) + break; + ipl_trun(req); + + packet = (struct eth *) pcap_next(handle, &hdr); + + if (!packet) { + if (ifaceCheck(false, req->ifname, NULL, ifindex)) + virNWFilterDHCPSnoopEnd(req->ifname); + continue; + } + + dhcpdecode(req, packet, hdr.caplen); + } + /* free all leases */ + for (ipl = req->start; ipl; ipl = req->start) + ipl_del(ipl); + + /* free all req data */ + VIR_FREE(req->ifname); + virNWFilterHashTableFree(req->vars); + VIR_FREE(req); + return 0; +} + +int +virNWFilterDHCPSnoopReq(virConnectPtr conn, + virNWFilterTechDriverPtr techdriver ATTRIBUTE_UNUSED, + enum virDomainNetType nettype ATTRIBUTE_UNUSED, + virNWFilterDefPtr filter ATTRIBUTE_UNUSED, + const char *ifname ATTRIBUTE_UNUSED, + virNWFilterHashTablePtr vars ATTRIBUTE_UNUSED, + virNWFilterDriverStatePtr driver ATTRIBUTE_UNUSED) +{ + struct virNWFilterSnoopReq *req; + + req = virHashLookup(SnoopReqs, ifname); + if (req) + return 0; + if (VIR_ALLOC(req) < 0) { + virReportOOMError(); + return 1; + } + + req->conn = conn; + req->techdriver = techdriver; + req->nettype = nettype; + req->filter = filter; + req->ifname = strdup(ifname); + req->vars = virNWFilterHashTableCreate(0); + if (!req->vars) { + virReportOOMError(); + return 1; + } + if (virNWFilterHashTablePutAll(vars, req->vars)) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("virNWFilterDHCPSnoopReq: can't copy variables" + " on if %s"), ifname); + return 1; + } + req->driver = driver; + req->start = req->end = 0; + + if (virHashAddEntry(SnoopReqs, ifname, req)) { + VIR_FREE(req); + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("virNWFilterDHCPSnoopReq req add failed on" + "interface \"%s\""), ifname); + return 1; + } + if (pthread_create(&req->thread, NULL, virNWFilterDHCPSnoop, req) != 0) { + (void) virHashRemoveEntry(SnoopReqs, ifname); + VIR_FREE(req); + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("virNWFilterDHCPSnoopReq pthread_create failed" + " oninterface \"%s\""), ifname); + return 1; + } + return 0; +} + +/* + * freeReq - hash table free function to kill a request + */ +static void +freeReq(void *req0, const void *name ATTRIBUTE_UNUSED) +{ + struct virNWFilterSnoopReq *req = (struct virNWFilterSnoopReq *) req0; + + if (!req) + return; + + req->die = 1; +} + +int +virNWFilterDHCPSnoopInit(void) +{ + if (SnoopReqs) + return 0; + SnoopReqs = virHashCreate(0, freeReq); + if (!SnoopReqs) { + virReportOOMError(); + return -1; + } + return 0; +} + +void +virNWFilterDHCPSnoopEnd(const char *ifname) +{ + if (!SnoopReqs) + return; + if (ifname) + virHashRemoveEntry(SnoopReqs, ifname); + else /* free all of them */ + virHashFree(SnoopReqs); +} diff --git a/src/nwfilter/nwfilter_dhcpsnoop.h b/src/nwfilter/nwfilter_dhcpsnoop.h new file mode 100644 index 0000000..248b1a0 --- /dev/null +++ b/src/nwfilter/nwfilter_dhcpsnoop.h @@ -0,0 +1,36 @@ +/* + * nwfilter_dhcpsnoop.h: support DHCP snooping for a VM on an interface + * + * Copyright (C) 2010 IBM Corp. + * Copyright (C) 2010 David L Stevens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: David L Stevens <dlstevens@us.ibm.com> + */ + +#ifndef __NWFILTER_DHCPSNOOP_H +# define __NWFILTER_DHCPSNOOP_H + +int virNWFilterDHCPSnoopInit(void); +int virNWFilterDHCPSnoopReq(virConnectPtr conn, + virNWFilterTechDriverPtr techdriver, + enum virDomainNetType, + virNWFilterDefPtr filter, + const char *ifname, + virNWFilterHashTablePtr vars, + virNWFilterDriverStatePtr driver); +void virNWFilterDHCPSnoopEnd(const char *ifname); +#endif /* __NWFILTER_DHCPSNOOP_H */ diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c index a735059..57eb52c 100644 --- a/src/nwfilter/nwfilter_driver.c +++ b/src/nwfilter/nwfilter_driver.c @@ -39,6 +39,7 @@ #include "nwfilter_gentech_driver.h" #include "configmake.h" +#include "nwfilter_dhcpsnoop.h" #include "nwfilter_learnipaddr.h" #define VIR_FROM_THIS VIR_FROM_NWFILTER @@ -66,6 +67,8 @@ static int nwfilterDriverStartup(int privileged) { char *base = NULL; + if (virNWFilterDHCPSnoopInit() < 0) + return -1; if (virNWFilterLearnInit() < 0) return -1; @@ -127,6 +130,7 @@ alloc_err_exit: conf_init_err: virNWFilterTechDriversShutdown(); + virNWFilterDHCPSnoopEnd(0); virNWFilterLearnShutdown(); return -1; @@ -201,6 +205,7 @@ nwfilterDriverShutdown(void) { virNWFilterConfLayerShutdown(); virNWFilterTechDriversShutdown(); + virNWFilterDHCPSnoopEnd(0); virNWFilterLearnShutdown(); nwfilterDriverLock(driverState); diff --git a/src/nwfilter/nwfilter_gentech_driver.c b/src/nwfilter/nwfilter_gentech_driver.c index 563a1f3..577b48d 100644 --- a/src/nwfilter/nwfilter_gentech_driver.c +++ b/src/nwfilter/nwfilter_gentech_driver.c @@ -33,6 +33,7 @@ #include "virterror_internal.h" #include "nwfilter_gentech_driver.h" #include "nwfilter_ebiptables_driver.h" +#include "nwfilter_dhcpsnoop.h" #include "nwfilter_learnipaddr.h" @@ -42,6 +43,8 @@ #define NWFILTER_STD_VAR_MAC "MAC" #define NWFILTER_STD_VAR_IP "IP" +#define NWFILTER_DFLT_LEARN "DHCP" + static int _virNWFilterTeardownFilter(const char *ifname); @@ -747,6 +750,8 @@ virNWFilterInstantiate(virConnectPtr conn, void **ptrs = NULL; int instantiate = 1; char *buf; + const char *learning = NWFILTER_DFLT_LEARN; + bool reportIP = false; virNWFilterHashTablePtr missing_vars = virNWFilterHashTableCreate(0); if (!missing_vars) { @@ -764,39 +769,65 @@ virNWFilterInstantiate(virConnectPtr conn, if (rc) goto err_exit; + learning = virHashLookup(vars->hashTable, "ip_learning"); + if (virHashSize(missing_vars->hashTable) == 1) { if (virHashLookup(missing_vars->hashTable, NWFILTER_STD_VAR_IP) != NULL) { - if (virNWFilterLookupLearnReq(ifindex) == NULL) { - rc = virNWFilterLearnIPAddress(techdriver, - ifname, - ifindex, - linkdev, - nettype, macaddr, - filter->name, - vars, driver, - DETECT_DHCP|DETECT_STATIC); + if (strcasecmp(learning, "none") == 0) { /* no learning */ + reportIP = true; + goto err_unresolvable_vars; } - goto err_exit; - } - goto err_unresolvable_vars; + if (strcasecmp(learning, "dhcp") == 0) { + rc = _virNWFilterInstantiateRec(conn, + techdriver, + nettype, + filter, + ifname, + vars, + NWFILTER_STD_VAR_IP, 1, + &nEntries, &insts, + useNewFilter, foundNewFilter, + driver); + if (!rc) + rc = virNWFilterDHCPSnoopReq(conn, techdriver, nettype, + filter, ifname, vars, driver); + } else if (strcasecmp(learning, "any") == 0) { + if (virNWFilterLookupLearnReq(ifindex) == NULL) { + rc = virNWFilterLearnIPAddress(techdriver, + ifname, + ifindex, + linkdev, + nettype, macaddr, + filter->name, + vars, driver, + DETECT_DHCP|DETECT_STATIC); + } + goto err_exit; + } else { + rc = 1; + virNWFilterReportError(VIR_ERR_PARSE_FAILED, _("filter '%s' " + "learning value '%s' invalid."), + filter->name, learning); + } + } else + goto err_unresolvable_vars; } else if (virHashSize(missing_vars->hashTable) > 1) { goto err_unresolvable_vars; } else if (!forceWithPendingReq && virNWFilterLookupLearnReq(ifindex) != NULL) { goto err_exit; - } - - rc = _virNWFilterInstantiateRec(conn, - techdriver, - nettype, - filter, - ifname, - vars, - 0, 0, - &nEntries, &insts, - useNewFilter, foundNewFilter, - driver); + } else + rc = _virNWFilterInstantiateRec(conn, + techdriver, + nettype, + filter, + ifname, + vars, + 0, 0, + &nEntries, &insts, + useNewFilter, foundNewFilter, + driver); if (rc) goto err_exit; @@ -848,7 +879,7 @@ err_exit: err_unresolvable_vars: - buf = virNWFilterPrintVars(missing_vars->hashTable, ", ", false, false); + buf = virNWFilterPrintVars(missing_vars->hashTable, ", ", false, reportIP); if (buf) { virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot instantiate filter due to unresolvable " @@ -1180,6 +1211,8 @@ _virNWFilterTeardownFilter(const char *ifname) return 1; } + virNWFilterDHCPSnoopEnd(ifname); + virNWFilterTerminateLearnReq(ifname); if (virNWFilterLockIface(ifname)) -- 1.7.6.4

This patch adds support for saving DHCP snooping leases to an on-disk file and restoring saved leases that are still active on restart. Signed-off-by: David L Stevens <dlstevens@us.ibm.com> --- src/nwfilter/nwfilter_dhcpsnoop.c | 370 +++++++++++++++++++++++++++++++++++-- 1 files changed, 353 insertions(+), 17 deletions(-) diff --git a/src/nwfilter/nwfilter_dhcpsnoop.c b/src/nwfilter/nwfilter_dhcpsnoop.c index f784a29..eedf550 100644 --- a/src/nwfilter/nwfilter_dhcpsnoop.c +++ b/src/nwfilter/nwfilter_dhcpsnoop.c @@ -56,10 +56,21 @@ #include "nwfilter_gentech_driver.h" #include "nwfilter_ebiptables_driver.h" #include "nwfilter_dhcpsnoop.h" +#include "configmake.h" #define VIR_FROM_THIS VIR_FROM_NWFILTER +#define LEASEFILE LOCALSTATEDIR "/run/libvirt/network/nwfilter.leases" +#define TMPLEASEFILE LOCALSTATEDIR "/run/libvirt/network/nwfilter.ltmp" +static int lease_fd = -1; +static int nleases = 0; /* number of active leases */ +static int wleases = 0; /* number of written leases */ + static virHashTablePtr SnoopReqs; +static pthread_mutex_t SnoopLock; + +#define snoop_lock() { pthread_mutex_lock(&SnoopLock); } +#define snoop_unlock() { pthread_mutex_unlock(&SnoopLock); } struct virNWFilterSnoopReq { virConnectPtr conn; @@ -90,7 +101,14 @@ struct iplease { static struct iplease *ipl_getbyip(struct iplease *start, uint32_t ipaddr); static void ipl_update(struct iplease *pl, uint32_t timeout); - + +static struct iflease *getiflease(const char *ifname); +static void lease_open(void); +static void lease_close(void); +static void lease_load(void); +static void lease_save(struct iplease *ipl); +static void lease_refresh(void); +static void lease_restore(struct virNWFilterSnoopReq *req); /* * ipl_ladd - add an IP lease to a list @@ -150,6 +168,7 @@ ipl_add(struct iplease *plnew) ipl_update(pl, plnew->ipl_timeout); return; } + nleases++; if (VIR_ALLOC(pl) < 0) { virReportOOMError(); return; @@ -184,6 +203,7 @@ ipl_add(struct iplease *plnew) return; } ipl_tadd(pl); + lease_save(pl); } /* @@ -231,6 +251,7 @@ ipl_del(struct iplease *ipl) req = ipl->ipl_req; ipl_tdel(ipl); + lease_save(ipl); if (inet_ntop(AF_INET, &ipl->ipl_ipaddr, ipbuf, sizeof(ipbuf))) { ipstr = strdup(ipbuf); @@ -248,6 +269,7 @@ ipl_del(struct iplease *ipl) _("ipl_del inet_ntop " "failed (0x%08X)"), ipl->ipl_ipaddr); VIR_FREE(ipl); + nleases--; } /* @@ -259,6 +281,7 @@ ipl_update(struct iplease *ipl, uint32_t timeout) ipl_tdel(ipl); ipl->ipl_timeout = timeout; ipl_tadd(ipl); + lease_save(ipl); return; } @@ -275,8 +298,6 @@ ipl_getbyip(struct iplease *start, uint32_t ipaddr) return pl; } -#define GRACE 5 - /* * ipl_trun - run the IP lease timeout list */ @@ -465,6 +486,19 @@ dhcpopen(const char *intf) return handle; } +/* + * snoopReqFree - release a snoop Req + */ +static void +snoopReqFree(struct virNWFilterSnoopReq *req) +{ + if (req->ifname) + VIR_FREE(req->ifname); + if (req->vars) + virNWFilterHashTableFree(req->vars); + VIR_FREE(req); +} + static void * virNWFilterDHCPSnoop(void *req0) { @@ -479,12 +513,19 @@ virNWFilterDHCPSnoop(void *req0) if (!handle) return 0; + /* restore any saved leases for this interface */ + snoop_lock(); + lease_restore(req); + snoop_unlock(); + ifindex = if_nametoindex(req->ifname); while (1) { if (req->die) break; + snoop_lock(); ipl_trun(req); + snoop_unlock(); packet = (struct eth *) pcap_next(handle, &hdr); @@ -494,16 +535,18 @@ virNWFilterDHCPSnoop(void *req0) continue; } + snoop_lock(); dhcpdecode(req, packet, hdr.caplen); + snoop_unlock(); } + + snoop_lock(); /* free all leases */ for (ipl = req->start; ipl; ipl = req->start) ipl_del(ipl); + snoop_unlock(); - /* free all req data */ - VIR_FREE(req->ifname); - virNWFilterHashTableFree(req->vars); - VIR_FREE(req); + snoopReqFree(req); return 0; } @@ -518,9 +561,12 @@ virNWFilterDHCPSnoopReq(virConnectPtr conn, { struct virNWFilterSnoopReq *req; + snoop_lock(); req = virHashLookup(SnoopReqs, ifname); - if (req) + snoop_unlock(); + if (req) { return 0; + } if (VIR_ALLOC(req) < 0) { virReportOOMError(); return 1; @@ -533,28 +579,30 @@ virNWFilterDHCPSnoopReq(virConnectPtr conn, req->ifname = strdup(ifname); req->vars = virNWFilterHashTableCreate(0); if (!req->vars) { + snoopReqFree(req); virReportOOMError(); return 1; } if (virNWFilterHashTablePutAll(vars, req->vars)) { virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, - _("virNWFilterDHCPSnoopReq: can't copy variables" - " on if %s"), ifname); + _("virNWFilterDHCPSnoopReq: can't copy " + "variables on if %s"), ifname); + snoopReqFree(req); return 1; } req->driver = driver; req->start = req->end = 0; if (virHashAddEntry(SnoopReqs, ifname, req)) { - VIR_FREE(req); + snoopReqFree(req); virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, - _("virNWFilterDHCPSnoopReq req add failed on" - "interface \"%s\""), ifname); + _("virNWFilterDHCPSnoopReq req add " + "failed oninterface \"%s\""), ifname); return 1; } if (pthread_create(&req->thread, NULL, virNWFilterDHCPSnoop, req) != 0) { (void) virHashRemoveEntry(SnoopReqs, ifname); - VIR_FREE(req); + (void) snoopReqFree(req); virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, _("virNWFilterDHCPSnoopReq pthread_create failed" " oninterface \"%s\""), ifname); @@ -577,12 +625,41 @@ freeReq(void *req0, const void *name ATTRIBUTE_UNUSED) req->die = 1; } +static void +lease_close(void) +{ + (void) close(lease_fd); + lease_fd = -1; +} + +static void +lease_open(void) +{ + lease_close(); + + lease_fd = open(LEASEFILE, O_CREAT|O_RDWR|O_APPEND, 0644); +} + int virNWFilterDHCPSnoopInit(void) { if (SnoopReqs) return 0; + + if (pthread_mutex_init(&SnoopLock, 0) < 0) + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("pthread_mutex_init: %s"), + strerror(errno)); + snoop_lock(); + + lease_load(); + + lease_open(); + SnoopReqs = virHashCreate(0, freeReq); + + snoop_unlock(); + if (!SnoopReqs) { virReportOOMError(); return -1; @@ -595,8 +672,267 @@ virNWFilterDHCPSnoopEnd(const char *ifname) { if (!SnoopReqs) return; - if (ifname) - virHashRemoveEntry(SnoopReqs, ifname); - else /* free all of them */ + snoop_lock(); + if (!ifname) { /* free all of them */ virHashFree(SnoopReqs); + lease_refresh(); + } else + virHashRemoveEntry(SnoopReqs, ifname); + lease_close(); + snoop_unlock(); +} + + +/* lease file handling */ + +struct iflease { + char *ifl_ifname; + struct iplease *ifl_start; + struct iplease *ifl_end; + struct iflease *ifl_prev; + struct iflease *ifl_next; +}; + +struct iflease *leases; + +static int +lease_write(int lfd, const char *ifname, struct iplease *ipl) +{ + char lbuf[256],ipstr[16],dhcpstr[16]; + + if (inet_ntop(AF_INET, &ipl->ipl_ipaddr, ipstr, sizeof ipstr) == 0) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("inet_ntop(0x%08X) failed"), ipl->ipl_ipaddr); + return -1; + } + if (inet_ntop(AF_INET, &ipl->ipl_server, dhcpstr, sizeof dhcpstr) == 0) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("inet_ntop(0x%08X) failed"), ipl->ipl_server); + return -1; + } + /* time intf ip dhcpserver */ + snprintf(lbuf, sizeof(lbuf), "%u %s %s %s\n", ipl->ipl_timeout, + ifname, ipstr, dhcpstr); + if (write(lfd, lbuf, strlen(lbuf)) < 0) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("lease file write failed: %s"), + strerror(errno)); + return -1; + } + (void) fsync(lfd); + return 0; +} + +static void +lease_save(struct iplease *ipl) +{ + struct virNWFilterSnoopReq *req = ipl->ipl_req; + struct iflease *ifl; + + /* add to the lease file list */ + ifl = getiflease(ipl->ipl_req->ifname); + if (ifl) { + struct iplease *ifipl = ipl_getbyip(ifl->ifl_start, ipl->ipl_ipaddr); + + if (ifipl) { + if (ipl->ipl_timeout) { + ifipl->ipl_timeout = ipl->ipl_timeout; + ifipl->ipl_server = ipl->ipl_server; + } else { + ipl_ldel(ifipl, &ifl->ifl_start, &ifl->ifl_end); + VIR_FREE(ifipl); + } + } else if (!VIR_ALLOC(ifipl)) { + ifipl->ipl_ipaddr = ipl->ipl_ipaddr; + ifipl->ipl_server = ipl->ipl_server; + ifipl->ipl_timeout = ipl->ipl_timeout; + ipl_ladd(ifipl, &ifl->ifl_start, &ifl->ifl_end); + } + } + if (lease_fd < 0) + lease_open(); + if (lease_write(lease_fd, req->ifname, ipl) < 0) + return; + /* keep dead leases at < ~95% of file size */ + if (++wleases >= nleases*20) + lease_load(); /* load & refresh lease file */ +} + +static void +lease_restore(struct virNWFilterSnoopReq *req) +{ + struct iflease *ifl; + struct iplease *ipl; + + ifl = getiflease(req->ifname); + if (!ifl) + return; + for (ipl = ifl->ifl_start; ipl; ipl = ipl->ipl_next) { + ipl->ipl_req = req; + ipl_add(ipl); + } +} + +static struct iflease * +getiflease(const char *ifname) +{ + struct iflease *ifl; + + for (ifl=leases; ifl; ifl=ifl->ifl_next) + if (strcmp(ifname, ifl->ifl_ifname) == 0) + return ifl; + if (VIR_ALLOC(ifl)) { + virReportOOMError(); + return 0; + } + ifl->ifl_ifname = strdup(ifname); + ifl->ifl_next = leases; + leases = ifl; + return ifl; +} + +static void +lease_refresh(void) +{ + struct iflease *ifl; + struct iplease *ipl; + int tfd; + + (void) unlink(TMPLEASEFILE); + /* lease file loaded, delete old one */ + tfd = open(TMPLEASEFILE, O_CREAT|O_RDWR|O_TRUNC|O_EXCL, 0644); + if (tfd < 0) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("open(\"%s\"): %s"), + TMPLEASEFILE, strerror(errno)); + return; + } + for (ifl=leases; ifl; ifl=ifl->ifl_next) + for (ipl = ifl->ifl_start; ipl; ipl = ipl->ipl_next) + if (lease_write(tfd, ifl->ifl_ifname, ipl) < 0) + break; + (void) close(tfd); + if (rename(TMPLEASEFILE, LEASEFILE) < 0) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("rename(\"%s\", \"%s\"): %s"), + TMPLEASEFILE, LEASEFILE, strerror(errno)); + (void) unlink(TMPLEASEFILE); + } + wleases = 0; + lease_open(); +} + +static void +LoadSnoopReqIter(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) +{ + struct virNWFilterSnoopReq *req = payload; + struct iplease *ipl, *ifipl; + struct iflease *ifl; + + ifl = getiflease(req->ifname); + if (!ifl) + return; + for (ipl = req->start; ipl; ipl = ipl->ipl_next) { + ifipl = ipl_getbyip(ifl->ifl_start, ipl->ipl_ipaddr); + if (ifipl) { + if (ifipl->ipl_timeout < ipl->ipl_timeout) { + ifipl->ipl_timeout = ipl->ipl_timeout; + ifipl->ipl_server = ipl->ipl_server; + } + continue; + } + if (VIR_ALLOC(ifipl)) { + virReportOOMError(); + continue; + } + ifipl->ipl_ipaddr = ipl->ipl_ipaddr; + ifipl->ipl_server = ipl->ipl_server; + ifipl->ipl_timeout = ipl->ipl_timeout; + ipl_ladd(ifipl, &ifl->ifl_start, &ifl->ifl_end); + } +} + +static void +lease_load(void) +{ + char line[256], ifname[16], ipstr[16], srvstr[16]; + uint32_t ipaddr, svaddr; + FILE *fp; + int ln = 0; + time_t timeout, now; + struct iflease *ifl; + struct iplease *ipl; + + fp = fopen(LEASEFILE, "r"); + time(&now); + while (fp && fgets(line, sizeof(line), fp)) { + if (line[strlen(line)-1] != '\n') { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("lease_load lease file line %d corrupt"), + ln); + break; + } + ln++; + if (sscanf(line, "%lu %16s %16s %16s", (unsigned long *)&timeout, + ifname, ipstr, srvstr) < 4) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("lease_load lease file line %d corrupt"), + ln); + break;; + } + if (timeout && timeout < now) + continue; + ifl = getiflease(ifname); + if (!ifl) + break; + + if (inet_pton(AF_INET, ipstr, &ipaddr) <= 0) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("line %d corrupt ipaddr \"%s\""), + ln, ipstr); + VIR_FREE(ipl); + continue; + } + (void) inet_pton(AF_INET, srvstr, &svaddr); + + ipl = ipl_getbyip(ifl->ifl_start, ipaddr); + if (ipl) { + if (timeout && timeout < ipl->ipl_timeout) + continue; /* out of order lease? skip. */ + ipl->ipl_timeout = timeout; + ipl->ipl_server = svaddr; + continue; + } + if (!timeout) + continue; /* don't add new lease deletions */ + if (VIR_ALLOC(ipl)) { + virReportOOMError(); + break; + } + ipl->ipl_ipaddr = ipaddr; + ipl->ipl_server = svaddr; + ipl->ipl_timeout = timeout; + ipl_ladd(ipl, &ifl->ifl_start, &ifl->ifl_end); + } + /* also load any active leases from memory, in case lease writes may + * have failed. + */ + if (SnoopReqs) + virHashForEach(SnoopReqs, LoadSnoopReqIter, 0); + /* remove any deleted leases */ + for (ifl = leases; ifl; ifl = ifl->ifl_next) { + struct iplease *iplnext; + + for (ipl = ifl->ifl_start; ipl; ipl = iplnext) { + iplnext = ipl->ipl_next; + if (ipl->ipl_timeout == 0) { + ipl_ldel(ipl, &ifl->ifl_start, &ifl->ifl_end); + VIR_FREE(ipl); + } + } + } + + lease_refresh(); } -- 1.7.6.4
participants (3)
-
Daniel P. Berrange
-
David L Stevens
-
David Stevens