bpf/accesslog/syscalls/connect_conntrack.c (109 lines of code) (raw):

// Licensed to Apache Software Foundation (ASF) under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Apache Software Foundation (ASF) licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. #include "connect_conntrack.h" #include "../common/data_args.h" static __always_inline void nf_conntrack_read_in6_addr(__u64 *addr_h, __u64 *addr_l, const struct in6_addr *in6) { bpf_probe_read(addr_h, sizeof(*addr_h), &in6->s6_addr32[0]); bpf_probe_read(addr_l, sizeof(*addr_l), &in6->s6_addr32[2]); } static __always_inline int nf_conntrack_tuple_to_conntrack_tuple(struct connect_args_t *connect_args, conntrack_tuple_t *t, const struct nf_conntrack_tuple *ct) { __builtin_memset(t, 0, sizeof(conntrack_tuple_t)); switch (ct->dst.protonum) { case IPPROTO_TCP: t->sport = ct->src.u.tcp.port; t->dport = ct->dst.u.tcp.port; break; case IPPROTO_UDP: t->sport = ct->src.u.udp.port; t->dport = ct->dst.u.udp.port; break; default: return 0; } t->sport = bpf_ntohs(t->sport); t->dport = bpf_ntohs(t->dport); if (t->sport == 0 || t->dport == 0) { return 0; } if (ct->src.l3num == AF_INET) { t->saddr_l = ct->src.u3.ip; t->daddr_l = ct->dst.u3.ip; if (!t->saddr_l || !t->daddr_l) { return 0; } } else if (ct->src.l3num == AF_INET6) { nf_conntrack_read_in6_addr(&t->saddr_h, &t->saddr_l, &ct->src.u3.in6); nf_conntrack_read_in6_addr(&t->daddr_h, &t->daddr_l, &ct->dst.u3.in6); if (!t->saddr_h || !t->saddr_l || !t->daddr_h || !t->daddr_l) { return 0; } } struct sock *sock = connect_args->sock; struct socket *tmps = _(sock->sk_socket); if (tmps != NULL) { struct sock* s; BPF_CORE_READ_INTO(&s, tmps, sk); short unsigned int skc_family; BPF_CORE_READ_INTO(&skc_family, s, __sk_common.skc_family); if (skc_family == AF_INET) { __u16 local_port; BPF_CORE_READ_INTO(&local_port, s, __sk_common.skc_num); __u32 local_addr_v4; BPF_CORE_READ_INTO(&local_addr_v4, s, __sk_common.skc_rcv_saddr); // make sure connntrack with the same socket address if (local_addr_v4 != t->daddr_l || local_port != t->dport) { return 0; } } } return 1; } static __always_inline int nf_conn_aware(struct pt_regs* ctx, struct nf_conn *ct) { if (ct == NULL) { return 0; } __u64 id = bpf_get_current_pid_tgid(); struct connect_args_t *connect_args = bpf_map_lookup_elem(&conecting_args, &id); if (!connect_args) { return 0; } // already contains the remote address if (connect_args->has_remote && &(connect_args->remote) != NULL) { return 0; } __u32 status; if (bpf_probe_read(&status, sizeof(status), &(ct->status)) != 0) { return 0; // Invalid ct pointer } if (!(status & IPS_CONFIRMED)) { return 0; } if (!(status & IPS_NAT_MASK)) { return 0; } struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; if (bpf_probe_read(&tuplehash, sizeof(tuplehash), &(ct->tuplehash)) != 0) { return 0; // Invalid ct pointer } struct nf_conntrack_tuple reply = tuplehash[IP_CT_DIR_REPLY].tuple; conntrack_tuple_t reply_conn = {}; if (!nf_conntrack_tuple_to_conntrack_tuple(connect_args, &reply_conn, &reply)) { return 0; } struct connect_track_remote remote = {}; remote.iph = reply_conn.saddr_h; remote.ipl = reply_conn.saddr_l; remote.port = reply_conn.sport; connect_args->remote = remote; connect_args->has_remote = 1; bpf_map_update_elem(&conecting_args, &id, connect_args, 0); return 0; } SEC("kprobe/__nf_conntrack_hash_insert") int nf_conntrack_hash_insert(struct pt_regs* ctx) { return nf_conn_aware(ctx, (struct nf_conn*)PT_REGS_PARM1(ctx)); } SEC("kprobe/nf_confirm") int nf_confirm(struct pt_regs* ctx) { return nf_conn_aware(ctx, (struct nf_conn*)PT_REGS_PARM3(ctx)); } SEC("kprobe/ctnetlink_fill_info") int nf_ctnetlink_fill_info(struct pt_regs* ctx) { return nf_conn_aware(ctx, (struct nf_conn*)PT_REGS_PARM5(ctx)); }