blob: a13c923f77a0d6243da2d7993a859c5b9e85f533 [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;
48
49 /*
50 * Is our packet too short to contain a valid UDP header?
51 */
52 if (unlikely(!pskb_may_pull(skb, (sizeof(struct udphdr) + ihl)))) {
53 sfe_ipv4_exception_stats_inc(si, SFE_IPV4_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 = iph->saddr;
64 dest_ip = iph->daddr;
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_ipv4_find_connection_match_rcu(si, dev, IPPROTO_UDP, src_ip, src_port, dest_ip, dest_port);
79 }
80#else
81 cm = sfe_ipv4_find_connection_match_rcu(si, dev, IPPROTO_UDP, src_ip, src_port, dest_ip, dest_port);
82#endif
83 if (unlikely(!cm)) {
84
85 rcu_read_unlock();
86 sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_UDP_NO_CONNECTION);
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_ipv4_connection *c = cm->connection;
98 spin_lock_bh(&si->lock);
99 ret = sfe_ipv4_remove_connection(si, c);
100 spin_unlock_bh(&si->lock);
101
102 if (ret) {
103 sfe_ipv4_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
104 }
105 rcu_read_unlock();
106 sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_UDP_IP_OPTIONS_OR_INITIAL_FRAGMENT);
107 DEBUG_TRACE("flush on find\n");
108 return 0;
109 }
110
111#ifdef CONFIG_XFRM
112 /*
113 * We can't accelerate the flow on this direction, just let it go
114 * through the slow path.
115 */
116 if (unlikely(!cm->flow_accel)) {
117 rcu_read_unlock();
118 this_cpu_inc(si->stats_pcpu->packets_not_forwarded64);
119 return 0;
120 }
121#endif
122
123 /*
124 * Does our TTL allow forwarding?
125 */
126 ttl = iph->ttl;
127 if (unlikely(ttl < 2)) {
128 struct sfe_ipv4_connection *c = cm->connection;
129 spin_lock_bh(&si->lock);
130 ret = sfe_ipv4_remove_connection(si, c);
131 spin_unlock_bh(&si->lock);
132
133 if (ret) {
134 sfe_ipv4_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
135 }
136 rcu_read_unlock();
137
138 DEBUG_TRACE("ttl too low\n");
139 sfe_ipv4_exception_stats_inc(si, SFE_IPV4_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_ipv4_connection *c = cm->connection;
149 spin_lock_bh(&si->lock);
150 ret = sfe_ipv4_remove_connection(si, c);
151 spin_unlock_bh(&si->lock);
152
153 DEBUG_TRACE("larger than mtu\n");
154 if (ret) {
155 sfe_ipv4_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
156 }
157 rcu_read_unlock();
158 sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_UDP_NEEDS_FRAGMENTATION);
159 return 0;
160 }
161
162 /*
163 * From this point on we're good to modify the packet.
164 */
165
166 /*
167 * Check if skb was cloned. If it was, unshare it. Because
168 * the data area is going to be written in this path and we don't want to
169 * change the cloned skb's data section.
170 */
171 if (unlikely(skb_cloned(skb))) {
172 DEBUG_TRACE("%px: skb is a cloned skb\n", skb);
173 skb = skb_unshare(skb, GFP_ATOMIC);
174 if (!skb) {
175 DEBUG_WARN("Failed to unshare the cloned skb\n");
176 rcu_read_unlock();
177 return 0;
178 }
179
180 /*
181 * Update the iph and udph pointers with the unshared skb's data area.
182 */
183 iph = (struct iphdr *)skb->data;
184 udph = (struct udphdr *)(skb->data + ihl);
185 }
186
187 /*
188 * Update DSCP
189 */
190 if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_DSCP_REMARK)) {
191 iph->tos = (iph->tos & SFE_IPV4_DSCP_MASK) | cm->dscp;
192 }
193
194 /*
195 * Decrement our TTL.
196 */
197 iph->ttl = ttl - 1;
198
199 /*
200 * Do we have to perform translations of the source address/port?
201 */
202 if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_XLATE_SRC)) {
203 u16 udp_csum;
204
205 iph->saddr = cm->xlate_src_ip;
206 udph->source = cm->xlate_src_port;
207
208 /*
209 * Do we have a non-zero UDP checksum? If we do then we need
210 * to update it.
211 */
212 udp_csum = udph->check;
213 if (likely(udp_csum)) {
214 u32 sum;
215
216 if (unlikely(skb->ip_summed == CHECKSUM_PARTIAL)) {
217 sum = udp_csum + cm->xlate_src_partial_csum_adjustment;
218 } else {
219 sum = udp_csum + cm->xlate_src_csum_adjustment;
220 }
221
222 sum = (sum & 0xffff) + (sum >> 16);
223 udph->check = (u16)sum;
224 }
225 }
226
227 /*
228 * Do we have to perform translations of the destination address/port?
229 */
230 if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_XLATE_DEST)) {
231 u16 udp_csum;
232
233 iph->daddr = cm->xlate_dest_ip;
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;
243
244 if (unlikely(skb->ip_summed == CHECKSUM_PARTIAL)) {
245 sum = udp_csum + cm->xlate_dest_partial_csum_adjustment;
246 } else {
247 sum = udp_csum + cm->xlate_dest_csum_adjustment;
248 }
249
250 sum = (sum & 0xffff) + (sum >> 16);
251 udph->check = (u16)sum;
252 }
253 }
254
255 /*
256 * Replace the IP checksum.
257 */
258 iph->check = sfe_ipv4_gen_ip_csum(iph);
259
260 /*
261 * Update traffic stats.
262 */
263 atomic_inc(&cm->rx_packet_count);
264 atomic_add(len, &cm->rx_byte_count);
265
266 xmit_dev = cm->xmit_dev;
267 skb->dev = xmit_dev;
268
269 /*
270 * Check to see if we need to write a header.
271 */
272 if (likely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_WRITE_L2_HDR)) {
273 if (unlikely(!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_WRITE_FAST_ETH_HDR))) {
274 dev_hard_header(skb, xmit_dev, ETH_P_IP,
275 cm->xmit_dest_mac, cm->xmit_src_mac, len);
276 } else {
277 /*
278 * For the simple case we write this really fast.
279 */
280 struct ethhdr *eth = (struct ethhdr *)__skb_push(skb, ETH_HLEN);
281 eth->h_proto = htons(ETH_P_IP);
282 ether_addr_copy((u8 *)eth->h_dest, (u8 *)cm->xmit_dest_mac);
283 ether_addr_copy((u8 *)eth->h_source, (u8 *)cm->xmit_src_mac);
284 }
285 }
286
287 /*
288 * Update priority of skb.
289 */
290 if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_PRIORITY_REMARK)) {
291 skb->priority = cm->priority;
292 }
293
294 /*
295 * Mark outgoing packet.
296 */
297 skb->mark = cm->connection->mark;
298 if (skb->mark) {
299 DEBUG_TRACE("SKB MARK is NON ZERO %x\n", skb->mark);
300 }
301
302 rcu_read_unlock();
303
304 this_cpu_inc(si->stats_pcpu->packets_forwarded64);
305
306 /*
307 * We're going to check for GSO flags when we transmit the packet so
308 * start fetching the necessary cache line now.
309 */
310 prefetch(skb_shinfo(skb));
311
312 /*
313 * Mark that this packet has been fast forwarded.
314 */
315 skb->fast_forwarded = 1;
316
317 /*
318 * Send the packet on its way.
319 */
320 dev_queue_xmit(skb);
321
322 return 1;
323}