github.com/datadog/cilium@v1.6.12/bpf/lib/arp.h (about) 1 /* 2 * Copyright (C) 2016-2019 Authors of Cilium 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 */ 18 #ifndef __LIB_ARP__ 19 #define __LIB_ARP__ 20 21 #include <linux/if_arp.h> 22 #include <linux/if_ether.h> 23 #include "eth.h" 24 #include "dbg.h" 25 #include "drop.h" 26 27 struct arp_eth { 28 unsigned char ar_sha[ETH_ALEN]; 29 __be32 ar_sip; 30 unsigned char ar_tha[ETH_ALEN]; 31 __be32 ar_tip; 32 } __attribute__((packed)); 33 34 /* Check if packet is ARP request for IP */ 35 static inline int arp_check(struct ethhdr *eth, struct arphdr *arp, 36 struct arp_eth *arp_eth, union macaddr *mac) 37 { 38 union macaddr *dmac = (union macaddr *) ð->h_dest; 39 40 return arp->ar_op == bpf_htons(ARPOP_REQUEST) && 41 arp->ar_hrd == bpf_htons(ARPHRD_ETHER) && 42 (eth_is_bcast(dmac) || !eth_addrcmp(dmac, mac)); 43 } 44 45 static inline int arp_prepare_response(struct __sk_buff *skb, struct ethhdr *eth, 46 struct arp_eth *arp_eth, __be32 ip, 47 union macaddr *mac) 48 { 49 union macaddr smac = *(union macaddr *) ð->h_source; 50 __be32 sip = arp_eth->ar_sip; 51 __be16 arpop = bpf_htons(ARPOP_REPLY); 52 53 if (eth_store_saddr(skb, mac->addr, 0) < 0 || 54 eth_store_daddr(skb, smac.addr, 0) < 0 || 55 skb_store_bytes(skb, 20, &arpop, sizeof(arpop), 0) < 0 || 56 skb_store_bytes(skb, 22, mac, 6, 0) < 0 || 57 skb_store_bytes(skb, 28, &ip, 4, 0) < 0 || 58 skb_store_bytes(skb, 32, &smac, sizeof(smac), 0) < 0 || 59 skb_store_bytes(skb, 38, &sip, sizeof(sip), 0) < 0) 60 return DROP_WRITE_ERROR; 61 62 return 0; 63 } 64 65 static inline int arp_respond(struct __sk_buff *skb, union macaddr *mac, int direction) 66 { 67 void *data_end = (void *) (long) skb->data_end; 68 void *data = (void *) (long) skb->data; 69 struct arphdr *arp = data + ETH_HLEN; 70 struct ethhdr *eth = data; 71 struct arp_eth *arp_eth; 72 int ret; 73 74 if (data + ETH_HLEN + sizeof(*arp) + sizeof(*arp_eth) > data_end) 75 return TC_ACT_OK; 76 77 arp_eth = data + ETH_HLEN + sizeof(*arp); 78 79 if (arp_check(eth, arp, arp_eth, mac)) { 80 __be32 target_ip = arp_eth->ar_tip; 81 ret = arp_prepare_response(skb, eth, arp_eth, target_ip, mac); 82 if (unlikely(ret != 0)) 83 goto error; 84 85 cilium_dbg_capture(skb, DBG_CAPTURE_DELIVERY, skb->ifindex); 86 return redirect(skb->ifindex, direction); 87 } 88 89 /* Pass any unknown ARP requests to the Linux stack */ 90 return TC_ACT_OK; 91 92 error: 93 return send_drop_notify_error(skb, 0, ret, TC_ACT_SHOT, METRIC_EGRESS); 94 } 95 96 #endif /* __LIB_ARP__ */