Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 54 additions & 5 deletions ofctrl/cookie/allocator.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cookie

import (
"fmt"
"sync"
)

Expand All @@ -10,8 +11,11 @@ const (
BitWidthFlowId = 64 - BitWidthReserved - BitWidthRoundNum
RoundNumMask uint64 = 0x0000_0000_f000_0000
FlowIdMask uint64 = 0x0000_0000_0fff_ffff
InitFlowID uint64 = 1
)

var FlowIDExhausted error = fmt.Errorf("flow id has exhausted")

type ID uint64

func newId(round uint64, flowId uint64) ID {
Expand All @@ -31,37 +35,82 @@ func (i ID) Round() uint64 {
}

type Allocator interface {
RequestCookie() uint64
RequestCookie() (uint64, error)
SetFixedMask(uint64)
}

type AllocatorOption func(*allocator) error

type allocator struct {
roundNum uint64
flowID uint64
fixedMask uint64
flowIDLock sync.RWMutex
maxFlowID uint64
}

// cookie will 'OR' fixed mask
func (a *allocator) SetFixedMask(mask uint64) {
a.fixedMask = mask
}

func (a *allocator) RequestCookie() uint64 {
func (a *allocator) RequestCookie() (uint64, error) {
a.flowIDLock.Lock()
defer a.flowIDLock.Unlock()

if a.maxFlowID != 0 && a.flowID > a.maxFlowID {
return 0, FlowIDExhausted
}
rawID := newId(a.roundNum, a.flowID).RawId()
a.flowID += 1
return rawID | a.fixedMask
return rawID | a.fixedMask, nil
}

func NewAllocator(roundNum uint64) Allocator {
func (a *allocator) setFlowIDRange(start, end uint64) error {
if start > end {
return fmt.Errorf("param error, start %x can't bigger than end %x", start, end)
}
maxFlowIDOrigin := uint64(1<<BitWidthFlowId - 1)
if start < InitFlowID {
return fmt.Errorf("start %x can't small than minFlowID %x", start, InitFlowID)
}
if end > maxFlowIDOrigin {
return fmt.Errorf("end %x can't biggger than maxFlowID %x", end, maxFlowIDOrigin)
}
a.flowID = start
a.maxFlowID = end
return nil
}

func (a *allocator) setDefaultFlowIDRange() {
a.flowID = InitFlowID
a.maxFlowID = uint64(1<<BitWidthFlowId - 1)
}

func SetFlowIDRange(start, end uint64) AllocatorOption {
return func(a *allocator) error {
return a.setFlowIDRange(start, end)
}
}

func SetDefaultFlowIDRange() AllocatorOption {
return func(a *allocator) error {
a.setDefaultFlowIDRange()
return nil
}
}

func NewAllocator(roundNum uint64, options ...AllocatorOption) Allocator {
a := &allocator{
roundNum: roundNum,
flowID: 1,
flowID: InitFlowID,
flowIDLock: sync.RWMutex{},
}
for _, o := range options {
if err := o(a); err != nil {
return nil
}
}
return a
}

Expand Down
78 changes: 72 additions & 6 deletions ofctrl/cookie/allocator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,105 @@ import (
func TestAllocateCookie(t *testing.T) {
allocator := NewAllocator(0)

cookie := allocator.RequestCookie()
cookie, err := allocator.RequestCookie()
if err != nil {
t.Errorf("unexpectd return err %s", err)
}
if cookie != 0x1 {
t.Errorf("request cookie fail, expect 0x1, got %x", cookie)
}

cookie = allocator.RequestCookie()
cookie, err = allocator.RequestCookie()
if err != nil {
t.Errorf("unexpectd return err %s", err)
}
if cookie != 0x2 {
t.Errorf("request cookie fail, expect 0x2, got %x", cookie)
}

allocator.SetFixedMask(0x10)

cookie = allocator.RequestCookie()
cookie, err = allocator.RequestCookie()
if err != nil {
t.Errorf("unexpectd return err %s", err)
}
if cookie != 0x13 {
t.Errorf("request cookie fail, expect 0x13, got %x", cookie)
}

allocator = NewAllocator(1)

cookie = allocator.RequestCookie()
cookie, err = allocator.RequestCookie()
if err != nil {
t.Errorf("unexpectd return err %s", err)
}
if cookie != 0x10000001 {
t.Errorf("request cookie fail, expect 0x10000001, got %x", cookie)
}

cookie = allocator.RequestCookie()
cookie, err = allocator.RequestCookie()
if err != nil {
t.Errorf("unexpectd return err %s", err)
}
if cookie != 0x10000002 {
t.Errorf("request cookie fail, expect 0x10000002, got %x", cookie)
}

allocator.SetFixedMask(0x100000000)

cookie = allocator.RequestCookie()
cookie, err = allocator.RequestCookie()
if err != nil {
t.Errorf("unexpectd return err %s", err)
}
if cookie != 0x110000003 {
t.Errorf("request cookie fail, expect 0x110000003, got %x", cookie)
}

allocator = NewAllocator(1, SetFlowIDRange(10, 9))
if allocator != nil {
t.Errorf("expect allocator is nil, but real is not")
}

allocator = NewAllocator(1, SetFlowIDRange(0, 9))
if allocator != nil {
t.Errorf("expect allocator is nil, but real is not")
}

allocator = NewAllocator(1, SetFlowIDRange(10, 1<<BitWidthFlowId))
if allocator != nil {
t.Errorf("expect allocator is nil, but real is not")
}

allocator = NewAllocator(1, SetFlowIDRange(10, 11))

cookie, err = allocator.RequestCookie()
if err != nil {
t.Errorf("unexpectd return err %s", err)
}
if cookie != 0x1000000a {
t.Errorf("request cookie fail, expect 0x1000000a , got %x", cookie)
}
cookie, err = allocator.RequestCookie()
if err != nil {
t.Errorf("unexpectd return err %s", err)
}
if cookie != 0x1000000b {
t.Errorf("request cookie fail, expect 0x1000000b, got %x", cookie)
}
cookie, err = allocator.RequestCookie()
if err == nil {
t.Errorf("expect allocate cookie failed, but success")
}

allocator = NewAllocator(1, SetDefaultFlowIDRange())
if allocator == nil {
t.Errorf("new allocator failed, is unexpected")
}
cookie, err = allocator.RequestCookie()
if err != nil {
t.Errorf("unexpectd return err %s", err)
}
if cookie != 0x10000001 {
t.Errorf("request cookie fail, expect 0x10000001, got %x", cookie)
}
}
28 changes: 22 additions & 6 deletions ofctrl/fgraphTable.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,35 @@ var globalFlowID uint64 = 1

// Create a new flow on the table
func (self *Table) NewFlow(match FlowMatch) (*Flow, error) {
flow := new(Flow)
flow.Table = self
flow.Match = match
flow.isInstalled = false
if self.Switch == nil {
return nil, dperror.NewDpError(dperror.SwitchDisconnectedError.Code, dperror.SwitchDisconnectedError.Msg, fmt.Errorf("ofSwitch disconnected"))
}

var flowID uint64
if self.Switch.CookieAllocator != nil {
flow.FlowID = self.Switch.CookieAllocator.RequestCookie()
var err error
flowID, err = self.Switch.CookieAllocator.RequestCookie()
if err != nil {
return nil, err
}
} else {
flow.FlowID = globalFlowID // FIXME: need a better id allocation
flowID = globalFlowID // FIXME: need a better id allocation
globalFlowID += 1
}

return self.NewFlowWithFlowID(match, flowID)
}

func (self *Table) NewFlowWithFlowID(match FlowMatch, flowID uint64) (*Flow, error) {
if self.Switch == nil {
return nil, dperror.NewDpError(dperror.SwitchDisconnectedError.Code, dperror.SwitchDisconnectedError.Msg, fmt.Errorf("ofSwitch disconnected"))
}
flow := new(Flow)
flow.Table = self
flow.Match = match
flow.isInstalled = false

flow.FlowID = flowID
flow.flowActions = make([]Action, 0)

log.Debugf("Creating new flow for match: %+v", match)
Expand Down
58 changes: 35 additions & 23 deletions ofctrl/ofctrl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ func ofctlFlowMatch(flowList []string, tableId int, matchStr, actStr string) boo
return false
}

func ofctlFlowMatchCookieID(flowList []string, cookieID uint64) bool {
mStr := fmt.Sprintf("cookie=%#x", cookieID)
for _, flowEntry := range flowList {
if strings.Contains(flowEntry, mStr) {
return true
}
}
return false
}

// ofctlDumpFlowMatch dumps flows and finds a match
func ofctlDumpFlowMatch(brName string, tableId int, matchStr, actStr string) bool {
// dump flows
Expand Down Expand Up @@ -292,6 +302,21 @@ func TestCreateDeleteFlow(t *testing.T) {
t.Errorf("Error installing the tcp flow")
}

// newflow with flowid
idFlow, err := ofActor.inputTable.NewFlowWithFlowID(FlowMatch{
Priority: 100,
Ethertype: 0x0800,
IpProto: 6,
TcpDstPort: 8080,
}, 0x80)
if err != nil {
t.Errorf("Error creating flow with flowid")
}
log.Infof("Creating id flow: %+v", idFlow)
if err := idFlow.Next(output); err != nil {
t.Errorf("Error installing the id flow")
}

// verify it got installed
flowList, err := ofctlFlowDump("ovsbr11")
if err != nil {
Expand Down Expand Up @@ -323,6 +348,10 @@ func TestCreateDeleteFlow(t *testing.T) {
t.Errorf("IP flow not found in OVS.")
}

if !ofctlFlowMatchCookieID(flowList, 0x80) {
t.Errorf("fix cookieID flow not found in OVS.")
}

// Delete the flow
err = inPortFlow.Delete()
if err != nil {
Expand All @@ -347,34 +376,17 @@ func TestCreateDeleteFlow(t *testing.T) {
t.Errorf("Error deleting the tcp flow. Err: %v", err)
}

if err = idFlow.Delete(); err != nil {
t.Errorf("Error deleting the id flow. Err: %v", err)
}

// Make sure they are really gone
flowList, err = ofctlFlowDump("ovsbr11")
if err != nil {
t.Errorf("Error getting flow entry")
}

// Match inport flow and see if its still there..
if ofctlFlowMatch(flowList, 0, "priority=100,in_port=1",
"push_vlan:0x8100,set_field:4097->vlan_vid,goto_table:1") {
t.Errorf("in port flow still found in OVS after deleting it.")
}

// match ip flow
if ofctlFlowMatch(flowList, 1, "priority=100,ip,nw_dst=10.10.10.10",
"output:1") {
t.Errorf("IP flow not found in OVS.")
}

// match mac flow
if ofctlFlowMatch(flowList, 1, "priority=100,dl_vlan=1,dl_dst=02:01:01:01:01:01",
"pop_vlan,output:1") {
t.Errorf("Mac flow not found in OVS.")
}

// match tcp flow
if ofctlFlowMatch(flowList, 1, "priority=100,tcp,tp_dst=80,tcp_flags=+syn",
"output:1") {
t.Errorf("IP flow not found in OVS.")
if len(flowList) != 0 {
t.Errorf("doesn't delete all flow: %s", flowList)
}
}

Expand Down