mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-28 01:31:17 +00:00
363 lines
7.9 KiB
Go
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
|
|
}
|
|
})
|
|
}
|