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 -- 2.9.5