● ● ● ● ●
● ○ ○ ○ ● ○
● ● ● ●
… …
packet_socket = socket(AF_PACKET, int socket_type, int protocol);
● ○ ● ○ ● ○ ● ○ ● ●
● ○ htons(ETH_P_EAPOL), htons(ETH_P_IP) ○ … …
● ○ ○ ○ … … … …
● ○ ● ○ ● ○ ● struct sockaddr_ll { unsigned short sll_family; unsigned short sll_protocol; int sll_ifindex; unsigned short sll_hatype; unsigned char sll_pkttype; unsigned char sll_halen; unsigned char sll_addr[8]; };
● ○ setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, …) ● # sudo tcpdump -d arp (000) ldh [12] (001) jeq #0x806 jt 2 jf 3 (002) ret #262144 (003) ret #0 # sudo tcpdump -dd arp { 0x28, 0, 0, 0x0000000c }, { 0x15, 0, 1, 0x00000806 }, { 0x6, 0, 0, 0x00040000 }, { 0x6, 0, 0, 0x00000000 },
● ● ● ● ●
● ● ○ ○
● ● ● ● ● ● ● ● ○ …
● ● ● ●
int nf_register_net_hook(struct net *net, const struct nf_hook_ops *ops); int nf_register_net_hooks(struct net *net, const struct nf_hook_ops *reg, unsigned int n); int nf_register_hook(struct nf_hook_ops *reg); int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n);
static struct nf_hook_ops ipv4_synproxy_ops[] = { { .hook = ipv4_synproxy_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, .priv = NULL, }, { .hook = ipv4_synproxy_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, .priv = NULL, }, };
static unsigned int ipv4_synproxy_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *nhs) { do_stuff(); if (...) return NF_ACCEPT; do_more_stuff(); if (...) return NF_DROP; return NF_ACCEPT; }
● ○ ● ○ ● ○ ● ○ ● ○
● ● ○ ○ ● ○ ○ ○ ● ●
● ● ● ○ ○ ○ ● ○ ●
● ● ● ○ ● ● ● ○ ○
● ● ● ● ● ●
● ○ ○ ○ ○ ○
… …
● ●
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip src 4.3.2.1/32 match ip sport 80 0xffff flowid 1:3 # tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match u16 0x0000 0xffc0 at 2 flowid 1:4 # tc filter add dev eth0 parent 1:0 protocol ip prio 1 handle 1 fw classid 1:10 # tc filter add dev eth0 parent 1: basic match 'meta(priority eq 6)' classid 1:10 # tc filter add dev eth0 parent 1:0 bpf obj bpf.o sec mycls flowid 1:1
● ● ● ○ ●
… …
# tc qdisc add dev eth0 ingress # tc filter add dev eth0 parent ffff: protocol all prio 1 u32 match u32 0 0 action mirred egress redirect dev eth1 # tc filter add dev eth0 parent ffff: protocol ip prio 6 u32 match ip src 10.0.0.9/32 action drop # tc filter add dev eth0 parent ffff: pref 11 protocol ip u32 match ip protocol 1 0xff flowid 1:1 u32 match ip src 10.0.0.2 flowid 1:1 action vlan push id 123 # tc filter replace dev eth0 parent ffff: basic action bpf obj bpf.o sec my-action
● ● ● ● ○ ● ○ ●
● ○ ○ ○ ● ○ ○ ○ ● →
● ● ○
● ● ● ● ● ● ● ● ● ●
static inline void set_tcp_dest_port(struct __sk_buff *skb, __u16 new_port) { __u16 old_port = htons(load_half(skb, TCP_DPORT_OFF)); bpf_skb_store_bytes(skb, TCP_DPORT_OFF, &new_port, sizeof(new_port), 0); bpf_l4_csum_replace(skb, TCP_CSUM_OFF, old_port, new_port, sizeof(new_port)); } __attribute__((section("redirect_xmit"), used)) int _redirect_xmit(struct __sk_buff *skb) { __u8 proto = load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol)); if (proto == IPPROTO_TCP) set_tcp_dest_port(skb, 5001); return bpf_redirect(skb->ifindex + 1, 0); }
struct bpf_map_def __attribute__((section("maps"), used)) my_map = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(long), .max_entries = 256, }; __attribute__((section("socket1"), used)) int bpf_prog1(struct __sk_buff *skb) { int index = load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol)); long *value; if (skb->pkt_type != PACKET_OUTGOING) return 0; value = bpf_map_lookup_elem(&my_map, &index); if (value) __sync_fetch_and_add(value, skb->len); return 0; }
● ● ○ ○ ○ ○ ● ● ● ○
● ● ● ●
● ● ● ● ● ● ● ●

Specializing the Data Path - Hooking into the Linux Network Stack