mirror of https://github.com/subgraph/fw-daemon
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
258 lines
5.6 KiB
258 lines
5.6 KiB
package nfnetlink
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"syscall"
|
|
"net"
|
|
)
|
|
|
|
// NLAttr represents a single netlink attribute.
|
|
type NLAttr struct {
|
|
Type uint16
|
|
Data []byte
|
|
nested *NLAttrSet
|
|
Nested bool
|
|
Len uint16
|
|
}
|
|
|
|
type NLAttrSet struct {
|
|
attrList []*NLAttr // list of attributes
|
|
attrMap map[uint16]*NLAttr // mapping of attributes by type
|
|
}
|
|
|
|
func NewNLAttrSet() *NLAttrSet {
|
|
return &NLAttrSet{
|
|
attrMap: make(map[uint16]*NLAttr),
|
|
}
|
|
}
|
|
|
|
func (as *NLAttrSet) String() string {
|
|
bb := new(bytes.Buffer)
|
|
for i,a := range as.attrList {
|
|
if i != 0 {
|
|
bb.WriteString(" ")
|
|
}
|
|
bb.WriteString(a.String())
|
|
}
|
|
return bb.String()
|
|
}
|
|
|
|
func (as *NLAttrSet) Add(a *NLAttr) {
|
|
as.attrList = append(as.attrList, a)
|
|
t := a.Type & ^uint16(syscall.NLA_F_NESTED)
|
|
as.attrMap[t] = a
|
|
}
|
|
|
|
func (as *NLAttrSet) Get(atypes ...uint16) *NLAttr {
|
|
if len(atypes) == 0 {
|
|
return nil
|
|
}
|
|
attr := as.attrMap[atypes[0]]
|
|
return attr.Get(atypes[1:]...)
|
|
}
|
|
|
|
func (as *NLAttrSet) WriteTo(bb *bytes.Buffer) {
|
|
for _,a := range as.attrList {
|
|
a.WriteTo(bb)
|
|
}
|
|
}
|
|
|
|
func (as *NLAttrSet) Size() int {
|
|
var sz int
|
|
for _,a := range as.attrList {
|
|
if !a.Nested {
|
|
sz += a.Size()
|
|
} else {
|
|
sz += 4
|
|
}
|
|
}
|
|
return sz
|
|
}
|
|
|
|
// nlaAlignOf returns attrlen aligned to a 4 byte boundary
|
|
func nlaAlignOf(attrlen int) int {
|
|
return (attrlen + syscall.NLA_ALIGNTO - 1) & ^(syscall.NLA_ALIGNTO - 1)
|
|
}
|
|
|
|
// NewAttrFromFields creates and returns a new NLAttr instance by serializing the provided
|
|
// fields into a slice of bytes which is stored as the Data element of the attribute.
|
|
func NewAttrFromFields(atype uint16, fields ...interface{}) (*NLAttr, error) {
|
|
b := new(bytes.Buffer)
|
|
for _, f := range fields {
|
|
if err := binary.Write(b, binary.BigEndian, f); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return NewAttr(atype, b.Bytes()), nil
|
|
}
|
|
|
|
// NewAttr creates and returns a new NLAttr instance from the provided type and data payload
|
|
func NewAttr(atype uint16, data []byte) *NLAttr {
|
|
return &NLAttr{
|
|
Type: atype,
|
|
Data: data,
|
|
Nested: false,
|
|
}
|
|
}
|
|
|
|
func NewAttrNested(atype uint16, alen uint16) *NLAttr {
|
|
return &NLAttr{
|
|
Type: atype,
|
|
Len: alen,
|
|
Data: nil,
|
|
Nested: true,
|
|
}
|
|
}
|
|
|
|
func (a *NLAttr) String() string {
|
|
if a.Type & syscall.NLA_F_NESTED != 0 {
|
|
t := a.Type & ^uint16(syscall.NLA_F_NESTED)
|
|
return fmt.Sprintf("(%d %v)", t, a.nested)
|
|
}
|
|
return fmt.Sprintf("(%d %s)", a.Type, hex.EncodeToString(a.Data))
|
|
}
|
|
|
|
// ParseAttr reads a serialized attribute from r and parses it into an NLAttr instance.
|
|
func ParseAttr(r *bytes.Reader) (*NLAttr, error) {
|
|
attr := &NLAttr{}
|
|
if err := attr.parse(r); err != nil {
|
|
return nil, err
|
|
}
|
|
if attr.Type & syscall.NLA_F_NESTED != 0 {
|
|
if err := attr.parseNested(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return attr, nil
|
|
}
|
|
|
|
// parse reads a serialized attribute from r and parses it into this NLAttr instance.
|
|
func (a *NLAttr) parse(r *bytes.Reader) error {
|
|
if r.Len() < syscall.NLA_HDRLEN {
|
|
return errors.New("Truncated attribute")
|
|
}
|
|
var alen uint16
|
|
binary.Read(r, native, &alen)
|
|
binary.Read(r, native, &a.Type)
|
|
|
|
if alen < syscall.NLA_HDRLEN || int(alen - syscall.NLA_HDRLEN) > r.Len() {
|
|
return errors.New("Truncated attribute")
|
|
}
|
|
alen -= syscall.NLA_HDRLEN
|
|
if alen == 0 {
|
|
a.Data = nil
|
|
return nil
|
|
}
|
|
|
|
a.Data = make([]byte, alen)
|
|
r.Read(a.Data)
|
|
padlen := nlaAlignOf(int(alen)) - int(alen)
|
|
for i := 0; i < padlen; i++ {
|
|
r.ReadByte()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Size returns the size in bytes of this attribute when serialized
|
|
func (a *NLAttr) Size() int {
|
|
if a.Nested {
|
|
return syscall.NLA_HDRLEN + nlaAlignOf(int(a.Len))
|
|
}
|
|
return syscall.NLA_HDRLEN + nlaAlignOf(len(a.Data))
|
|
}
|
|
|
|
// serialize the attribute and return the raw bytes
|
|
func (a *NLAttr) serialize() []byte {
|
|
bs := new(bytes.Buffer)
|
|
a.WriteTo(bs)
|
|
return bs.Bytes()
|
|
}
|
|
|
|
// WriteTo serializes the attribute instance into the provided bytes.Buffer
|
|
func (a *NLAttr) WriteTo(b *bytes.Buffer) {
|
|
|
|
if a.Nested {
|
|
binary.Write(b, native, uint16(a.Len))
|
|
binary.Write(b, native, uint16(a.Type))
|
|
return
|
|
}
|
|
|
|
alen := syscall.NLA_HDRLEN + len(a.Data)
|
|
binary.Write(b, native, uint16(alen))
|
|
binary.Write(b, native, a.Type)
|
|
b.Write(a.Data)
|
|
a.writePadding(b)
|
|
}
|
|
|
|
// ReadFields parses the attribute data into the provided array of
|
|
// fields using binary.Read() to parse each individual field.
|
|
func (a *NLAttr) ReadFields(fields ...interface{}) error {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
r := bytes.NewReader(a.Data)
|
|
for _, f := range fields {
|
|
if err := binary.Read(r, binary.BigEndian, f); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *NLAttr) Get(atypes ...uint16) *NLAttr {
|
|
if len(atypes) == 0 {
|
|
return a
|
|
}
|
|
if a == nil || a.nested == nil {
|
|
return nil
|
|
}
|
|
head := atypes[0]
|
|
tail := atypes[1:]
|
|
return a.nested.attrMap[head].Get(tail...)
|
|
}
|
|
|
|
func (a *NLAttr) AsIPv4(ip *net.IP) bool {
|
|
if a == nil {
|
|
return false
|
|
}
|
|
var n uint32
|
|
if err := a.ReadFields(&n); err != nil {
|
|
return false
|
|
}
|
|
*ip = net.IPv4(byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
|
return true
|
|
}
|
|
|
|
func (a *NLAttr) parseNested() error {
|
|
as := NewNLAttrSet()
|
|
r := bytes.NewReader(a.Data)
|
|
for r.Len() >= syscall.NLA_HDRLEN {
|
|
attr, err := ParseAttr(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
as.Add(attr)
|
|
}
|
|
a.nested = as
|
|
return nil
|
|
}
|
|
|
|
// writePadding is called while serializing the attribute instance to write
|
|
// an appropriate number of '0' bytes to the buffer b so that the length of
|
|
// data in the buffer is 4 byte aligned
|
|
func (a *NLAttr) writePadding(b *bytes.Buffer) {
|
|
padlen := a.Size() - (syscall.NLA_HDRLEN + len(a.Data))
|
|
/* if a.Nested {
|
|
padlen = a.Size() - (syscall.NLA_HDRLEN + int(a.Len))
|
|
} */
|
|
|
|
for i := 0; i < padlen; i++ {
|
|
b.WriteByte(0)
|
|
}
|
|
}
|