Brillo: Brillo-m8-release @ RPi 2B(树莓派)目前发现的问题

  • adb 通过网络连接很不方便
  • 开机之后有时候不能自动获取到IP地址
  • 不支持USB无线网卡

之前配置的kernel configuration只是保证Raspberrry Pi 2B(树莓派)能够运行起来并且当时手中并没有一块能用的USB网卡,所以无线网卡的驱动没有有包含进去。近日在网上买了个EDUP-EPN8508GS的usb无线网卡。重新配置了一下kernel的参数,相关的配置文件可以从这里下载:rpi2b-brillo-rtl8192cu-kconfig.gz。同时还需要修改一下kernel的代码(原来kernel中也有这个网卡的驱动,同时驱动中也包含所需的固件,无耐发现iw无法识别,只能使用另外一份驱动。相关的做法如下:

回退相关的patch:

$ cd hardware/bsp/kernel/hzak/rpi-4.1.y
$ git revert ff0b85db2322f6454c4c10634678c0c8b0f8a9cd
$ git revert 3dc2a38d0437635d471babdc78c99244aec06aaf
$ git revert 6d4d3a978afbc332af02e548bd0e8ced16dff296

或者是直接修改drivers/net/wireless/kconfig文件,使用drivers/net/wireless/rtlwifi的驱动:

source "drivers/net/wireless/rt2x00/Kconfig"
source "drivers/net/wireless/mediatek/Kconfig"
source "drivers/net/wireless/rtlwifi/Kconfig"
#source "drivers/net/wireless/rtl8192cu/Kconfig"
source "drivers/net/wireless/ti/Kconfig"

修改完之后,就可以用上面提交的kernel配置文件重新编译kernel了。运行之后,就可以用iw进行相关的操作了,如查看网卡的基本信息:

$ adb shell iw dev
phy#0
	Interface wlan0
		ifindex 4
		wdev 0x1
		addr e8:4e:06:33:41:2e
		type managed
  • USB无线网卡需要的固件没办法自动获取

之前kernel的配置同样没办法让kernel通知udev去加载固件(这个方法貌似不太好,kernel中有相关的文档说明 driver/base/Kconfig):

config FW_LOADER_USER_HELPER_FALLBACK
    bool "Fallback user-helper invocation for firmware loading"
    depends on FW_LOADER
    select FW_LOADER_USER_HELPER
    help
      This option enables / disables the invocation of user-helper
      (e.g. udev) for loading firmware files as a fallback after the
      direct file loading in kernel fails.  The user-mode helper is
      no longer required unless you have a special firmware file that
      resides in a non-standard path. Moreover, the udev support has
      been deprecated upstream.

当有个这个配置,kernel就可以通过udev来加载固件了。

driver调用request firmware, 会先从指定的path或builtin中找,如果没有找到,再调用firmware_uevent()通知uevent进行加载固件,system/core/init/devices.cpp中包含相关的实现。

NOTE: 可以参考这里:https://wiki.ubuntu.com/Kernel/Firmware。

  • kernel包含cfg80211/nl80211的代码后,需要更新world regulatory domain

在运行时,kernel log中会有这样的信息:cfg80211: Calling CRDA to update world regulatory domain

相关的文档可以看这里hardware/bsp/kernel/hzak/rpi-4.1.y/net/wireless/db.txt以及http://wireless.kernel.org/en/developers/Regulatory

  • firewalld没办法调用iptables去更新防火墙

在logcat中会有如下信息:

12-20 09:15:22.591 +0000   177   177 I /system/bin/webservd: [INFO:server.cc(119)] Firewall service is on-line. Opening firewall for protocol handlers
12-20 09:15:22.595 +0000   148   148 I /system/bin/firewalld: Punching hole for TCP port 80 on interface ''
12-20 09:15:22.624 +0000   148   148 E /system/bin/firewalld: Could not add ACCEPT rule using '/system/bin/iptables'
12-20 09:15:22.625 +0000   148   148 E /system/bin/firewalld: Adding ACCEPT rules failed
12-20 09:15:22.627 +0000   177   177 E /system/bin/webservd: [ERROR:server.cc(53)] Failed to open up port 80, interface:
12-20 09:15:22.627 +0000   148   148 I /system/bin/firewalld: Punching hole for TCP port 443 on interface ''
12-20 09:15:22.656 +0000   148   148 E /system/bin/firewalld: Could not add ACCEPT rule using '/system/bin/iptables'
12-20 09:15:22.657 +0000   148   148 E /system/bin/firewalld: Adding ACCEPT rules failed
12-20 09:15:22.659 +0000   177   177 E /system/bin/webservd: [ERROR:server.cc(53)] Failed to open up port 443, interface:

从log可以知道,当firewalld运行的时候,webservd会通知firewalld去开放80和443端口,而firewalld收到信息后会去执行iptables程序开放这两个端口,但是很显然,iptables执行失败了。

在system/firewalld/iptables.cc中相关的代码如下:

bool IpTables::PunchTcpHole(uint16_t in_port, const std::string& in_interface) {
  return PunchHole(in_port, in_interface, &tcp_holes_, kProtocolTcp);
}

bool IpTables::PunchHole(uint16_t port,
                         const std::string& interface,
                         std::set<Hole>* holes,
                         ProtocolEnum protocol) {
  if (port == 0) {
    // Port 0 is not a valid TCP/UDP port.
    return false;
  }

  if (!IsValidInterfaceName(interface)) {
    LOG(ERROR) << "Invalid interface name '" << interface << "'";
    return false;
  }

  Hole hole = std::make_pair(port, interface);
  if (holes->find(hole) != holes->end()) {
    // We have already punched a hole for |port| on |interface|.
    // Be idempotent: do nothing and succeed.
    return true;
  }

  std::string sprotocol = protocol == kProtocolTcp ? "TCP" : "UDP";
  LOG(INFO) << "Punching hole for " << sprotocol << " port " << port
            << " on interface '" << interface << "'";
  if (!AddAcceptRules(protocol, port, interface)) {
    // If the 'iptables' command fails, this method fails.
    LOG(ERROR) << "Adding ACCEPT rules failed";
    return false;
  }

  // Track the hole we just punched.
  holes->insert(hole);

  return true;
}

bool IpTables::AddAcceptRules(ProtocolEnum protocol,
                              uint16_t port,
                              const std::string& interface) {
  if (!AddAcceptRule(kIpTablesPath, protocol, port, interface)) {
    LOG(ERROR) << "Could not add ACCEPT rule using '" << kIpTablesPath << "'";
    return false;
  }

  if (AddAcceptRule(kIp6TablesPath, protocol, port, interface)) {
    // This worked, record this fact and insist that it works thereafter.
    ip6_enabled_ = true;
  } else if (ip6_enabled_) {
    // It's supposed to work, fail.
    LOG(ERROR) << "Could not add ACCEPT rule using '" << kIp6TablesPath
               << "', aborting operation";
    DeleteAcceptRule(kIpTablesPath, protocol, port, interface);
    return false;
  } else {
    // It never worked, just ignore it.
    LOG(WARNING) << "Could not add ACCEPT rule using '" << kIp6TablesPath
                 << "', ignoring";
  }

  return true;
}

bool IpTables::AddAcceptRule(const std::string& executable_path,
                             ProtocolEnum protocol,
                             uint16_t port,
                             const std::string& interface) {
  std::vector<std::string> argv;
  argv.push_back(executable_path);
  argv.push_back("-I");  // insert
  argv.push_back("INPUT");
  argv.push_back("-p");  // protocol
  argv.push_back(protocol == kProtocolTcp ? "tcp" : "udp");
  argv.push_back("--dport");  // destination port
  argv.push_back(std::to_string(port));
  if (!interface.empty()) {
    argv.push_back("-i");  // interface
    argv.push_back(interface);
  }
  argv.push_back("-j");
  argv.push_back("ACCEPT");
  argv.push_back("-w");  // Wait for xtables lock.

  // Use CAP_NET_ADMIN|CAP_NET_RAW.
  return ExecvNonRoot(argv, kIpTablesCapMask) == 0;
}

在这里可以看到在AddAcceptRule的时候需要CAP_NET_ADMIN和CAP_NET_RAW,所以这时候就会用到minijail了(相关的文档可以看这里:https://www.chromium.org/chromium-os/chromiumos-design-docs/system-hardening):

const uint64_t kIpTablesCapMask =
    CAP_TO_MASK(CAP_NET_ADMIN) | CAP_TO_MASK(CAP_NET_RAW);

int IpTables::ExecvNonRoot(const std::vector<std::string>& argv,
                           uint64_t capmask) {
  brillo::Minijail* m = brillo::Minijail::GetInstance();
  minijail* jail = m->New();
#if !defined(__ANDROID__)
  // TODO(garnold) This needs to be re-enabled once we figure out which
  // unprivileged user we want to use.
  m->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser);
#endif  // __ANDROID__
  m->UseCapabilities(jail, capmask);

  std::vector<char*> args;
  for (const auto& arg : argv) {
    args.push_back(const_cast<char*>(arg.c_str()));
  }
  args.push_back(nullptr);

  int status;
  bool ran = m->RunSyncAndDestroy(jail, args, &status);
  return ran ? status : -1;
}

minijail相关的代码在这里:

  1. external/libbrillo/brillo/minijail/minijail.cc
  2. external/minijail

虽然fock出了iptables, 但是在执行到external/iptables/libiptc/libiptc.c

struct xtc_handle *
TC_INIT(const char *tablename)
{
    struct xtc_handle *h;
    STRUCT_GETINFO info;
    unsigned int tmp;
    socklen_t s;
    int sockfd;

retry:
    iptc_fn = TC_INIT;

    if (strlen(tablename) >= TABLE_MAXNAMELEN) {
        errno = EINVAL;
        return NULL;
    }

    sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
    if (sockfd < 0)
        return NULL;
    // ...
}

在创建IPPROTO_RAW socket的时候,需要CAP_NET & CAP_NET_RAW capablity, 在执行到下面代码的时候会,会返回-EPERM

hardware/bsp/kernel/hzak/rpi-4.1.y/net/ipv4/af_inet.c

/*
 *  Create an inet socket.
 */

static int inet_create(struct net *net, struct socket *sock, int protocol,
               int kern)
{
    // ...
    err = -EPERM;
    if (sock->type == SOCK_RAW && !kern &&
        !ns_capable(net->user_ns, CAP_NET_RAW))
        goto out_rcu_unlock;

    // ...
}

奇怪的是RPi 2B的代码与brilloemulator的代码基本一样,但是brilloemulator却没有这样的问题。

更新(2015-12-27 23:21:18):brilloemulator-x86没有这个问题,但brilloemulator-arm也有这样的问题, x86使用的是android-3.18的kernel, 而arm使用的是v4.1的kernel,kernel更新,不允许minijail这么做?), 看来要在device/generic/brillo/sepolicy中添加policy文件暂时解决这个问题了。

[2016-04-12 22:16:19] 已经找到原因: ->@<-

PS: 防火墙的配置可以通过如下命令查看:

$ adb shell iptables --list
Chain INPUT (policy DROP)
target     prot opt source               destination         
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:https
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:http
ACCEPT     udp  --  anywhere             anywhere             udp spts:bootps:bootpc dpts:bootps:bootpc
ACCEPT     all  --  anywhere             anywhere             state RELATED,ESTABLISHED
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:https
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:http
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:5555
ACCEPT     icmp --  anywhere             anywhere            
ACCEPT     udp  --  anywhere             224.0.0.251          udp dpt:mdns

Chain FORWARD (policy DROP)
target     prot opt source               destination         

Chain OUTPUT (policy DROP)
target     prot opt source               destination         
ACCEPT     udp  --  anywhere             anywhere             udp spts:bootps:bootpc dpts:bootps:bootpc
ACCEPT     all  --  anywhere             anywhere             state NEW,RELATED,ESTABLISHED
ACCEPT     all  --  anywhere             anywhere            

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注