package xt import ( "errors" "net" "github.com/google/nftables/alignedbuff" ) // See https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/nf_nat.h#L25 type NatIPv4Range struct { Flags uint // sic! MinIP net.IP MaxIP net.IP MinPort uint16 MaxPort uint16 } // NatIPv4MultiRangeCompat despite being a slice of NAT IPv4 ranges is currently allowed to // only hold exactly one element. // // See https://elixir.bootlin.com/linux/v5.17.7/source/include/uapi/linux/netfilter/nf_nat.h#L33 type NatIPv4MultiRangeCompat []NatIPv4Range func (x *NatIPv4MultiRangeCompat) marshal(fam TableFamily, rev uint32) ([]byte, error) { ab := alignedbuff.New() if len(*x) != 1 { return nil, errors.New("MasqueradeIp must contain exactly one NatIPv4Range") } ab.PutUint(uint(len(*x))) for _, nat := range *x { if err := nat.marshalAB(fam, rev, &ab); err != nil { return nil, err } } return ab.Data(), nil } func (x *NatIPv4MultiRangeCompat) unmarshal(fam TableFamily, rev uint32, data []byte) error { ab := alignedbuff.NewWithData(data) l, err := ab.Uint() if err != nil { return err } nats := make(NatIPv4MultiRangeCompat, l) for l > 0 { l-- if err := nats[l].unmarshalAB(fam, rev, &ab); err != nil { return err } } *x = nats return nil } func (x *NatIPv4Range) marshalAB(fam TableFamily, rev uint32, ab *alignedbuff.AlignedBuff) error { ab.PutUint(x.Flags) ab.PutBytesAligned32(x.MinIP.To4(), 4) ab.PutBytesAligned32(x.MaxIP.To4(), 4) ab.PutUint16BE(x.MinPort) ab.PutUint16BE(x.MaxPort) return nil } func (x *NatIPv4Range) unmarshalAB(fam TableFamily, rev uint32, ab *alignedbuff.AlignedBuff) error { var err error if x.Flags, err = ab.Uint(); err != nil { return err } var ip []byte if ip, err = ab.BytesAligned32(4); err != nil { return err } x.MinIP = net.IP(ip) if ip, err = ab.BytesAligned32(4); err != nil { return err } x.MaxIP = net.IP(ip) if x.MinPort, err = ab.Uint16BE(); err != nil { return err } if x.MaxPort, err = ab.Uint16BE(); err != nil { return err } return nil }