-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit redesigns the OpenFlow drivers in a way that enables us to support multiple OpenFlow versions.
- Loading branch information
Showing
13 changed files
with
3,414 additions
and
1,401 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.