mbox series

[v3,bpf-next,0/9] bpf: introduce cgroup-bpf bind, connect, post-bind hooks

Message ID 20180330220808.763556-1-ast@kernel.org
Headers show
Series bpf: introduce cgroup-bpf bind, connect, post-bind hooks | expand

Message

Alexei Starovoitov March 30, 2018, 10:07 p.m. UTC
v2->v3:
- rebase due to conflicts
- fix ipv6=m build

v1->v2:
- support expected_attach_type at prog load time so that prog (incl.
  context accesses and calls to helpers) can be validated with regard to
  specific attach point it is supposed to be attached to.
  Later, at attach time, attach type is checked so that it must be same as
  at load time if it was provided
- reworked hooks to rely on expected_attach_type, and reduced number of new
  prog types from 6 to just 1: BPF_PROG_TYPE_CGROUP_SOCK_ADDR
- reused BPF_PROG_TYPE_CGROUP_SOCK for sys_bind post-hooks
- add selftests for post-sys_bind hook

For our container management we've been using complicated and fragile setup
consisting of LD_PRELOAD wrapper intercepting bind and connect calls from
all containerized applications. Unfortunately it doesn't work for apps that
don't use glibc and changing all applications that run in the datacenter
is not possible due to 3rd party code and libraries (despite being
open source code) and sheer amount of legacy code that has to be rewritten
(we're rewriting what we can in parallel)

These applications are written without containers in mind and have
builtin assumptions about network services. Like an application X
expects to connect localhost:special_port and find service Y in there.
To move application X and service Y into two different containers
LD_PRELOAD approach is used to help one service connect to another
without rewriting them.
Moving these two applications into different L2 (netns) or L3 (vrf)
network isolation scopes doesn't help to solve the problem, since
applications need to see each other like they were running on
the host without containers.
So if app X and app Y would run in different netns something
would need to punch a connectivity hole in those namespaces.
That would be real layering violation (with corresponding
network debugging pains), since clean l2, l3 abstraction would
suddenly support something that breaks through the layers.

Instead we used LD_PRELOAD (and now bpf programs) at bind/connect
time to help applications discover and connect to each other.
All applications are running in init_nens and there are no vrfs.
After bind/connect the normal fib/neighbor core networking
logic works as it should always do and the whole system is
clean from network point of view and can be debugged with
standard tools.

We also considered resurrecting Hannes's afnetns work,
but all hierarchical namespace abstraction don't work due
to these builtin networking assumptions inside the apps.
To run an application inside cgroup container that was not written
with containers in mind we have to make an illusion of running
in non-containerized environment.
In some cases we remember the port and container id in the post-bind hook
in a bpf map and when some other task in a different container is trying
to connect to a service we need to know where this service is running.
It can be remote and can be local. Both client and service may or may not
be written with containers in mind and this sockaddr rewrite is providing
connectivity and load balancing feature.

BPF+cgroup looks to be the best solution for this problem.
Hence we introduce 3 hooks:
- at entry into sys_bind and sys_connect
  to let bpf prog look and modify 'struct sockaddr' provided
  by user space and fail bind/connect when appropriate
- post sys_bind after port is allocated

The approach works great and has zero overhead for anyone who doesn't
use it and very low overhead when deployed.

Different use case for this feature is to do low overhead firewall
that doesn't need to inspect all packets and works at bind/connect time.

Andrey Ignatov (9):
  bpf: Check attach type at prog load time
  libbpf: Support expected_attach_type at prog load
  bpf: Hooks for sys_bind
  selftests/bpf: Selftest for sys_bind hooks
  net: Introduce __inet_bind() and __inet6_bind
  bpf: Hooks for sys_connect
  selftests/bpf: Selftest for sys_connect hooks
  bpf: Post-hooks for sys_bind
  selftests/bpf: Selftest for sys_bind post-hooks.

 include/linux/bpf-cgroup.h                    |  68 ++-
 include/linux/bpf.h                           |   5 +-
 include/linux/bpf_types.h                     |   1 +
 include/linux/filter.h                        |  11 +
 include/net/addrconf.h                        |   7 +
 include/net/inet_common.h                     |   2 +
 include/net/ipv6.h                            |   2 +
 include/net/sock.h                            |   3 +
 include/net/udp.h                             |   1 +
 include/uapi/linux/bpf.h                      |  51 ++-
 kernel/bpf/cgroup.c                           |  39 +-
 kernel/bpf/syscall.c                          | 102 ++++-
 kernel/bpf/verifier.c                         |   7 +-
 kernel/trace/bpf_trace.c                      |  27 +-
 net/core/filter.c                             | 442 +++++++++++++++++--
 net/ipv4/af_inet.c                            |  71 +++-
 net/ipv4/tcp_ipv4.c                           |  16 +
 net/ipv4/udp.c                                |  14 +
 net/ipv6/af_inet6.c                           |  66 ++-
 net/ipv6/tcp_ipv6.c                           |  16 +
 net/ipv6/udp.c                                |  20 +
 tools/include/uapi/linux/bpf.h                |  51 ++-
 tools/lib/bpf/bpf.c                           |  44 +-
 tools/lib/bpf/bpf.h                           |  17 +-
 tools/lib/bpf/libbpf.c                        | 113 +++--
 tools/lib/bpf/libbpf.h                        |   8 +
 tools/testing/selftests/bpf/Makefile          |  10 +-
 tools/testing/selftests/bpf/bpf_helpers.h     |   2 +
 tools/testing/selftests/bpf/connect4_prog.c   |  45 ++
 tools/testing/selftests/bpf/connect6_prog.c   |  61 +++
 tools/testing/selftests/bpf/test_sock.c       | 479 +++++++++++++++++++++
 tools/testing/selftests/bpf/test_sock_addr.c  | 588 ++++++++++++++++++++++++++
 tools/testing/selftests/bpf/test_sock_addr.sh |  57 +++
 33 files changed, 2314 insertions(+), 132 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/connect4_prog.c
 create mode 100644 tools/testing/selftests/bpf/connect6_prog.c
 create mode 100644 tools/testing/selftests/bpf/test_sock.c
 create mode 100644 tools/testing/selftests/bpf/test_sock_addr.c
 create mode 100755 tools/testing/selftests/bpf/test_sock_addr.sh

Comments

Daniel Borkmann March 31, 2018, 9:22 p.m. UTC | #1
On 03/31/2018 12:07 AM, Alexei Starovoitov wrote:
> v2->v3:
> - rebase due to conflicts
> - fix ipv6=m build
> 
> v1->v2:
> - support expected_attach_type at prog load time so that prog (incl.
>   context accesses and calls to helpers) can be validated with regard to
>   specific attach point it is supposed to be attached to.
>   Later, at attach time, attach type is checked so that it must be same as
>   at load time if it was provided
> - reworked hooks to rely on expected_attach_type, and reduced number of new
>   prog types from 6 to just 1: BPF_PROG_TYPE_CGROUP_SOCK_ADDR
> - reused BPF_PROG_TYPE_CGROUP_SOCK for sys_bind post-hooks
> - add selftests for post-sys_bind hook
[...]

Applied to bpf-next, thanks everyone!