Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Paste
P5408
qdisc_linux.go
Active
Public
Actions
Authored by
•
ema
on May 9 2017, 2:49 PM.
Edit Paste
Archive Paste
View Raw File
Subscribe
Mute Notifications
Award Token
Flag For Later
Tags
None
Referenced Files
F7997760: qdisc_linux.go
May 9 2017, 2:49 PM
2017-05-09 14:49:01 (UTC+0)
Subscribers
fgiunchedi
// Copyright 2017 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !noqdisc
package
collector
/*
#include <stdlib.h>
#include <net/if.h>
*/
import
"C"
import
"unsafe"
import
(
"fmt"
"math"
"github.com/mdlayher/netlink"
"github.com/mdlayher/netlink/nlenc"
"github.com/prometheus/client_golang/prometheus"
)
const
(
TCA_UNSPEC
=
iota
TCA_KIND
TCA_OPTIONS
TCA_STATS
TCA_XSTATS
TCA_RATE
TCA_FCNT
TCA_STATS2
TCA_STAB
__TCA_MAX
)
const
(
TCA_STATS_UNSPEC
=
iota
TCA_STATS_BASIC
TCA_STATS_RATE_EST
TCA_STATS_QUEUE
TCA_STATS_APP
TCA_STATS_RATE_EST64
__TCA_STATS_MAX
)
// See struct tc_stats in /usr/include/linux/pkt_sched.h
type
TC_Stats
struct
{
Bytes
uint64
Packets
uint32
Drops
uint32
Overlimits
uint32
Bps
uint32
Pps
uint32
Qlen
uint32
Backlog
uint32
}
// See /usr/include/linux/gen_stats.h
type
TC_Stats2
struct
{
// struct gnet_stats_basic
Bytes
uint64
Packets
uint32
// struct gnet_stats_queue
Qlen
uint32
Backlog
uint32
Drops
uint32
Requeues
uint32
Overlimits
uint32
}
type
Metric
struct
{
IfaceIdx
uint32
Parent
uint32
Handle
uint32
Kind
string
Bytes
uint64
Packets
uint32
Drops
uint32
Requeues
uint32
Overlimits
uint32
}
// See if_indextoname(3)
func
ifIndexToName
(
index
uint32
)
string
{
var
name
string
cName
:=
C
.
CString
(
name
)
defer
C
.
free
(
unsafe
.
Pointer
(
cName
))
ret
:=
C
.
if_indextoname
(
C
.
uint
(
index
),
cName
)
return
C
.
GoString
(
ret
)
}
func
parseTCAStats
(
attr
netlink
.
Attribute
)
TC_Stats
{
var
stats
TC_Stats
stats
.
Bytes
=
nlenc
.
Uint64
(
attr
.
Data
[
0
:
8
])
stats
.
Packets
=
nlenc
.
Uint32
(
attr
.
Data
[
8
:
12
])
stats
.
Drops
=
nlenc
.
Uint32
(
attr
.
Data
[
12
:
16
])
stats
.
Overlimits
=
nlenc
.
Uint32
(
attr
.
Data
[
16
:
20
])
stats
.
Bps
=
nlenc
.
Uint32
(
attr
.
Data
[
20
:
24
])
stats
.
Pps
=
nlenc
.
Uint32
(
attr
.
Data
[
24
:
28
])
stats
.
Qlen
=
nlenc
.
Uint32
(
attr
.
Data
[
28
:
32
])
stats
.
Backlog
=
nlenc
.
Uint32
(
attr
.
Data
[
32
:
36
])
return
stats
}
func
parseTCAStats2
(
attr
netlink
.
Attribute
)
TC_Stats2
{
var
stats
TC_Stats2
nested
,
_
:=
netlink
.
UnmarshalAttributes
(
attr
.
Data
)
for
_
,
a
:=
range
nested
{
switch
a
.
Type
{
case
TCA_STATS_BASIC
:
stats
.
Bytes
=
nlenc
.
Uint64
(
a
.
Data
[
0
:
8
])
stats
.
Packets
=
nlenc
.
Uint32
(
a
.
Data
[
8
:
12
])
case
TCA_STATS_QUEUE
:
stats
.
Qlen
=
nlenc
.
Uint32
(
a
.
Data
[
0
:
4
])
stats
.
Backlog
=
nlenc
.
Uint32
(
a
.
Data
[
4
:
8
])
stats
.
Drops
=
nlenc
.
Uint32
(
a
.
Data
[
8
:
12
])
stats
.
Requeues
=
nlenc
.
Uint32
(
a
.
Data
[
12
:
16
])
stats
.
Overlimits
=
nlenc
.
Uint32
(
a
.
Data
[
16
:
20
])
default
:
// TODO: TCA_STATS_APP
}
}
return
stats
}
func
getQdiscMsgs
()
([]
netlink
.
Message
,
error
)
{
// Speak to route netlink using netlink
const
familyRoute
=
0
c
,
err
:=
netlink
.
Dial
(
familyRoute
,
nil
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to dial netlink: %v"
,
err
)
}
defer
c
.
Close
()
req
:=
netlink
.
Message
{
Header
:
netlink
.
Header
{
Flags
:
netlink
.
HeaderFlagsRequest
|
netlink
.
HeaderFlagsDump
,
Type
:
38
,
// RTM_GETQDISC
},
Data
:
[]
byte
{
0
},
}
// Perform a request, receive replies, and validate the replies
msgs
,
err
:=
c
.
Execute
(
req
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to execute request: %v"
,
err
)
}
return
msgs
,
nil
}
// See https://tools.ietf.org/html/rfc3549#section-3.1.3
func
parseMessage
(
msg
netlink
.
Message
)
(
Metric
,
error
)
{
var
m
Metric
var
s
TC_Stats
var
s2
TC_Stats2
/*
struct tcmsg {
unsigned char tcm_family;
unsigned char tcm__pad1;
unsigned short tcm__pad2;
int tcm_ifindex;
__u32 tcm_handle;
__u32 tcm_parent;
__u32 tcm_info;
};
*/
m
.
IfaceIdx
=
nlenc
.
Uint32
(
msg
.
Data
[
4
:
8
])
m
.
Handle
=
nlenc
.
Uint32
(
msg
.
Data
[
8
:
12
])
m
.
Parent
=
nlenc
.
Uint32
(
msg
.
Data
[
12
:
16
])
if
m
.
Parent
==
math
.
MaxUint32
{
m
.
Parent
=
0
}
// The first 20 bytes are taken by tcmsg
attrs
,
err
:=
netlink
.
UnmarshalAttributes
(
msg
.
Data
[
20
:])
if
err
!=
nil
{
return
m
,
fmt
.
Errorf
(
"failed to unmarshal attributes: %v"
,
err
)
}
for
_
,
attr
:=
range
attrs
{
switch
attr
.
Type
{
case
TCA_KIND
:
m
.
Kind
=
nlenc
.
String
(
attr
.
Data
)
case
TCA_STATS2
:
s2
=
parseTCAStats2
(
attr
)
m
.
Bytes
=
s2
.
Bytes
m
.
Packets
=
s2
.
Packets
m
.
Drops
=
s2
.
Drops
// requeues only available in TCA_STATS2, not in TCA_STATS
m
.
Requeues
=
s2
.
Requeues
m
.
Overlimits
=
s2
.
Overlimits
case
TCA_STATS
:
// Legacy
s
=
parseTCAStats
(
attr
)
m
.
Bytes
=
s
.
Bytes
m
.
Packets
=
s
.
Packets
m
.
Drops
=
s
.
Drops
m
.
Overlimits
=
s
.
Overlimits
default
:
// TODO: TCA_OPTIONS and TCA_XSTATS
}
}
return
m
,
nil
}
type
qdiscStatCollector
struct
{
bytes
typedDesc
pkts
typedDesc
drops
typedDesc
requeues
typedDesc
overlimits
typedDesc
}
func
init
()
{
Factories
[
"qdisc"
]
=
NewQdiscStatCollector
}
// NewQdiscStatCollector returns a new Collector exposing network stats.
func
NewQdiscStatCollector
()
(
Collector
,
error
)
{
return
&
qdiscStatCollector
{
bytes
:
typedDesc
{
prometheus
.
NewDesc
(
prometheus
.
BuildFQName
(
Namespace
,
"qdisc"
,
"bytes"
),
"Number of bytes sent."
,
[]
string
{
"iface"
,
"kind"
},
nil
,
),
prometheus
.
CounterValue
},
pkts
:
typedDesc
{
prometheus
.
NewDesc
(
prometheus
.
BuildFQName
(
Namespace
,
"qdisc"
,
"pkts"
),
"Number of packets sent."
,
[]
string
{
"iface"
,
"kind"
},
nil
,
),
prometheus
.
CounterValue
},
drops
:
typedDesc
{
prometheus
.
NewDesc
(
prometheus
.
BuildFQName
(
Namespace
,
"qdisc"
,
"drops"
),
"Number of packets sent."
,
[]
string
{
"iface"
,
"kind"
},
nil
,
),
prometheus
.
CounterValue
},
requeues
:
typedDesc
{
prometheus
.
NewDesc
(
prometheus
.
BuildFQName
(
Namespace
,
"qdisc"
,
"requeues"
),
"Number of packets sent."
,
[]
string
{
"iface"
,
"kind"
},
nil
,
),
prometheus
.
CounterValue
},
overlimits
:
typedDesc
{
prometheus
.
NewDesc
(
prometheus
.
BuildFQName
(
Namespace
,
"qdisc"
,
"overlimits"
),
"Number of packets sent."
,
[]
string
{
"iface"
,
"kind"
},
nil
,
),
prometheus
.
CounterValue
},
},
nil
}
func
(
c
*
qdiscStatCollector
)
Update
(
ch
chan
<-
prometheus
.
Metric
)
error
{
msgs
,
err
:=
getQdiscMsgs
()
if
err
!=
nil
{
return
err
}
for
_
,
msg
:=
range
msgs
{
m
,
err
:=
parseMessage
(
msg
)
if
err
!=
nil
{
return
err
}
// Only report root qdisc info
if
m
.
Parent
!=
0
{
continue
}
ifname
:=
ifIndexToName
(
m
.
IfaceIdx
)
ch
<-
c
.
bytes
.
mustNewConstMetric
(
float64
(
m
.
Bytes
),
ifname
,
m
.
Kind
)
ch
<-
c
.
pkts
.
mustNewConstMetric
(
float64
(
m
.
Packets
),
ifname
,
m
.
Kind
)
ch
<-
c
.
drops
.
mustNewConstMetric
(
float64
(
m
.
Drops
),
ifname
,
m
.
Kind
)
ch
<-
c
.
requeues
.
mustNewConstMetric
(
float64
(
m
.
Requeues
),
ifname
,
m
.
Kind
)
ch
<-
c
.
overlimits
.
mustNewConstMetric
(
float64
(
m
.
Overlimits
),
ifname
,
m
.
Kind
)
//fmt.Printf("%+v\n", m)
}
return
nil
}
Event Timeline
•
ema
created this paste.
May 9 2017, 2:49 PM
2017-05-09 14:49:01 (UTC+0)
fgiunchedi
subscribed.
May 9 2017, 3:00 PM
2017-05-09 15:00:40 (UTC+0)
Log In to Comment