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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions