版本信息:

ODP(Open Data Plane): 1.19.0.2

OFP(Open Fast Path): 3.0.0


1、存在的问题

OpenFastPath作为一个开源的用户态TCP/IP协议栈,其对用户提供的Socket API,无论是宏定义、数据结构还是函数,均以OFP_开头。如下图所示:

 int    ofp_socket(int, int, int);
int ofp_socket_vrf(int, int, int, int);
int ofp_accept(int, struct ofp_sockaddr *, ofp_socklen_t *);
int ofp_bind(int, const struct ofp_sockaddr *, ofp_socklen_t);
int ofp_connect(int, const struct ofp_sockaddr *, ofp_socklen_t);
int ofp_listen(int, int);
int ofp_shutdown(int, int);
int ofp_close(int); struct ofp_timeval {
uint32_t tv_sec; /* seconds */
uint32_t tv_usec; /* microseconds */
}; /*
* Option flags per-socket, kept in so_options.
*/
#define OFP_SO_DEBUG 0x00000001 /* turn on debugging info recording */
#define OFP_SO_ACCEPTCONN 0x00000002 /* socket has had listen() */
#define OFP_SO_REUSEADDR 0x00000004 /* allow local address reuse */
#define OFP_SO_KEEPALIVE 0x00000008 /* keep connections alive */
#define OFP_SO_DONTROUTE 0x00000010 /* just use interface addresses */

这样子的实现,会带来一个问题:即用户之前编写的基于Linux Socket的应用程序(比如用户基于Linux Socket编写的WebServer),如果想移植到OFP上,由于上述符号的差异,将需要大量的修改。 

如何解决这一问题,是本文讨论的话题。

2、OFP提供的解决方案

OFP提供给用户的example中,已经提供了一种可行的方案。读者在理解OFP提供的方案之前,可以先看两个C语言中的基础知识。

(1)C语言的构造函数

在gcc下可以使用关键字__attribute__((constructor))指定构造函数。这些构造函数由编译器处理,在执行main函数之前,就会执行。构造函数的定义参考如下:

void __attribute__((constructor))  func(void)。func即会在程序执行main函数之前执行。

(2)程序运行时,动态库的搜索先后顺序。

  • LD_PRELOAD中指定的搜索路径。

  • LD_LIBRARY_PATH中指定的搜索路径。

  • 配置文件/etc/ld.so.conf中指定的动态库。

  • 默认的动态库搜索路径(/lib, /usr/lib)。

OFP提供的具体方案:

OFP在example中提供了两个库,其中ofp_netwrap_proc.so库用来实现ODP/OFP的配置以及初始化。ofp_netwrap_crt.so库用来实现符号的重载和参数的转换,这些系统调用包括:socket(), close(), shutdown()等。

ofp_netwrap_proc.so是使用构造函数的方法来实现ODP/OFP的配置以及初始化,具体代码参考(example/ofp_netwrap_proc/app_main.c):

 __attribute__((constructor)) static void ofp_netwrap_main_ctor(void)
{
appl_args_t params;
int core_count, ret_val;
odp_cpumask_t cpumask;
char cpumaskstr[];
odph_odpthread_params_t thr_params; memset(&params, , sizeof(params));
if (parse_env(&params) != EXIT_SUCCESS)
return; /*
* Before any ODP API functions can be called, we must first init ODP
* globals, e.g. availale accelerators or software implementations for
* shared memory, threads, pool, qeueus, sheduler, pktio, timer, crypto
* and classification.
*/
if (odp_init_global(&netwrap_proc_instance, NULL, NULL)) {
printf("Error: ODP global init failed.\n");
return;
}
netwrap_state = NETWRAP_ODP_INIT_GLOBAL;

说明:ofp_netwrap_main_ctor函数为构造函数,此函数会在main之前执行,用来实现ODP/OFP初始化及线程创建(包括命令行线程)。OFP运行时需要指定的参数(比如-i eth0)则是通过环境变量传入的,环境变量名为OFP_NETWRAP_ENV。

ofp_netwrap_crt.so库重写了socket的系统调用,我们可以通过一个例子来分析一下(example/ofp_netwrap_crt/netwrap_socket.c)。

 int close(int sockfd)
{
int close_value; if (IS_OFP_SOCKET(sockfd)) {
close_value = ofp_close(sockfd);
errno = NETWRAP_ERRNO(ofp_errno);
} else if (libc_close)
close_value = (*libc_close)(sockfd);
else { /* pre init*/
LIBC_FUNCTION(close); if (libc_close)
close_value = (*libc_close)(sockfd);
else {
close_value = -;
errno = EACCES;
}
} /*printf("Socket '%d' closed returns:'%d'\n",
sockfd, close_value);*/
return close_value;
}

说明:以close()函数为例,ofp_netwrap_crt.so重载了此函数,当判断出是属于OFP创建的socket时,则调用ofp_close()函数;而其他情况下,则调用libc_close指针(则指针可以参考LIBC_FUNCTION宏),是通过dlsym的方法加载到标准的C库socket函数。

ofp_netwrap_crt.so库同样提供了一个构造函数,此构造函数用来预加载所有的socket符号(example/ofp_netwrap_crt/netwrap.c)

 __attribute__((constructor)) static void setup_wrappers(void)
{
printf("start to setup netwrap");
sleep(); /* add for test */ setup_socket_wrappers();
setup_sockopt_wrappers();
setup_ioctl_wrappers();
setup_fork_wrappers();
setup_select_wrappers();
setup_uio_wrappers();
setup_sendfile_wrappers();
setup_epoll_wrappers(); printf("finish to setup netwrap");
}

3、具体例子

1)写一个标准的且简单的TCP服务端程序:

 #include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h> #define PORT 1500
#define BACKLOG 5 int main(){
int sockfd,new_fd;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
int sin_size; sockfd=socket(AF_INET,SOCK_STREAM,);
if(sockfd==-){
printf("socket failed:%d",errno);
return -;
}
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(PORT);
my_addr.sin_addr.s_addr=htonl(INADDR_ANY);
bzero(&(my_addr.sin_zero),);
if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))<){
printf("bind error");
return -;
} listen(sockfd,BACKLOG);
while(){
sin_size=sizeof(struct sockaddr_in);
new_fd=accept(sockfd,(struct sockaddr*)&their_addr,&sin_size);
if(new_fd==-){
printf("receive failed");
} else{
printf("receive success");
send(new_fd,"Hello World!",,);
}
}
return ;
}

说明:此程序是标准的基于linux socket的TCP服务端程序,程序的功能是当TCP建链成功后,向客户端发送hello world!将此程序直接编译成可执行文件tcpsrv。

2)./configure --prefix=/usr/local --with-odp=/usr/local --with-config-flv=netwrap-webserver --enable-sp=no

说明:原生态Linux Socket应用运行在OFP上时,慢平面(sp)的功能必须disable,另外需要配置netwrap-webserver。然后make clean;make;make install

3)修改ofp提供的配置文件scripts/ofp_netwrap.cli,配置fp0接口的ip地址:ifconfig fp0 192.168.43.2/24

4)修改ofp提供的脚本文件scripts/ofp_netwrap.sh

#!/bin/bash

export ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

export OFP_NETWRAP_ENV_DEFAULT="-i eth0 -f ${ROOT_DIR}/ofp_netwrap.cli"
export OFP_NETWRAP_ENV="${OFP_NETWRAP_ENV:-${OFP_NETWRAP_ENV_DEFAULT}}" LD_PRELOAD=libofp_netwrap_crt.so.0.0.:libofp.so.0.0.:libofp_netwrap_proc.so.0.0. $@

其中,第二行是用来指定OFP运行时的网卡和配置文件,根据实际情况修改。最后一行通过LD_PRELOAD导入libofp_netwrap_crt.so和libofp_netwrap_proc.so库。这两个库的入口都是构造函数,前文已经讲过了。

5)运行原生态Linux应用。

./ofp_netwrap.sh ./ofp_netwrap.sh /home/tcp-example/tcpsrv

特别说明:之前运行时,程序一直coredump时,后来花了较多时间调试,发现是odp/ofp初始化时,需要调用linux socket创建的socket多达40多个,但当配置--with-config-flv=netwrap-webserver时,分配给linux创建的socket总数不大于20,而将ofp socket创建的socket id的编号分配从20开始。所以,odp/ofp初始化时编号20以后的socket将使用ofp_socket创建,这会导致问题。

解决方案:OFP_SOCK_NUM_OFFSET的值从20修改为60。

/**Socket handle values returned are in the interval:
* [OFP_SOCK_NUM_OFFSET, OFP_SOCK_NUM_OFFSET + OFP_NUM_SOCKETS_MAX] */
#if defined(OFP_CONFIG_WEBSERVER)
/**Maximum number of sockets. */
# define OFP_NUM_SOCKETS_MAX
/**First socket number value. */
# define OFP_SOCK_NUM_OFFSET /**Maximum number of TCP PCBs. */
# define OFP_NUM_PCB_TCP_MAX # define OFP_TCP_MAX_CONNECTION_RATE #elif defined(OFP_CONFIG_NETWRAP_WEBSERVER)
/**Maximum number of sockets. */
# define OFP_NUM_SOCKETS_MAX
/**First socket number value. */
# define OFP_SOCK_NUM_OFFSET 60 /* modify form 20 to 60 */
/**Maximum number of TCP PCBs. */
# define OFP_NUM_PCB_TCP_MAX
# define OFP_TCP_MAX_CONNECTION_RATE #else /*OFP_CONFIG_DEFAULT*/
/**Maximum number of sockets. */
# define OFP_NUM_SOCKETS_MAX
/**First socket number value. */
# define OFP_SOCK_NUM_OFFSET /**Maximum number of TCP PCBs. */
# define OFP_NUM_PCB_TCP_MAX
#endif /* OFP_CONFIGS*/

configure时,配置了--with-config-flv=netwrap-webserver选项,OFP_CONFIG_NETWRAP_WEBSERVER宏将被定义。

4、测试结果

1)linux下执行ifconfig,没有fp0接口,说明慢平面的功能被关闭。

2)另外一台机器ping 192.168.43.2(ofp快平面配置的ip),可以ping通。

3)PC开启tcp客户端功能,看tcp功能是否正常。

OpenFastPath(2):原生态Linux Socket应用如何移植到OpenFastPath上?的更多相关文章

  1. Lwip:原生态的Linux socket应用如何移植到Lwip上

    lwIP - A Lightweight TCP/IP stack 在上一篇中,我们了解到在OpenFastPath上如何移植原生态的Linux Socket应用程序,那么,对于另外一个老牌的小型TC ...

  2. 将socket程序从linux移植到windows上

    今天突然想试下纯socket编程在两个系统上代码重合量有多大,只要不使用VC自定义的宏(比如SOCKET.SOCKADDR等等)感觉代码重合量挺大的. 比如最简单的TCP客户端和服务端对话,在VC中用 ...

  3. linux socket高性能服务器处理框架

    这个博客很多东西 http://blog.csdn.net/luozhonghua2014/article/details/37041765   思考一种高性能的服务器处理框架 1.首先需要一个内存池 ...

  4. 基于s5pv210嵌入式linux系统sqlite3数据库移植

    基于s5pv210嵌入式linux系统sqlite3数据库移植 1.下载源码 http://www.sqlite.org/download.html 最新源码为3080100 2.解压 tar xvf ...

  5. Linux socket编程 DNS查询IP地址

    本来是一次计算机网络的实验,但是还没有完全写好,DNS的响应请求报文的冗余信息太多了,不只有IP地址.所以这次的实验主要就是解析DNS报文.同时也需要正确的填充请求报文.如果代码有什么bug,欢迎指正 ...

  6. Linux socket 类封装 (面向对象方法)

    /* * socketfactory.h * * Created on: 2014-7-19 * Author: root */ #ifndef SOCKETFACTORY_H_ #define SO ...

  7. Linux Socket 编程简介

    在 TCP/IP 协议中,"IP地址 + TCP或UDP端口号" 可以唯一标识网络通讯中的一个进程,"IP地址+端口号" 就称为 socket.本文以一个简单的 ...

  8. Linux Socket - 基本socket链接

    0x0000 Linux Socket 函数 bind listen connect accept send recv read write 0x0001 Server绑不上ip 报错位置在bind函 ...

  9. linux socket详解

    1 linux socket编程的固定模式 server端,bind.listen.accept client端,connect client端和server端之间的一次通信: client端,wri ...

随机推荐

  1. Undefined function or method 'deploywhich' for input arguments of type 'char'

    在进行matlab和java混合编程的时候.由matlab打包,把m文件转换为jar文件.供java调用.有时在Tomcat中调用此类jar类会出现如题或者以下的错误: ??? Error using ...

  2. IntelliJ IDEA使用技巧一览表

    1 .写代码时用 Alt-Insert ( Code|Generate… )可以创建类里面任何字段的 getter 与 setter 方法. 2 .右键点击断点标记(在文本的左边栏里)激活速查菜单,你 ...

  3. 整合Yolov3到游戏引擎

    这篇其实是前文 CUDA版Grabcut的实现 的后续,和上文一样,先放视频. (博客园好像不支持视频,gif文件太大,视频链接) 在上文用CUDA实现opencv下的grabcut后,当时问题主要是 ...

  4. BZOJ1076:[SCOI2008]奖励关(状压DP,期望)

    Description 你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关.在这个奖励关里,系统将依次随机抛出k次宝物, 每次你都可以选择吃或者不吃(必须在抛出下一个宝物之前做出选择,且现在决定不吃的 ...

  5. Vmware10组建局域网

    Vmware10组建局域网很简单,特别是用Ubuntu16.04作为操作系统,基本上按照如下步骤来,是不会出现问题的. 1.首先,启动虚拟机,选择“编辑”->“虚拟网络编辑器” 2.改为桥接模式 ...

  6. 第三方开源插件zTree的使用

    zTree实现树形节点勾选效果图 使用流程: JS文件导入和引用 css文件导入和引用 demo代码 JS.css文件导入和引用 3个核心JS文件及两个核心css文件 demo相关代码: <!D ...

  7. P1481 魔族密码

    题目描述 风之子刚走进他的考场,就…… 花花:当当当当~~偶是魅力女皇——花花!!^^(华丽出场,礼炮,鲜花) 风之子:我呕……(杀死人的眼神)快说题目!否则……-_-### 花花:……咦好冷我们现在 ...

  8. SD卡受损,千万不要再格式化了

    1.手机提示SD卡受损; 2.把内卡插入电脑后,提示格式化,点取消.然后查看一下属性; 3.点电脑上的“开始菜单”--“运行”- chkdsk H:/F (H:就是你的SD卡盘符,/F是修复参数); ...

  9. jQuery----五星好评实现

    在美团.淘宝.京东等网页上,有许多商品.服务评价页面,五星好评功能很常见,本文利用jQuery实现五星好评功能. 案例图片:                                       ...

  10. 记一次ss无法上网的排查

    从日志开始排查. 登录服务器端 $ ssh root@[IP] 关闭 ss,再次启动并其指定日志输出文件 $ ssserver -c /etc/shadowsocks.json -d stop $ s ...