Skip to content

Commit 534f610

Browse files
Date Huangtjjh89017
Date Huang
authored andcommitted
bridge: add vlan trunk support
add vlan trunk support for veth vlan trunk only support L2 only mode without any IPAM refer ovs-cni design https://github.com/k8snetworkplumbingwg/ovs-cni/blob/main/pkg/plugin/plugin.go design: origin "vlan" option will be PVID or untagged vlan for the network. "vlanTrunk" will setup tagged vlan for veth. entry type: `{ "id": 100 }` will specify only tagged vlan 100 `{ "minID": 100, "maxID": 120 }` will specify tagged vlan from 100 to 120 (include 100 and 120) vlanTrunk is a list of above entry type, so you can use this to add tagged vlan `[ { "id": 100 }, { "minID": 1000, "maxID": 2000 } ]` complete config will be like this { "cniVersion": "0.3.1", "name": "mynet", "type": "bridge", "bridge": "mynet0", "vlan": 100, "vlanTrunk": [ { "id": 101 }, { "minID": 1000, "maxID": 2000 }, { "minID": 3000, "maxID": 4000 } ], "ipam": {} } Signed-off-by: Date Huang <[email protected]>
1 parent 9f1f9a5 commit 534f610

File tree

2 files changed

+215
-20
lines changed

2 files changed

+215
-20
lines changed

plugins/main/bridge/bridge.go

+100-17
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"net"
2222
"os"
2323
"runtime"
24+
"sort"
2425
"syscall"
2526
"time"
2627

@@ -46,18 +47,19 @@ const defaultBrName = "cni0"
4647

4748
type NetConf struct {
4849
types.NetConf
49-
BrName string `json:"bridge"`
50-
IsGW bool `json:"isGateway"`
51-
IsDefaultGW bool `json:"isDefaultGateway"`
52-
ForceAddress bool `json:"forceAddress"`
53-
IPMasq bool `json:"ipMasq"`
54-
MTU int `json:"mtu"`
55-
HairpinMode bool `json:"hairpinMode"`
56-
PromiscMode bool `json:"promiscMode"`
57-
Vlan int `json:"vlan"`
58-
PreserveDefaultVlan bool `json:"preserveDefaultVlan"`
59-
MacSpoofChk bool `json:"macspoofchk,omitempty"`
60-
EnableDad bool `json:"enabledad,omitempty"`
50+
BrName string `json:"bridge"`
51+
IsGW bool `json:"isGateway"`
52+
IsDefaultGW bool `json:"isDefaultGateway"`
53+
ForceAddress bool `json:"forceAddress"`
54+
IPMasq bool `json:"ipMasq"`
55+
MTU int `json:"mtu"`
56+
HairpinMode bool `json:"hairpinMode"`
57+
PromiscMode bool `json:"promiscMode"`
58+
Vlan int `json:"vlan"`
59+
VlanTrunk []*VlanTrunk `json:"vlanTrunk,omitempty"`
60+
PreserveDefaultVlan bool `json:"preserveDefaultVlan"`
61+
MacSpoofChk bool `json:"macspoofchk,omitempty"`
62+
EnableDad bool `json:"enabledad,omitempty"`
6163

6264
Args struct {
6365
Cni BridgeArgs `json:"cni,omitempty"`
@@ -66,7 +68,14 @@ type NetConf struct {
6668
Mac string `json:"mac,omitempty"`
6769
} `json:"runtimeConfig,omitempty"`
6870

69-
mac string
71+
mac string
72+
vlans []int
73+
}
74+
75+
type VlanTrunk struct {
76+
MinID *int `json:"minID,omitempty"`
77+
MaxID *int `json:"maxID,omitempty"`
78+
ID *int `json:"id,omitempty"`
7079
}
7180

7281
type BridgeArgs struct {
@@ -104,6 +113,12 @@ func loadNetConf(bytes []byte, envArgs string) (*NetConf, string, error) {
104113
if n.Vlan < 0 || n.Vlan > 4094 {
105114
return nil, "", fmt.Errorf("invalid VLAN ID %d (must be between 0 and 4094)", n.Vlan)
106115
}
116+
var err error
117+
n.vlans, err = collectVlanTrunk(n.VlanTrunk)
118+
if err != nil {
119+
// fail to parsing
120+
return nil, "", err
121+
}
107122

108123
if envArgs != "" {
109124
e := MacEnvArgs{}
@@ -127,6 +142,59 @@ func loadNetConf(bytes []byte, envArgs string) (*NetConf, string, error) {
127142
return n, n.CNIVersion, nil
128143
}
129144

145+
// This method is copied from https://github.com/k8snetworkplumbingwg/ovs-cni/blob/v0.27.2/pkg/plugin/plugin.go
146+
func collectVlanTrunk(vlanTrunk []*VlanTrunk) ([]int, error) {
147+
if vlanTrunk == nil {
148+
return nil, nil
149+
}
150+
151+
vlanMap := make(map[int]bool)
152+
for _, item := range vlanTrunk {
153+
var minID int
154+
var maxID int
155+
var ID int
156+
if item.MinID != nil {
157+
minID = *item.MinID
158+
if minID < 0 || minID > 4094 {
159+
return nil, errors.New("incorrect trunk minID parameter")
160+
}
161+
}
162+
if item.MaxID != nil {
163+
maxID = *item.MaxID
164+
if maxID < 0 || maxID > 4094 {
165+
return nil, errors.New("incorrect trunk maxID parameter")
166+
}
167+
if maxID < minID {
168+
return nil, errors.New("minID is greater than maxID in trunk parameter")
169+
}
170+
}
171+
if minID > 0 && maxID > 0 {
172+
for v := minID; v <= maxID; v++ {
173+
vlanMap[v] = true
174+
}
175+
}
176+
177+
// single vid
178+
if item.ID != nil {
179+
ID = *item.ID
180+
if ID < 0 || ID > 4094 {
181+
return nil, errors.New("incorrect trunk id parameter")
182+
}
183+
vlanMap[ID] = true
184+
}
185+
}
186+
187+
if len(vlanMap) == 0 {
188+
return nil, nil
189+
}
190+
vlans := make([]int, 0, len(vlanMap))
191+
for k := range vlanMap {
192+
vlans = append(vlans, k)
193+
}
194+
sort.Slice(vlans, func(i int, j int) bool { return vlans[i] < vlans[j] })
195+
return vlans, nil
196+
}
197+
130198
// calcGateways processes the results from the IPAM plugin and does the
131199
// following for each IP family:
132200
// - Calculates and compiles a list of gateway addresses
@@ -316,7 +384,7 @@ func ensureVlanInterface(br *netlink.Bridge, vlanID int, preserveDefaultVlan boo
316384
return nil, fmt.Errorf("faild to find host namespace: %v", err)
317385
}
318386

319-
_, brGatewayIface, err := setupVeth(hostNS, br, name, br.MTU, false, vlanID, preserveDefaultVlan, "")
387+
_, brGatewayIface, err := setupVeth(hostNS, br, name, br.MTU, false, vlanID, nil, preserveDefaultVlan, "")
320388
if err != nil {
321389
return nil, fmt.Errorf("faild to create vlan gateway %q: %v", name, err)
322390
}
@@ -335,7 +403,7 @@ func ensureVlanInterface(br *netlink.Bridge, vlanID int, preserveDefaultVlan boo
335403
return brGatewayVeth, nil
336404
}
337405

338-
func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairpinMode bool, vlanID int, preserveDefaultVlan bool, mac string) (*current.Interface, *current.Interface, error) {
406+
func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairpinMode bool, vlanID int, vlans []int, preserveDefaultVlan bool, mac string) (*current.Interface, *current.Interface, error) {
339407
contIface := &current.Interface{}
340408
hostIface := &current.Interface{}
341409

@@ -386,6 +454,14 @@ func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairp
386454
}
387455
}
388456

457+
// S1031: unnecessary nil check around range (gosimple)
458+
for _, v := range vlans {
459+
err = netlink.BridgeVlanAdd(hostVeth, uint16(v), false, false, false, true)
460+
if err != nil {
461+
return nil, nil, fmt.Errorf("failed to setup vlan tag on interface %q: %w", hostIface.Name, err)
462+
}
463+
}
464+
389465
return hostIface, contIface, nil
390466
}
391467

@@ -414,7 +490,10 @@ func calcGatewayIP(ipn *net.IPNet) net.IP {
414490
}
415491

416492
func setupBridge(n *NetConf) (*netlink.Bridge, *current.Interface, error) {
417-
vlanFiltering := n.Vlan != 0
493+
vlanFiltering := false
494+
if n.Vlan != 0 || n.VlanTrunk != nil {
495+
vlanFiltering = true
496+
}
418497
// create bridge if necessary
419498
br, err := ensureBridge(n.BrName, n.MTU, n.PromiscMode, vlanFiltering)
420499
if err != nil {
@@ -452,6 +531,10 @@ func cmdAdd(args *skel.CmdArgs) error {
452531
return fmt.Errorf("cannot set hairpin mode and promiscuous mode at the same time")
453532
}
454533

534+
if n.vlans != nil && len(n.vlans) > 0 && isLayer3 {
535+
return fmt.Errorf("cannot set vlanTrunk and IPAM at the same time")
536+
}
537+
455538
br, brInterface, err := setupBridge(n)
456539
if err != nil {
457540
return err
@@ -463,7 +546,7 @@ func cmdAdd(args *skel.CmdArgs) error {
463546
}
464547
defer netns.Close()
465548

466-
hostInterface, containerInterface, err := setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode, n.Vlan, n.PreserveDefaultVlan, n.mac)
549+
hostInterface, containerInterface, err := setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode, n.Vlan, n.vlans, n.PreserveDefaultVlan, n.mac)
467550
if err != nil {
468551
return err
469552
}

plugins/main/bridge/bridge_test.go

+115-3
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ type testCase struct {
7474
isLayer2 bool
7575
expGWCIDRs []string // Expected gateway addresses in CIDR form
7676
vlan int
77+
vlanTrunk []*VlanTrunk
7778
removeDefaultVlan bool
7879
ipMasq bool
7980
macspoofchk bool
@@ -130,6 +131,23 @@ const (
130131
vlan = `,
131132
"vlan": %d`
132133

134+
vlanTrunkStartStr = `,
135+
"vlanTrunk": [`
136+
137+
vlanTrunk = `
138+
{
139+
"id": %d
140+
}`
141+
142+
vlanTrunkRange = `
143+
{
144+
"minID": %d,
145+
"maxID": %d
146+
}`
147+
148+
vlanTrunkEndStr = `
149+
]`
150+
133151
preserveDefaultVlan = `,
134152
"preserveDefaultVlan": false`
135153

@@ -200,6 +218,23 @@ func (tc testCase) netConfJSON(dataDir string) string {
200218
conf += preserveDefaultVlan
201219
}
202220
}
221+
222+
if tc.isLayer2 && tc.vlanTrunk != nil {
223+
conf += vlanTrunkStartStr
224+
for i, vlan := range tc.vlanTrunk {
225+
if i > 0 {
226+
conf += ","
227+
}
228+
if vlan.ID != nil {
229+
conf += fmt.Sprintf(vlanTrunk, *vlan.ID)
230+
}
231+
if vlan.MinID != nil && vlan.MaxID != nil {
232+
conf += fmt.Sprintf(vlanTrunkRange, *vlan.MinID, *vlan.MaxID)
233+
}
234+
}
235+
conf += vlanTrunkEndStr
236+
}
237+
203238
if tc.ipMasq {
204239
conf += tc.ipMasqConfig()
205240
}
@@ -541,7 +576,7 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
541576
}
542577

543578
// Check the bridge vlan filtering equals true
544-
if tc.vlan != 0 {
579+
if tc.vlan != 0 || tc.vlanTrunk != nil {
545580
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeTrue())
546581
} else {
547582
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeFalse())
@@ -598,6 +633,25 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
598633
}
599634
}
600635

636+
// check VlanTrunks exist on the veth interface
637+
if tc.vlanTrunk != nil {
638+
interfaceMap, err := netlink.BridgeVlanList()
639+
Expect(err).NotTo(HaveOccurred())
640+
vlans, isExist := interfaceMap[int32(link.Attrs().Index)]
641+
Expect(isExist).To(BeTrue())
642+
643+
for _, vlanEntry := range tc.vlanTrunk {
644+
if vlanEntry.ID != nil {
645+
Expect(checkVlan(*vlanEntry.ID, vlans)).To(BeTrue())
646+
}
647+
if vlanEntry.MinID != nil && vlanEntry.MaxID != nil {
648+
for vid := *vlanEntry.MinID; vid <= *vlanEntry.MaxID; vid++ {
649+
Expect(checkVlan(vid, vlans)).To(BeTrue())
650+
}
651+
}
652+
}
653+
}
654+
601655
// Check that the bridge has a different mac from the veth
602656
// If not, it means the bridge has an unstable mac and will change
603657
// as ifs are added and removed
@@ -852,7 +906,7 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
852906
}
853907

854908
// Check the bridge vlan filtering equals true
855-
if tc.vlan != 0 {
909+
if tc.vlan != 0 || tc.vlanTrunk != nil {
856910
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeTrue())
857911
} else {
858912
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeFalse())
@@ -909,6 +963,25 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
909963
}
910964
}
911965

966+
// check VlanTrunks exist on the veth interface
967+
if tc.vlanTrunk != nil {
968+
interfaceMap, err := netlink.BridgeVlanList()
969+
Expect(err).NotTo(HaveOccurred())
970+
vlans, isExist := interfaceMap[int32(link.Attrs().Index)]
971+
Expect(isExist).To(BeTrue())
972+
973+
for _, vlanEntry := range tc.vlanTrunk {
974+
if vlanEntry.ID != nil {
975+
Expect(checkVlan(*vlanEntry.ID, vlans)).To(BeTrue())
976+
}
977+
if vlanEntry.MinID != nil && vlanEntry.MaxID != nil {
978+
for vid := *vlanEntry.MinID; vid <= *vlanEntry.MaxID; vid++ {
979+
Expect(checkVlan(vid, vlans)).To(BeTrue())
980+
}
981+
}
982+
}
983+
}
984+
912985
// Check that the bridge has a different mac from the veth
913986
// If not, it means the bridge has an unstable mac and will change
914987
// as ifs are added and removed
@@ -1158,7 +1231,7 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
11581231
}
11591232

11601233
// Check the bridge vlan filtering equals true
1161-
if tc.vlan != 0 {
1234+
if tc.vlan != 0 || tc.vlanTrunk != nil {
11621235
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeTrue())
11631236
} else {
11641237
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeFalse())
@@ -1215,6 +1288,25 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
12151288
}
12161289
}
12171290

1291+
// check VlanTrunks exist on the veth interface
1292+
if tc.vlanTrunk != nil {
1293+
interfaceMap, err := netlink.BridgeVlanList()
1294+
Expect(err).NotTo(HaveOccurred())
1295+
vlans, isExist := interfaceMap[int32(link.Attrs().Index)]
1296+
Expect(isExist).To(BeTrue())
1297+
1298+
for _, vlanEntry := range tc.vlanTrunk {
1299+
if vlanEntry.ID != nil {
1300+
Expect(checkVlan(*vlanEntry.ID, vlans)).To(BeTrue())
1301+
}
1302+
if vlanEntry.MinID != nil && vlanEntry.MaxID != nil {
1303+
for vid := *vlanEntry.MinID; vid <= *vlanEntry.MaxID; vid++ {
1304+
Expect(checkVlan(vid, vlans)).To(BeTrue())
1305+
}
1306+
}
1307+
}
1308+
}
1309+
12181310
// Check that the bridge has a different mac from the veth
12191311
// If not, it means the bridge has an unstable mac and will change
12201312
// as ifs are added and removed
@@ -1804,6 +1896,26 @@ var _ = Describe("bridge Operations", func() {
18041896
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
18051897
})
18061898

1899+
// TODO find some way to put pointer
1900+
It(fmt.Sprintf("[%s] configures and deconfigures a l2 bridge with vlan id 100, vlanTrunk 101,200~210 using ADD/DEL", ver), func() {
1901+
id, minID, maxID := 101, 200, 210
1902+
tc := testCase{
1903+
cniVersion: ver,
1904+
isLayer2: true,
1905+
vlan: 100,
1906+
vlanTrunk: []*VlanTrunk{
1907+
{ID: &id},
1908+
{
1909+
MinID: &minID,
1910+
MaxID: &maxID,
1911+
},
1912+
},
1913+
AddErr020: "cannot convert: no valid IP addresses",
1914+
AddErr010: "cannot convert: no valid IP addresses",
1915+
}
1916+
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
1917+
})
1918+
18071919
It(fmt.Sprintf("[%s] configures and deconfigures a l2 bridge with vlan id 100 and no default vlan using ADD/DEL", ver), func() {
18081920
tc := testCase{
18091921
cniVersion: ver,

0 commit comments

Comments
 (0)