]>
Commit | Line | Data |
---|---|---|
fe616055 DA |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2017-18 David Ahern <dsahern@gmail.com> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of version 2 of the GNU General Public | |
6 | * License as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, but | |
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
11 | * General Public License for more details. | |
12 | */ | |
13 | #define KBUILD_MODNAME "foo" | |
14 | #include <uapi/linux/bpf.h> | |
15 | #include <linux/in.h> | |
16 | #include <linux/if_ether.h> | |
17 | #include <linux/if_packet.h> | |
18 | #include <linux/if_vlan.h> | |
19 | #include <linux/ip.h> | |
20 | #include <linux/ipv6.h> | |
21 | ||
22 | #include "bpf_helpers.h" | |
23 | ||
24 | #define IPV6_FLOWINFO_MASK cpu_to_be32(0x0FFFFFFF) | |
25 | ||
26 | struct bpf_map_def SEC("maps") tx_port = { | |
27 | .type = BPF_MAP_TYPE_DEVMAP, | |
28 | .key_size = sizeof(int), | |
29 | .value_size = sizeof(int), | |
30 | .max_entries = 64, | |
31 | }; | |
32 | ||
44edef77 DA |
33 | /* from include/net/ip.h */ |
34 | static __always_inline int ip_decrease_ttl(struct iphdr *iph) | |
35 | { | |
36 | u32 check = (__force u32)iph->check; | |
37 | ||
38 | check += (__force u32)htons(0x0100); | |
39 | iph->check = (__force __sum16)(check + (check >= 0xFFFF)); | |
40 | return --iph->ttl; | |
41 | } | |
42 | ||
fe616055 DA |
43 | static __always_inline int xdp_fwd_flags(struct xdp_md *ctx, u32 flags) |
44 | { | |
45 | void *data_end = (void *)(long)ctx->data_end; | |
46 | void *data = (void *)(long)ctx->data; | |
47 | struct bpf_fib_lookup fib_params; | |
48 | struct ethhdr *eth = data; | |
44edef77 DA |
49 | struct ipv6hdr *ip6h; |
50 | struct iphdr *iph; | |
fe616055 DA |
51 | int out_index; |
52 | u16 h_proto; | |
53 | u64 nh_off; | |
54 | ||
55 | nh_off = sizeof(*eth); | |
56 | if (data + nh_off > data_end) | |
57 | return XDP_DROP; | |
58 | ||
59 | __builtin_memset(&fib_params, 0, sizeof(fib_params)); | |
60 | ||
61 | h_proto = eth->h_proto; | |
62 | if (h_proto == htons(ETH_P_IP)) { | |
44edef77 | 63 | iph = data + nh_off; |
fe616055 DA |
64 | |
65 | if (iph + 1 > data_end) | |
66 | return XDP_DROP; | |
67 | ||
44edef77 DA |
68 | if (iph->ttl <= 1) |
69 | return XDP_PASS; | |
70 | ||
fe616055 DA |
71 | fib_params.family = AF_INET; |
72 | fib_params.tos = iph->tos; | |
73 | fib_params.l4_protocol = iph->protocol; | |
74 | fib_params.sport = 0; | |
75 | fib_params.dport = 0; | |
76 | fib_params.tot_len = ntohs(iph->tot_len); | |
77 | fib_params.ipv4_src = iph->saddr; | |
78 | fib_params.ipv4_dst = iph->daddr; | |
79 | } else if (h_proto == htons(ETH_P_IPV6)) { | |
80 | struct in6_addr *src = (struct in6_addr *) fib_params.ipv6_src; | |
81 | struct in6_addr *dst = (struct in6_addr *) fib_params.ipv6_dst; | |
fe616055 | 82 | |
44edef77 DA |
83 | ip6h = data + nh_off; |
84 | if (ip6h + 1 > data_end) | |
fe616055 DA |
85 | return XDP_DROP; |
86 | ||
44edef77 DA |
87 | if (ip6h->hop_limit <= 1) |
88 | return XDP_PASS; | |
89 | ||
fe616055 | 90 | fib_params.family = AF_INET6; |
44edef77 DA |
91 | fib_params.flowlabel = *(__be32 *)ip6h & IPV6_FLOWINFO_MASK; |
92 | fib_params.l4_protocol = ip6h->nexthdr; | |
fe616055 DA |
93 | fib_params.sport = 0; |
94 | fib_params.dport = 0; | |
44edef77 DA |
95 | fib_params.tot_len = ntohs(ip6h->payload_len); |
96 | *src = ip6h->saddr; | |
97 | *dst = ip6h->daddr; | |
fe616055 DA |
98 | } else { |
99 | return XDP_PASS; | |
100 | } | |
101 | ||
102 | fib_params.ifindex = ctx->ingress_ifindex; | |
103 | ||
104 | out_index = bpf_fib_lookup(ctx, &fib_params, sizeof(fib_params), flags); | |
105 | ||
106 | /* verify egress index has xdp support | |
107 | * TO-DO bpf_map_lookup_elem(&tx_port, &key) fails with | |
108 | * cannot pass map_type 14 into func bpf_map_lookup_elem#1: | |
109 | * NOTE: without verification that egress index supports XDP | |
110 | * forwarding packets are dropped. | |
111 | */ | |
112 | if (out_index > 0) { | |
44edef77 DA |
113 | if (h_proto == htons(ETH_P_IP)) |
114 | ip_decrease_ttl(iph); | |
115 | else if (h_proto == htons(ETH_P_IPV6)) | |
116 | ip6h->hop_limit--; | |
117 | ||
fe616055 DA |
118 | memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN); |
119 | memcpy(eth->h_source, fib_params.smac, ETH_ALEN); | |
120 | return bpf_redirect_map(&tx_port, out_index, 0); | |
121 | } | |
122 | ||
123 | return XDP_PASS; | |
124 | } | |
125 | ||
126 | SEC("xdp_fwd") | |
127 | int xdp_fwd_prog(struct xdp_md *ctx) | |
128 | { | |
129 | return xdp_fwd_flags(ctx, 0); | |
130 | } | |
131 | ||
132 | SEC("xdp_fwd_direct") | |
133 | int xdp_fwd_direct_prog(struct xdp_md *ctx) | |
134 | { | |
135 | return xdp_fwd_flags(ctx, BPF_FIB_LOOKUP_DIRECT); | |
136 | } | |
137 | ||
138 | char _license[] SEC("license") = "GPL"; |