0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #define KBUILD_MODNAME "foo"
0014 #include <uapi/linux/bpf.h>
0015 #include <linux/in.h>
0016 #include <linux/if_ether.h>
0017 #include <linux/if_packet.h>
0018 #include <linux/if_vlan.h>
0019 #include <linux/ip.h>
0020 #include <linux/icmp.h>
0021 #include <bpf/bpf_helpers.h>
0022
0023 #define DEFAULT_TTL 64
0024 #define MAX_PCKT_SIZE 600
0025 #define ICMP_TOOBIG_SIZE 98
0026 #define ICMP_TOOBIG_PAYLOAD_SIZE 92
0027
0028
0029 static volatile __u32 max_pcktsz = MAX_PCKT_SIZE;
0030
0031 struct {
0032 __uint(type, BPF_MAP_TYPE_ARRAY);
0033 __type(key, __u32);
0034 __type(value, __u64);
0035 __uint(max_entries, 1);
0036 } icmpcnt SEC(".maps");
0037
0038 static __always_inline void count_icmp(void)
0039 {
0040 u64 key = 0;
0041 u64 *icmp_count;
0042
0043 icmp_count = bpf_map_lookup_elem(&icmpcnt, &key);
0044 if (icmp_count)
0045 *icmp_count += 1;
0046 }
0047
0048 static __always_inline void swap_mac(void *data, struct ethhdr *orig_eth)
0049 {
0050 struct ethhdr *eth;
0051
0052 eth = data;
0053 memcpy(eth->h_source, orig_eth->h_dest, ETH_ALEN);
0054 memcpy(eth->h_dest, orig_eth->h_source, ETH_ALEN);
0055 eth->h_proto = orig_eth->h_proto;
0056 }
0057
0058 static __always_inline __u16 csum_fold_helper(__u32 csum)
0059 {
0060 return ~((csum & 0xffff) + (csum >> 16));
0061 }
0062
0063 static __always_inline void ipv4_csum(void *data_start, int data_size,
0064 __u32 *csum)
0065 {
0066 *csum = bpf_csum_diff(0, 0, data_start, data_size, *csum);
0067 *csum = csum_fold_helper(*csum);
0068 }
0069
0070 static __always_inline int send_icmp4_too_big(struct xdp_md *xdp)
0071 {
0072 int headroom = (int)sizeof(struct iphdr) + (int)sizeof(struct icmphdr);
0073
0074 if (bpf_xdp_adjust_head(xdp, 0 - headroom))
0075 return XDP_DROP;
0076 void *data = (void *)(long)xdp->data;
0077 void *data_end = (void *)(long)xdp->data_end;
0078
0079 if (data + (ICMP_TOOBIG_SIZE + headroom) > data_end)
0080 return XDP_DROP;
0081
0082 struct iphdr *iph, *orig_iph;
0083 struct icmphdr *icmp_hdr;
0084 struct ethhdr *orig_eth;
0085 __u32 csum = 0;
0086 __u64 off = 0;
0087
0088 orig_eth = data + headroom;
0089 swap_mac(data, orig_eth);
0090 off += sizeof(struct ethhdr);
0091 iph = data + off;
0092 off += sizeof(struct iphdr);
0093 icmp_hdr = data + off;
0094 off += sizeof(struct icmphdr);
0095 orig_iph = data + off;
0096 icmp_hdr->type = ICMP_DEST_UNREACH;
0097 icmp_hdr->code = ICMP_FRAG_NEEDED;
0098 icmp_hdr->un.frag.mtu = htons(max_pcktsz - sizeof(struct ethhdr));
0099 icmp_hdr->checksum = 0;
0100 ipv4_csum(icmp_hdr, ICMP_TOOBIG_PAYLOAD_SIZE, &csum);
0101 icmp_hdr->checksum = csum;
0102 iph->ttl = DEFAULT_TTL;
0103 iph->daddr = orig_iph->saddr;
0104 iph->saddr = orig_iph->daddr;
0105 iph->version = 4;
0106 iph->ihl = 5;
0107 iph->protocol = IPPROTO_ICMP;
0108 iph->tos = 0;
0109 iph->tot_len = htons(
0110 ICMP_TOOBIG_SIZE + headroom - sizeof(struct ethhdr));
0111 iph->check = 0;
0112 csum = 0;
0113 ipv4_csum(iph, sizeof(struct iphdr), &csum);
0114 iph->check = csum;
0115 count_icmp();
0116 return XDP_TX;
0117 }
0118
0119
0120 static __always_inline int handle_ipv4(struct xdp_md *xdp)
0121 {
0122 void *data_end = (void *)(long)xdp->data_end;
0123 void *data = (void *)(long)xdp->data;
0124 int pckt_size = data_end - data;
0125 int offset;
0126
0127 if (pckt_size > max(max_pcktsz, ICMP_TOOBIG_SIZE)) {
0128 offset = pckt_size - ICMP_TOOBIG_SIZE;
0129 if (bpf_xdp_adjust_tail(xdp, 0 - offset))
0130 return XDP_PASS;
0131 return send_icmp4_too_big(xdp);
0132 }
0133 return XDP_PASS;
0134 }
0135
0136 SEC("xdp_icmp")
0137 int _xdp_icmp(struct xdp_md *xdp)
0138 {
0139 void *data_end = (void *)(long)xdp->data_end;
0140 void *data = (void *)(long)xdp->data;
0141 struct ethhdr *eth = data;
0142 __u16 h_proto;
0143
0144 if (eth + 1 > data_end)
0145 return XDP_DROP;
0146
0147 h_proto = eth->h_proto;
0148
0149 if (h_proto == htons(ETH_P_IP))
0150 return handle_ipv4(xdp);
0151 else
0152 return XDP_PASS;
0153 }
0154
0155 char _license[] SEC("license") = "GPL";