Files
2015-12-04 09:57:44 +00:00

363 lines
7.9 KiB
Go

package odp
import (
"fmt"
"syscall"
)
type VportSpec interface {
TypeName() string
Name() string
typeId() uint32
optionNlAttrs(req *NlMsgBuilder)
}
type VportSpecBase struct {
name string
}
func (v VportSpecBase) Name() string {
return v.name
}
type SimpleVportSpec struct {
VportSpecBase
typ uint32
typeName string
}
func (s SimpleVportSpec) TypeName() string {
return s.typeName
}
func (s SimpleVportSpec) typeId() uint32 {
return s.typ
}
func (SimpleVportSpec) optionNlAttrs(req *NlMsgBuilder) {
}
func NewNetdevVportSpec(name string) VportSpec {
return SimpleVportSpec{
VportSpecBase{name},
OVS_VPORT_TYPE_NETDEV,
"netdev",
}
}
func NewInternalVportSpec(name string) VportSpec {
return SimpleVportSpec{
VportSpecBase{name},
OVS_VPORT_TYPE_INTERNAL,
"internal",
}
}
type VxlanVportSpec struct {
VportSpecBase
Port uint16
}
func (VxlanVportSpec) TypeName() string {
return "vxlan"
}
func (VxlanVportSpec) typeId() uint32 {
return OVS_VPORT_TYPE_VXLAN
}
func (v VxlanVportSpec) optionNlAttrs(req *NlMsgBuilder) {
req.PutUint16Attr(OVS_TUNNEL_ATTR_DST_PORT, v.Port)
}
func NewVxlanVportSpec(name string, port uint16) VportSpec {
return VxlanVportSpec{VportSpecBase{name}, port}
}
func parseVxlanVportSpec(name string, opts Attrs) (VportSpec, error) {
port, err := opts.GetUint16(OVS_TUNNEL_ATTR_DST_PORT)
if err != nil {
return nil, err
}
return VxlanVportSpec{VportSpecBase{name}, port}, nil
}
// Vport numbers are scoped to a particular datapath
type VportID uint32
func parseVport(msg *NlMsgParser) (id VportID, s VportSpec, err error) {
attrs, err := msg.TakeAttrs()
if err != nil {
return
}
rawid, err := attrs.GetUint32(OVS_VPORT_ATTR_PORT_NO)
if err != nil {
return
}
id = VportID(rawid)
typ, err := attrs.GetUint32(OVS_VPORT_ATTR_TYPE)
if err != nil {
return
}
name, err := attrs.GetString(OVS_VPORT_ATTR_NAME)
if err != nil {
return
}
opts, err := attrs.GetNestedAttrs(OVS_VPORT_ATTR_OPTIONS, true)
if err != nil {
return
}
if opts == nil {
opts = make(Attrs)
}
switch typ {
case OVS_VPORT_TYPE_NETDEV:
s = NewNetdevVportSpec(name)
break
case OVS_VPORT_TYPE_INTERNAL:
s = NewInternalVportSpec(name)
break
case OVS_VPORT_TYPE_VXLAN:
s, err = parseVxlanVportSpec(name, opts)
break
default:
err = fmt.Errorf("unsupported vport type %d", typ)
}
return
}
func (dp DatapathHandle) CreateVport(spec VportSpec) (VportID, error) {
dpif := dp.dpif
req := NewNlMsgBuilder(RequestFlags, dpif.families[VPORT].id)
req.PutGenlMsghdr(OVS_VPORT_CMD_NEW, OVS_VPORT_VERSION)
req.putOvsHeader(dp.ifindex)
req.PutStringAttr(OVS_VPORT_ATTR_NAME, spec.Name())
req.PutUint32Attr(OVS_VPORT_ATTR_TYPE, spec.typeId())
req.PutNestedAttrs(OVS_VPORT_ATTR_OPTIONS, func() {
spec.optionNlAttrs(req)
})
req.PutUint32Attr(OVS_VPORT_ATTR_UPCALL_PID, 0)
resp, err := dpif.sock.Request(req)
if err != nil {
return 0, err
}
_, _, err = dpif.checkNlMsgHeaders(resp, VPORT, OVS_VPORT_CMD_NEW)
if err != nil {
return 0, err
}
id, _, err := parseVport(resp)
if err != nil {
return 0, err
}
return id, nil
}
func IsNoSuchVportError(err error) bool {
return err == NetlinkError(syscall.ENODEV)
}
type Vport struct {
ID VportID
Spec VportSpec
}
func lookupVport(dpif *Dpif, dpifindex DatapathID, name string) (DatapathID, Vport, error) {
req := NewNlMsgBuilder(RequestFlags, dpif.families[VPORT].id)
req.PutGenlMsghdr(OVS_VPORT_CMD_GET, OVS_VPORT_VERSION)
req.putOvsHeader(dpifindex)
req.PutStringAttr(OVS_VPORT_ATTR_NAME, name)
resp, err := dpif.sock.Request(req)
if err != nil {
return 0, Vport{}, err
}
_, ovshdr, err := dpif.checkNlMsgHeaders(resp, VPORT, OVS_VPORT_CMD_NEW)
if err != nil {
return 0, Vport{}, err
}
id, s, err := parseVport(resp)
if err != nil {
return 0, Vport{}, err
}
return ovshdr.datapathID(), Vport{id, s}, nil
}
func (dpif *Dpif) LookupVportByName(name string) (DatapathHandle, Vport, error) {
dpifindex, vport, err := lookupVport(dpif, 0, name)
return DatapathHandle{dpif: dpif, ifindex: dpifindex}, vport, err
}
func (dp DatapathHandle) LookupVportByName(name string) (Vport, error) {
_, vport, err := lookupVport(dp.dpif, dp.ifindex, name)
return vport, err
}
func (dp DatapathHandle) LookupVport(id VportID) (Vport, error) {
req := NewNlMsgBuilder(RequestFlags, dp.dpif.families[VPORT].id)
req.PutGenlMsghdr(OVS_VPORT_CMD_GET, OVS_VPORT_VERSION)
req.putOvsHeader(dp.ifindex)
req.PutUint32Attr(OVS_VPORT_ATTR_PORT_NO, uint32(id))
resp, err := dp.dpif.sock.Request(req)
if err != nil {
return Vport{}, err
}
err = dp.checkNlMsgHeaders(resp, VPORT, OVS_VPORT_CMD_NEW)
if err != nil {
return Vport{}, err
}
id, s, err := parseVport(resp)
if err != nil {
return Vport{}, err
}
return Vport{id, s}, nil
}
func (dp DatapathHandle) LookupVportName(id VportID) (string, error) {
vport, err := dp.LookupVport(id)
if err != nil {
if !IsNoSuchVportError(err) {
return "", err
}
// No vport with the given port number, so just
// show the number
return fmt.Sprintf("%d:%d", dp.ifindex, id), nil
}
return vport.Spec.Name(), nil
}
func (dp DatapathHandle) EnumerateVports() ([]Vport, error) {
req := NewNlMsgBuilder(DumpFlags, dp.dpif.families[VPORT].id)
req.PutGenlMsghdr(OVS_VPORT_CMD_GET, OVS_VPORT_VERSION)
req.putOvsHeader(dp.ifindex)
var res []Vport
consumer := func(resp *NlMsgParser) error {
err := dp.checkNlMsgHeaders(resp, VPORT, OVS_VPORT_CMD_NEW)
if err != nil {
return err
}
id, spec, err := parseVport(resp)
if err != nil {
return err
}
res = append(res, Vport{id, spec})
return nil
}
err := dp.dpif.sock.RequestMulti(req, consumer)
if err != nil {
return nil, err
}
return res, nil
}
func (dp DatapathHandle) DeleteVport(id VportID) error {
req := NewNlMsgBuilder(RequestFlags, dp.dpif.families[VPORT].id)
req.PutGenlMsghdr(OVS_VPORT_CMD_DEL, OVS_VPORT_VERSION)
req.putOvsHeader(dp.ifindex)
req.PutUint32Attr(OVS_VPORT_ATTR_PORT_NO, uint32(id))
_, err := dp.dpif.sock.Request(req)
return err
}
func (dp DatapathHandle) setVportUpcallPortId(id VportID, pid uint32) error {
req := NewNlMsgBuilder(RequestFlags, dp.dpif.families[VPORT].id)
req.PutGenlMsghdr(OVS_VPORT_CMD_SET, OVS_VPORT_VERSION)
req.putOvsHeader(dp.ifindex)
req.PutUint32Attr(OVS_VPORT_ATTR_PORT_NO, uint32(id))
req.PutUint32Attr(OVS_VPORT_ATTR_UPCALL_PID, pid)
_, err := dp.dpif.sock.Request(req)
return err
}
type VportEventsConsumer interface {
VportCreated(dpid DatapathID, vport Vport) error
VportDeleted(dpid DatapathID, vport Vport) error
Error(err error, stopped bool)
}
func (dpif *Dpif) ConsumeVportEvents(consumer VportEventsConsumer) (Cancelable, error) {
return DatapathHandle{dpif, -1}.ConsumeVportEvents(consumer)
}
func (dp DatapathHandle) ConsumeVportEvents(consumer VportEventsConsumer) (Cancelable, error) {
mcGroup, err := dp.dpif.getMCGroup(VPORT, "ovs_vport")
if err != nil {
return nil, err
}
consumeDpif, err := dp.dpif.Reopen()
if err != nil {
return nil, err
}
err = syscall.SetsockoptInt(consumeDpif.sock.fd, SOL_NETLINK, syscall.NETLINK_ADD_MEMBERSHIP, int(mcGroup))
if err != nil {
consumeDpif.Close()
return nil, err
}
go consumeDpif.consumeVportEvents(consumer, dp.ifindex)
return cancelableDpif{consumeDpif}, nil
}
func (dpif *Dpif) consumeVportEvents(consumer VportEventsConsumer, ifindex DatapathID) {
dpif.sock.consume(consumer, func(msg *NlMsgParser) error {
genlhdr, ovshdr, err := dpif.checkNlMsgHeaders(msg, VPORT, -1)
if err != nil {
return err
}
// filter by ifindex, if consuming on a specific datapath
if ifindex >= 0 && ovshdr.datapathID() != ifindex {
return nil
}
id, spec, err := parseVport(msg)
if err != nil {
return err
}
switch genlhdr.Cmd {
case OVS_VPORT_CMD_NEW:
return consumer.VportCreated(ovshdr.datapathID(), Vport{id, spec})
case OVS_VPORT_CMD_DEL:
return consumer.VportDeleted(ovshdr.datapathID(), Vport{id, spec})
default:
return nil
}
})
}