Skip to content

Cannot parse negated CIDR ex: "!192.168.122.0/24" #70

@stealthybox

Description

@stealthybox

Calling ipt.StructuredStats("nat", "POSTROUTING") when rules have negated ranges can error:

invalid CIDR address: !192.168.122.0/24%!(EXTRA string=could not parse destination)

On my machine, I set up a virtual bridge for this subnet, and these iptables rules were auto-created:

ifconfig virbr0; \
sudo iptables -L | grep 192.168; \
sudo iptables -t nat -L POSTROUTING | grep 192.168 \

virbr0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 192.168.122.1  netmask 255.255.255.0  broadcast 192.168.122.255
        ether 06:79:79:15:1a:de  txqueuelen 1000  (Ethernet)
        RX packets 76642  bytes 4332785 (4.3 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 476675  bytes 726146534 (726.1 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ACCEPT     all  --  anywhere             192.168.122.0/24     ctstate RELATED,ESTABLISHED
ACCEPT     all  --  192.168.122.0/24     anywhere            
RETURN     all  --  192.168.122.0/24     base-address.mcast.net/24 
RETURN     all  --  192.168.122.0/24     255.255.255.255     
MASQUERADE  tcp  --  192.168.122.0/24    !192.168.122.0/24     masq ports: 1024-65535
MASQUERADE  udp  --  192.168.122.0/24    !192.168.122.0/24     masq ports: 1024-65535
MASQUERADE  all  --  192.168.122.0/24    !192.168.122.0/24   

ignite uses this library call to cleanup chains, and these MASQ rules fail to net.ParseCIDR due to the leading exclamation mark negating the subnet: weaveworks/ignite#393


Here's a minimal reproduction:

Test Code
package main

import (
	"fmt"

	"github.com/coreos/go-iptables/iptables"
)

// testActual is a forked version of ipt.StructuredStats() that prints debug info
func testActual(ipt *iptables.IPTables) ([]iptables.Stat, error) {
	table := "nat"
	chain := "POSTROUTING"

	rawStats, err := ipt.Stats(table, chain)
	if err != nil {
		return nil, err
	}

	structStats := []iptables.Stat{}
	for _, rawStat := range rawStats {
		fmt.Println(rawStat[7], rawStat[8])
		stat, err := ipt.ParseStat(rawStat)
		if err != nil {
			fmt.Println("rawStat: ", rawStat)
			fmt.Println("stat:    ", stat)
			fmt.Println("err:     ", err)
			return nil, err
		}
		structStats = append(structStats, stat)
	}

	return structStats, nil
}

func minimalReproduction(ipt *iptables.IPTables) (iptables.Stat, error) {
	return ipt.ParseStat(
		[]string{
			"1859",
			"112600",
			"MASQUERADE",
			"tcp",
			"--",
			"*",
			"*",
			"192.168.122.0/24",
			"!192.168.122.0/24",
			"masq ports: 1024-65535",
		},
	)
}

func main() {
	ipt, _ := iptables.NewWithProtocol(iptables.ProtocolIPv4)
	fmt.Println("minimalReproduction")
	fmt.Println(minimalReproduction(ipt))
	fmt.Println()
	fmt.Println("testActual")
	fmt.Println(testActual(ipt))
}

Test logs:

sudo $(which go) run ./testnet.go
minimalReproduction
{1859 112600      192.168.122.0/24 <nil> } invalid CIDR address: !192.168.122.0/24%!(EXTRA string=could not parse destination)

testActual
172.19.0.0/16 0.0.0.0/0
172.17.0.0/16 0.0.0.0/0
192.168.122.0/24 224.0.0.0/24
192.168.122.0/24 255.255.255.255/32
192.168.122.0/24 !192.168.122.0/24
rawStat:  [1883 114040 MASQUERADE tcp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535]
stat:     {1883 114040      192.168.122.0/24 <nil> }
err:      invalid CIDR address: !192.168.122.0/24%!(EXTRA string=could not parse destination)
[] invalid CIDR address: !192.168.122.0/24%!(EXTRA string=could not parse destination)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions