diff --git a/filter.go b/filter.go index 84e1ca7a..a722e0a2 100644 --- a/filter.go +++ b/filter.go @@ -231,6 +231,35 @@ func NewCsumAction() *CsumAction { } } +type VlanAct int8 + +type VlanAction struct { + ActionAttrs + Action VlanAct + VlanID uint16 +} + +const ( + TCA_VLAN_ACT_POP VlanAct = 1 + TCA_VLAN_ACT_PUSH VlanAct = 2 +) + +func (action *VlanAction) Type() string { + return "vlan" +} + +func (action *VlanAction) Attrs() *ActionAttrs { + return &action.ActionAttrs +} + +func NewVlanAction() *VlanAction { + return &VlanAction{ + ActionAttrs: ActionAttrs{ + Action: TC_ACT_PIPE, + }, + } +} + type MirredAct uint8 func (a MirredAct) String() string { diff --git a/filter_linux.go b/filter_linux.go index 19306612..404e50d5 100644 --- a/filter_linux.go +++ b/filter_linux.go @@ -65,6 +65,9 @@ type Flower struct { EncSrcIPMask net.IPMask EncDestPort uint16 EncKeyId uint32 + SrcMac net.HardwareAddr + DestMac net.HardwareAddr + VlanId uint16 SkipHw bool SkipSw bool IPProto *nl.IPProto @@ -135,6 +138,15 @@ func (filter *Flower) encode(parent *nl.RtAttr) error { if filter.EncKeyId != 0 { parent.AddRtAttr(nl.TCA_FLOWER_KEY_ENC_KEY_ID, htonl(filter.EncKeyId)) } + if filter.SrcMac != nil { + parent.AddRtAttr(nl.TCA_FLOWER_KEY_ETH_SRC, filter.SrcMac) + } + if filter.DestMac != nil { + parent.AddRtAttr(nl.TCA_FLOWER_KEY_ETH_DST, filter.DestMac) + } + if filter.VlanId != 0 { + parent.AddRtAttr(nl.TCA_FLOWER_KEY_VLAN_ID, nl.Uint16Attr(filter.VlanId)) + } if filter.IPProto != nil { ipproto := *filter.IPProto parent.AddRtAttr(nl.TCA_FLOWER_KEY_IP_PROTO, ipproto.Serialize()) @@ -201,6 +213,13 @@ func (filter *Flower) decode(data []syscall.NetlinkRouteAttr) error { filter.EncDestPort = ntohs(datum.Value) case nl.TCA_FLOWER_KEY_ENC_KEY_ID: filter.EncKeyId = ntohl(datum.Value) + case nl.TCA_FLOWER_KEY_ETH_SRC: + filter.SrcMac = datum.Value + case nl.TCA_FLOWER_KEY_ETH_DST: + filter.DestMac = datum.Value + case nl.TCA_FLOWER_KEY_VLAN_ID: + filter.VlanId = native.Uint16(datum.Value[0:2]) + filter.EthType = unix.ETH_P_8021Q case nl.TCA_FLOWER_KEY_IP_PROTO: val := new(nl.IPProto) *val = nl.IPProto(datum.Value[0]) @@ -622,6 +641,22 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error { } toTcGen(action.Attrs(), &mirred.TcGen) aopts.AddRtAttr(nl.TCA_MIRRED_PARMS, mirred.Serialize()) + case *VlanAction: + table := attr.AddRtAttr(tabIndex, nil) + tabIndex++ + table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("vlan")) + aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) + vlan := nl.TcVlan{ + Action: int32(action.Action), + } + toTcGen(action.Attrs(), &vlan.TcGen) + aopts.AddRtAttr(nl.TCA_VLAN_PARMS, vlan.Serialize()) + if action.Action == TCA_VLAN_ACT_PUSH && action.VlanID == 0 { + return fmt.Errorf("vlan id is required for push action") + } + if action.VlanID != 0 { + aopts.AddRtAttr(nl.TCA_VLAN_PUSH_VLAN_ID, nl.Uint16Attr(action.VlanID)) + } case *TunnelKeyAction: table := attr.AddRtAttr(tabIndex, nil) tabIndex++ @@ -792,6 +827,8 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { action = &CsumAction{} case "gact": action = &GenericAction{} + case "vlan": + action = &VlanAction{} case "tunnel_key": action = &TunnelKeyAction{} case "skbedit": @@ -822,7 +859,17 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { tcTs := nl.DeserializeTcf(adatum.Value) actionTimestamp = toTimeStamp(tcTs) } - + case "vlan": + switch adatum.Attr.Type { + case nl.TCA_VLAN_PARMS: + vlan := *nl.DeserializeTcVlan(adatum.Value) + action.(*VlanAction).ActionAttrs = ActionAttrs{} + toAttrs(&vlan.TcGen, action.Attrs()) + action.(*VlanAction).Action = VlanAct(vlan.Action) + case nl.TCA_VLAN_PUSH_VLAN_ID: + vlanId := native.Uint16(adatum.Value[0:2]) + action.(*VlanAction).VlanID = vlanId + } case "tunnel_key": switch adatum.Attr.Type { case nl.TCA_TUNNEL_KEY_PARMS: diff --git a/filter_test.go b/filter_test.go index 3a49f1b8..3b60d1c4 100644 --- a/filter_test.go +++ b/filter_test.go @@ -1768,6 +1768,14 @@ func TestFilterFlowerAddDel(t *testing.T) { } testMask := net.CIDRMask(24, 32) + srcMac, err := net.ParseMAC("2C:54:91:88:C9:E3") + if err != nil { + t.Fatal(err) + } + destMac, err := net.ParseMAC("2C:54:91:88:C9:E5") + if err != nil { + t.Fatal(err) + } ipproto := new(nl.IPProto) *ipproto = nl.IPPROTO_TCP @@ -1790,10 +1798,19 @@ func TestFilterFlowerAddDel(t *testing.T) { EncSrcIPMask: testMask, EncDestPort: 8472, EncKeyId: 1234, + SrcMac: srcMac, + DestMac: destMac, IPProto: ipproto, DestPort: 1111, SrcPort: 1111, Actions: []Action{ + &VlanAction{ + ActionAttrs: ActionAttrs{ + Action: TC_ACT_PIPE, + }, + Action: TCA_VLAN_ACT_PUSH, + VlanID: 1234, + }, &MirredAction{ ActionAttrs: ActionAttrs{ Action: TC_ACT_STOLEN, @@ -1871,8 +1888,31 @@ func TestFilterFlowerAddDel(t *testing.T) { if filter.SrcPort != flower.SrcPort { t.Fatalf("Flower SrcPort doesn't match") } + if !(filter.SrcMac.String() == flower.SrcMac.String()) { + t.Fatalf("Flower SrcMac doesn't match") + } + if !(filter.DestMac.String() == flower.DestMac.String()) { + t.Fatalf("Flower DestMac doesn't match") + } - mia, ok := flower.Actions[0].(*MirredAction) + vla, ok := flower.Actions[0].(*VlanAction) + if !ok { + t.Fatal("Unable to find vlan action") + } + + if vla.Attrs().Action != TC_ACT_PIPE { + t.Fatal("Vlan action isn't TC_ACT_PIPE") + } + + if vla.Action != TCA_VLAN_ACT_PUSH { + t.Fatal("Second Vlan action isn't push") + } + + if vla.VlanID != 1234 { + t.Fatal("Second Vlan action vlanId isn't correct") + } + + mia, ok := flower.Actions[1].(*MirredAction) if !ok { t.Fatal("Unable to find mirred action") } @@ -1889,7 +1929,7 @@ func TestFilterFlowerAddDel(t *testing.T) { t.Fatal("Incorrect mirred action stats") } - ga, ok := flower.Actions[1].(*GenericAction) + ga, ok := flower.Actions[2].(*GenericAction) if !ok { t.Fatal("Unable to find generic action") } @@ -1917,6 +1957,94 @@ func TestFilterFlowerAddDel(t *testing.T) { t.Fatal("Failed to remove filter") } + filter = &Flower{ + FilterAttrs: FilterAttrs{ + LinkIndex: link.Attrs().Index, + Parent: MakeHandle(0xffff, 0), + Priority: 1, + Protocol: unix.ETH_P_8021Q, + }, + EthType: unix.ETH_P_8021Q, + VlanId: 2046, + Actions: []Action{ + &VlanAction{ + ActionAttrs: ActionAttrs{ + Action: TC_ACT_PIPE, + }, + Action: TCA_VLAN_ACT_POP, + }, + &MirredAction{ + ActionAttrs: ActionAttrs{ + Action: TC_ACT_STOLEN, + }, + MirredAction: TCA_EGRESS_REDIR, + Ifindex: redir.Attrs().Index, + }, + }, + } + + if err := FilterAdd(filter); err != nil { + t.Fatal(err) + } + + time.Sleep(time.Second) + filters, err = FilterList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(filters) != 1 { + t.Fatal("Failed to add filter") + } + flower, ok = filters[0].(*Flower) + if !ok { + t.Fatal("Filter is the wrong type") + } + + if filter.VlanId != flower.VlanId { + t.Fatalf("Flower VlanId doesn't match") + } + + vla, ok = flower.Actions[0].(*VlanAction) + if !ok { + t.Fatal("Unable to find vlan action") + } + + if vla.Attrs().Action != TC_ACT_PIPE { + t.Fatal("Vlan action isn't TC_ACT_PIPE") + } + + if vla.Action != TCA_VLAN_ACT_POP { + t.Fatal("First Vlan action isn't pop") + } + + mia, ok = flower.Actions[1].(*MirredAction) + if !ok { + t.Fatal("Unable to find mirred action") + } + + if mia.Attrs().Action != TC_ACT_STOLEN { + t.Fatal("Mirred action isn't TC_ACT_STOLEN") + } + + if mia.Timestamp == nil || mia.Timestamp.Installed == 0 { + t.Fatal("Incorrect mirred action timestamp") + } + + if mia.Statistics == nil { + t.Fatal("Incorrect mirred action stats") + } + + if err := FilterDel(filter); err != nil { + t.Fatal(err) + } + filters, err = FilterList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(filters) != 0 { + t.Fatal("Failed to remove filter") + } + if err := QdiscDel(qdisc); err != nil { t.Fatal(err) } diff --git a/nl/tc_linux.go b/nl/tc_linux.go index 0720729a..b8f50079 100644 --- a/nl/tc_linux.go +++ b/nl/tc_linux.go @@ -115,6 +115,7 @@ const ( SizeofTcConnmark = SizeofTcGen + 0x04 SizeofTcCsum = SizeofTcGen + 0x04 SizeofTcMirred = SizeofTcGen + 0x08 + SizeofTcVlan = SizeofTcGen + 0x04 SizeofTcTunnelKey = SizeofTcGen + 0x04 SizeofTcSkbEdit = SizeofTcGen SizeofTcPolice = 2*SizeofTcRateSpec + 0x20 @@ -816,6 +817,41 @@ func (x *TcMirred) Serialize() []byte { return (*(*[SizeofTcMirred]byte)(unsafe.Pointer(x)))[:] } +const ( + TCA_VLAN_UNSPEC = iota + TCA_VLAN_TM + TCA_VLAN_PARMS + TCA_VLAN_PUSH_VLAN_ID + TCA_VLAN_PUSH_VLAN_PROTOCOL + TCA_VLAN_PAD + TCA_VLAN_PUSH_VLAN_PRIORITY + TCA_VLAN_PUSH_ETH_DST + TCA_VLAN_PUSH_ETH_SRC + TCA_VLAN_MAX +) + +//struct tc_vlan { +// tc_gen; +// int v_action; +//}; + +type TcVlan struct { + TcGen + Action int32 +} + +func (msg *TcVlan) Len() int { + return SizeofTcVlan +} + +func DeserializeTcVlan(b []byte) *TcVlan { + return (*TcVlan)(unsafe.Pointer(&b[0:SizeofTcVlan][0])) +} + +func (x *TcVlan) Serialize() []byte { + return (*(*[SizeofTcVlan]byte)(unsafe.Pointer(x)))[:] +} + const ( TCA_TUNNEL_KEY_UNSPEC = iota TCA_TUNNEL_KEY_TM @@ -1239,8 +1275,8 @@ const ( ) // /* TCA_PEDIT_KEY_EX_HDR_TYPE_NETWROK is a special case for legacy users. It -// * means no specific header type - offset is relative to the network layer -// */ +// - means no specific header type - offset is relative to the network layer +// */ type PeditHeaderType uint16 const (