blob: 2523255e72e5f44318e8aca112a069f7809a6856 [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;
Ratheesh Kannotha3cf0e02021-12-09 09:44:10 +053047 bool hw_csum;
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +053048
49 /*
50 * Is our packet too short to contain a valid UDP header?
51 */
52 if (!pskb_may_pull(skb, (sizeof(struct udphdr) + ihl))) {
53
54 sfe_ipv6_exception_stats_inc(si,SFE_IPV6_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 = (struct sfe_ipv6_addr *)iph->saddr.s6_addr32;
65 dest_ip = (struct sfe_ipv6_addr *)iph->daddr.s6_addr32;
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_ipv6_find_connection_match_rcu(si, dev, IPPROTO_UDP, src_ip, src_port, dest_ip, dest_port);
80 }
81#else
82 cm = sfe_ipv6_find_connection_match_rcu(si, dev, IPPROTO_UDP, src_ip, src_port, dest_ip, dest_port);
83#endif
84 if (unlikely(!cm)) {
85 rcu_read_unlock();
86 sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_UDP_NO_CONNECTION);
87
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_ipv6_connection *c = cm->connection;
99 spin_lock_bh(&si->lock);
100 ret = sfe_ipv6_remove_connection(si, c);
101 spin_unlock_bh(&si->lock);
102
103 DEBUG_TRACE("flush on find\n");
104 if (ret) {
105 sfe_ipv6_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
106 }
107 rcu_read_unlock();
108
109 sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_UDP_IP_OPTIONS_OR_INITIAL_FRAGMENT);
110 return 0;
111 }
112
113#ifdef CONFIG_XFRM
114 /*
115 * We can't accelerate the flow on this direction, just let it go
116 * through the slow path.
117 */
118 if (unlikely(!cm->flow_accel)) {
119 rcu_read_unlock();
120 this_cpu_inc(si->stats_pcpu->packets_not_forwarded64);
121 return 0;
122 }
123#endif
124
125 /*
126 * Does our hop_limit allow forwarding?
127 */
128 if (unlikely(iph->hop_limit < 2)) {
129 struct sfe_ipv6_connection *c = cm->connection;
130 spin_lock_bh(&si->lock);
131 ret = sfe_ipv6_remove_connection(si, c);
132 spin_unlock_bh(&si->lock);
133
134 DEBUG_TRACE("hop_limit too low\n");
135 if (ret) {
136 sfe_ipv6_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
137 }
138 rcu_read_unlock();
139
140 sfe_ipv6_exception_stats_inc(si, SFE_IPV6_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_ipv6_connection *c = cm->connection;
150 spin_lock_bh(&si->lock);
151 ret = sfe_ipv6_remove_connection(si, c);
152 spin_unlock_bh(&si->lock);
153
154 DEBUG_TRACE("larger than mtu\n");
155 if (ret) {
156 sfe_ipv6_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
157 }
158 rcu_read_unlock();
159
160 sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_UDP_NEEDS_FRAGMENTATION);
161 return 0;
162 }
163
164 /*
165 * From this point on we're good to modify the packet.
166 */
167
168 /*
169 * Check if skb was cloned. If it was, unshare it. Because
170 * the data area is going to be written in this path and we don't want to
171 * change the cloned skb's data section.
172 */
173 if (unlikely(skb_cloned(skb))) {
174 DEBUG_TRACE("%px: skb is a cloned skb\n", skb);
175 skb = skb_unshare(skb, GFP_ATOMIC);
176 if (!skb) {
177 DEBUG_WARN("Failed to unshare the cloned skb\n");
178 rcu_read_unlock();
179 return 0;
180 }
181
182 /*
183 * Update the iph and udph pointers with the unshared skb's data area.
184 */
185 iph = (struct ipv6hdr *)skb->data;
186 udph = (struct udphdr *)(skb->data + ihl);
187 }
188
189 /*
190 * Update DSCP
191 */
192 if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_DSCP_REMARK)) {
193 sfe_ipv6_change_dsfield(iph, cm->dscp);
194 }
195
196 /*
197 * Decrement our hop_limit.
198 */
199 iph->hop_limit -= 1;
200
201 /*
Ratheesh Kannotha3cf0e02021-12-09 09:44:10 +0530202 * Enable HW csum if rx checksum is verified and xmit interface is CSUM offload capable.
203 * Note: If L4 csum at Rx was found to be incorrect, we (router) should use incremental L4 checksum here
204 * so that HW does not re-calculate/replace the L4 csum
205 */
206 hw_csum = !!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_CSUM_OFFLOAD) && (skb->ip_summed == CHECKSUM_UNNECESSARY);
207
208 /*
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +0530209 * Do we have to perform translations of the source address/port?
210 */
211 if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_XLATE_SRC)) {
212 u16 udp_csum;
213
214 iph->saddr.s6_addr32[0] = cm->xlate_src_ip[0].addr[0];
215 iph->saddr.s6_addr32[1] = cm->xlate_src_ip[0].addr[1];
216 iph->saddr.s6_addr32[2] = cm->xlate_src_ip[0].addr[2];
217 iph->saddr.s6_addr32[3] = cm->xlate_src_ip[0].addr[3];
218 udph->source = cm->xlate_src_port;
219
220 /*
221 * Do we have a non-zero UDP checksum? If we do then we need
222 * to update it.
223 */
Ratheesh Kannotha3cf0e02021-12-09 09:44:10 +0530224 if (unlikely(!hw_csum)) {
225 udp_csum = udph->check;
226 if (likely(udp_csum)) {
227 u32 sum = udp_csum + cm->xlate_src_csum_adjustment;
228 sum = (sum & 0xffff) + (sum >> 16);
229 udph->check = (u16)sum;
230 }
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +0530231 }
232 }
233
234 /*
235 * Do we have to perform translations of the destination address/port?
236 */
237 if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_XLATE_DEST)) {
238 u16 udp_csum;
239
240 iph->daddr.s6_addr32[0] = cm->xlate_dest_ip[0].addr[0];
241 iph->daddr.s6_addr32[1] = cm->xlate_dest_ip[0].addr[1];
242 iph->daddr.s6_addr32[2] = cm->xlate_dest_ip[0].addr[2];
243 iph->daddr.s6_addr32[3] = cm->xlate_dest_ip[0].addr[3];
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 = udp_csum + cm->xlate_dest_csum_adjustment;
254 sum = (sum & 0xffff) + (sum >> 16);
255 udph->check = (u16)sum;
256 }
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +0530257 }
258 }
259
260 /*
Ratheesh Kannotha3cf0e02021-12-09 09:44:10 +0530261 * If HW checksum offload is not possible, incremental L4 checksum is used to update the packet.
262 * Setting ip_summed to CHECKSUM_UNNECESSARY ensures checksum is not recalculated further in packet
263 * path.
264 */
265 if (likely(hw_csum)) {
266 skb->ip_summed = CHECKSUM_PARTIAL;
267 } else {
268 skb->ip_summed = CHECKSUM_UNNECESSARY;
269 }
270
271 /*
Ratheesh Kannoth6307bec2021-11-25 08:26:39 +0530272 * Update traffic stats.
273 */
274 atomic_inc(&cm->rx_packet_count);
275 atomic_add(len, &cm->rx_byte_count);
276
277 xmit_dev = cm->xmit_dev;
278 skb->dev = xmit_dev;
279
280 /*
281 * Check to see if we need to write a header.
282 */
283 if (likely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_WRITE_L2_HDR)) {
284 if (unlikely(!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_WRITE_FAST_ETH_HDR))) {
285 dev_hard_header(skb, xmit_dev, ETH_P_IPV6,
286 cm->xmit_dest_mac, cm->xmit_src_mac, len);
287 } else {
288 /*
289 * For the simple case we write this really fast.
290 */
291 struct ethhdr *eth = (struct ethhdr *)__skb_push(skb, ETH_HLEN);
292 eth->h_proto = htons(ETH_P_IPV6);
293 ether_addr_copy((u8 *)eth->h_dest, (u8 *)cm->xmit_dest_mac);
294 ether_addr_copy((u8 *)eth->h_source, (u8 *)cm->xmit_src_mac);
295
296 }
297 }
298
299 /*
300 * Update priority of skb.
301 */
302 if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PRIORITY_REMARK)) {
303 skb->priority = cm->priority;
304 }
305
306 /*
307 * Mark outgoing packet.
308 */
309 skb->mark = cm->connection->mark;
310 if (skb->mark) {
311 DEBUG_TRACE("SKB MARK is NON ZERO %x\n", skb->mark);
312 }
313
314 rcu_read_unlock();
315
316 this_cpu_inc(si->stats_pcpu->packets_forwarded64);
317
318 /*
319 * We're going to check for GSO flags when we transmit the packet so
320 * start fetching the necessary cache line now.
321 */
322 prefetch(skb_shinfo(skb));
323
324 /*
325 * Mark that this packet has been fast forwarded.
326 */
327 skb->fast_forwarded = 1;
328
329 /*
330 * Send the packet on its way.
331 */
332 dev_queue_xmit(skb);
333
334 return 1;
335}