blob: 559425e2487ed4fafb741a1dbf15e63fd44548d3 [file] [log] [blame]
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +05301/*
2 * sfe_ipv6_udp.c
3 * Shortcut forwarding engine file for IPv6 UDP
4 *
5 * Copyright (c) 2015-2016, 2019-2020, The Linux Foundation. All rights reserved.
6 * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
7 *
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21#include <linux/skbuff.h>
22#include <net/udp.h>
23#include <linux/etherdevice.h>
24#include <linux/version.h>
25
26#include "sfe_debug.h"
27#include "sfe_api.h"
28#include "sfe.h"
29#include "sfe_flow_cookie.h"
30#include "sfe_ipv6.h"
31
32/*
33 * sfe_ipv6_recv_udp()
34 * Handle UDP packet receives and forwarding.
35 */
36int sfe_ipv6_recv_udp(struct sfe_ipv6 *si, struct sk_buff *skb, struct net_device *dev,
37 unsigned int len, struct ipv6hdr *iph, unsigned int ihl, bool flush_on_find)
38{
39 struct udphdr *udph;
40 struct sfe_ipv6_addr *src_ip;
41 struct sfe_ipv6_addr *dest_ip;
42 __be16 src_port;
43 __be16 dest_port;
44 struct sfe_ipv6_connection_match *cm;
45 struct net_device *xmit_dev;
46 bool ret;
47
48 /*
49 * Is our packet too short to contain a valid UDP header?
50 */
51 if (!pskb_may_pull(skb, (sizeof(struct udphdr) + ihl))) {
52
53 sfe_ipv6_exception_stats_inc(si,SFE_IPV6_EXCEPTION_EVENT_UDP_HEADER_INCOMPLETE);
54 DEBUG_TRACE("packet too short for UDP header\n");
55 return 0;
56 }
57
58 /*
59 * Read the IP address and port information. Read the IP header data first
60 * because we've almost certainly got that in the cache. We may not yet have
61 * the UDP header cached though so allow more time for any prefetching.
62 */
63 src_ip = (struct sfe_ipv6_addr *)iph->saddr.s6_addr32;
64 dest_ip = (struct sfe_ipv6_addr *)iph->daddr.s6_addr32;
65
66 udph = (struct udphdr *)(skb->data + ihl);
67 src_port = udph->source;
68 dest_port = udph->dest;
69
70 rcu_read_lock();
71
72 /*
73 * Look for a connection match.
74 */
75#ifdef CONFIG_NF_FLOW_COOKIE
76 cm = si->sfe_flow_cookie_table[skb->flow_cookie & SFE_FLOW_COOKIE_MASK].match;
77 if (unlikely(!cm)) {
78 cm = sfe_ipv6_find_connection_match_rcu(si, dev, IPPROTO_UDP, src_ip, src_port, dest_ip, dest_port);
79 }
80#else
81 cm = sfe_ipv6_find_connection_match_rcu(si, dev, IPPROTO_UDP, src_ip, src_port, dest_ip, dest_port);
82#endif
83 if (unlikely(!cm)) {
84 rcu_read_unlock();
85 sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_UDP_NO_CONNECTION);
86
87 DEBUG_TRACE("no connection found\n");
88 return 0;
89 }
90
91 /*
92 * If our packet has beern marked as "flush on find" we can't actually
93 * forward it in the fast path, but now that we've found an associated
94 * connection we can flush that out before we process the packet.
95 */
96 if (unlikely(flush_on_find)) {
97 struct sfe_ipv6_connection *c = cm->connection;
98 spin_lock_bh(&si->lock);
99 ret = sfe_ipv6_remove_connection(si, c);
100 spin_unlock_bh(&si->lock);
101
102 DEBUG_TRACE("flush on find\n");
103 if (ret) {
104 sfe_ipv6_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
105 }
106 rcu_read_unlock();
107
108 sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_UDP_IP_OPTIONS_OR_INITIAL_FRAGMENT);
109 return 0;
110 }
111
112#ifdef CONFIG_XFRM
113 /*
114 * We can't accelerate the flow on this direction, just let it go
115 * through the slow path.
116 */
117 if (unlikely(!cm->flow_accel)) {
118 rcu_read_unlock();
119 this_cpu_inc(si->stats_pcpu->packets_not_forwarded64);
120 return 0;
121 }
122#endif
123
124 /*
125 * Does our hop_limit allow forwarding?
126 */
127 if (unlikely(iph->hop_limit < 2)) {
128 struct sfe_ipv6_connection *c = cm->connection;
129 spin_lock_bh(&si->lock);
130 ret = sfe_ipv6_remove_connection(si, c);
131 spin_unlock_bh(&si->lock);
132
133 DEBUG_TRACE("hop_limit too low\n");
134 if (ret) {
135 sfe_ipv6_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
136 }
137 rcu_read_unlock();
138
139 sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_UDP_SMALL_TTL);
140 return 0;
141 }
142
143 /*
144 * If our packet is larger than the MTU of the transmit interface then
145 * we can't forward it easily.
146 */
147 if (unlikely(len > cm->xmit_dev_mtu)) {
148 struct sfe_ipv6_connection *c = cm->connection;
149 spin_lock_bh(&si->lock);
150 ret = sfe_ipv6_remove_connection(si, c);
151 spin_unlock_bh(&si->lock);
152
153 DEBUG_TRACE("larger than mtu\n");
154 if (ret) {
155 sfe_ipv6_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
156 }
157 rcu_read_unlock();
158
159 sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_UDP_NEEDS_FRAGMENTATION);
160 return 0;
161 }
162
163 /*
164 * From this point on we're good to modify the packet.
165 */
166
167 /*
168 * Check if skb was cloned. If it was, unshare it. Because
169 * the data area is going to be written in this path and we don't want to
170 * change the cloned skb's data section.
171 */
172 if (unlikely(skb_cloned(skb))) {
173 DEBUG_TRACE("%px: skb is a cloned skb\n", skb);
174 skb = skb_unshare(skb, GFP_ATOMIC);
175 if (!skb) {
176 DEBUG_WARN("Failed to unshare the cloned skb\n");
177 rcu_read_unlock();
178 return 0;
179 }
180
181 /*
182 * Update the iph and udph pointers with the unshared skb's data area.
183 */
184 iph = (struct ipv6hdr *)skb->data;
185 udph = (struct udphdr *)(skb->data + ihl);
186 }
187
188 /*
189 * Update DSCP
190 */
191 if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_DSCP_REMARK)) {
192 sfe_ipv6_change_dsfield(iph, cm->dscp);
193 }
194
195 /*
196 * Decrement our hop_limit.
197 */
198 iph->hop_limit -= 1;
199
200 /*
201 * Do we have to perform translations of the source address/port?
202 */
203 if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_XLATE_SRC)) {
204 u16 udp_csum;
205
206 iph->saddr.s6_addr32[0] = cm->xlate_src_ip[0].addr[0];
207 iph->saddr.s6_addr32[1] = cm->xlate_src_ip[0].addr[1];
208 iph->saddr.s6_addr32[2] = cm->xlate_src_ip[0].addr[2];
209 iph->saddr.s6_addr32[3] = cm->xlate_src_ip[0].addr[3];
210 udph->source = cm->xlate_src_port;
211
212 /*
213 * Do we have a non-zero UDP checksum? If we do then we need
214 * to update it.
215 */
216 udp_csum = udph->check;
217 if (likely(udp_csum)) {
218 u32 sum = udp_csum + cm->xlate_src_csum_adjustment;
219 sum = (sum & 0xffff) + (sum >> 16);
220 udph->check = (u16)sum;
221 }
222 }
223
224 /*
225 * Do we have to perform translations of the destination address/port?
226 */
227 if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_XLATE_DEST)) {
228 u16 udp_csum;
229
230 iph->daddr.s6_addr32[0] = cm->xlate_dest_ip[0].addr[0];
231 iph->daddr.s6_addr32[1] = cm->xlate_dest_ip[0].addr[1];
232 iph->daddr.s6_addr32[2] = cm->xlate_dest_ip[0].addr[2];
233 iph->daddr.s6_addr32[3] = cm->xlate_dest_ip[0].addr[3];
234 udph->dest = cm->xlate_dest_port;
235
236 /*
237 * Do we have a non-zero UDP checksum? If we do then we need
238 * to update it.
239 */
240 udp_csum = udph->check;
241 if (likely(udp_csum)) {
242 u32 sum = udp_csum + cm->xlate_dest_csum_adjustment;
243 sum = (sum & 0xffff) + (sum >> 16);
244 udph->check = (u16)sum;
245 }
246 }
247
248 /*
249 * Update traffic stats.
250 */
251 atomic_inc(&cm->rx_packet_count);
252 atomic_add(len, &cm->rx_byte_count);
253
254 xmit_dev = cm->xmit_dev;
255 skb->dev = xmit_dev;
256
257 /*
258 * Check to see if we need to write a header.
259 */
260 if (likely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_WRITE_L2_HDR)) {
261 if (unlikely(!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_WRITE_FAST_ETH_HDR))) {
262 dev_hard_header(skb, xmit_dev, ETH_P_IPV6,
263 cm->xmit_dest_mac, cm->xmit_src_mac, len);
264 } else {
265 /*
266 * For the simple case we write this really fast.
267 */
268 struct ethhdr *eth = (struct ethhdr *)__skb_push(skb, ETH_HLEN);
269 eth->h_proto = htons(ETH_P_IPV6);
270 ether_addr_copy((u8 *)eth->h_dest, (u8 *)cm->xmit_dest_mac);
271 ether_addr_copy((u8 *)eth->h_source, (u8 *)cm->xmit_src_mac);
272
273 }
274 }
275
276 /*
277 * Update priority of skb.
278 */
279 if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PRIORITY_REMARK)) {
280 skb->priority = cm->priority;
281 }
282
283 /*
284 * Mark outgoing packet.
285 */
286 skb->mark = cm->connection->mark;
287 if (skb->mark) {
288 DEBUG_TRACE("SKB MARK is NON ZERO %x\n", skb->mark);
289 }
290
291 rcu_read_unlock();
292
293 this_cpu_inc(si->stats_pcpu->packets_forwarded64);
294
295 /*
296 * We're going to check for GSO flags when we transmit the packet so
297 * start fetching the necessary cache line now.
298 */
299 prefetch(skb_shinfo(skb));
300
301 /*
302 * Mark that this packet has been fast forwarded.
303 */
304 skb->fast_forwarded = 1;
305
306 /*
307 * Send the packet on its way.
308 */
309 dev_queue_xmit(skb);
310
311 return 1;
312}