blob: 1d1a4df1aab59c0e31aedc2eaea3f5d2cebba9cb [file] [log] [blame]
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +05301/*
2 * sfe_ipv4_udp.c
3 * Shortcut forwarding engine - IPv4 UDP implementation
4 *
5 * Copyright (c) 2013-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/lockdep.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_ipv4.h"
31
32/*
33 * sfe_ipv4_recv_udp()
34 * Handle UDP packet receives and forwarding.
35 */
36int sfe_ipv4_recv_udp(struct sfe_ipv4 *si, struct sk_buff *skb, struct net_device *dev,
37 unsigned int len, struct iphdr *iph, unsigned int ihl, bool flush_on_find)
38{
39 struct udphdr *udph;
40 __be32 src_ip;
41 __be32 dest_ip;
42 __be16 src_port;
43 __be16 dest_port;
44 struct sfe_ipv4_connection_match *cm;
45 u8 ttl;
46 struct net_device *xmit_dev;
47 bool ret;
Ratheesh Kannotha3cf0e02021-12-09 09:44:10 +053048 bool hw_csum;
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +053049
50 /*
51 * Is our packet too short to contain a valid UDP header?
52 */
53 if (unlikely(!pskb_may_pull(skb, (sizeof(struct udphdr) + ihl)))) {
54 sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_UDP_HEADER_INCOMPLETE);
55 DEBUG_TRACE("packet too short for UDP header\n");
56 return 0;
57 }
58
59 /*
60 * Read the IP address and port information. Read the IP header data first
61 * because we've almost certainly got that in the cache. We may not yet have
62 * the UDP header cached though so allow more time for any prefetching.
63 */
64 src_ip = iph->saddr;
65 dest_ip = iph->daddr;
66
67 udph = (struct udphdr *)(skb->data + ihl);
68 src_port = udph->source;
69 dest_port = udph->dest;
70
71 rcu_read_lock();
72
73 /*
74 * Look for a connection match.
75 */
76#ifdef CONFIG_NF_FLOW_COOKIE
77 cm = si->sfe_flow_cookie_table[skb->flow_cookie & SFE_FLOW_COOKIE_MASK].match;
78 if (unlikely(!cm)) {
79 cm = sfe_ipv4_find_connection_match_rcu(si, dev, IPPROTO_UDP, src_ip, src_port, dest_ip, dest_port);
80 }
81#else
82 cm = sfe_ipv4_find_connection_match_rcu(si, dev, IPPROTO_UDP, src_ip, src_port, dest_ip, dest_port);
83#endif
84 if (unlikely(!cm)) {
85
86 rcu_read_unlock();
87 sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_UDP_NO_CONNECTION);
88 DEBUG_TRACE("no connection found\n");
89 return 0;
90 }
91
92 /*
93 * If our packet has beern marked as "flush on find" we can't actually
94 * forward it in the fast path, but now that we've found an associated
95 * connection we can flush that out before we process the packet.
96 */
97 if (unlikely(flush_on_find)) {
98 struct sfe_ipv4_connection *c = cm->connection;
99 spin_lock_bh(&si->lock);
100 ret = sfe_ipv4_remove_connection(si, c);
101 spin_unlock_bh(&si->lock);
102
103 if (ret) {
104 sfe_ipv4_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
105 }
106 rcu_read_unlock();
107 sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_UDP_IP_OPTIONS_OR_INITIAL_FRAGMENT);
108 DEBUG_TRACE("flush on find\n");
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 TTL allow forwarding?
126 */
127 ttl = iph->ttl;
128 if (unlikely(ttl < 2)) {
129 struct sfe_ipv4_connection *c = cm->connection;
130 spin_lock_bh(&si->lock);
131 ret = sfe_ipv4_remove_connection(si, c);
132 spin_unlock_bh(&si->lock);
133
134 if (ret) {
135 sfe_ipv4_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
136 }
137 rcu_read_unlock();
138
139 DEBUG_TRACE("ttl too low\n");
140 sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_UDP_SMALL_TTL);
141 return 0;
142 }
143
144 /*
145 * If our packet is larger than the MTU of the transmit interface then
146 * we can't forward it easily.
147 */
148 if (unlikely(len > cm->xmit_dev_mtu)) {
149 struct sfe_ipv4_connection *c = cm->connection;
150 spin_lock_bh(&si->lock);
151 ret = sfe_ipv4_remove_connection(si, c);
152 spin_unlock_bh(&si->lock);
153
154 DEBUG_TRACE("larger than mtu\n");
155 if (ret) {
156 sfe_ipv4_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
157 }
158 rcu_read_unlock();
159 sfe_ipv4_exception_stats_inc(si, SFE_IPV4_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 iphdr *)skb->data;
185 udph = (struct udphdr *)(skb->data + ihl);
186 }
187
188 /*
189 * Update DSCP
190 */
191 if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_DSCP_REMARK)) {
192 iph->tos = (iph->tos & SFE_IPV4_DSCP_MASK) | cm->dscp;
193 }
194
195 /*
196 * Decrement our TTL.
197 */
198 iph->ttl = ttl - 1;
199
200 /*
Ratheesh Kannotha3cf0e02021-12-09 09:44:10 +0530201 * Enable HW csum if rx checksum is verified and xmit interface is CSUM offload capable.
202 * Note: If L4 csum at Rx was found to be incorrect, we (router) should use incremental L4 checksum here
203 * so that HW does not re-calculate/replace the L4 csum
204 */
205 hw_csum = !!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_CSUM_OFFLOAD) && (skb->ip_summed == CHECKSUM_UNNECESSARY);
206
207 /*
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +0530208 * Do we have to perform translations of the source address/port?
209 */
210 if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_XLATE_SRC)) {
211 u16 udp_csum;
212
213 iph->saddr = cm->xlate_src_ip;
214 udph->source = cm->xlate_src_port;
215
216 /*
217 * Do we have a non-zero UDP checksum? If we do then we need
218 * to update it.
219 */
Ratheesh Kannotha3cf0e02021-12-09 09:44:10 +0530220 if (unlikely(!hw_csum)) {
221 udp_csum = udph->check;
222 if (likely(udp_csum)) {
223 u32 sum;
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +0530224
Ratheesh Kannotha3cf0e02021-12-09 09:44:10 +0530225 if (unlikely(skb->ip_summed == CHECKSUM_PARTIAL)) {
226 sum = udp_csum + cm->xlate_src_partial_csum_adjustment;
227 } else {
228 sum = udp_csum + cm->xlate_src_csum_adjustment;
229 }
230
231 sum = (sum & 0xffff) + (sum >> 16);
232 udph->check = (u16)sum;
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +0530233 }
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +0530234 }
235 }
236
237 /*
238 * Do we have to perform translations of the destination address/port?
239 */
240 if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_XLATE_DEST)) {
241 u16 udp_csum;
242
243 iph->daddr = cm->xlate_dest_ip;
244 udph->dest = cm->xlate_dest_port;
245
246 /*
247 * Do we have a non-zero UDP checksum? If we do then we need
248 * to update it.
249 */
Ratheesh Kannotha3cf0e02021-12-09 09:44:10 +0530250 if (unlikely(!hw_csum)) {
251 udp_csum = udph->check;
252 if (likely(udp_csum)) {
253 u32 sum;
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +0530254
Ratheesh Kannotha3cf0e02021-12-09 09:44:10 +0530255 /*
256 * TODO: Use a common API for below incremental checksum calculation
257 * for IPv4/IPv6 UDP/TCP
258 */
259 if (unlikely(skb->ip_summed == CHECKSUM_PARTIAL)) {
260 sum = udp_csum + cm->xlate_dest_partial_csum_adjustment;
261 } else {
262 sum = udp_csum + cm->xlate_dest_csum_adjustment;
263 }
264
265 sum = (sum & 0xffff) + (sum >> 16);
266 udph->check = (u16)sum;
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +0530267 }
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +0530268 }
269 }
270
271 /*
Ratheesh Kannotha3cf0e02021-12-09 09:44:10 +0530272 * If HW checksum offload is not possible, full L3 checksum and incremental L4 checksum
273 * are used to update the packet. Setting ip_summed to CHECKSUM_UNNECESSARY ensures checksum is
274 * not recalculated further in packet path.
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +0530275 */
Ratheesh Kannotha3cf0e02021-12-09 09:44:10 +0530276 if (likely(hw_csum)) {
277 skb->ip_summed = CHECKSUM_PARTIAL;
278 } else {
279 iph->check = sfe_ipv4_gen_ip_csum(iph);
280 skb->ip_summed = CHECKSUM_UNNECESSARY;
281 }
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +0530282
283 /*
284 * Update traffic stats.
285 */
286 atomic_inc(&cm->rx_packet_count);
287 atomic_add(len, &cm->rx_byte_count);
288
289 xmit_dev = cm->xmit_dev;
290 skb->dev = xmit_dev;
291
292 /*
293 * Check to see if we need to write a header.
294 */
295 if (likely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_WRITE_L2_HDR)) {
296 if (unlikely(!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_WRITE_FAST_ETH_HDR))) {
297 dev_hard_header(skb, xmit_dev, ETH_P_IP,
298 cm->xmit_dest_mac, cm->xmit_src_mac, len);
299 } else {
300 /*
301 * For the simple case we write this really fast.
302 */
303 struct ethhdr *eth = (struct ethhdr *)__skb_push(skb, ETH_HLEN);
304 eth->h_proto = htons(ETH_P_IP);
305 ether_addr_copy((u8 *)eth->h_dest, (u8 *)cm->xmit_dest_mac);
306 ether_addr_copy((u8 *)eth->h_source, (u8 *)cm->xmit_src_mac);
307 }
308 }
309
310 /*
311 * Update priority of skb.
312 */
313 if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_PRIORITY_REMARK)) {
314 skb->priority = cm->priority;
315 }
316
317 /*
318 * Mark outgoing packet.
319 */
320 skb->mark = cm->connection->mark;
321 if (skb->mark) {
322 DEBUG_TRACE("SKB MARK is NON ZERO %x\n", skb->mark);
323 }
324
325 rcu_read_unlock();
326
327 this_cpu_inc(si->stats_pcpu->packets_forwarded64);
328
329 /*
330 * We're going to check for GSO flags when we transmit the packet so
331 * start fetching the necessary cache line now.
332 */
333 prefetch(skb_shinfo(skb));
334
335 /*
336 * Mark that this packet has been fast forwarded.
337 */
338 skb->fast_forwarded = 1;
339
340 /*
341 * Send the packet on its way.
342 */
343 dev_queue_xmit(skb);
344
345 return 1;
346}