Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
B
bcc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
bcc
Commits
7679a2e0
Commit
7679a2e0
authored
Jun 16, 2015
by
4ast
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #63 from iovisor/bblanco_dev
Add example for BPF+tc rate limiting
parents
7d61e62e
a5f3eda7
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
186 additions
and
0 deletions
+186
-0
examples/tc_neighbor_sharing.c
examples/tc_neighbor_sharing.c
+61
-0
examples/tc_neighbor_sharing.py
examples/tc_neighbor_sharing.py
+125
-0
No files found.
examples/tc_neighbor_sharing.c
0 → 100644
View file @
7679a2e0
// Copyright (c) PLUMgrid, Inc.
// Licensed under the Apache License, Version 2.0 (the "License")
#include <bcc/proto.h>
struct
ipkey
{
u32
client_ip
;
};
BPF_TABLE
(
"hash"
,
struct
ipkey
,
int
,
learned_ips
,
1024
);
// trivial action
int
pass
(
struct
__sk_buff
*
skb
)
{
return
1
;
}
// Process each wan packet, and determine if the packet is in the IP
// table or not. Learned IPs are rate-limited and unclassified are not.
// returns: > 0 when an IP is known
// = 0 when an IP is not known, or non-IP traffic
int
classify_wan
(
struct
__sk_buff
*
skb
)
{
BEGIN
(
ethernet
);
PROTO
(
ethernet
)
{
switch
(
ethernet
->
type
)
{
case
0x0800
:
goto
ip
;
}
goto
EOP
;
}
PROTO
(
ip
)
{
u32
dip
=
ip
->
dst
;
struct
ipkey
key
=
{.
client_ip
=
dip
};
int
*
val
=
learned_ips
.
lookup
(
&
key
);
if
(
val
)
return
*
val
;
goto
EOP
;
}
EOP:
return
0
;
}
// Process each neighbor packet, and store the source IP in the learned table.
// Mark the inserted entry with a non-zero value to be used by the classify_wan
// lookup.
int
classify_neighbor
(
struct
__sk_buff
*
skb
)
{
BEGIN
(
ethernet
);
PROTO
(
ethernet
)
{
switch
(
ethernet
->
type
)
{
case
0x0800
:
goto
ip
;
}
goto
EOP
;
}
PROTO
(
ip
)
{
u32
sip
=
ip
->
src
;
struct
ipkey
key
=
{.
client_ip
=
sip
};
int
val
=
1
;
learned_ips
.
update
(
&
key
,
&
val
);
goto
EOP
;
}
EOP:
return
1
;
}
examples/tc_neighbor_sharing.py
0 → 100755
View file @
7679a2e0
#!/usr/bin/env python
# Copyright (c) PLUMgrid, Inc.
# Licensed under the Apache License, Version 2.0 (the "License")
# This example shows how a combination of BPF programs can be used to perform
# per-IP classification and rate limiting. The simulation in this example
# shows an example where N+M devices are combined and use 1 WAN. Traffic sent
# from/to the "neighbor" devices have their combined bandwidth capped at
# 128kbit, and the rest of the traffic can use an additional 1Mbit.
# This works by sharing a map between various tc ingress filters, each with
# a related set of bpf functions attached. The map stores a list of dynamically
# learned ip addresses that were seen on the neighbor devices and should be
# throttled.
# /------------\ |
# neigh1 --|->->->->->->->-| | |
# neigh2 --|->->->->->->->-| <-128kb-| /------\ |
# neigh3 --|->->->->->->->-| | wan0 | wan | |
# | ^ | br100 |-<-<-<--| sim | |
# | clsfy_neigh() | | ^ \------/ |
# lan1 ----|->->->->->->->-| <--1Mb--| | |
# lan2 ----|->->->->->->->-| | classify_wan() |
# ^ \------------/ |
# pass() |
from
bpf
import
BPF
from
pyroute2
import
IPRoute
,
NetNS
,
IPDB
,
NSPopen
import
sys
from
time
import
sleep
ipr
=
IPRoute
()
ipdb
=
IPDB
(
nl
=
ipr
)
b
=
BPF
(
src_file
=
"tc_neighbor_sharing.c"
,
debug
=
0
)
wan_fn
=
b
.
load_func
(
"classify_wan"
,
BPF
.
SCHED_CLS
)
pass_fn
=
b
.
load_func
(
"pass"
,
BPF
.
SCHED_CLS
)
neighbor_fn
=
b
.
load_func
(
"classify_neighbor"
,
BPF
.
SCHED_CLS
)
num_neighbors
=
3
num_locals
=
2
# class to build the simulation network
class
SharedNetSimulation
(
object
):
def
__init__
(
self
):
self
.
ipdbs
=
[]
self
.
namespaces
=
[]
self
.
processes
=
[]
# Create the wan namespace, and attach an ingress filter for throttling
# inbound (download) traffic
(
self
.
wan
,
wan_if
)
=
self
.
_create_ns
(
"wan0"
,
"172.16.1.5/24"
,
None
)
ipr
.
tc
(
"add"
,
"ingress"
,
wan_if
[
"index"
],
"ffff:"
)
ipr
.
tc
(
"add-filter"
,
"bpf"
,
wan_if
[
"index"
],
":1"
,
fd
=
wan_fn
.
fd
,
prio
=
1
,
name
=
wan_fn
.
name
,
parent
=
"ffff:"
,
action
=
"drop"
,
classid
=
1
,
rate
=
"128kbit"
,
burst
=
1024
*
32
,
mtu
=
16
*
1024
)
ipr
.
tc
(
"add-filter"
,
"bpf"
,
wan_if
[
"index"
],
":2"
,
fd
=
pass_fn
.
fd
,
prio
=
2
,
name
=
pass_fn
.
name
,
parent
=
"ffff:"
,
action
=
"drop"
,
classid
=
2
,
rate
=
"1024kbit"
,
burst
=
1024
*
32
,
mtu
=
16
*
1024
)
self
.
wan_if
=
wan_if
# helper function to create a namespace and a veth connecting it
def
_create_ns
(
self
,
name
,
ipaddr
,
fn
):
ns_ipdb
=
IPDB
(
nl
=
NetNS
(
name
))
ipdb
.
create
(
ifname
=
"%sa"
%
name
,
kind
=
"veth"
,
peer
=
"%sb"
%
name
).
commit
()
with
ipdb
.
interfaces
[
"%sb"
%
name
]
as
v
:
# move half of veth into namespace
v
.
net_ns_fd
=
ns_ipdb
.
nl
.
netns
with
ipdb
.
interfaces
[
"%sa"
%
name
]
as
v
:
v
.
up
()
with
ns_ipdb
.
interfaces
[
"%sb"
%
name
]
as
v
:
v
.
ifname
=
"eth0"
v
.
add_ip
(
"%s"
%
ipaddr
)
v
.
up
()
ifc
=
ipdb
.
interfaces
[
"%sa"
%
name
]
if
fn
:
ipr
.
tc
(
"add"
,
"ingress"
,
ifc
[
"index"
],
"ffff:"
)
ipr
.
tc
(
"add-filter"
,
"bpf"
,
ifc
[
"index"
],
":1"
,
fd
=
fn
.
fd
,
name
=
fn
.
name
,
parent
=
"ffff:"
,
action
=
"ok"
,
classid
=
1
)
self
.
ipdbs
.
append
(
ns_ipdb
)
self
.
namespaces
.
append
(
ns_ipdb
.
nl
)
cmd
=
[
"netserver"
,
"-D"
]
self
.
processes
.
append
(
NSPopen
(
ns_ipdb
.
nl
.
netns
,
cmd
))
return
(
ns_ipdb
,
ifc
)
# start the namespaces that compose the network, interconnect them with the bridge,
# and attach the tc filters
def
start
(
self
):
neighbor_list
=
[]
local_list
=
[]
for
i
in
range
(
0
,
num_neighbors
):
neighbor_list
.
append
(
self
.
_create_ns
(
"neighbor%d"
%
i
,
"172.16.1.%d/24"
%
(
i
+
100
),
neighbor_fn
))
for
i
in
range
(
0
,
num_locals
):
local_list
.
append
(
self
.
_create_ns
(
"local%d"
%
i
,
"172.16.1.%d/24"
%
(
i
+
150
),
pass_fn
))
with
ipdb
.
create
(
ifname
=
"br100"
,
kind
=
"bridge"
)
as
br100
:
for
x
in
neighbor_list
:
br100
.
add_port
(
x
[
1
])
for
x
in
local_list
:
br100
.
add_port
(
x
[
1
])
br100
.
add_port
(
self
.
wan_if
)
br100
.
up
()
def
release
(
self
):
for
p
in
self
.
processes
:
p
.
kill
();
p
.
release
()
for
db
in
self
.
ipdbs
:
db
.
release
()
for
ns
in
self
.
namespaces
:
ns
.
remove
()
try
:
sim
=
SharedNetSimulation
()
sim
.
start
()
print
(
"Network ready. Create a shell in the wan0 namespace and test with netperf"
)
print
(
" (Neighbors are 172.16.1.100-%d, and LAN clients are 172.16.1.150-%d)"
%
(
100
+
num_neighbors
-
1
,
150
+
num_locals
-
1
))
print
(
" e.g.: ip netns exec wan0 netperf -H 172.16.1.100 -l 2"
)
input
(
"Press enter when finished: "
)
finally
:
if
"sim"
in
locals
():
sim
.
release
()
if
"br100"
in
ipdb
.
interfaces
:
ipdb
.
interfaces
.
br100
.
remove
().
commit
()
sleep
(
2
)
ipdb
.
release
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment