1、有使用的例子吗
FrelDX opened this issue · 30 comments
我想实现的功能,从host1 通过xdp的方式发送数据包到host2.我看了一下你的库好像更适合我的需求,我也是从xdp的个库然后看到这个项目的。
目前XDPRelay的实现还不是很理想,性能没有比RawSocketRelay好多少,我还得再想想有没有更好地实现方式;你可以就用RawSocketRelay(其用的是AF_PACKET socket),性能也不错的,Doc里有例子的;
原始的socket应该比xdp要慢很多吧??
就目前的实现来说,两者差别不是很大,尤其是你如果只是用来发的话;AF_Packet和XDP都跳过了kernel大部分的网络处理; XDP目前主要的优势还是在ingress方向
你可以先用RawSocketRelay试试,测一下, 看是否能满足你性能的要求;XDP的我还得再改改
嗯嗯。我目前在测试RawSocketRelay,我之前使用udp传输,发现性能瓶颈才调研 dpdk和xdp的,,谢谢。
好像用起来有一点问题。
package main
import (
"context"
"fmt"
"net"
"time"
"github.com/hujun-open/etherconn"
)
func main() {
node1Mac, _ := net.ParseMAC("00:0c:29:7f:4a:89")
node2Mac, _ := net.ParseMAC("00:0c:29:f0:60:8d")
r, _ := etherconn.NewRawSocketRelay(context.Background(), "ens33")
econnA := etherconn.NewEtherConn(node1Mac, r)
for {
time.Sleep(time.Duration(5) * time.Second)
n, err := econnA.WritePktTo([]byte{'h', 'e', 'l', 'l', 'o'}, 1, node2Mac)
if err != nil {
panic(err)
}
fmt.Printf("发送了%d个字节\n", n)
fmt.Println([]byte{'h', 'e', 'l', 'l', 'o'})
}
}
这是发送包的节点
运行代码输出
[root@node01 c]# ./c
发送了5个字节
[104 101 108 108 111]
发送了5个字节
[104 101 108 108 111]
发送了5个字节
[104 101 108 108 111]
发送了5个字节
[104 101 108 108 111]
发送了5个字节
[104 101 108 108 111]
发送了5个字节
[104 101 108 108 111]
发送了5个字节
[104 101 108 108 111]
发送了5个字节
[104 101 108 108 111]
发送了5个字节
[104 101 108 108 111]
发送了5个字节
[104 101 108 108 111]
发送了5个字节
[104 101 108 108 111]
发送了5个字节
[104 101 108 108 111]
发送了5个字节
[104 101 108 108 111]
下面是接收包节点的代码
package main
import (
"context"
"fmt"
"net"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/hujun-open/etherconn"
)
func main() {
//node1Mac, _ := net.ParseMAC("00:0c:29:7f:4a:89")
node2Mac, _ := net.ParseMAC("00:0c:29:f0:60:8d")
r, _ := etherconn.NewRawSocketRelay(context.Background(), "ens33")
econnb := etherconn.NewEtherConn(node2Mac, r)
for {
time.Sleep(time.Duration(5) * time.Second)
data, mac, err := econnb.ReadPkt()
if err != nil {
panic(err)
}
fmt.Println(string(data))
fmt.Println(data)
fmt.Println(mac)
fmt.Printf("接收到来自%s 的%d个字节\n", mac, len(data))
s := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default)
d := s.Data()
fmt.Println(d)
}
}
运行代码输出
[69 0 0 52 196 234 64 0 64 6 193 122 192 168 153 134 192 168 153 135 9 76 192 224 76 238 53 167 201 10 147 13 128 17 1 251 133 242 0 0 1 1 8 10 221 186 44 4 0 0 135 214]
00:0c:29:7f:4a:89
接收到来自00:0c:29:7f:4a:89 的52个字节
[69 0 0 52 196 234 64 0 64 6 193 122 192 168 153 134 192 168 153 135 9 76 192 224 76 238 53 167 201 10 147 13 128 17 1 251 133 242 0 0 1 1 8 10 221 186 44 4 0 0 135 214]
E��<��@�@�]������ L�iq��B������
����
ݺ?'����
[69 0 0 60 0 0 64 0 64 6 134 93 192 168 153 134 192 168 153 135 9 76 193 187 105 113 173 162 66 153 172 170 160 18 254 136 11 205 0 0 2 4 5 180 4 2 8 10 221 186 63 39 0 0 154 249 1 3 3 7]
00:0c:29:7f:4a:89
接收到来自00:0c:29:7f:4a:89 的60个字节
[69 0 0 60 0 0 64 0 64 6 134 93 192 168 153 134 192 168 153 135 9 76 193 187 105 113 173 162 66 153 172 170 160 18 254 136 11 205 0 0 2 4 5 180 4 2 8 10 221 186 63 39 0 0 154 249 1 3 3 7]
^C
[root@node02 opt]#
从结果来看,接收端好像没有正确的收到[104 101 108 108 111](hello)
还有一个疑问,为什么是以太层的数据 接受端里面会有 192 168 153 134 192 168 153 135 这种ip地址? 这个应该不是发送端发过来的吧?
node01 为发送端 mac 0:0c:29:7f:4a:89
node02是接收端 mac 00:0c:29:f0:60:8d
我不知道,是否在创建连接的时候mac地址传错了
一个relay上面可以有很多个EtherConn,每个EtherConn由(MAC+EtherType)来区分,当relay收到一个以太网包的时候,它会根据其目的MAC地址和EtheType来决定发给哪一个EtherConn;
当你创建一个新的EtherCOnn,缺省的接受的EtherType(DefaultEtherTypes )包括ARP, IPv4 和IPv6;但是你的发送程序econnA.WritePktTo([]byte{'h', 'e', 'l', 'l', 'o'}, 1, node2Mac)
用了1作为发送包的EtherType,这样的话自然接收方就收不到发送方发的东西因为EtherType 1不在接收方的EtherType list里;要修正的话,在接收方code创建EtherConn,需要指定接收方的EtherType为1,用这个econnb := etherconn.NewEtherConn(node2Mac, r, etherconn.WithEtherTypes([]uint16{1}))
你现在在接收方收到的包都是一些其他发送到这个MAC地址的ARP,IPv4,IPv6的包;
其实MAC+EtherType在doc里面都有提到,你要仔细读文档
我重试了很多次,还是收到不到包
代码如下
package main
import (
"context"
"fmt"
"net"
"time"
"github.com/hujun-open/etherconn"
)
func main() {
node1Mac, _ := net.ParseMAC("00:0c:29:7f:4a:89")
node2Mac, _ := net.ParseMAC("00:0c:29:f0:60:8d")
r, _ := etherconn.NewRawSocketRelay(context.Background(), "ens33")
econnA := etherconn.NewEtherConn(node1Mac, r, etherconn.WithEtherTypes([]uint16{1}))
for {
time.Sleep(time.Duration(5) * time.Second)
n, err := econnA.WritePktTo([]byte{'h', 'e', 'l', 'l', 'o'}, 1, node2Mac)
if err != nil {
panic(err)
}
fmt.Printf("发送了%d个字节\n", n)
}
}
这个是发送端
发送端的mac地址00:0c:29:7f:4a:89
接受端00:0c:29:f0:60:8d
package main
import (
"context"
"fmt"
"net"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/hujun-open/etherconn"
)
func main() {
//node1Mac, _ := net.ParseMAC("00:0c:29:7f:4a:89")
node2Mac, _ := net.ParseMAC("00:0c:29:f0:60:8d")
r, _ := etherconn.NewRawSocketRelay(context.Background(), "ens33")
econnb := etherconn.NewEtherConn(node2Mac, r, etherconn.WithEtherTypes([]uint16{1}))
for {
data := []byte{}
n, mac, err := econnb.ReadPktFrom(data)
if err != nil {
panic(err)
}
fmt.Println(n)
fmt.Println(data)
fmt.Println(mac)
fmt.Printf("接收到来自%s 的%d个字节\n", mac, len(data))
s := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default)
d := s.Data()
fmt.Println(d)
d, nn, err := econnb.ReadPkt()
if err != nil {
panic(err)
}
fmt.Println(d)
fmt.Println(nn)
}
}
这个是接受端的
还是无法接受到发送端发过来的hello
不过当指定了etherconn.WithEtherTypes([]uint16{1}后,就收不到任何的数据包了
和系统有关系吗,
在vm环境下,两台centos7 的主机。
你可以在接收方用tcpdump之类的抓一下包,看看有没有发过来,如果没有的话,有如下可能原因:
- 你用了个非标准的ethertype 1,中间的交换机可能会drop这种非标准的包
- 你只发了5个字节太少了,以太网有最小包长度64字节的限制,交换机也可能会drop掉,多发点东西
好的我继续测试一下。。
package main
import (
"context"
"fmt"
"net"
"time"
"github.com/hujun-open/etherconn"
)
func main() {
node1Mac, _ := net.ParseMAC("00:0c:29:7f:4a:89")
node2Mac, _ := net.ParseMAC("00:0c:29:f0:60:8d")
r, _ := etherconn.NewRawSocketRelay(context.Background(), "ens33")
econnA := etherconn.NewEtherConn(node1Mac, r, etherconn.WithEtherTypes([]uint16{0x8100}))
for {
time.Sleep(time.Duration(5) * time.Second)
str := "hello world,hello world,hello world,hello world,hello world,hello world,hello world,hello world,hello world,hello world,hello world"
n, err := econnA.WritePktTo([]byte(str), 0x8100, node2Mac)
if err != nil {
panic(err)
}
fmt.Printf("发送了%d个字节\n", n)
}
}
package main
import (
"context"
"fmt"
"net"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/hujun-open/etherconn"
)
func main() {
//node1Mac, _ := net.ParseMAC("00:0c:29:7f:4a:89")
node2Mac, _ := net.ParseMAC("00:0c:29:f0:60:8d")
r, _ := etherconn.NewRawSocketRelay(context.Background(), "ens33")
econnb := etherconn.NewEtherConn(node2Mac, r, etherconn.WithEtherTypes([]uint16{0x8100}))
for {
data := []byte{}
n, mac, err := econnb.ReadPktFrom(data)
if err != nil {
panic(err)
}
fmt.Println(n)
fmt.Println(data)
fmt.Println(mac)
fmt.Printf("接收到来自%s 的%d个字节\n", mac, len(data))
s := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default)
d := s.Data()
fmt.Println(d)
d, nn, err := econnb.ReadPkt()
if err != nil {
panic(err)
}
fmt.Println(d)
fmt.Println(nn)
}
}
代码调整成这样还是收不到包
大佬你那边有没有测试通过的环境信息(系统 内核版本,代码)
你的问题是这次把EtherType又设置成了0x8100,那个是VLAN的Ethertype,这样会导致relay把8100后面的字节当作vlan tag尝试匹配,那自然接收方就会失败;
我把你的code稍微改了改,这个code是在我这里能够运行的(你把interface name再改一下就可以了):
package main
import (
"context"
"fmt"
"net"
"time"
"github.com/hujun-open/etherconn"
)
func main() {
node1Mac, _ := net.ParseMAC("00:0c:29:7f:4a:89")
node2Mac, _ := net.ParseMAC("00:0c:29:f0:60:8d")
const etherType = 0x0800
r, err := etherconn.NewRawSocketRelay(context.Background(), "SIF")
if err != nil {
panic(err)
}
econnA := etherconn.NewEtherConn(node1Mac, r, etherconn.WithEtherTypes([]uint16{etherType}))
for {
time.Sleep(time.Duration(1) * time.Second)
str := "hello world,hello world,hello world,hello world,hello world,hello world,hello world,hello world,hello world,hello world,hello world"
n, err := econnA.WritePktTo([]byte(str), etherType, node2Mac)
if err != nil {
panic(err)
}
fmt.Printf("发送了%d个字节\n", n)
}
}
package main
import (
"context"
"fmt"
"net"
"time"
"github.com/hujun-open/etherconn"
)
func main() {
//node1Mac, _ := net.ParseMAC("00:0c:29:7f:4a:89")
node2Mac, _ := net.ParseMAC("00:0c:29:f0:60:8d")
// it is best to uses a common ethertype like IPv4 here, even if you don't plan to use standard IP packet encap
const etherType = 0x0800
r, err := etherconn.NewRawSocketRelay(context.Background(), "RIF")
if err != nil {
panic(err)
}
go func() {
for {
time.Sleep(time.Second)
fmt.Printf("\nrelay stats:\n%+v\n", r.GetStats())
}
}()
econnb := etherconn.NewEtherConn(node2Mac, r, etherconn.WithEtherTypes([]uint16{etherType}))
//if you want to use ReadPktFrom, then you need to pass in a byte slice with clear max capacity,
// and you should try to reuse the byte slice in the loop
const maxPktSize = 2000
data := make([]byte, maxPktSize)
for {
n, mac, err := econnb.ReadPktFrom(data)
if err != nil {
panic(err)
}
fmt.Printf("接收到来自%s 的%d个字节: %v\n", mac, n, string(data[:n]))
}
}
好的 我试试谢谢大佬。
现在测试正常了,谢谢 还有个问题const maxPktSize = 2000 这里设置了切片的大小为2000 但是econnb.ReadPktFrom(data)返回的n 有时候大于2000导致代码报错。
那说明node2Mac这里收到了大于2000的包,你把maxPktSize设大点就好了;如果node2Mac是网卡的真实MAC那接收方的EtherConn会收到所有发到这个MAC地址的包,这大概不是你想要的,etherconn的一个好处在于你可以使用任意的MAC地址,不一定非要真实的网卡MAC地址,你可以用一些虚构的MAC地址,这样的话就不会收到那些不想收到的包了。
明白了,问一下,这样的话网卡是开启了混杂模式吧?这样是否会影响性能哈?
在我的电脑上面测试,使用ethconn 来传输数据,还没有udp快,udp的延迟1ms左右。ethconn 100ms左右
100ms包传送延迟?在两台直连的电脑之间?那是不可能的,事实上1ms都慢了,你要再检查一下你的环境设置还有你检测延迟的方法
在两台vm虚拟机之间,,,代码从tun设备中读取字节通过你的库发送到另外一台机器的tun设备, 延迟测试的确比使用udp传输慢很多很多。
我这里用RawPacketRelay 能达到130k pps,如果用新的XDPRelay最高可以达到300k pps
可以看下你的测试代码吗
你测试的这个130k pps 是如何测试的哈?
你的同等环境测试过udp吗?我用上面的代码测试,很奇怪udp就是比ethconn快
我刚刚push了一些更新,重写了不少地方,而且还加了个例子 (在example folder),你可以再试试
使用xdp 模式后这台主机就会处于没有网络的状态,xdp模式是否可以支持过滤不是程序需要的包,让数据包继续进入协议栈??其实就是xdp_pass,我目前测试 两台机器可以通过xdp模式通信,但开启后主机就会没有网络
使用xdp 模式后这台主机就会处于没有网络的状态,xdp模式是否可以支持过滤不是程序需要的包,让数据包继续进入协议栈??其实就是xdp_pass,我目前测试 两台机器可以通过xdp模式通信,但开启后主机就会没有网络
etherconn内置的xdp kernel program只是将所有收到的包发给xdp socket,你如果想只发需要的包给xdp socket,需要自己写一个xdp kernel program,在那里面过滤;然后用etherconn.WithXDPExtProg()来加载这个program
目前在用raw,xdp的过滤能否支持一下哈哈,不然好像xdp没有办法使用,etherconn发送数据的时候可以加一个字节表示协议吧,然后xdp过滤的时候根据这个协议来判断数据是否丢给内核协议栈吧???不太了解xdp内核程序的编写哈哈哈哈哈。
发送数据? xdp kernel program目前只能在ingress方向work,egress方向目前如果你用xdp socket发送数据包,这些包会bypass kernel entirely;
目前在用raw,xdp的过滤能否支持一下哈哈,不然好像xdp没有办法使用,etherconn发送数据的时候可以加一个字节表示协议吧,然后xdp过滤的时候根据这个协议来判断数据是否丢给内核协议栈吧???不太了解xdp内核程序的编写哈哈哈哈哈。