Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
stealthybox opened this issue Sep 12, 2019 · 1 comment
Open

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

stealthybox opened this issue Sep 12, 2019 · 1 comment

Comments

@stealthybox
Copy link

stealthybox commented Sep 12, 2019

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)
stealthybox added a commit to stealthybox/ignite that referenced this issue Sep 13, 2019
move fmt.Sprintf out of loop
access stat.Options through rawStat[9] with hard-coded index
only parse for IPNet when we are working with the proper ignite CNI rules
  ^ avoids coreos/go-iptables#70
stealthybox added a commit to stealthybox/ignite that referenced this issue Sep 13, 2019
move fmt.Sprintf out of loop
access stat.Options through rawStat[9] with hard-coded index
remove break in the event there are multiple rules per containerID
only parse for IPNet when we are working with the proper ignite CNI rules
  ^ avoids coreos/go-iptables#70
stealthybox added a commit to stealthybox/ignite that referenced this issue Sep 13, 2019
move fmt.Sprintf out of loop
access stat.Options through rawStat[9] with hard-coded index
remove break in the event there are multiple rules per containerID
only parse for IPNet when we are working with the proper ignite CNI rules
  ^ avoids coreos/go-iptables#70
@nor1su
Copy link

nor1su commented May 21, 2024

type CustomStat struct {
    Packets     uint64 `json:"pkts"`
    Bytes       uint64 `json:"bytes"`
    Target      string `json:"target"`
    Protocol    string `json:"prot"`
    Opt         string `json:"opt"`
    Input       string `json:"in"`
    Output      string `json:"out"`
    Source      string `json:"source"`
    Destination string `json:"destination"`
    Port        string `json:"port"`
    Options     string `json:"options"`
}

func parseIptablesOutput(output string) ([]CustomStat, error) {
    var customStats []CustomStat
    scanner := bufio.NewScanner(strings.NewReader(output))
    for scanner.Scan() {
        line := scanner.Text()
        fields := strings.Fields(line)
        if len(fields) < 10 {
            continue
        }
        packets, _ := strconv.ParseUint(fields[0], 10, 64)
        bytes, _ := strconv.ParseUint(fields[1], 10, 64)
        customStats = append(customStats, CustomStat{
            Packets:     packets,
            Bytes:       bytes,
            Target:      fields[2],
            Protocol:    fields[3],
            Opt:         fields[4],
            Input:       fields[5],
            Output:      fields[6],
            Source:      fields[7],
            Destination: fields[8],
            Options:     strings.Join(fields[9:], " "),
        })
    }
    return customStats, scanner.Err()
} 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants