mirror of
https://github.com/vishvananda/netlink
synced 2024-12-29 02:02:21 +00:00
Add Support for Virtual XFRM Interfaces
XFRM interfaces are available in Linux Kernel 4.19+ When an IF_ID is applied to a XFRM policy and state, the corresponding traffic will be sent through the virtual interface with the same IF_ID.
This commit is contained in:
parent
48a75e0e38
commit
1e2e7ab670
17
link.go
17
link.go
@ -848,11 +848,26 @@ func (gtp *GTP) Type() string {
|
||||
return "gtp"
|
||||
}
|
||||
|
||||
// Virtual XFRM Interfaces
|
||||
// Named "xfrmi" to prevent confusion with XFRM objects
|
||||
type Xfrmi struct {
|
||||
LinkAttrs
|
||||
Ifid uint32
|
||||
}
|
||||
|
||||
func (xfrm *Xfrmi) Attrs() *LinkAttrs {
|
||||
return &xfrm.LinkAttrs
|
||||
}
|
||||
|
||||
func (xfrm *Xfrmi) Type() string {
|
||||
return "xfrm"
|
||||
}
|
||||
|
||||
// iproute2 supported devices;
|
||||
// vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
|
||||
// bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |
|
||||
// gre | gretap | ip6gre | ip6gretap | vti | vti6 | nlmon |
|
||||
// bond_slave | ipvlan
|
||||
// bond_slave | ipvlan | xfrm
|
||||
|
||||
// LinkNotFoundError wraps the various not found errors when
|
||||
// getting/reading links. This is intended for better error
|
||||
|
@ -1190,6 +1190,8 @@ func (h *Handle) linkModify(link Link, flags int) error {
|
||||
addBridgeAttrs(link, linkInfo)
|
||||
case *GTP:
|
||||
addGTPAttrs(link, linkInfo)
|
||||
case *Xfrmi:
|
||||
addXfrmiAttrs(link, linkInfo)
|
||||
}
|
||||
|
||||
req.AddData(linkInfo)
|
||||
@ -1441,6 +1443,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
|
||||
link = &Vrf{}
|
||||
case "gtp":
|
||||
link = >P{}
|
||||
case "xfrm":
|
||||
link = &Xfrmi{}
|
||||
default:
|
||||
link = &GenericLink{LinkType: linkType}
|
||||
}
|
||||
@ -1482,6 +1486,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
|
||||
parseBridgeData(link, data)
|
||||
case "gtp":
|
||||
parseGTPData(link, data)
|
||||
case "xfrm":
|
||||
parseXfrmiData(link, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2488,6 +2494,25 @@ func parseVfInfo(data []syscall.NetlinkRouteAttr, id int) VfInfo {
|
||||
return vf
|
||||
}
|
||||
|
||||
func addXfrmiAttrs(xfrmi *Xfrmi, linkInfo *nl.RtAttr) {
|
||||
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
|
||||
data.AddRtAttr(nl.IFLA_XFRM_LINK, nl.Uint32Attr(uint32(xfrmi.ParentIndex)))
|
||||
data.AddRtAttr(nl.IFLA_XFRM_IF_ID, nl.Uint32Attr(xfrmi.Ifid))
|
||||
|
||||
}
|
||||
|
||||
func parseXfrmiData(link Link, data []syscall.NetlinkRouteAttr) {
|
||||
xfrmi := link.(*Xfrmi)
|
||||
for _, datum := range data {
|
||||
switch datum.Attr.Type {
|
||||
case nl.IFLA_XFRM_LINK:
|
||||
xfrmi.ParentIndex = int(native.Uint32(datum.Value))
|
||||
case nl.IFLA_XFRM_IF_ID:
|
||||
xfrmi.Ifid = native.Uint32(datum.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LinkSetBondSlave add slave to bond link via ioctl interface.
|
||||
func LinkSetBondSlave(link Link, master *Bond) error {
|
||||
fd, err := getSocketUDP()
|
||||
|
35
link_test.go
35
link_test.go
@ -201,6 +201,14 @@ func testLinkAddDel(t *testing.T, link Link) {
|
||||
compareGretun(t, gretun, other)
|
||||
}
|
||||
|
||||
if xfrmi, ok := link.(*Xfrmi); ok {
|
||||
other, ok := result.(*Xfrmi)
|
||||
if !ok {
|
||||
t.Fatal("Result of create is not a xfrmi")
|
||||
}
|
||||
compareXfrmi(t, xfrmi, other)
|
||||
}
|
||||
|
||||
if err = LinkDel(link); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -409,6 +417,12 @@ func compareVxlan(t *testing.T, expected, actual *Vxlan) {
|
||||
}
|
||||
}
|
||||
|
||||
func compareXfrmi(t *testing.T, expected, actual *Xfrmi) {
|
||||
if expected.Ifid != actual.Ifid {
|
||||
t.Fatal("Xfrmi.Ifid doesn't match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkAddDelWithIndex(t *testing.T) {
|
||||
tearDown := setUpNetlinkTest(t)
|
||||
defer tearDown()
|
||||
@ -1746,6 +1760,27 @@ func TestLinkAddDelGTP(t *testing.T) {
|
||||
testLinkAddDel(t, gtp)
|
||||
}
|
||||
|
||||
func TestLinkAddDelXfrmi(t *testing.T) {
|
||||
minKernelRequired(t, 4, 19)
|
||||
defer setUpNetlinkTest(t)()
|
||||
|
||||
lo, _ := LinkByName("lo")
|
||||
|
||||
testLinkAddDel(t, &Xfrmi{
|
||||
LinkAttrs: LinkAttrs{Name: "xfrm123", ParentIndex: lo.Attrs().Index},
|
||||
Ifid: 123})
|
||||
}
|
||||
|
||||
func TestLinkAddDelXfrmiNoId(t *testing.T) {
|
||||
minKernelRequired(t, 4, 19)
|
||||
defer setUpNetlinkTest(t)()
|
||||
|
||||
lo, _ := LinkByName("lo")
|
||||
|
||||
testLinkAddDel(t, &Xfrmi{
|
||||
LinkAttrs: LinkAttrs{Name: "xfrm0", ParentIndex: lo.Attrs().Index}})
|
||||
}
|
||||
|
||||
func TestLinkByNameWhenLinkIsNotFound(t *testing.T) {
|
||||
_, err := LinkByName("iammissing")
|
||||
if err == nil {
|
||||
|
@ -573,3 +573,11 @@ const (
|
||||
GTP_ROLE_GGSN = iota
|
||||
GTP_ROLE_SGSN
|
||||
)
|
||||
|
||||
const (
|
||||
IFLA_XFRM_UNSPEC = iota
|
||||
IFLA_XFRM_LINK
|
||||
IFLA_XFRM_IF_ID
|
||||
|
||||
IFLA_XFRM_MAX = iota - 1
|
||||
)
|
||||
|
@ -50,32 +50,40 @@ const (
|
||||
// Attribute types
|
||||
const (
|
||||
/* Netlink message attributes. */
|
||||
XFRMA_UNSPEC = 0x00
|
||||
XFRMA_ALG_AUTH = 0x01 /* struct xfrm_algo */
|
||||
XFRMA_ALG_CRYPT = 0x02 /* struct xfrm_algo */
|
||||
XFRMA_ALG_COMP = 0x03 /* struct xfrm_algo */
|
||||
XFRMA_ENCAP = 0x04 /* struct xfrm_algo + struct xfrm_encap_tmpl */
|
||||
XFRMA_TMPL = 0x05 /* 1 or more struct xfrm_user_tmpl */
|
||||
XFRMA_SA = 0x06 /* struct xfrm_usersa_info */
|
||||
XFRMA_POLICY = 0x07 /* struct xfrm_userpolicy_info */
|
||||
XFRMA_SEC_CTX = 0x08 /* struct xfrm_sec_ctx */
|
||||
XFRMA_LTIME_VAL = 0x09
|
||||
XFRMA_REPLAY_VAL = 0x0a
|
||||
XFRMA_REPLAY_THRESH = 0x0b
|
||||
XFRMA_ETIMER_THRESH = 0x0c
|
||||
XFRMA_SRCADDR = 0x0d /* xfrm_address_t */
|
||||
XFRMA_COADDR = 0x0e /* xfrm_address_t */
|
||||
XFRMA_LASTUSED = 0x0f /* unsigned long */
|
||||
XFRMA_POLICY_TYPE = 0x10 /* struct xfrm_userpolicy_type */
|
||||
XFRMA_MIGRATE = 0x11
|
||||
XFRMA_ALG_AEAD = 0x12 /* struct xfrm_algo_aead */
|
||||
XFRMA_KMADDRESS = 0x13 /* struct xfrm_user_kmaddress */
|
||||
XFRMA_ALG_AUTH_TRUNC = 0x14 /* struct xfrm_algo_auth */
|
||||
XFRMA_MARK = 0x15 /* struct xfrm_mark */
|
||||
XFRMA_TFCPAD = 0x16 /* __u32 */
|
||||
XFRMA_REPLAY_ESN_VAL = 0x17 /* struct xfrm_replay_esn */
|
||||
XFRMA_SA_EXTRA_FLAGS = 0x18 /* __u32 */
|
||||
XFRMA_MAX = 0x18
|
||||
XFRMA_UNSPEC = iota
|
||||
XFRMA_ALG_AUTH /* struct xfrm_algo */
|
||||
XFRMA_ALG_CRYPT /* struct xfrm_algo */
|
||||
XFRMA_ALG_COMP /* struct xfrm_algo */
|
||||
XFRMA_ENCAP /* struct xfrm_algo + struct xfrm_encap_tmpl */
|
||||
XFRMA_TMPL /* 1 or more struct xfrm_user_tmpl */
|
||||
XFRMA_SA /* struct xfrm_usersa_info */
|
||||
XFRMA_POLICY /* struct xfrm_userpolicy_info */
|
||||
XFRMA_SEC_CTX /* struct xfrm_sec_ctx */
|
||||
XFRMA_LTIME_VAL
|
||||
XFRMA_REPLAY_VAL
|
||||
XFRMA_REPLAY_THRESH
|
||||
XFRMA_ETIMER_THRESH
|
||||
XFRMA_SRCADDR /* xfrm_address_t */
|
||||
XFRMA_COADDR /* xfrm_address_t */
|
||||
XFRMA_LASTUSED /* unsigned long */
|
||||
XFRMA_POLICY_TYPE /* struct xfrm_userpolicy_type */
|
||||
XFRMA_MIGRATE
|
||||
XFRMA_ALG_AEAD /* struct xfrm_algo_aead */
|
||||
XFRMA_KMADDRESS /* struct xfrm_user_kmaddress */
|
||||
XFRMA_ALG_AUTH_TRUNC /* struct xfrm_algo_auth */
|
||||
XFRMA_MARK /* struct xfrm_mark */
|
||||
XFRMA_TFCPAD /* __u32 */
|
||||
XFRMA_REPLAY_ESN_VAL /* struct xfrm_replay_esn */
|
||||
XFRMA_SA_EXTRA_FLAGS /* __u32 */
|
||||
XFRMA_PROTO /* __u8 */
|
||||
XFRMA_ADDRESS_FILTER /* struct xfrm_address_filter */
|
||||
XFRMA_PAD
|
||||
XFRMA_OFFLOAD_DEV /* struct xfrm_state_offload */
|
||||
XFRMA_SET_MARK /* __u32 */
|
||||
XFRMA_SET_MARK_MASK /* __u32 */
|
||||
XFRMA_IF_ID /* __u32 */
|
||||
|
||||
XFRMA_MAX = iota - 1
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -85,11 +85,12 @@ type XfrmPolicy struct {
|
||||
Index int
|
||||
Action PolicyAction
|
||||
Ifindex int
|
||||
Ifid int
|
||||
Mark *XfrmMark
|
||||
Tmpls []XfrmPolicyTmpl
|
||||
}
|
||||
|
||||
func (p XfrmPolicy) String() string {
|
||||
return fmt.Sprintf("{Dst: %v, Src: %v, Proto: %s, DstPort: %d, SrcPort: %d, Dir: %s, Priority: %d, Index: %d, Action: %s, Ifindex: %d, Mark: %s, Tmpls: %s}",
|
||||
p.Dst, p.Src, p.Proto, p.DstPort, p.SrcPort, p.Dir, p.Priority, p.Index, p.Action, p.Ifindex, p.Mark, p.Tmpls)
|
||||
return fmt.Sprintf("{Dst: %v, Src: %v, Proto: %s, DstPort: %d, SrcPort: %d, Dir: %s, Priority: %d, Index: %d, Action: %s, Ifindex: %d, Ifid: %d, Mark: %s, Tmpls: %s}",
|
||||
p.Dst, p.Src, p.Proto, p.DstPort, p.SrcPort, p.Dir, p.Priority, p.Index, p.Action, p.Ifindex, p.Ifid, p.Mark, p.Tmpls)
|
||||
}
|
||||
|
@ -92,6 +92,9 @@ func (h *Handle) xfrmPolicyAddOrUpdate(policy *XfrmPolicy, nlProto int) error {
|
||||
req.AddData(out)
|
||||
}
|
||||
|
||||
ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(policy.Ifid)))
|
||||
req.AddData(ifId)
|
||||
|
||||
_, err := req.Execute(unix.NETLINK_XFRM, 0)
|
||||
return err
|
||||
}
|
||||
@ -185,6 +188,9 @@ func (h *Handle) xfrmPolicyGetOrDelete(policy *XfrmPolicy, nlProto int) (*XfrmPo
|
||||
req.AddData(out)
|
||||
}
|
||||
|
||||
ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(policy.Ifid)))
|
||||
req.AddData(ifId)
|
||||
|
||||
resType := nl.XFRM_MSG_NEWPOLICY
|
||||
if nlProto == nl.XFRM_MSG_DELPOLICY {
|
||||
resType = 0
|
||||
@ -248,6 +254,8 @@ func parseXfrmPolicy(m []byte, family int) (*XfrmPolicy, error) {
|
||||
policy.Mark = new(XfrmMark)
|
||||
policy.Mark.Value = mark.Value
|
||||
policy.Mark.Mask = mark.Mask
|
||||
case nl.XFRMA_IF_ID:
|
||||
policy.Ifid = int(native.Uint32(attr.Value))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,10 @@ func TestXfrmPolicyAddUpdateDel(t *testing.T) {
|
||||
t.Fatalf("default policy has a non-zero interface index.\nGot %d", policies[0].Ifindex)
|
||||
}
|
||||
|
||||
if policies[0].Ifid != 0 {
|
||||
t.Fatalf("default policy has non-zero if_id.\nGot %d", policies[0].Ifid)
|
||||
}
|
||||
|
||||
if policies[0].Action != XFRM_POLICY_ALLOW {
|
||||
t.Fatalf("default policy has non-allow action.\nGot %s", policies[0].Action)
|
||||
}
|
||||
@ -161,6 +165,31 @@ func TestXfrmPolicyBlockWithIfindex(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestXfrmPolicyWithIfid(t *testing.T) {
|
||||
minKernelRequired(t, 4, 19)
|
||||
defer setUpNetlinkTest(t)()
|
||||
|
||||
pol := getPolicy()
|
||||
pol.Ifid = 54321
|
||||
|
||||
if err := XfrmPolicyAdd(pol); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
policies, err := XfrmPolicyList(FAMILY_ALL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(policies) != 1 {
|
||||
t.Fatalf("unexpected number of policies: %d", len(policies))
|
||||
}
|
||||
if !comparePolicies(pol, &policies[0]) {
|
||||
t.Fatalf("unexpected policy returned.\nExpected: %v.\nGot %v", pol, policies[0])
|
||||
}
|
||||
if err = XfrmPolicyDel(&policies[0]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func comparePolicies(a, b *XfrmPolicy) bool {
|
||||
if a == b {
|
||||
return true
|
||||
@ -173,7 +202,7 @@ func comparePolicies(a, b *XfrmPolicy) bool {
|
||||
compareIPNet(a.Src, b.Src) && compareIPNet(a.Dst, b.Dst) &&
|
||||
a.Action == b.Action && a.Ifindex == b.Ifindex &&
|
||||
a.Mark.Value == b.Mark.Value && a.Mark.Mask == b.Mark.Mask &&
|
||||
compareTemplates(a.Tmpls, b.Tmpls)
|
||||
a.Ifid == b.Ifid && compareTemplates(a.Tmpls, b.Tmpls)
|
||||
}
|
||||
|
||||
func compareTemplates(a, b []XfrmPolicyTmpl) bool {
|
||||
|
@ -94,6 +94,7 @@ type XfrmState struct {
|
||||
Limits XfrmStateLimits
|
||||
Statistics XfrmStateStats
|
||||
Mark *XfrmMark
|
||||
Ifid int
|
||||
Auth *XfrmStateAlgo
|
||||
Crypt *XfrmStateAlgo
|
||||
Aead *XfrmStateAlgo
|
||||
@ -102,8 +103,8 @@ type XfrmState struct {
|
||||
}
|
||||
|
||||
func (sa XfrmState) String() string {
|
||||
return fmt.Sprintf("Dst: %v, Src: %v, Proto: %s, Mode: %s, SPI: 0x%x, ReqID: 0x%x, ReplayWindow: %d, Mark: %v, Auth: %v, Crypt: %v, Aead: %v, Encap: %v, ESN: %t",
|
||||
sa.Dst, sa.Src, sa.Proto, sa.Mode, sa.Spi, sa.Reqid, sa.ReplayWindow, sa.Mark, sa.Auth, sa.Crypt, sa.Aead, sa.Encap, sa.ESN)
|
||||
return fmt.Sprintf("Dst: %v, Src: %v, Proto: %s, Mode: %s, SPI: 0x%x, ReqID: 0x%x, ReplayWindow: %d, Mark: %v, Ifid: %d, Auth: %v, Crypt: %v, Aead: %v, Encap: %v, ESN: %t",
|
||||
sa.Dst, sa.Src, sa.Proto, sa.Mode, sa.Spi, sa.Reqid, sa.ReplayWindow, sa.Mark, sa.Ifid, sa.Auth, sa.Crypt, sa.Aead, sa.Encap, sa.ESN)
|
||||
}
|
||||
func (sa XfrmState) Print(stats bool) string {
|
||||
if !stats {
|
||||
|
@ -159,6 +159,9 @@ func (h *Handle) xfrmStateAddOrUpdate(state *XfrmState, nlProto int) error {
|
||||
req.AddData(out)
|
||||
}
|
||||
|
||||
ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid)))
|
||||
req.AddData(ifId)
|
||||
|
||||
_, err := req.Execute(unix.NETLINK_XFRM, 0)
|
||||
return err
|
||||
}
|
||||
@ -270,6 +273,9 @@ func (h *Handle) xfrmStateGetOrDelete(state *XfrmState, nlProto int) (*XfrmState
|
||||
req.AddData(out)
|
||||
}
|
||||
|
||||
ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid)))
|
||||
req.AddData(ifId)
|
||||
|
||||
resType := nl.XFRM_MSG_NEWSA
|
||||
if nlProto == nl.XFRM_MSG_DELSA {
|
||||
resType = 0
|
||||
@ -367,6 +373,8 @@ func parseXfrmState(m []byte, family int) (*XfrmState, error) {
|
||||
state.Mark = new(XfrmMark)
|
||||
state.Mark.Value = mark.Value
|
||||
state.Mark.Mask = mark.Mask
|
||||
case nl.XFRMA_IF_ID:
|
||||
state.Ifid = int(native.Uint32(attr.Value))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,6 +201,27 @@ func TestXfrmStateStats(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestXfrmStateWithIfid(t *testing.T) {
|
||||
minKernelRequired(t, 4, 19)
|
||||
defer setUpNetlinkTest(t)()
|
||||
|
||||
state := getBaseState()
|
||||
state.Ifid = 54321
|
||||
if err := XfrmStateAdd(state); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, err := XfrmStateGet(state)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !compareStates(state, s) {
|
||||
t.Fatalf("unexpected state returned.\nExpected: %v.\nGot %v", state, s)
|
||||
}
|
||||
if err = XfrmStateDel(s); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func getBaseState() *XfrmState {
|
||||
return &XfrmState{
|
||||
// Force 4 byte notation for the IPv4 addresses
|
||||
@ -251,6 +272,7 @@ func compareStates(a, b *XfrmState) bool {
|
||||
}
|
||||
return a.Src.Equal(b.Src) && a.Dst.Equal(b.Dst) &&
|
||||
a.Mode == b.Mode && a.Spi == b.Spi && a.Proto == b.Proto &&
|
||||
a.Ifid == b.Ifid &&
|
||||
compareAlgo(a.Auth, b.Auth) &&
|
||||
compareAlgo(a.Crypt, b.Crypt) &&
|
||||
compareAlgo(a.Aead, b.Aead) &&
|
||||
|
Loading…
Reference in New Issue
Block a user