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 *) &eth->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 *) &eth->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__ */