3 个版本 (重大更新)
0.6.0 | 2022年11月9日 |
---|---|
0.5.0 | 2021年7月15日 |
0.4.0 | 2018年10月30日 |
#10 in #control-plane
300KB
7K SLoC
pyportus
本模块提供了对Portus CCP实现的Python接口。
设置
这些Python绑定可在PyPI上找到,使用命令pip install pyportus
安装。
手动构建时,本项目使用maturin。
测试
- 创建虚拟环境
maturin develop
安装
maturin build
pip install
生成的.whl文件
然后运行/测试,执行命令cd examples && ./<venv>/python3 aimd.py
(可能需要以root身份运行)
编写算法
概述
Portus中的算法由一个Python类表示,该类的实例表示单个TCP流。对于每个流创建一个新实例。
此类必须是portus.AlgBase
的子类,并必须实现以下两个方法签名:
on_create(self)
on_report(self,r)
r
是一个包含您数据路径程序中定义的所有字段以及当前Cwnd
和Rate
的报告对象。假设您的程序只定义了一个变量:(def (acked 0))
,其中acked
是自上次报告以来累加的总字节数。这个值可以通过r.acked
来访问。同样,您可以像访问 cwnd 或 rate 一样访问它,即r.Cwnd
和r.Rate
(注意大小写重要)。
类的每个实例都会自动在 self 中包含两个字段
self.datapath
是指向数据路径对象的指针,可以用来安装新的数据路径程序。它有两个可用方法datapath.install( str )
,它接受一个字符串作为数据路径程序。它会编译程序并将其安装到数据路径中。它不会返回任何内容,但如果您的程序无法编译,可能会引发异常。datapath.update_field(field, val)
,它接受数据路径程序Report
范围中的一个变量,并将其值设置为val
。例如,要仅更新 cwnd,您可以使用datapath.update_field("Cwnd", 10000)
(注意:cwnd 以字节为单位表示,而不是数据包)。
self.datapath_info
是一个结构体,包含有关数据路径中此特定流字段的详细信息(例如,在on_create
中,可以根据数据路径的mss
设置初始 cwnd)sock_id
:此流在数据路径中的唯一标识符init_cwnd
:此流在您设置之前将具有的初始拥塞窗口src_ip
、src_port
、dst_ip
、dst_port
:流的数据源和目的地的 IP 地址和端口
数据路径程序
数据路径程序用于(1)定义要发送回您的用户空间程序的统计信息以及发送的频率,以及(2)设置拥塞窗口和/或速率。数据路径程序是用一个非常简单的类似于 Lisp 的方言编写的,由一个变量定义行后跟任意数量的 when 子句组成
(def ( ... ) ( ... ))
(when (event) (
do_stuff ...
)
(when (other_event) (
do_other_stuff ...
)
注意:以下信息已过时,因为数据路径程序 API 已更新
更新
1. 报告变量定义
示例:(def (Report.acked 0) (Report.rtt 0) (Report.timeout false))
此行定义了报告范围内的变量名称及其初始值。在数据路径程序中调用 (report)
会导致调用算法的 on_report
函数,并传递这些变量的当前值。 调用后,这些变量将重置为其初始值。
注意:数据路径程序中的变量以 {scope}.{name}
的形式书写。例如,在 Report
范围内的 acked
变量被写成 Report.acked
。因此,此行定义的所有变量都必须 以 Report.
开头。然而,当你在 on_report
中访问它们时,只需提供变量名。在我们的示例中,Report.rtt
在 Report
范围内定义了变量 rtt
。如果我们想在 on_report(r)
中访问此值,我们将使用 r.rtt
(即 不是 r.Report.rtt
)。
2. 当子句
当子句由一个布尔表达式和一组指令组成。在每次确认后,数据路径会检查布尔表达式,如果它评估为 true
,则运行指令集。例如,以下当子句会在每个 rtt 后发送报告(即调用 on_report
函数)。
(when (> Micros Flow.rtt_sample_us)
(report)
)
综合起来
一个示例算法定义,显示完整的 API
import portus
# Class must sublcass portus.AlgBase
class SampleCCAlg(portus.AlgBase):
# Init must take exactly these parameters
def __init__(self, datapath, datapath_info):
# Store a copy of the datapath and info for later
self.datapath = datapath
self.datapath_info = datapath_info
# Internally store an initial cwnd value
self.cwnd = 10 * self.datapath_info.mss
# Install an initial datapath program to keep track of the RTT and report it once per RTT
# The first when clause is true on every single ack,
# which means the 'Report.rtt' field will always keep the latest rtt sample
# The second when clause is true once one rtt's worth of time has passed,
# at which point it will trigger on_report, and Micros (and Report.rtt) will be reset to 0
self.datapath.install("""\
(def
(Report.rtt 0)
)
(when true
(:= Report.rtt Flow.rtt_sample_us)
(fallthrough)
)
(when (> Micros Flow.rtt_sample_us)
(report)
)
""")
# This function will be called once per RTT, and the report struct `r` will contain:
# "rtt", "Cwnd", and "Rate"
def on_report(self, r):
# Compute new cwnd internally
# If the rtt has decreased, increase the cwnd by 1 packet, else decrease by 1 packet
if self.last_rtt < r.rtt:
self.cwnd += self.datapath_info.mss
else:
self.cwnd -= self.datapath_info.mss
self.last_rtt = r.rtt
# Send this new value of cwnd to the datapath
self.datapath.update_field("Cwnd", self.cwnd)
重要提示
- 您应该在您的
__init__
实现中安装一个初始数据路径程序,否则您将不会收到任何报告,也不会发生任何事情。您可以在处理on_report
时稍后安装不同的数据路径程序。 - 如果您想打印任何内容,应使用
sys.stderr.write()
(注意您需要import sys
,并且它不会像print
那样自动添加新行)。 - 您 必须 在
self
中存储对datapath
的引用,名为 "datapath"(即self.datapath = datapath
),因为库内部使用它来访问数据路径结构。
启动 CCP
CCP 的入口点是 portus.connect(ipc_type, class, debug, blocking)
ipc_type (string)
:在 Linux 上为(netlink | unix | char),在 macOS 上为(unix)class
:您的算法类,例如SampleCCAlg
debug (bool)
:如果为 true,则 CCP 将记录 ccp 和数据路径之间传递的所有消息blocking (bool)
:如果为 true,则使用阻塞 ipc 读取,否则使用非阻塞
例如: portus.connect("netlink", SampleCCAlg, debug=True, blocking=True)
。
无论您使用阻塞或非阻塞套接字,connect
都将永久阻塞(要停止CCP,请发送ctrl+c或终止进程)。
示例
有关定义算法和运行CCP的完整示例,请参阅./aimd.py
中的简单AIMD方案,并尝试运行它: sudo python aimd.py
。
依赖项
~10–16MB
~218K SLoC