~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/samples/bpf/xdp_tx_iptunnel_kern.c

Version: ~ [ linux-5.12-rc7 ] ~ [ linux-5.11.13 ] ~ [ linux-5.10.29 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.111 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.186 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.230 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.266 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.266 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /* Copyright (c) 2016 Facebook
  2  *
  3  * This program is free software; you can redistribute it and/or
  4  * modify it under the terms of version 2 of the GNU General Public
  5  * License as published by the Free Software Foundation.
  6  *
  7  * This program shows how to use bpf_xdp_adjust_head() by
  8  * encapsulating the incoming packet in an IPv4/v6 header
  9  * and then XDP_TX it out.
 10  */
 11 #define KBUILD_MODNAME "foo"
 12 #include <uapi/linux/bpf.h>
 13 #include <linux/in.h>
 14 #include <linux/if_ether.h>
 15 #include <linux/if_packet.h>
 16 #include <linux/if_vlan.h>
 17 #include <linux/ip.h>
 18 #include <linux/ipv6.h>
 19 #include "bpf_helpers.h"
 20 #include "xdp_tx_iptunnel_common.h"
 21 
 22 struct bpf_map_def SEC("maps") rxcnt = {
 23         .type = BPF_MAP_TYPE_PERCPU_ARRAY,
 24         .key_size = sizeof(__u32),
 25         .value_size = sizeof(__u64),
 26         .max_entries = 256,
 27 };
 28 
 29 struct bpf_map_def SEC("maps") vip2tnl = {
 30         .type = BPF_MAP_TYPE_HASH,
 31         .key_size = sizeof(struct vip),
 32         .value_size = sizeof(struct iptnl_info),
 33         .max_entries = MAX_IPTNL_ENTRIES,
 34 };
 35 
 36 static __always_inline void count_tx(u32 protocol)
 37 {
 38         u64 *rxcnt_count;
 39 
 40         rxcnt_count = bpf_map_lookup_elem(&rxcnt, &protocol);
 41         if (rxcnt_count)
 42                 *rxcnt_count += 1;
 43 }
 44 
 45 static __always_inline int get_dport(void *trans_data, void *data_end,
 46                                      u8 protocol)
 47 {
 48         struct tcphdr *th;
 49         struct udphdr *uh;
 50 
 51         switch (protocol) {
 52         case IPPROTO_TCP:
 53                 th = (struct tcphdr *)trans_data;
 54                 if (th + 1 > data_end)
 55                         return -1;
 56                 return th->dest;
 57         case IPPROTO_UDP:
 58                 uh = (struct udphdr *)trans_data;
 59                 if (uh + 1 > data_end)
 60                         return -1;
 61                 return uh->dest;
 62         default:
 63                 return 0;
 64         }
 65 }
 66 
 67 static __always_inline void set_ethhdr(struct ethhdr *new_eth,
 68                                        const struct ethhdr *old_eth,
 69                                        const struct iptnl_info *tnl,
 70                                        __be16 h_proto)
 71 {
 72         memcpy(new_eth->h_source, old_eth->h_dest, sizeof(new_eth->h_source));
 73         memcpy(new_eth->h_dest, tnl->dmac, sizeof(new_eth->h_dest));
 74         new_eth->h_proto = h_proto;
 75 }
 76 
 77 static __always_inline int handle_ipv4(struct xdp_md *xdp)
 78 {
 79         void *data_end = (void *)(long)xdp->data_end;
 80         void *data = (void *)(long)xdp->data;
 81         struct iptnl_info *tnl;
 82         struct ethhdr *new_eth;
 83         struct ethhdr *old_eth;
 84         struct iphdr *iph = data + sizeof(struct ethhdr);
 85         u16 *next_iph_u16;
 86         u16 payload_len;
 87         struct vip vip = {};
 88         int dport;
 89         u32 csum = 0;
 90         int i;
 91 
 92         if (iph + 1 > data_end)
 93                 return XDP_DROP;
 94 
 95         dport = get_dport(iph + 1, data_end, iph->protocol);
 96         if (dport == -1)
 97                 return XDP_DROP;
 98 
 99         vip.protocol = iph->protocol;
100         vip.family = AF_INET;
101         vip.daddr.v4 = iph->daddr;
102         vip.dport = dport;
103         payload_len = ntohs(iph->tot_len);
104 
105         tnl = bpf_map_lookup_elem(&vip2tnl, &vip);
106         /* It only does v4-in-v4 */
107         if (!tnl || tnl->family != AF_INET)
108                 return XDP_PASS;
109 
110         /* The vip key is found.  Add an IP header and send it out */
111 
112         if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct iphdr)))
113                 return XDP_DROP;
114 
115         data = (void *)(long)xdp->data;
116         data_end = (void *)(long)xdp->data_end;
117 
118         new_eth = data;
119         iph = data + sizeof(*new_eth);
120         old_eth = data + sizeof(*iph);
121 
122         if (new_eth + 1 > data_end ||
123             old_eth + 1 > data_end ||
124             iph + 1 > data_end)
125                 return XDP_DROP;
126 
127         set_ethhdr(new_eth, old_eth, tnl, htons(ETH_P_IP));
128 
129         iph->version = 4;
130         iph->ihl = sizeof(*iph) >> 2;
131         iph->frag_off = 0;
132         iph->protocol = IPPROTO_IPIP;
133         iph->check = 0;
134         iph->tos = 0;
135         iph->tot_len = htons(payload_len + sizeof(*iph));
136         iph->daddr = tnl->daddr.v4;
137         iph->saddr = tnl->saddr.v4;
138         iph->ttl = 8;
139 
140         next_iph_u16 = (u16 *)iph;
141 #pragma clang loop unroll(full)
142         for (i = 0; i < sizeof(*iph) >> 1; i++)
143                 csum += *next_iph_u16++;
144 
145         iph->check = ~((csum & 0xffff) + (csum >> 16));
146 
147         count_tx(vip.protocol);
148 
149         return XDP_TX;
150 }
151 
152 static __always_inline int handle_ipv6(struct xdp_md *xdp)
153 {
154         void *data_end = (void *)(long)xdp->data_end;
155         void *data = (void *)(long)xdp->data;
156         struct iptnl_info *tnl;
157         struct ethhdr *new_eth;
158         struct ethhdr *old_eth;
159         struct ipv6hdr *ip6h = data + sizeof(struct ethhdr);
160         __u16 payload_len;
161         struct vip vip = {};
162         int dport;
163 
164         if (ip6h + 1 > data_end)
165                 return XDP_DROP;
166 
167         dport = get_dport(ip6h + 1, data_end, ip6h->nexthdr);
168         if (dport == -1)
169                 return XDP_DROP;
170 
171         vip.protocol = ip6h->nexthdr;
172         vip.family = AF_INET6;
173         memcpy(vip.daddr.v6, ip6h->daddr.s6_addr32, sizeof(vip.daddr));
174         vip.dport = dport;
175         payload_len = ip6h->payload_len;
176 
177         tnl = bpf_map_lookup_elem(&vip2tnl, &vip);
178         /* It only does v6-in-v6 */
179         if (!tnl || tnl->family != AF_INET6)
180                 return XDP_PASS;
181 
182         /* The vip key is found.  Add an IP header and send it out */
183 
184         if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct ipv6hdr)))
185                 return XDP_DROP;
186 
187         data = (void *)(long)xdp->data;
188         data_end = (void *)(long)xdp->data_end;
189 
190         new_eth = data;
191         ip6h = data + sizeof(*new_eth);
192         old_eth = data + sizeof(*ip6h);
193 
194         if (new_eth + 1 > data_end ||
195             old_eth + 1 > data_end ||
196             ip6h + 1 > data_end)
197                 return XDP_DROP;
198 
199         set_ethhdr(new_eth, old_eth, tnl, htons(ETH_P_IPV6));
200 
201         ip6h->version = 6;
202         ip6h->priority = 0;
203         memset(ip6h->flow_lbl, 0, sizeof(ip6h->flow_lbl));
204         ip6h->payload_len = htons(ntohs(payload_len) + sizeof(*ip6h));
205         ip6h->nexthdr = IPPROTO_IPV6;
206         ip6h->hop_limit = 8;
207         memcpy(ip6h->saddr.s6_addr32, tnl->saddr.v6, sizeof(tnl->saddr.v6));
208         memcpy(ip6h->daddr.s6_addr32, tnl->daddr.v6, sizeof(tnl->daddr.v6));
209 
210         count_tx(vip.protocol);
211 
212         return XDP_TX;
213 }
214 
215 SEC("xdp_tx_iptunnel")
216 int _xdp_tx_iptunnel(struct xdp_md *xdp)
217 {
218         void *data_end = (void *)(long)xdp->data_end;
219         void *data = (void *)(long)xdp->data;
220         struct ethhdr *eth = data;
221         __u16 h_proto;
222 
223         if (eth + 1 > data_end)
224                 return XDP_DROP;
225 
226         h_proto = eth->h_proto;
227 
228         if (h_proto == htons(ETH_P_IP))
229                 return handle_ipv4(xdp);
230         else if (h_proto == htons(ETH_P_IPV6))
231 
232                 return handle_ipv6(xdp);
233         else
234                 return XDP_PASS;
235 }
236 
237 char _license[] SEC("license") = "GPL";
238 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp