BESS【11】Writing a BESS Configuration Script

If you've reached this page, we suspect you've read the BESS Overview and Build and Install BESS. So far, you've configured BESS by telling it to load some built-in BESS sample scripts. This page will tell you how to write your own configuration scripts. If you have any questions or comments, please post them to the BESS issue tracker to get help.

Looking Inside a Script

First let's look at a familiar configuration script: samples/acl. From the bess/ main directory, the file is stored in bess/bessctl/conf/samples/acl.bess; go ahead and open it in your editor of choice (which we presume is vim if you are a cool person).

Here's what you will see:

import scapy.all as scapy
import socket

def aton(ip):
     return socket.inet_aton(ip)

# Craft a packet with the specified IP addresses
def gen_packet(proto, src_ip, dst_ip):
    eth = scapy.Ether(src='02:1e:67:9f:4d:ae', dst='06:16:3e:1b:72:32')
    ip = scapy.IP(src=src_ip, dst=dst_ip)
    udp = proto(sport=10001, dport=10002)
    payload = 'helloworld'
    pkt = eth/ip/udp/payload
    return str(pkt)

packets = [gen_packet(scapy.UDP, '172.16.100.1', '10.0.0.1'),
           gen_packet(scapy.UDP, '172.12.55.99', '12.34.56.78'),
           gen_packet(scapy.UDP, '172.12.55.99', '10.0.0.1'),
           gen_packet(scapy.UDP, '172.16.100.1', '12.34.56.78'),
           gen_packet(scapy.TCP, '172.12.55.99', '12.34.56.78'),
           gen_packet(scapy.UDP, '192.168.1.123', '12.34.56.78'),
          ]

fw::ACL(rules=[{'src_ip': '172.12.0.0/16', 'drop': False}])

Source() -> Rewrite(templates=packets) -> fw -> Sink()

At first glance, a couple of things should become clear to you:

  • BESS scripts are really just Python programs with a few additional features glued in.
    • You can see how these additional features are glued in in bess/bessctl/sugar.py if you are super curious.
    • The key "sugar" features to know about: you can create modules by declaring module objects, you can connect modules to each other (connect their gates) using arrows -> (which represent unidirectional packet flow) and you can give a module a name by assigning a name with :: (like fw::ACL(... in the example above).
  • You can create packets in your script using scapy, which is a convenient packet-manipulation library in Python.
  • You can write functions, create lists, etc. all like in normal Python.

To understand what this script says in particular, really all the action is happening on the line
Source() -> Rewrite(templates=packets) -> fw -> Sink()

Here we have a chain of four modules. Since we are not connected to any network interfaces or applications to give us "real traffic", this example creates all of its own packets using a Source() module. A Source() module creates literally empty packets with no data in them. With a -> we connect the output of Source() to the input of a Rewrite module. Rewrite is a module that takes in a parameter -- a packet "template" -- and Rewrite fills in all the packets that come in to it with a copy of one of the packet templates.

After the packets have been filled in with data, we send the packets to fw, our ACL. Recall above that we named our ACL module fw, so now we can reference it in Python by this name. When we instantiated fw, we gave ACL a parameter as well. By default, ACL drops all packets that comes in. However, we gave it a rule to allow through (drop:False) all packets where src_ip matches 172.12.0.0/16, so packets in this prefix will be allowed through. All of the packets that are allowed through by fw are passed (via another ->) to Sink(), which is just a module that deletes every packet that comes in to it. Once again, normally we would probably pass all of the traffic out on a port to an application or to a network interface, but since this is just an example we will just drop all of the traffic.

What is happening under the hood when I run a script?

Recall from the overview that bessd actually forwards the packets, and bessctl is just a program that tells bessd what to do.

When you declare a module object in Python (e.g., Sink(), fw::ACL(...parameters...)), bessctl tells bessd to create a Sink module inside bessd; this Sink module that runs inside bessd is actually implemented in C++! You can look at all of the implementations of all modules in bess/core/modules. Whenever you write a new module in C++, the bess build script will create corresponding objects in Python for you to use in bessctl. Whatever you tell the Python object to do will be sent to the C++ object in bessd. Similarly, whenever you declare a -> in Python connecting two Python objects, bessd will connect the C++ modules representing those same modules on the dataplane.

All of this magic glue between your Python script and the actual, running C++ code in bessd is achieved using protobufs and grpc.

You can read all of the commands and parameters for all built-in modules here.

Writing a new script

To start your first script, cd into bess/bessctl/conf/. You can create any file in the conf/ directory (or subdirectories of conf/ for it to show up in bessctl. Let's create a file in conf/ called my_script.bess.

touch my_script.bess
cat >my_script.bess
> Source() -> Sink()
Ctrl^C

Ta-da! You have created your simplest, easiest BESS script. It creates a Source module and connects it to a Sink module -- that is, it creates blank packets and then immediately destroys them. How boring!

Open up your script in your editor of choice, and let's replace our Source with a FlowGen, which generates packet by simulating TCP connections. Delete the line that says Source() -> Sink() and replace it with this:

import scapy.all as scapy

pkt_size = int($SN_PKT_SIZE!'60')
assert(60 <= pkt_size <= 1522)

#use scapy to build a packet template
eth = scapy.Ether(src='02:1e:67:9f:4d:ae', dst='06:16:3e:1b:72:32')
ip = scapy.IP(src='10.0.0.1', dst='10.0.0.2')   # dst IP is overwritten
tcp = scapy.TCP(sport=10001, dport=10002)
payload = ('hello' + '0123456789' * 200)[:pkt_size-len(eth/ip/tcp)]
pkt = eth/ip/tcp/payload
pkt_data = str(pkt)

FlowGen(template=pkt_data, pps=1000, flow_rate = 10, flow_duration = 5.0, arrival='uniform', duration='uniform', quick_rampup=True, ip_src_range=100000) -> Sink()

Now we have replaced Source() with a module FlowGen that takes some initialization parameters. These parameters tell the FlowGen to create packets based on a template packet we created, to generate 1000 packets every second, to generate 10 new flows every second, that each flow should last for 5 seconds, that the arrival and duration of flows should be generated with uniform distributions, that the FlowGen should start generating 1000 packets per second from the startup, and that new flows should be generated by modifying the IP source address by changing its value to one of 100,000 values (phew!). Some modules have no parameters (like Source), others have a few required parameters (like FlowGen) and others have some optional parameters.

You can read all of the commands and parameters for all built-in modules here.

You can run this script in bess (run my_script, followed by monitor pipeline) and see packets flowing through it. To see samples of packets running through, we can add another module in between FlowGen and Sink adding in a module -> Dump(interval=1) -> between the two. This module will print out the contents of one packet every 1 second to the BESS log file, which is stored in /tmp/bessd.INFO.

Okay! So far, you have written a new script, used some parameters to configure the modules in the script, and seen how things are logged to /tmp/bessd.INFO.

Using modules with multiple gates

We discussed in the BESS Overview how modules have gates that accept and receive packets.

  • Source and Flowgen have no input gates and one output gates.
  • Sink has one input gate and no output gates.
  • Dump has one input gate and one output gate.

Some modules have more than one input gate or output gates. When this happens, we specify the gates by number. For example, the RoundRobin module can divide packets that come in on one input gate and release them over multiple output gates. Going back to our FlowGen example:

rr::RoundRobin(gates=[0,1]) #create a RoundRobin with two gates
FlowGen(...) -> rr  # push packets from Flowgen to RoundRobin (parameters omitted for brevity)
rr:0 -> Sink()
rr:1 -> Sink()

If you run this new script, you will see that half the packets go out over port 0, and half the packets go out over port 1.

The same syntax works for input ports. The Merge module is just like RoundRobin, only backwards: it takes in packets over multiple input ports and pushes them out over a single output port, e.g.

m::Merge() 
Source() -> 0:m
Source() -> 1:m
Merge -> Sink()

Calling a function on a module

Some modules have functions, which let you update properties of the module after you have initialized them. Let's say we wanted to replace our RoundRobin module with a module that sends packets with even-numbered source addresses out one port, and with odd-numbered source addresses out another port.

#create an ExactMatch module selecting over the last bit in the IP src
em::ExactMatch(fields=[size=1, mask=0x1, offset=29]) 
#create a rule telling ExactMatch to send odd numbered packets out gate 1, and even numbered packets out gate 0.
em.add(fields=[0x1], gate=1)
em.add(fields=[0x0], gate=0)
FlowGen(...) -> em #push packets from Flowgen to ExactMatch (parameters omitted for brevity)
em:0 -> Sink()
em:1 -> Sink()

Here we used the add(...) function from ExactMatch to provide additional information to the ExactMatch module. Some functions even return data from the module, e.g., see the Measure module in the Module documentation.

Metadata attributes vs. Packet Data

Many modules read or write to Packet Data -- e.g., data in the IP header, TCP header, packet payload, etc.
BESS also allows you to attach to packets metadata attributes -- these are pieces of data that are not actually part of the packet (if you push the packet out to the network, these values are deleted) but carried with the packet so long as they stay inside BESS.

Some modules expect to read or write from these metadata attributes. For example the EtherEncap module takes in an IP packet with Ethernet addresses stored in its metadata, and creates an Ethernet header in the packet data using these addresses from the metadata, like this example from bessctl/conf/samples/vxlan.bess:

...
-> SetMetadata(attrs=
        [{'name': 'ether_src', 'size': 6, 'value_bin': '\x02\x01\x02\x03\x04\x05'},
         {'name': 'ether_dst', 'size': 6, 'value_bin': '\x02\x0a\x0b\x0c\x0d\x0e'}]) \
-> EtherEncap()  \
-> ...

Namespace notes

You may have noticed that BESS-script variable names like rr and RoundRobin in rr::RoundRobin(gates=[0,1]) appear out of nowhere. A BESS script acts a lot like a Python module: it has a global name space, pre-populated with all the C++ module classes and port driver classes. Using rr::RoundRobin(...) invokes the existing RoundRobin. It then creates or overwrites the global variable rr, which you can use from then on. For all the Python-specific details, click here.

Check out some more examples for fun

Ta-da! You know all of the core concepts required to develop in BESS! To try out some more advanced techniques or build more complex scripts, we recommend you dig in to the many sample scripts that ship with BESS in bess/bessctl/conf/. And as always, if you have any questions or confusions, feel free to post them in the BESS issue tracker to get help. To deploy your new BESS configuration script, you'll need to connect the script to some ports to a network interface, VM, etc.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343