Add XfrmMonitor

The implementation subscribes only to XFRMNLGRP_EXPIRE.
This commit is contained in:
Martynas Pumputis 2017-01-16 15:19:47 +00:00 committed by Vish Ishaya
parent ebdfb74020
commit 9a7970b3b6
6 changed files with 262 additions and 35 deletions

View File

@ -11,34 +11,40 @@ const (
XFRM_INF = ^uint64(0)
)
type XfrmMsgType uint8
type XfrmMsg interface {
Type() XfrmMsgType
}
// Message Types
const (
XFRM_MSG_BASE = 0x10
XFRM_MSG_NEWSA = 0x10
XFRM_MSG_DELSA = 0x11
XFRM_MSG_GETSA = 0x12
XFRM_MSG_NEWPOLICY = 0x13
XFRM_MSG_DELPOLICY = 0x14
XFRM_MSG_GETPOLICY = 0x15
XFRM_MSG_ALLOCSPI = 0x16
XFRM_MSG_ACQUIRE = 0x17
XFRM_MSG_EXPIRE = 0x18
XFRM_MSG_UPDPOLICY = 0x19
XFRM_MSG_UPDSA = 0x1a
XFRM_MSG_POLEXPIRE = 0x1b
XFRM_MSG_FLUSHSA = 0x1c
XFRM_MSG_FLUSHPOLICY = 0x1d
XFRM_MSG_NEWAE = 0x1e
XFRM_MSG_GETAE = 0x1f
XFRM_MSG_REPORT = 0x20
XFRM_MSG_MIGRATE = 0x21
XFRM_MSG_NEWSADINFO = 0x22
XFRM_MSG_GETSADINFO = 0x23
XFRM_MSG_NEWSPDINFO = 0x24
XFRM_MSG_GETSPDINFO = 0x25
XFRM_MSG_MAPPING = 0x26
XFRM_MSG_MAX = 0x26
XFRM_NR_MSGTYPES = 0x17
XFRM_MSG_BASE XfrmMsgType = 0x10
XFRM_MSG_NEWSA = 0x10
XFRM_MSG_DELSA = 0x11
XFRM_MSG_GETSA = 0x12
XFRM_MSG_NEWPOLICY = 0x13
XFRM_MSG_DELPOLICY = 0x14
XFRM_MSG_GETPOLICY = 0x15
XFRM_MSG_ALLOCSPI = 0x16
XFRM_MSG_ACQUIRE = 0x17
XFRM_MSG_EXPIRE = 0x18
XFRM_MSG_UPDPOLICY = 0x19
XFRM_MSG_UPDSA = 0x1a
XFRM_MSG_POLEXPIRE = 0x1b
XFRM_MSG_FLUSHSA = 0x1c
XFRM_MSG_FLUSHPOLICY = 0x1d
XFRM_MSG_NEWAE = 0x1e
XFRM_MSG_GETAE = 0x1f
XFRM_MSG_REPORT = 0x20
XFRM_MSG_MIGRATE = 0x21
XFRM_MSG_NEWSADINFO = 0x22
XFRM_MSG_GETSADINFO = 0x23
XFRM_MSG_NEWSPDINFO = 0x24
XFRM_MSG_GETSPDINFO = 0x25
XFRM_MSG_MAPPING = 0x26
XFRM_MSG_MAX = 0x26
XFRM_NR_MSGTYPES = 0x17
)
// Attribute types
@ -81,6 +87,20 @@ const (
SizeofXfrmMark = 0x08
)
// Netlink groups
const (
XFRMNLGRP_NONE = 0x0
XFRMNLGRP_ACQUIRE = 0x1
XFRMNLGRP_EXPIRE = 0x2
XFRMNLGRP_SA = 0x3
XFRMNLGRP_POLICY = 0x4
XFRMNLGRP_AEVENTS = 0x5
XFRMNLGRP_REPORT = 0x6
XFRMNLGRP_MIGRATE = 0x7
XFRMNLGRP_MAPPING = 0x8
__XFRMNLGRP_MAX = 0x9
)
// typedef union {
// __be32 a4;
// __be32 a6[4];

32
nl/xfrm_monitor_linux.go Normal file
View File

@ -0,0 +1,32 @@
package nl
import (
"unsafe"
)
const (
SizeofXfrmUserExpire = 0xe8
)
// struct xfrm_user_expire {
// struct xfrm_usersa_info state;
// __u8 hard;
// };
type XfrmUserExpire struct {
XfrmUsersaInfo XfrmUsersaInfo
Hard uint8
Pad [7]byte
}
func (msg *XfrmUserExpire) Len() int {
return SizeofXfrmUserExpire
}
func DeserializeXfrmUserExpire(b []byte) *XfrmUserExpire {
return (*XfrmUserExpire)(unsafe.Pointer(&b[0:SizeofXfrmUserExpire][0]))
}
func (msg *XfrmUserExpire) Serialize() []byte {
return (*(*[SizeofXfrmUserExpire]byte)(unsafe.Pointer(msg)))[:]
}

View File

@ -0,0 +1,34 @@
package nl
import (
"bytes"
"crypto/rand"
"encoding/binary"
"testing"
)
func (msg *XfrmUserExpire) write(b []byte) {
msg.XfrmUsersaInfo.write(b[0:SizeofXfrmUsersaInfo])
b[SizeofXfrmUsersaInfo] = msg.Hard
copy(b[SizeofXfrmUsersaInfo+1:SizeofXfrmUserExpire], msg.Pad[:])
}
func (msg *XfrmUserExpire) serializeSafe() []byte {
b := make([]byte, SizeofXfrmUserExpire)
msg.write(b)
return b
}
func deserializeXfrmUserExpireSafe(b []byte) *XfrmUserExpire {
var msg = XfrmUserExpire{}
binary.Read(bytes.NewReader(b[0:SizeofXfrmUserExpire]), NativeEndian(), &msg)
return &msg
}
func TestXfrmUserExpireDeserializeSerialize(t *testing.T) {
var orig = make([]byte, SizeofXfrmUserExpire)
rand.Read(orig)
safemsg := deserializeXfrmUserExpireSafe(orig)
msg := DeserializeXfrmUserExpire(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}

98
xfrm_monitor_linux.go Normal file
View File

@ -0,0 +1,98 @@
package netlink
import (
"fmt"
"syscall"
"github.com/vishvananda/netns"
"github.com/vishvananda/netlink/nl"
)
type XfrmMsg interface {
Type() nl.XfrmMsgType
}
type XfrmMsgExpire struct {
XfrmState *XfrmState
Hard bool
}
func (ue *XfrmMsgExpire) Type() nl.XfrmMsgType {
return nl.XFRM_MSG_EXPIRE
}
func parseXfrmMsgExpire(b []byte) *XfrmMsgExpire {
var e XfrmMsgExpire
msg := nl.DeserializeXfrmUserExpire(b)
e.XfrmState = xfrmStateFromXfrmUsersaInfo(&msg.XfrmUsersaInfo)
e.Hard = msg.Hard == 1
return &e
}
func XfrmMonitor(ch chan<- XfrmMsg, done <-chan struct{}, errorChan chan<- error,
types ...nl.XfrmMsgType) error {
groups, err := xfrmMcastGroups(types)
if err != nil {
return nil
}
s, err := nl.SubscribeAt(netns.None(), netns.None(), syscall.NETLINK_XFRM, groups...)
if err != nil {
return err
}
if done != nil {
go func() {
<-done
s.Close()
}()
}
go func() {
defer close(ch)
for {
msgs, err := s.Receive()
if err != nil {
errorChan <- err
return
}
for _, m := range msgs {
switch m.Header.Type {
case nl.XFRM_MSG_EXPIRE:
ch <- parseXfrmMsgExpire(m.Data)
default:
errorChan <- fmt.Errorf("unsupported msg type: %x", m.Header.Type)
}
}
}
}()
return nil
}
func xfrmMcastGroups(types []nl.XfrmMsgType) ([]uint, error) {
groups := make([]uint, 0)
if len(types) == 0 {
return nil, fmt.Errorf("no xfrm msg type specified")
}
for _, t := range types {
var group uint
switch t {
case nl.XFRM_MSG_EXPIRE:
group = nl.XFRMNLGRP_EXPIRE
default:
return nil, fmt.Errorf("unsupported group: %x", t)
}
groups = append(groups, group)
}
return groups, nil
}

37
xfrm_monitor_test.go Normal file
View File

@ -0,0 +1,37 @@
package netlink
import (
"testing"
"github.com/vishvananda/netlink/nl"
)
func TestXfrmMonitorExpire(t *testing.T) {
setUpNetlinkTest(t)()
ch := make(chan XfrmMsg)
done := make(chan struct{})
defer close(done)
errChan := make(chan error)
if err := XfrmMonitor(ch, nil, errChan, nl.XFRM_MSG_EXPIRE); err != nil {
t.Fatal(err)
}
// Program state with limits
state := getBaseState()
state.Limits.TimeHard = 2
state.Limits.TimeSoft = 1
if err := XfrmStateAdd(state); err != nil {
t.Fatal(err)
}
msg := (<-ch).(*XfrmMsgExpire)
if msg.XfrmState.Spi != state.Spi || msg.Hard {
t.Fatal("Received unexpected msg")
}
msg = (<-ch).(*XfrmMsgExpire)
if msg.XfrmState.Spi != state.Spi || !msg.Hard {
t.Fatal("Received unexpected msg")
}
}

View File

@ -241,14 +241,7 @@ func (h *Handle) xfrmStateGetOrDelete(state *XfrmState, nlProto int) (*XfrmState
var familyError = fmt.Errorf("family error")
func parseXfrmState(m []byte, family int) (*XfrmState, error) {
msg := nl.DeserializeXfrmUsersaInfo(m)
// This is mainly for the state dump
if family != FAMILY_ALL && family != int(msg.Family) {
return nil, familyError
}
func xfrmStateFromXfrmUsersaInfo(msg *nl.XfrmUsersaInfo) *XfrmState {
var state XfrmState
state.Dst = msg.Id.Daddr.ToIP()
@ -260,6 +253,19 @@ func parseXfrmState(m []byte, family int) (*XfrmState, error) {
state.ReplayWindow = int(msg.ReplayWindow)
lftToLimits(&msg.Lft, &state.Limits)
return &state
}
func parseXfrmState(m []byte, family int) (*XfrmState, error) {
msg := nl.DeserializeXfrmUsersaInfo(m)
// This is mainly for the state dump
if family != FAMILY_ALL && family != int(msg.Family) {
return nil, familyError
}
state := xfrmStateFromXfrmUsersaInfo(msg)
attrs, err := nl.ParseRouteAttr(m[nl.SizeofXfrmUsersaInfo:])
if err != nil {
return nil, err
@ -310,7 +316,7 @@ func parseXfrmState(m []byte, family int) (*XfrmState, error) {
}
}
return &state, nil
return state, nil
}
// XfrmStateFlush will flush the xfrm state on the system.