OpenWRT WIFI驱动的iwpriv兼容

最近OpenWRT升级到18.06.2后,内核变为4.14,移植WIFI驱动的过程中遇到了困难,原因是WIFI驱动比较老依赖WEXT的旧接口ndo_do_ioctl方式,导致运行iwpriv出错。

以下是解决问题的思路:

一、用strace调试iwpriv

root@XXX:~# strace iwpriv ra0
execve("/usr/sbin/iwpriv", ["iwpriv", "ra0"], 0x7feeb574 /* 12 vars */) = 0
set_thread_area(0x77fc4dc0)             = 0
set_tid_address(0x77fbdd28)             = 5485
open("/etc/ld-musl-mipsel-sf.path", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/lib/libgcc_s.so.1", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
fcntl64(3, F_SETFD, FD_CLOEXEC)         = 0
fstat64(3, {st_mode=S_IFREG|0644, st_size=78095, ...}) = 0
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\10\0\1\0\0\0p(\0\0004\0\0\0"..., 936) = 936
mmap2(NULL, 147456, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x77ef5000
mmap2(0x77f18000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x13000) = 0x77f18000
close(3)                                = 0
mprotect(0x418000, 4096, PROT_READ)     = 0
socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
ioctl(3, SIOCGIWPRIV, 0x7f8e699c)       = -1 EOPNOTSUPP (Not supported)
writev(2, [{iov_base="eth0.2    no private ioctls.\n\n", iov_len=30}, {iov_base=NULL, iov_len=0}], 2eth0.2    no private ioctls.

) = 30
close(3)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

分析到iwpriv调用了socket ioctl 获取SIOCGIWPRIV返回的私有ioctl信息,但是返回EOPNOTSUPP不支持,下一步重点就在内核的sock_ioctl是怎么处理SIOCGIWPRIV?

二、查看内核代码(4.14):

    static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
    {
        struct socket *sock;
        struct sock *sk;
        void __user *argp = (void __user *)arg;
        int pid, err;
        struct net *net;

        sock = file->private_data;
        sk = sock->sk;
        net = sock_net(sk);
        if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
            err = dev_ioctl(net, cmd, argp);
        } else
    #ifdef CONFIG_WEXT_CORE
        if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
            err = dev_ioctl(net, cmd, argp); // 看这里
        } else
    #endif
    ......

发现sock_ioctl有WEXT的特殊处理,因为SIOCGIWPRIV是在SIOCIWFIRST和SIOCIWLAST之间,直接调用了dev_ioctl(好像是/dev/下设备文件的ioctl),直接看dev_ioctl:

    int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
    {
        struct ifreq ifr;
        int ret;
        char *colon;

        /* One special case: SIOCGIFCONF takes ifconf argument
           and requires shared lock, because it sleeps writing
           to user space.
         */

        if (cmd == SIOCGIFCONF) {
            rtnl_lock();
            ret = dev_ifconf(net, (char __user *) arg);
            rtnl_unlock();
            return ret;
        }
        if (cmd == SIOCGIFNAME)
            return dev_ifname(net, (struct ifreq __user *)arg);

        /*
         * Take care of Wireless Extensions. Unfortunately struct iwreq
         * isn't a proper subset of struct ifreq (it's 8 byte shorter)
         * so we need to treat it specially, otherwise applications may
         * fault if the struct they're passing happens to land at the
         * end of a mapped page.
         */
        if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
            struct iwreq iwr;

            if (copy_from_user(&iwr, arg, sizeof(iwr)))
                return -EFAULT;

            iwr.ifr_name[sizeof(iwr.ifr_name) - 1] = 0;

            return wext_handle_ioctl(net, &iwr, cmd, arg);  //看这里
        }

这里直接调用了wext_handle_ioctl,注意用了iwreq的结构,这个是和ifreq结构类似的,但是比ifreq小8个字节(这个导致后面的解决补丁有隐患,因为补丁是直接把iwreq转换成了ifreq,如果超过32个字节直接就截掉了)。

中间省略步骤,最后跟踪到wireless_process_ioctl函数里:

/*
 * Main IOCTl dispatcher.
 * Check the type of IOCTL and call the appropriate wrapper...
 */
static int wireless_process_ioctl(struct net *net, struct iwreq *iwr,
                  unsigned int cmd,
                  struct iw_request_info *info,
                  wext_ioctl_func standard,
                  wext_ioctl_func private)
{
    struct net_device *dev;
    iw_handler  handler;

    /* Permissions are already checked in dev_ioctl() before calling us.
     * The copy_to/from_user() of ifr is also dealt with in there */

    /* Make sure the device exist */
    if ((dev = __dev_get_by_name(net, iwr->ifr_name)) == NULL)
        return -ENODEV;

    /* A bunch of special cases, then the generic case...
     * Note that 'cmd' is already filtered in dev_ioctl() with
     * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
    if (cmd == SIOCGIWSTATS)
        return standard(dev, iwr, cmd, info,
                &iw_handler_get_iwstats);

#ifdef CONFIG_WEXT_PRIV
    if (cmd == SIOCGIWPRIV && dev->wireless_handlers) //看这里---------
        return standard(dev, iwr, cmd, info,
                iw_handler_get_private);
#endif

    /* Basic check */
    if (!netif_device_present(dev))
        return -ENODEV;

    /* New driver API : try to find the handler */
    handler = get_handler(dev, cmd);
    if (handler) {
        /* Standard and private are not the same */
        if (cmd < SIOCIWFIRSTPRIV)
            return standard(dev, iwr, cmd, info, handler);
        else if (private)
            return private(dev, iwr, cmd, info, handler);
    }
    return -EOPNOTSUPP;
}

这里压根看不到哪里调用了WIFI驱动配置用的ndo_do_ioctl入口,可以确定的是用了net_device->wireless_handlers获取private和standard的handler方式处理,但没有ndo_do_ioctl什么事情,一番google后发现2017年的时候某一版内核删掉了ndo_do_ioctl支持

diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c
index 1a4db6790e20..24ba8a99b946 100644
--- a/net/wireless/wext-core.c
+++ b/net/wireless/wext-core.c
@@ -957,9 +957,6 @@  static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,
        else if (private)
            return private(dev, iwr, cmd, info, handler);
    }
-   /* Old driver API : call driver ioctl handler */
-   if (dev->netdev_ops->ndo_do_ioctl)
-       return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);
    return -EOPNOTSUPP;
 }

最后的解决办法为了兼容,加上上面删掉的代码,不过要把ifr替换成(struct iwreq *)iwr,隐患是毕竟iwreq和ifreq相似但是大小不同,一个是32个字节,一个是40个,可能会有问题,不过我测试了一下貌似OK,那就不管了。

理财相关

最近在看理财相关的书籍,特别是这本LAOK写的<解读基金>,虽然是07年写的,但仍然不过时。

对于投资股市,特别是基金来说,大道理都懂:长线投资,避免交易,采用定投,杜绝波段操作。特别是如果定位是理财那就要接受每年大约10%的收益,不要追逐波段,那就是投机了。

这个道理都懂,市场是不可预测的,那些电台里的送股短信估计不靠谱(追逐波段),那些研究K线理论或者波段炒作的也不靠谱,国外有个通过机器学习去预测K线图的走势,最后的结论是对标普500模型无法预测,但是也不是一点用处都没有,通过机器学习还是有参考研究价值的。

K线

说了这么多,我也是刚刚入门,还在慢慢摸索中。。。

在Firefox上测试DoH

最近听一个播课节目提到了Cloudflare和1.1.1.1,对最新的DNS技术有点好奇。虽然我也听过不少技术新闻了,而且最近也对Web隐私感兴趣就折腾了一下。

对Cloudflare印象不错,之前还看到一个新闻是说他们用熔岩灯作为服务器的随机数生成器(熔岩灯就是一个蜡块在水浮动里,底部加热,蜡块预热浮起,遇冷下沉,周而复始)。不过有部美剧演绎了一个公司的熔岩灯随机数系统遭黑客破解云云,后来Cloudflare发博客做了澄清

跑题了,APNIC苦与找不到合适的接盘侠接受1.1.1.1这个自带巨量垃圾流量的烫手山芋,Cloudflare搞公共DNS也苦与找不到朗朗上口的IP地址。两家一拍即合,你提供IP,我提供服务分析垃圾流量顺便把DNS给做了。于是https://1.1.1.1在今年愚人节横空出世。

Cloudflare的1.1.1.1除了支持DoH、DoT,Cloudflare还推出了各种搞笑的DNS查询方式,包括通过短信、邮件、Twitter查DNS等等 🙂

Cloudflare最大的不同是,它一开始的口号是提倡互联网的隐私(google躺枪),而且也是这么做的。为了隐私,Cloudflare不存不必要的信息,改进了很多DNS技术细节确保最少的信息泄露,可以去看他们的官方博客有很多详细的介绍。估计Firefox也是看中这点才与Cloudflare合作推出了内置的DoH功能,从Firefox 62开始支持。只要在about:config中设置network.trr.mode为2就开启了。内置默认TRR是<https://mozilla.cloudflare-dns.com/dns-query >。这里有一篇DoH的漫画介绍,这里有一篇firefox 的 DoH参数,里面说道一个DoH的bonus是可以知道TTL了。

我试了下速度还可以,但用了DoH就不能用Ubuntu自己的dnsmasq缓存了(我的Ubuntu 14.04 dnsmasq缓存默认还没开启-_-!!!),其他的没怎么影响。不过现在的Web保护隐私提倡HTTPS,现在解决了DNS的问题,剩下好像只有HTTPS的SNI什么时候解决?

怀旧文一篇

今天看别人的博客提到了unix-center.net和蒋清野先生,这也勾起了我的回忆。我大概是在大三的时候接触Linux,当时是看到别人不知道从那里得到的免费Ubuntu7或者是8 CD光盘。我也就在电脑上装了一个,记得安装过程好像比现在的复杂。可惜的是我在玩了一下Ubuntu中的一款penguin企鹅游戏后就放弃了,也没花精力深入研究,大概是GUI体验不是很好吧。

重回XP后,为了继续学习Linux就找到了这个当时在国内还是独一无二的unix-center.net,我记得很清楚的是提供很多发行版,好像还有采用龙芯CPU的服务器。我每次用putty远程登录,体验命令行操作,写几行C语言程序,再跟着鸟哥的网站测试一下学到的命令。当时感觉很神奇,很方便,受益匪浅。当时同时登录服务器的人很多,很多人的文件目录都是可以访问的,我当时的乐趣之一就是挨个翻一翻每个人的文件主目录,看看有什么好玩的东西(好东西不多,估计都是我这种小白),好像还可以通过命令互发消息,但我没试过。后来记得有一次蒋先生在网站上发公告说有人在服务器上运行破解密码的软件,占用了大量CPU等等,当时我就隐隐感觉网站的脆弱和孤立无源,网站好像也没什么赞助商什么的。

后来知道了unix-center.net是蒋清野先生独自创办的,花光了个人积蓄付网站的服务器和流量费用。但后来遇到了困难很难维持这个网站了,他在网站发公开信说:

我于2000年底硕士毕业之后,在美国工作了三年,又在中国工作了六年。在过去的两年半中,这个系统烧掉了我大部分的工资和积蓄。购买各种服务器设备的费用,大概是70万人民币;北京赛尔机房的服务器托管和带宽费用,大概是每年30万人民币。。。和我同年龄的人,现在大部分都有车有房了吧。而我最值钱的资产,是在保安严密的机房中的三个机柜。

后来我也一直没再关注过unix-center.net,现在也访问不了,应该是很早就停掉了。今天抽空看了一下蒋先生的近况。unix-center遇到困难后,他想方设法度过难关,后来估计不成功吧,就回到海南老家,看这篇帖子蒋先生的评论,好像回海南创业搞农业??最近
根据他博客的内容是在澳洲AWS搞云计算和在悉尼大学读博士(真是老当益壮啊)。

蒋先生也坦言自己是个纯粹的搞技术的,不懂如果把技术变现,估计也不屑于谈钱。这是技术人员的通病吧,很难找到既有技术又有商业头脑的人。中国的教育的一个短板就是强调专才教育而忽略了通识教育,固然专才教育可以提供大量的工程师满足当前的经济需求,但就当下提倡技术创新和全民创业来说就很那满足了,现在需要的就是既有技术专才又有情商高的通才。

OpenWRT第一次提交补丁

最近在工作中发现了MT7688 OpenWRT的一个看门狗BUG,简单的来说就是MT7688(包括MT7628/MT7621)设备树描述的看门狗寄存器地址与看门狗内核模块的寄存器地址不符导致不能工作。

所以我第一次向OpenWRT github提交了issue, 对方很nice,指出了我的不少提交错误格式。下次递交一定改正:)

最后等了很久终于合并进主仓库了,提交的注释还是对方帮我写的:)

MT7688看门狗模块还有个寄存器可以知道最近一次重启是看门狗导致的还是系统软重启,这个很有用,可以帮助判断是否死机过。

BTW: 最近空余时间在玩arduino,感觉这是单片机里的OpenWRT啊:),大大降低了开发单片机的难度,而且几个大的创客社区都提供现成的模块和软件包,与我之前上学时候玩的单片机是天壤之别啊,概况技术的进步!!! 最后抱怨一下电压是个坑,5V和3.3V模块混杂,这个有点头疼。

AM335X移植OpenWRT 二

上篇说到OK335xD开发板移植到OpenWRT后网口一直不行,装了tcpdump抓包发现数据能接受但不能发送。

最后搜索到这个TI官方论坛的帖子,说明可能是TCLK频率不对,因为RCLK是PHY芯片AR8031提供的,频率是25MHZ,但TCLK是AM335X提供的PLL分频后提供的,如果PLL配置不对TCLK会错误的频率导致发生失败。

AM335X 以太网Clock是通过CORE_CLKOUTM5提供的(250MHZ),一般来说TCLK千兆网口的话需要125MHZ,百兆网口需要25MHZ,十兆需要2.5MHZ, 我现在用的100M网口,TCLK需要达到25MHZ。

检查一下现在系统中的CORE_CLKOUTM5:

root@OpenWrt:/tmp# cat /sys/kernel/debug/clk/clk_summary | grep m5
         dpll_core_m5_ck              2            2   50000000          0 0  

发现只有50MHZ,所以问题出在这里,应该是uboot没配置好PLL参数,换了一个板子自带的就OK了:

root@OpenWrt:/tmp# cat /sys/kernel/debug/clk/clk_summary | grep m5
         dpll_core_m5_ck              2            2   250000000          0 0  

AM335X移植OpenWRT 一

我手上有一个forlinx的AM335X开发板OK335xD,自带的Linux内核是3.2的而且我测试发现一旦网络传输数据稍大kernel就panic,调试了好久都没搞好(我试了TI官方的SDK,也一样)。

最近看到LEDE是支持OMAP的,抱着试试看的精神就下了最新的LEDE 17.1.04试试。编译用了默认的AM33xx target profile,uboot随便找了个之前AM335x的uboot(这是个坑)。编译后bin目录里是内核,文件系统需要到build_dir的linux_omap里去找root.ext4,我嫌麻烦就上LEDE官网下了一个am335x-evm-ext4-sdcard.img.gz(别看是5M的压缩文件,解压后是256M大文件,估计里面都是0XFF或者是0x00,其实真正文件系统用到10M左右,这个以后可以优化缩小一下-_-!!!)烧到SD卡里,然后替换一下uboot和kernel。

接下来是device tree文件了,这个就要花时间调试了。参考TI EVM的am335x-evm.dts文件复制到forlinx.dts,修改dts/Makefile只编译forlinx.dtb。

需要注意的点是:

  1. 根据板子原理图电源管理芯片是tps65217,需要添加tps65217的节点,而且要根据原理图写好各个dcdc和LDO的节点。 TI的evm板子里DCDC2是MPU,DCDC3是core,而OK335xD反了一下,而且DCDC1用来控制DDR3。
    dcdc1_reg: regulator@0 {
        /* DDR3 VDD_CORE voltage limits 0.95V - 1.1V with +/-4% tolerance */
        regulator-name = "vdd_ddr3";
        regulator-min-microvolt = <925000>;
        regulator-max-microvolt = <1150000>;
        regulator-boot-on;
        regulator-always-on;
        regulator-always-on;
    };
    
    dcdc2_reg: regulator@1 {
        /* VDD_CORE voltage limits 0.95V - 1.1V with +/-4% tolerance */
        regulator-name = "vdd_core";
        regulator-min-microvolt = <925000>;
        regulator-max-microvolt = <1150000>;
        regulator-boot-on;
        regulator-always-on;
    };
    
    dcdc3_reg: regulator@2 {
        /* VDD_MPU voltage limits 0.95V - 1.26V with +/-4% tolerance */
        regulator-name = "vdd_mpu";
        regulator-min-microvolt = <925000>;
        regulator-max-microvolt = <1351500>;
        regulator-boot-on;
        regulator-always-on;
    };
    

    具体电压可以比对官方的datasheet的OPP:

    上图能看到OPP turbo时能到720M,其实AM335x A8最高能支持到1G,好像最新LEDE主分支里合并了4.9支持1G了,我现在用的4.4.92还不支持,最高只能跑到720M,等下次更新了再说吧。

    root@OpenWrt:~# cat /sys/devices/system/cpu/cpufreq/policy0/scaling_available_frequencies 
    275000 500000 600000 720000 
    
  2. OK335xD用的是NAND(暂时不用),所以只有添加一个SD卡启动的MMC1就好了:
    &mmc1 {
        bus-width = <4>;
        vmmc-supply = <&vdd33>;
        pinctrl-names = "default";
        pinctrl-0 = <&mmc1_pins>;
        cd-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
        disable-wp;
        status = "okay";
    };
    
  3. AM335X支持双网口(可以作为路由器),而OK335xD只用了网口1,PHY芯片用了AR8031,而且支持千兆网口(用了rgmii接口),一般rgmii接口信号线需要引入人为的delay,参考这篇有说明。我看了AR8031的datasheet和内核的代码,AR8031是直接支持tx和rx delay的,只要设置一下debug register就行了,内核驱动at803x.c里也是这么干的:
        if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
            ret = phy_write(phydev, AT803X_DEBUG_ADDR,
                    AT803X_DEBUG_SYSTEM_MODE_CTRL);
            if (ret)
                return ret;
            ret = phy_write(phydev, AT803X_DEBUG_DATA,
                AT803X_DEBUG_RGMII_TX_CLK_DLY);
            if (ret)
                return ret;
        }
    

    但我没看到rgmii-rxid和rgmii-id的支持,所以至少对于我手上的4.4.92来说device tree只支持rgmii-txid:

    &mac {
        pinctrl-names = "default", "sleep";
        pinctrl-0 = <&cpsw_default>;
        pinctrl-1 = <&cpsw_sleep>;
        slaves = <1>;
        status = "okay";
    };
    
    &davinci_mdio {
        pinctrl-names = "default", "sleep";
        pinctrl-0 = <&davinci_mdio_default>;
        pinctrl-1 = <&davinci_mdio_sleep>;
        status = "okay";
    };
    
    &cpsw_emac0 {
        phy_id = <&davinci_mdio>, <0>;
        phy-mode = "rgmii-txid";
    };
    

    mac集成在AM335X里,而且只用了一个网口, phy-mode是rgmii-txid。

好吧,先写到这里吧,还有很多东西没写,完整的dts在这里。下一篇要写写折腾了我很久的网口问题!!!

参考

http://www.eefocus.com/marianna/blog/14-10/306285_18a90.html

Spectre和Meltdown相关

过完年第一天上班,我抽空看了一下Meltdown漏洞的机制,这里有一篇Raspberry Pi的Blog讲的很好。看完了解了个大概,感叹现代的处理器越来越先进复杂,触发漏洞的是不是单单一个子系统,而是CPU一堆子系统构成的机制(内存缓存、乱序执行、分支预判和Speculative execution),而且都是跟CPU性能相关的核心部件,所以修复起来肯定要伤筋动骨了。

这次的攻击是side-channel attack(翻译成旁路攻击吗?),就是类似观察系统执行指令消耗的时间去推测具体执行了什么(内存缓存对时间影响很大,所以可以判断出来地址时候缓存),这个攻击只能是查看内核或者别的进程的内存数据,还修改不了内存,不过跟之前的DRAM Row hammer结合起来就威力比较大了:)。

最后看了一下ARM受此漏洞的影响,看到A8受到影响,看来A8用了Speculative execution, Raspberry Pi用的是A7不受影响。

最近情绪低落中…

想起了 Lionel Richie 唱的 Say You Say Me :

Say you, say me
Say it for always
That's the way it should be
Say you, say me
Say it together, naturally
I had a dream, I had an awesome dream
People in the park
Playing game in the dark
And what they played
Was a masquerade
But from behind the walls of doubt
A voice was crying out 
。。。。

MT7688 AES硬件加速

最近我无聊翻翻MT7688的datasheet,注意到居然支持AES加速,赶紧搜github找到了一个能用的OpenWRT驱动MTK_AES

赶紧开搞,一路搞好好几天才慢慢摸索出linux的初步加密框架。内核的加密框架的应用层接口是AF_ALG 套接字形式,但是有一个专门的驱动模块crpytodev抽象出一个/dev/crypto设备,采用ioctl接口,而且根据官网数据对比性能更好。

于是选中OpenWRT自带的kmod-cryptodev模块,默认会选中kmod-crypto-core等一大堆依赖,其实最终只依赖aead.ko,编译好后全部加载驱动。由于系统库的加密是openssl提供的libcrypto加密库,所以需要开启openssl库的硬件加密选项 “Crypto acceleration support”(Libraries>SSL>libopenssl) ,顺便选中openssl-util工具进行测试。

  • 测试未开启AES硬件加速

    root@:/# openssl speed -evp aes-128-cbc
    Doing aes-128-cbc for 3s on 16 size blocks: 1242779 aes-128-cbc's in 2.96s
    Doing aes-128-cbc for 3s on 64 size blocks: 359528 aes-128-cbc's in 2.95s
    Doing aes-128-cbc for 3s on 256 size blocks: 93663 aes-128-cbc's in 2.95s
    Doing aes-128-cbc for 3s on 1024 size blocks: 23499 aes-128-cbc's in 2.92s
    Doing aes-128-cbc for 3s on 8192 size blocks: 2953 aes-128-cbc's in 2.96s
    bala...bala...
    The 'numbers' are in 1000s of bytes per second processed.
    type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
    aes-128-cbc       6717.72k     7799.93k     8128.04k     8240.75k   
    
  • 测试开启AES硬件加速

    root@:/# openssl speed -evp aes-128-cbc
    Doing aes-128-cbc for 3s on 16 size blocks: 276231 aes-128-cbc's in 0.65s
    Doing aes-128-cbc for 3s on 64 size blocks: 270678 aes-128-cbc's in 0.45s
    Doing aes-128-cbc for 3s on 256 size blocks: 243448 aes-128-cbc's in 0.29s
    Doing aes-128-cbc for 3s on 1024 size blocks: 179638 aes-128-cbc's in 0.34s
    Doing aes-128-cbc for 3s on 8192 size blocks: 45879 aes-128-cbc's in 0.11s
    bala...bala...
    type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
    aes-128-cbc       6799.53k    38496.43k   214905.82k   541027.39k  3416734.25k
    

主要是看最后一行的吞吐量测试,效果惊人啊!!!!

不开启加速时数据吞吐量变化不大,毕竟这里的性能瓶颈是在AES软件实现这块,而开启硬件加速后,数据块越大效果越好,最后的8192字节块效果就提升了414倍。

这里openssl需要开启-evp选项,这样底层的crpyto库会优先选择硬件加速。openssl里的cryptodev引擎是EVP接口形式提供的,所以如果使用openssl库需要加速,必须用EVP接口。不过openssl测试工具默认的加密引擎engine会自动选择cryptodev,不用再手动指定-engine cryptodev了。

这里比较郁闷的是mtk_aes支持AES的ECB和CBC,但是openssl的libcrypto库的enc_cryptodev.c里只支持CBC,所有最终能加速的只能是AES的CBC了,估计ECB不太安全吧,连CBC也不是很安全,现在普遍用CTR吧。CBC只是比ECB多了个IV矩阵,保证每次加密不重样。

static struct {
    int id;
    int nid;
    int ivmax;
    int keylen;
} ciphers[] = {
    {
        CRYPTO_ARC4, NID_rc4, 0, 16,
    },
    {
        CRYPTO_DES_CBC, NID_des_cbc, 8, 8,
    },
    {
        CRYPTO_3DES_CBC, NID_des_ede3_cbc, 8, 24,
    },
    {
        CRYPTO_AES_CBC, NID_aes_128_cbc, 16, 16,
    },
    {
        CRYPTO_AES_CBC, NID_aes_192_cbc, 16, 24,
    },
    {
        CRYPTO_AES_CBC, NID_aes_256_cbc, 16, 32,
    },
# ifdef CRYPTO_AES_CTR
    {
        CRYPTO_AES_CTR, NID_aes_128_ctr, 14, 16,
    },
    {
        CRYPTO_AES_CTR, NID_aes_192_ctr, 14, 24,
    },
    {
        CRYPTO_AES_CTR, NID_aes_256_ctr, 14, 32,
    },
# endif
    {
        CRYPTO_BLF_CBC, NID_bf_cbc, 8, 16,
    },
    {
        CRYPTO_CAST_CBC, NID_cast5_cbc, 8, 16,
    },
    {
        CRYPTO_SKIPJACK_CBC, NID_undef, 0, 0,
    },
    {
        0, NID_undef, 0, 0,
    },
};

接下来用AES CBC加密一个20M的文件:

root@:/tmp# head -c 20m /dev/zero >test

root@:/tmp# time openssl enc -e -aes-128-cbc -in test -out test.out -kfile pgrade_pass 
real 0m 0.74s
user 0m 0.10s 
sys 0m 0.63s

root@:/tmp# rmmod mtk_aes 
root@:/tmp# time openssl enc -e -aes-128-cbc -in test -out test.out -kfile upgrade_pass 
real 0m 3.33s
user 0m 2.93s 
sys 0m 0.38s

提升了5倍。

我这里的openssl的版本是1.0.2e,最新的1.1版本支持AES的ECB的,我没试过,Cryptodev是1.7版本。现在应用就是以后加解密文件有点用,如果是优化ssh和scp估计也不是很好,比较这些应用瓶颈往往是网速,并且如果是小数据加密的话效果差别不大,只在大数据吞吐时才有差别。