Skip to content

Commit

Permalink
Enhances the OpenFlow driver
Browse files Browse the repository at this point in the history
This commit redesigns the OpenFlow drivers in a way that enables us to support
multiple OpenFlow versions.
  • Loading branch information
soheilhy committed Sep 8, 2014
1 parent 8e98665 commit 8dd1c16
Show file tree
Hide file tree
Showing 13 changed files with 3,414 additions and 1,401 deletions.
45 changes: 45 additions & 0 deletions openflow/bh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package openflow

import (
"flag"

"github.com/golang/glog"

"github.com/soheilhy/beehive/bh"
)

// OFConfig stores the configuration of the OpenFlow driver.
type OFConfig struct {
Proto string // The driver's listening protocol.
Addr string // The driver's listening address.
ReadBufLen int // Maximum number of packets to read.
}

var defaultOFConfig = OFConfig{}

func init() {
flag.StringVar(&defaultOFConfig.Proto, "ofproto", "tcp",
"Protocol of the OpenFlow listener.")
flag.StringVar(&defaultOFConfig.Addr, "ofaddr", "0.0.0.0:6633",
"Address of the OpenFlow listener in the form of HOST:PORT.")
flag.IntVar(&defaultOFConfig.ReadBufLen, "rbuflen", 1<<8,
"Maximum number of packets to read per each read call.")
}

// StartOpenFlow starts the OpenFlow driver on the given hive using the default
// OpenFlow configuration that can be set through command line arguments.
func StartOpenFlow(hive bh.Hive) error {
return StartOpenFlowWithConfig(hive, defaultOFConfig)
}

// StartOpenFlowWithConfig starts the OpenFlow driver on the give hive with the
// provided configuration.
func StartOpenFlowWithConfig(hive bh.Hive, cfg OFConfig) error {
app := hive.NewApp("OFDriver")
app.Detached(&ofListener{
cfg: cfg,
})

glog.V(2).Infof("OpenFlow driver registered on %s:%s", cfg.Proto, cfg.Addr)
return nil
}
62 changes: 62 additions & 0 deletions openflow/conn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package openflow

import (
"github.com/golang/glog"
"github.com/soheilhy/beehive-netctrl/nom"
"github.com/soheilhy/beehive-netctrl/openflow/of"
"github.com/soheilhy/beehive/bh"
)

type ofConnConfig struct {
readBufLen int
}

type ofConn struct {
of.HeaderConn
cfg ofConnConfig
ctx bh.RcvContext
node nom.NodeID
driver ofDriver
}

func (c *ofConn) Start(ctx bh.RcvContext) {
defer func() {
c.Close()
}()

var err error
if c.driver, err = c.handshake(); err != nil {
glog.Errorf("Error in OpenFlow handshake: %v", err)
return
}

pkts := make([]of.Header, c.cfg.readBufLen)
for {
n, err := c.ReadHeaders(pkts)
if err != nil {
glog.Errorf("Cannot read from the connection: %v", err)
return
}

for _, pkt := range pkts[:n] {
if err := c.driver.handlePkt(pkt, c); err != nil {
glog.Errorf("%s", err)
return
}
}

pkts = pkts[n:]
if len(pkts) == 0 {
pkts = make([]of.Header, c.cfg.readBufLen)
}
}
}

func (c *ofConn) Stop(ctx bh.RcvContext) {
c.Close()
}

func (c *ofConn) Rcv(msg bh.Msg, ctx bh.RcvContext) error {
pkt := msg.Data().(of.Header)
return c.WriteHeader(pkt)
}
252 changes: 39 additions & 213 deletions openflow/driver.go
Original file line number Diff line number Diff line change
@@ -1,242 +1,68 @@
package openflow

import (
"errors"
"flag"
"net"
"fmt"

"github.com/golang/glog"
"github.com/soheilhy/beehive-netctrl/openflow/of"
"github.com/soheilhy/beehive-netctrl/openflow/of10"
"github.com/soheilhy/beehive-netctrl/openflow/of12"
"github.com/soheilhy/beehive/bh"
)

// OFConfig stores the configuration of the OpenFlow driver.
type OFConfig struct {
Proto string // The driver's listening protocol.
Addr string // The driver's listening address.
type ofDriver interface {
handshake(conn *ofConn) error
handlePkt(pkt of.Header, conn *ofConn) error
handleMsg(msg bh.Msg, conn *ofConn) error
}

var defaultOFConfig = OFConfig{}
type of10Driver struct{}

func init() {
flag.StringVar(&defaultOFConfig.Proto, "ofproto", "tcp",
"Protocol of the OpenFlow listener.")
flag.StringVar(&defaultOFConfig.Addr, "ofaddr", "0.0.0.0:6633",
"Address of the OpenFlow listener in the form of HOST:PORT.")
}

// StartOpenFlow starts the OpenFlow driver on the given hive using the default
// OpenFlow configuration that can be set through command line arguments.
func StartOpenFlow(hive bh.Hive) error {
return StartOpenFlowWithConfig(hive, defaultOFConfig)
}

// StartOpenFlowWithConfig starts the OpenFlow driver on the give hive with the
// provided configuration.
func StartOpenFlowWithConfig(hive bh.Hive, cfg OFConfig) error {
app := hive.NewApp("OFDriver")
app.Detached(&ofListener{
cfg: cfg,
})

glog.V(2).Infof("OpenFlow driver registered on %s:%s", cfg.Proto, cfg.Addr)
return nil
}

type ofListener struct {
cfg OFConfig
}

func (l *ofListener) Start(ctx bh.RcvContext) {
nl, err := net.Listen(l.cfg.Proto, l.cfg.Addr)
if err != nil {
glog.Errorf("Cannot start the OF listener: %v", err)
return
}

glog.Infof("OF listener started on %s:%s", l.cfg.Proto, l.cfg.Addr)

defer func() {
glog.Infof("OF listener closed")
nl.Close()
}()

for {
c, err := nl.Accept()
if err != nil {
glog.Errorf("Error in OF accept: %v", err)
return
}

l.startOFConn(c, ctx)
}
}

func (l *ofListener) startOFConn(conn net.Conn, ctx bh.RcvContext) {
ofc := &ofConn{
HeaderConn: of.NewHeaderConn(conn),
listener: l,
ctx: ctx,
}

ctx.StartDetached(ofc)
}

func (l *ofListener) Stop(ctx bh.RcvContext) {
}

func (l *ofListener) Rcv(msg bh.Msg, ctx bh.RcvContext) error {
return errors.New("No message should be sent to the listener")
}

type ofConn struct {
of.HeaderConn
listener *ofListener
ctx bh.RcvContext
swtch of10.FeaturesReply
}

func (c *ofConn) Start(ctx bh.RcvContext) {
defer func() {
c.Close()
}()

if err := c.handshake(); err != nil {
glog.Errorf("Error in OpenFlow handshake: %v", err)
return
}

c.readPkts(ctx)
}

func (c *ofConn) Stop(ctx bh.RcvContext) {
c.Close()
}

func (c *ofConn) handshake() error {
pkts := make([]of.Header, 1)
_, err := c.Read(pkts)
if err != nil {
return err
}

h, err := of.ToHello(pkts[0])
if err != nil {
return err
}

glog.V(2).Info("Received hello from a switch")

h.SetVersion(uint8(of.OPENFLOW_1_0))
if err = c.Write([]of.Header{h.Header}); err != nil {
return err
}

glog.V(2).Info("Sent hello to the switch")
type of12Driver struct{}

freq := of10.NewFeaturesRequest()
if err = c.Write([]of.Header{freq.Header}); err != nil {
return err
}

glog.V(2).Info("Sent features request to the switch")

_, err = c.Read(pkts)
func (d *of10Driver) handlePkt(pkt of.Header, c *ofConn) error {
pkt10, err := of10.ToHeader10(pkt)
if err != nil {
return err
}

v10, err := of10.ToHeader10(pkts[0])
if err != nil {
return err
switch {
case of10.IsEchoRequest(pkt10):
return d.handleEchoRequest(of10.NewEchoRequestWithBuf(pkt10.Buf), c)
case of10.IsFeaturesReply(pkt10):
return d.handleFeaturesReply(of10.NewFeaturesReplyWithBuf(pkt10.Buf), c)
case of10.IsPacketIn(pkt10):
return d.handlePacketIn(of10.NewPacketInWithBuf(pkt10.Buf), c)
case of10.IsErrorMsg(pkt10):
return d.handleErrorMsg(of10.NewErrorMsgWithBuf(pkt10.Buf), c)
default:
return fmt.Errorf("Received unsupported packet: %v", pkt.Type())
}
}

frep, err := of10.ToFeaturesReply(v10)
func (d *of12Driver) handlePkt(pkt of.Header, c *ofConn) error {
pkt12, err := of12.ToHeader12(pkt)
if err != nil {
return err
}

frep, err = frep.Clone()
if err != nil {
return err
switch {
case of12.IsEchoRequest(pkt12):
return d.handleEchoRequest(of12.NewEchoRequestWithBuf(pkt12.Buf), c)
case of12.IsFeaturesReply(pkt12):
return d.handleFeaturesReply(of12.NewFeaturesReplyWithBuf(pkt12.Buf), c)
case of12.IsPacketIn(pkt12):
return d.handlePacketIn(of12.NewPacketInWithBuf(pkt12.Buf), c)
case of12.IsErrorMsg(pkt12):
return d.handleErrorMsg(of12.NewErrorMsgWithBuf(pkt12.Buf), c)
default:
return fmt.Errorf("Received unsupported packet: %v", pkt.Type())
}

glog.Infof("Handshake completed for switch %016x", frep.DatapathId())

c.swtch = frep
return nil
}

func (c *ofConn) readPkts(ctx bh.RcvContext) {
pkts := make([]of.Header, 10)
for {
n, err := c.Read(pkts)
if err != nil {
glog.Errorf("Cannot read from the connection: %v", err)
return
}

for _, pkt := range pkts[:n] {
pkt10, err := of10.ToHeader10(pkt)
if err != nil {
glog.Errorf("OF Driver only support OF v1.0")
return
}

switch {
case of10.IsEchoRequest(pkt10):
glog.V(2).Infof("Received an echo request from the switch")
rep := of10.NewEchoReply()
rep.SetXid(pkt.Xid())
err := c.Write([]of.Header{rep.Header})
if err != nil {
glog.Errorf("Error in writing an echo reply: %v", err)
return
}
glog.V(2).Infof("Sent an echo reply from the switch")

case of10.IsFeaturesReply(pkt10):
r, _ := of10.ToFeaturesReply(pkt10)
glog.Infof("Switch joined %016x", r.DatapathId())
for _, p := range r.Ports() {
glog.Infof("Port (switch=%016x, no=%d, mac=%012x, name=%s)\n",
r.DatapathId(), p.PortNo(), p.HwAddr(), p.Name())
}

glog.Infof("Disabling packet buffers in the switch.")
cfg := of10.NewSwitchSetConfig()
cfg.SetMissSendLen(0xFFFF)
c.Write([]of.Header{cfg.Header})

case of10.IsPacketIn(pkt10):
in, _ := of10.ToPacketIn(pkt10)
out := of10.NewPacketOut()
out.SetBufferId(in.BufferId())
out.SetInPort(in.InPort())

bcast := of10.NewActionOutput()
bcast.SetPort(uint16(of10.PP_FLOOD))

out.AddActions(bcast.ActionHeader)
for _, d := range in.Data() {
out.AddData(d)
}
c.Write([]of.Header{out.Header})

default:
c.ctx.Emit(pkt10)
}
}

pkts = pkts[n:]
if len(pkts) == 0 {
pkts = make([]of.Header, 10)
}
}
func (d *of10Driver) handleMsg(msg bh.Msg, c *ofConn) error {
return nil
}

func (c *ofConn) Rcv(msg bh.Msg, ctx bh.RcvContext) error {
pkt := msg.Data().(of.Header)
return c.Write([]of.Header{pkt})
func (d *of12Driver) handleMsg(msg bh.Msg, c *ofConn) error {
return nil
}
Loading

0 comments on commit 8dd1c16

Please sign in to comment.