Linux Application

在 openvswitch 上配置 VXLAN

本文讲的是 korg 内核中的 openvswitch,而非 openvswitch.org 上提供的openvswitch模块。

之前讲过 VXLAN,但是一直没有动手实践。其实配置 VXLAN 要比配置 GRE tunnel 简单很多,你所需要做的只要两步:

1. 添加 VXLAN 设备(需要比较新的 iproute2 和内核支持):

# ./ip/ip li add vxlan0 type vxlan id 42 group 239.1.1.1 local 10.16.43.214 mtu 1500 dev eth0

其中,10.16.43.214 是配置在 eth0 (或其它连接两台主机的设备)上的本地IP地址。

2. 把 vxlan0 添加到 openvswitch 的 bridge 中去:

# ovs-vsctl add-port ovsbr0 vxlan0

最终的结果如下所示:

# ./ip/ip -d li show vxlan0
7: vxlan0:  mtu 1500 qdisc noqueue
state UNKNOWN mode DEFAULT
    link/ether d2:93:a2:08:10:1e brd ff:ff:ff:ff:ff:ff
    vxlan id 42 group 239.1.1.1 local 10.16.43.214 dev eth0 port 32768
61000 ageing 300
# ifconfig eth0
eth0      Link encap:Ethernet  HWaddr AC:16:2D:05:14:3A
          inet addr:10.16.43.214  Bcast:10.16.47.255  Mask:255.255.248.0
          inet6 addr: 2620:52:0:102f:ae16:2dff:fe05:143a/64 Scope:Global
          inet6 addr: fe80::ae16:2dff:fe05:143a/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:386702 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1505515 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:42804806 (40.8 MiB)  TX bytes:1202562852 (1.1 GiB)
          Interrupt:20 Memory:fbc00000-fbc20000

# ifconfig vxlan0
vxlan0    Link encap:Ethernet  HWaddr D2:93:A2:08:10:1E
          inet6 addr: fe80::d093:a2ff:fe08:101e/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:346746 errors:0 dropped:0 overruns:0 frame:0
          TX packets:752833 errors:36 dropped:0 overruns:0 carrier:36
          collisions:0 txqueuelen:0
          RX bytes:18081642 (17.2 MiB)  TX bytes:1139323399 (1.0 GiB)

# ovs-vsctl show
2dce25d0-ac18-4626-905d-c4995464785d
    Bridge "ovsbr0"
        Port "vnet0"
            Interface "vnet0"
        Port "vxlan0"
            Interface "vxlan0"
        Port "vnet1"
            Interface "vnet1"
        Port "ovsbr0"
            Interface "ovsbr0"
                type: internal
    ovs_version: "1.7.0"

另外一台机器上基本上完全相同,结果如下:

# ./ip/ip -d li show vxlan0
8: vxlan0:  mtu 1450 qdisc noqueue
state UNKNOWN mode DEFAULT
    link/ether 92:8b:0f:8b:31:f1 brd ff:ff:ff:ff:ff:ff
    vxlan id 42 group 239.1.1.1 local 10.16.43.215 dev eth0 port 32768
61000 ageing 300
# ifconfig vxlan0
vxlan0    Link encap:Ethernet  HWaddr 92:8B:0F:8B:31:F1
          inet6 addr: fe80::908b:fff:fe8b:31f1/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:752833 errors:0 dropped:0 overruns:0 frame:0
          TX packets:346770 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:1128783737 (1.0 GiB)  TX bytes:22940562 (21.8 MiB)

# ifconfig eth0
eth0      Link encap:Ethernet  HWaddr E8:39:35:30:1D:1E
          inet addr:10.16.43.215  Bcast:10.16.47.255  Mask:255.255.248.0
          inet6 addr: 2620:52:0:102f:ea39:35ff:fe30:1d1e/64 Scope:Global
          inet6 addr: fe80::ea39:35ff:fe30:1d1e/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1535775 errors:0 dropped:0 overruns:0 frame:0
          TX packets:346951 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:1210625058 (1.1 GiB)  TX bytes:41681835 (39.7 MiB)
          Interrupt:17

# ovs-vsctl show
71f0f455-ccc8-4781-88b2-4b663dd48c5f
    Bridge "ovsbr0"
        Port "vnet1"
            Interface "vnet1"
        Port "ovsbr0"
            Interface "ovsbr0"
                type: internal
        Port "vnet0"
            Interface "vnet0"
        Port "vxlan0"
            Interface "vxlan0"
    ovs_version: "1.7.0"

配置好了之后,你可以尝试用 tcpdump 在 eth0 上捕捉一下包试试,你会发现 L2 是直接封装到 UDP 包中的:

05:39:05.107906 IP 10.16.43.215.52908 > 10.16.43.214.otv: UDP, length
106
        0x0000:  ac16 2d05 143a e839 3530 1d1e 0800 4500
        0x0010:  0086 abd0 0000 4011 62ca 0a10 2bd7 0a10
        0x0020:  2bd6 ceac 2118 0072 0000 0800 0000 0000
        0x0030:  2a00 5254 00f7 e2e7 5254 0090 5117 0800
        0x0040:  4500 0054 0000 4000 4001 f152 c0a8 6402
        0x0050:  c0a8 6403 0800 073c 3e05 0009 ca8f 0f51
        0x0060:  0000 0000 1102 0900 0000 0000 1011 1213
        0x0070:  1415 1617 1819 1a1b 1c1d 1e1f 2021 2223
        0x0080:  2425 2627 2829 2a2b 2c2d 2e2f 3031 3233
        0x0090:  3435 3637

在 openvswitch 上配置 GRE tunnel

如果你是用 openvswitch 内置的 GRE tunnel,那么配置很简单,基本上就一条命令:

ovs-vsctl add-port br0 gre0 -- set interface gre0 type=gre options:remote_ip=192.168.1.10

本文想谈的显然不是这个。因为 upstream 内核(指 Linus tree)中的 openvswitch 是不支持 GRE tunnel 的,那我们如何在 upstream 内核中上使用 GRE tunnel 呢?

其实也不难,我们可以创建一个普通的 GRE tunnel,然后把它添加到 openvswitch bridge 中去就可以了。至少这在理论上是可行的,而实际操作中却有一些问题,你可以动手试一试。我们假设网络环境如下图所示:

我们的目标是让 HOST1 上面的两个 VM 和 HOST2 上面的两个 VM 通过 GRE tunnel 实现通信。因为两边的配置是对称的,所以下面只说明 HOST2 上是如何配置的,HOST1 上以此类推即可。

在这个环境中,一个很可能的错误是把 HOST2 上的 uplink,即 eth0 也加入到 openvswitch 的 bridge 中,这是不对的,需要加入仅仅的是 GRE tunnel 设备,即 gre1 (你当然也可以把它命名为其它名字)。

剩下的一个最重要的问题是,GRE tunnel 是无法回应 ARP 的,因为它是一个 point to point 的设备(ip addr add 192.168.2.1/24 peer 192.168.1.1/24 dev gre1),所以很明显设置了 NOARP。这个问题是这里的关键。因为这个的缘故,即使你在 VM1 上也无法 ping HOST2 上的 gre1。所以这里需要一个技巧,就是要给 bridge 本身配置一个 IP 地址,然后让 bridge 做一个 ARP proxy

所以最后在 HOST2 上面的配置如下:

[root@host2 ~]# ip tunnel show
gre0: gre/ip  remote any  local any  ttl inherit  nopmtudisc
gre1: gre/ip  remote 10.16.43.214  local 10.16.43.215  ttl inherit
[root@host2 ~]# ip r s
192.168.2.0/24 dev ovsbr0  proto kernel  scope link  src 192.168.2.4
192.168.1.0/24 dev gre1  scope link
192.168.122.0/24 dev virbr0  proto kernel  scope link  src 192.168.122.1
10.16.40.0/21 dev eth0  proto kernel  scope link  src 10.16.43.215
169.254.0.0/16 dev eth0  scope link  metric 1005
default via 10.16.47.254 dev eth0
[root@host2 ~]# ovs-vsctl show
71f0f455-ccc8-4781-88b2-4b663dd48c5f
    Bridge "ovsbr0"
        Port "vnet0"
            Interface "vnet0"
        Port "ovsbr0"
            Interface "ovsbr0"
                type: internal
        Port "vnet1"
            Interface "vnet1"
        Port "gre1"
            Interface "gre1"
    ovs_version: "1.7.0"

[root@host2 ~]# ip addr ls gre1 && ip addr ls ovsbr0
17: gre1:  mtu 1476 qdisc noqueue state UNKNOWN
    link/gre 10.16.43.215 peer 10.16.43.214
    inet 192.168.2.1/24 scope global gre1
    inet 192.168.2.1 peer 192.168.1.1/24 scope global gre1
13: ovsbr0:  mtu 1500 qdisc noqueue state UNKNOWN
    link/ether 66:d7:ae:42:db:44 brd ff:ff:ff:ff:ff:ff
    inet 192.168.2.4/24 brd 192.168.2.255 scope global ovsbr0
    inet6 fe80::64d7:aeff:fe42:db44/64 scope link
       valid_lft forever preferred_lft forever

[root@host2 ~]# cat /proc/sys/net/ipv4/conf/ovsbr0/proxy_arp
1

HOST2 上面的 VM1 只需添加一个路由即可,配置如下:
[root@localhost ~]# ip r s
192.168.2.0/24 dev eth1  proto kernel  scope link  src 192.168.2.2
192.168.1.0/24 dev eth1  scope link  src 192.168.2.2
[root@localhost ~]# ping 192.168.1.2
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
64 bytes from 192.168.1.2: icmp_seq=1 ttl=62 time=561 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=62 time=0.731 ms
64 bytes from 192.168.1.2: icmp_seq=3 ttl=62 time=0.669 ms
64 bytes from 192.168.1.2: icmp_seq=4 ttl=62 time=0.765 ms

--- 192.168.1.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3823ms
rtt min/avg/max/mdev = 0.669/140.860/561.275/242.726 ms

用 scapy 发送 vlan 包的例子

scapy 绝对是你应该学习的一大利器,功能十分强大,把 Python 的优势发挥的淋漓尽致。用它发送定制的数据包比你用C写快好几个数量级。。。

下面是我在测试前面文章中的 vlan 问题时发送带指定 vlan tag 的 arp request,不到10行代码就可以搞定!

[python]

!/usr/bin/python

import sys
from scapy.all import *

mac=”52:54:00:2E:23:92”
eth=Ether(src=mac,type=0x8100)/Dot1Q(vlan=int(sys.argv[1]), prio=5)
arp=ARP(hwtype=0x0001,ptype=0x0800,op=0x0001,hwsrc=mac,psrc=’192.168.122.1’,pdst=’192.168.122.74’)
a=eth/arp
a.show()
sendp(a, iface=”virbr0”)

[/python]

bash 网络接口

我很早以前就见过 /dev/tcp/<host>/<port> 这个接口,但一直以为是某个 BSD 内核实现了一个特殊的文件系统,因为 Linux 上面很明显没有 /dev/tcp 这个目录。直到今天我才发现,这个目录其实是 bash 自己实现的!!这么以来就可以不借助wget,nc 来实现网络连接了!设计这么一个接口是要逆天了嘛!

使用这个接口很简单,如下所示:

bash-4.2$ cat </dev/tcp/time.nist.gov/13

56188 12-09-18 15:34:26 50 0 0 733.5 UTC(NIST) *

bash-4.2$ exec 3<>/dev/tcp/www.w3.org/80
bash-4.2$ echo -e “GET / HTTP/1.0nn” >&3
bash-4.2$ cat <&3
HTTP/1.1 200 OK
Date: Tue, 18 Sep 2012 15:41:08 GMT
Server: Apache/2
Content-Location: Home.html
Vary: negotiate,accept
TCN: choice
Last-Modified: Tue, 18 Sep 2012 14:06:27 GMT
ETag: “8f75-4c9fa65657ec0;89-3f26bd17a2f00”
Accept-Ranges: bytes
Content-Length: 36725
Cache-Control: max-age=600
Expires: Tue, 18 Sep 2012 15:51:08 GMT
P3P: policyref=”http://www.w3.org/2001/05/P3P/p3p.xml
Connection: close
Content-Type: text/html; charset=utf-8

在 bash 的源代码树 redir.c 文件中,我们不难发现下面的代码:

[c]
/ A list of pattern/value pairs for filenames that the redirection
code handles specially.
/
static STRING_INT_ALIST _redir_special_filenames[] = {

if !defined (HAVE_DEV_FD)

{ “/dev/fd/[0-9]*”, RF_DEVFD },

endif

if !defined (HAVE_DEV_STDIN)

{ “/dev/stderr”, RF_DEVSTDERR },
{ “/dev/stdin”, RF_DEVSTDIN },
{ “/dev/stdout”, RF_DEVSTDOUT },

endif

if defined (NETWORK_REDIRECTIONS)

{ “/dev/tcp//“, RF_DEVTCP },
{ “/dev/udp//“, RF_DEVUDP },

endif

{ (char *)NULL, -1 }
};


static int
redir_open (filename, flags, mode, ri)
char *filename;
int flags, mode;
enum r_instruction ri;
{
int fd, r;

r = find_string_in_alist (filename, _redir_special_filenames, 1);
if (r >= 0)
return (redir_special_open (r, filename, flags, mode, ri));

/ If we are in noclobber mode, you are not allowed to overwrite
existing files. Check before opening.
/
if (noclobber && CLOBBERING_REDIRECT (ri))
{
fd = noclobber_open (filename, flags, mode, ri);
if (fd == NOCLOBBER_REDIRECT)
return (NOCLOBBER_REDIRECT);
}
else
{
fd = open (filename, flags, mode);

if defined (AFS)

  if ((fd &lt; 0) &amp;&amp; (errno == EACCES))
    {
      fd = open (filename, flags &amp; ~O_CREAT, mode);
      errno = EACCES;       /* restore errno */
    }

endif / AFS /

}

return fd;
}

[/c]

可见,当 bash 发现重定向时它会先查看重定向的文件是不是特殊文件,如果是截获它,自行解释之,否则就打开这个文件。当然了,真正解释 /dev/tcp/HOST/PORT 的代码是在 lib/sh/netopen.c 中。

bash 的手册中也提到了这些特殊接口:

/dev/fd/fd
If fd is a valid integer, file descriptor fd is duplicated.
/dev/stdin
File descriptor 0 is duplicated.
/dev/stdout
File descriptor 1 is duplicated.
/dev/stderr
File descriptor 2 is duplicated.
/dev/tcp/host/port
If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts
to open a TCP connection to the corresponding socket.
/dev/udp/host/port
If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts
to open a UDP connection to the corresponding socket.

撤销 git 操作

今天脑抽了一下,在做 git rebase -i 的时候无意识地把某一个 commit 给合并到前一个 commit 里去了,结果是该 commit 丢失,而且前一个 commit 也搞砸了。怎么能撤销这个操作呢?

Google 搜索了一下,发现了还有 git reflog 这个命令,可以查看 git 命令的历史记录,比如我刚才的操作:

324c85e HEAD@{0}: rebase -i (finish): returning to refs/heads/ipv6
324c85e HEAD@{1}: checkout: moving from ipv6 to 324c85e
324c85e HEAD@{2}: rebase -i (finish): returning to refs/heads/ipv6
324c85e HEAD@{3}: rebase -i (pick): ipv6: use xxxx
9fc8e64 HEAD@{4}: rebase -i (fixup): ipv6: unify xxxx
2ea2149 HEAD@{5}: rebase -i (fixup): updating HEAD
8d1f07b HEAD@{6}: checkout: moving from ipv6 to 8d1f07b
0e118d2 HEAD@{7}: commit: ipv6: separate xxxx

可以看出 HEAD@{7} 这个点就是我应该重置回去的,然后执行 git reset HEAD@{7} 就行了!

通过 git reflog 也可以撤销 git reset 的操作,非常实用。

另外,我很惊讶似乎还有不少人不知道 git rebase -i 这个命令。这个命令真的十分强大,我几乎天天都在用,它既可以用来合并补丁,也可以用来切割补丁,还能修改 commit 的记录(具体可自行参考 git-rebase 的手册),可以说是 git 命令中的一把瑞士军刀啊!不过如果你的 git tree 是 public 的,最好不用或者少用 git rebase,我用 git rebase 大多都是在自己的非 public 的树上。

KVM 中搭建 VLAN 和 IPv6 环境

普通的 IPv4 环境很简单,如果你使用 virt-manager 的话它自动都给你搭好了。每个 kvm guest 都在同一个子网内,通过 bridge 连接到一起,然后通过 host 上的 NAT 访问外网,如下所示:

# brctl show
bridge name bridge id STP enabled interfaces
virbr0 8000.5254002e2392 yes virbr0-nic
vnet0
vnet1

iptables -L -t nat

Chain PREROUTING (policy ACCEPT)
target prot opt source destination

Chain INPUT (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination

Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE tcp — 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535
MASQUERADE udp — 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535
MASQUERADE all — 192.168.122.0/24 !192.168.122.0/24
稍微有点儿困难的是在此基础上搭建 VLAN 和 IPv6 环境。

先看VLAN,虽然我们现在还没有 openvswitch,我们可以通过 bridge 设置一个简单的 vlan 局域网。因为 virt-manager 默认使用的是 192.168.122.0/24,我为VLAN单独开了一个 192.168.1.0/24,有两台机器 A 和 B,A 上运行此VLAN的 DHCP 服务,B 就可以直接动态获取地址了。

机器 A 上运行:

vconfig add eth0 2
ifconfig eth0.2 192.168.1.1
dnsmasq —listen-address=192.168.1.1 -i eth0.2 —dhcp-range=192.168.1.2,192.168.1.240

机器 B 上运行:

vconfig add eth0 2
dhclient eth0.2

注:vconfig 已经过时了,现在都使用 ip,我之所以还使用它是因为整个命令比较短。;-)

再来看 IPv6,因为我对 IPv6 不熟悉,所以折腾了好长时间最后还是不得不使用 sit tunnel,而且 IP 地址也是静态手工设置的。还是假设 A 和 B 两台机器,先让 IPv4 在上面跑起来,然后在两台机器上分别运行下面的命令:

echo 1 > /proc/sys/net/ipv4/conf/all/forwarding
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
ip tunnel add sit1 mode sit local ${SELF} remote ${REMOTE} dev eth0
ip -6 addr add fd00:1:2:3::2/64 dev sit1
ip -6 route add fd00:1:2:3::1/64 dev sit1
ifconfig sit1 up

${SELF} 为自己的IPv4地址;${REMOTE}为对方的IPv4地址,即在A上面为B的地址,B上面为A的地址。他们的IPv6地址分别是fd00:1:2:3::1 和 fd00:1:2:3::2。

Update:如果使用 libvirt 的话,可以用 virsh net-edit <NET> 编辑网络配置,添加下面两行:

<ip family=’ipv6’ address=’fd00:1:2:3::1’ prefix=’64’>
</ip>

可参考这里

Dracut: Introduction and Overview

这是我今年去日本参加 LinuxCon 要讲的,主要是最近在做的一些和 dracut 相关的东西。

Slides 可以在这里下载:dracut.pdf

另外,今年日本 LinuxCon 的会议日程在这里。今年没有 Linus 大神了,他去年去参加果然是因为 Linux 20周年的原因。

抱怨一句:好羡慕 akong 他们部门,公司赞助参加 KVM Forum,光去听就可以。不像我这样,没有要讲的东西连去都去不了;就算有东西要讲,公司也不赞助,费用都是回来找 Linux Foudation 包销;开会三天还不能算工作日……唉,同一个公司的人出去开会差距怎么这么大呢……

bash 反斜线的一个疑问

ABS 里在讲解 echo 的转义时,给出过下面这几个例子:

bash-4.2$ echo \z
z
bash-4.2$ echo \z
z
bash-4.2$ echo echo \z
z
bash-4.2$ echo echo \z
z

我们知道反引号会把里面命令的输出直接传递给外面的命令,那么,既然前两个echo的输出一样,为什么后面就不一样呢?这里一定发生了什么我们没有注意到。我来根据我的理解来解释一下。

从第一个开始看,”\z”中的第一个反斜线表示转义后面的那个反斜线,所以输出””,”z”直接输出,最后得到“z”。

第二个也不难,”\z”中前两个反斜线同样输出””,后面剩下”z”,因为 echo 默认不解释转义,所以就算是”z”有特殊含义也不会被解释,因此”z”中的””会把”z”的特殊含义(本身就没有)去掉,然后输出”z”,最后得到”z”。

从第三个开始就复杂了。因为有了 subshell,所以里面的反斜线一定被处理过两次:一次是 subshell 中,另一次是外面的 shell。外面的shell开始处理,”\”同样被处理成””,处理完成后得到:echo echo z;然后 subshell 开始执行里面的”echo z”,处理””然后输出z,最后外面的shell 看到的是 echo z,输出”z”,结束。

第四个更复杂一些,外面的shell处理反斜线后得到:echo echo \z,前两个”\”依旧被解释成””,剩下的”z”会原封不动,因为”z”对于外面的shell来说没兴趣,它只对”`”或”$”这样的转义感兴趣!subshell 执行“echo \z”,同第一个例子,输出z,这时外面的shell看到的是echo z,输出”z”,结束。

例子虽简单,却能反映出我们确实对 bash 还有一些角落的地方没有理解清楚。理解 ABS 后面的例子就留给你作为课后作业了。:-)

扩展文件属性

我们知道,在 Linux 上最基本的文件属性是,

# ls -l foo
-rw-r--r--. 1 root root 0 Jan 19 00:03 foo

可以通过chmod 命令来更改,ls -l 来查看。

除此之外,一些文件系统上还提供额外的属性,比如 ext4 提供了额外的属性,比如 append only (a), compressed (c), immutable (i) 等属性,这些属性可以通过 e2fsprogs 软件包提供的 chattr(1) 来改变,lsattr(1) 来查看。更多属性也可参考 chattr(1)。比如:

# chattr +i foo
# lsattr foo
----i--------e- foo

这个是和文件系统密切相关的,一是因为 chattr 来自 e2fsprogs,二是因为它调用的系统调用也是和文件系统相关的一个 ioctl,可以看下面 strace 的输出:

open("foo", O_RDONLY|O_NONBLOCK|O_LARGEFILE) = 3
ioctl(3, EXT2_IOC_SETFLAGS or EXT3_IOC_SETFLAGS or FIONCLEX, 0xfffc347335c) = 0
close(3)                                = 0

还有一种文件属性叫扩展文件属性,eXtended ATTRibutes,也就是通常所说的 xattr。和前一种相比,这个就不那么和文件系统密切相关了,但是仍然需要文件系统的支持。这个是通过系统调用 getxattr(2) 和 setxattr(2) 来实现的,对应的命令是 getfattr(1) 和 setfattr(1),来自 attr 软件包。看例子:

# mount -o remount,acl,user_xattr /home
# touch bar
# setfattr -n user.comment -v "this is a comment" bar
# getfattr bar
# file: bar
user.comment

# getfattr bar -n user.comment
# file: bar
user.comment="this is a comment"

需要注意两点:一,挂载时需要加 "user_xattr" 选项;二,name 是有命名空间的,并不是任何命名空间都可以,比如,
# setfattr -n my.comment -v "this is my comment" bar
setfattr: bar: Operation not supported
用户只能用 user.* 名字,而后面的 ACL 则会用 system.posix_acl_access。当然了,SELinux 也会用到 xattr,security.*。 基于此,ACL 实现了访问控制列表,也就是说,设置某些用户的文件权限不再依赖于用户所在的组了,省去了不少麻烦。对应的命令是 setfacl(1) 和 getfacl(1) ,来自 [acl 软件包](http://acl.bestbits.at/)。从下面 strace 的输出,也可以看出它们确实是基于 xattr 的:
getxattr("foo", "system.posix_acl_access", 0xfffe455cca0, 132) = -1 ENODATA (No data available)
stat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
setxattr("foo", "system.posix_acl_access", "x02x00x00x00x01x00x06x00xffxffxffxffx02x00x06x00x00x00x00x00x04x00x04x00xffxffxffxffx10x00x06x00xffxffxffxff x00x04x00xffxffxffxff", 44, 0) = -1 EPERM (Operation not permitted)

之所以出现 ENODATA 是因为挂载文件系统时我没有指定”acl”,需要重新挂载:mount -o remount,acl /home 然后重新创建文件,因为旧的文件依旧是没有 ACL的。