xref: /linux/drivers/net/wireless/intel/iwlwifi/mei/net.c (revision 14e77332e74603efab8347c89d3cda447c3b97c9)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2021 Intel Corporation
4  */
5 
6 #include <uapi/linux/if_ether.h>
7 #include <uapi/linux/if_arp.h>
8 #include <uapi/linux/icmp.h>
9 
10 #include <linux/etherdevice.h>
11 #include <linux/netdevice.h>
12 #include <linux/skbuff.h>
13 #include <linux/ieee80211.h>
14 
15 #include <net/cfg80211.h>
16 #include <net/ip.h>
17 
18 #include <linux/if_arp.h>
19 #include <linux/icmp.h>
20 #include <linux/udp.h>
21 #include <linux/ip.h>
22 #include <linux/mm.h>
23 
24 #include "internal.h"
25 #include "sap.h"
26 #include "iwl-mei.h"
27 
28 /*
29  * Returns true if further filtering should be stopped. Only in that case
30  * pass_to_csme and rx_handler_res are set. Otherwise, next level of filters
31  * should be checked.
32  */
33 static bool iwl_mei_rx_filter_eth(const struct ethhdr *ethhdr,
34 				  const struct iwl_sap_oob_filters *filters,
35 				  bool *pass_to_csme,
36 				  rx_handler_result_t *rx_handler_res)
37 {
38 	const struct iwl_sap_eth_filter *filt;
39 
40 	/* This filter is not relevant for UCAST packet */
41 	if (!is_multicast_ether_addr(ethhdr->h_dest) ||
42 	    is_broadcast_ether_addr(ethhdr->h_dest))
43 		return false;
44 
45 	for (filt = &filters->eth_filters[0];
46 	     filt < &filters->eth_filters[0] + ARRAY_SIZE(filters->eth_filters);
47 	     filt++) {
48 		/* Assume there are no enabled filter after a disabled one */
49 		if (!(filt->flags & SAP_ETH_FILTER_ENABLED))
50 			break;
51 
52 		if (compare_ether_header(filt->mac_address, ethhdr->h_dest))
53 			continue;
54 
55 		/* Packet needs to reach the host's stack */
56 		if (filt->flags & SAP_ETH_FILTER_COPY)
57 			*rx_handler_res = RX_HANDLER_PASS;
58 		else
59 			*rx_handler_res = RX_HANDLER_CONSUMED;
60 
61 		/* We have an authoritative answer, stop filtering */
62 		if (filt->flags & SAP_ETH_FILTER_STOP) {
63 			*pass_to_csme = true;
64 			return true;
65 		}
66 
67 		return false;
68 	}
69 
70 	 /* MCAST frames that don't match layer 2 filters are not sent to ME */
71 	*pass_to_csme  = false;
72 
73 	return true;
74 }
75 
76 /*
77  * Returns true iff the frame should be passed to CSME in which case
78  * rx_handler_res is set.
79  */
80 static bool iwl_mei_rx_filter_arp(struct sk_buff *skb,
81 				  const struct iwl_sap_oob_filters *filters,
82 				  rx_handler_result_t *rx_handler_res)
83 {
84 	const struct iwl_sap_ipv4_filter *filt = &filters->ipv4_filter;
85 	const struct arphdr *arp;
86 	const __be32 *target_ip;
87 	u32 flags = le32_to_cpu(filt->flags);
88 
89 	if (!pskb_may_pull(skb, arp_hdr_len(skb->dev)))
90 		return false;
91 
92 	arp = arp_hdr(skb);
93 
94 	/* Handle only IPv4 over ethernet ARP frames */
95 	if (arp->ar_hrd != htons(ARPHRD_ETHER) ||
96 	    arp->ar_pro != htons(ETH_P_IP))
97 		return false;
98 
99 	/*
100 	 * After the ARP header, we have:
101 	 * src MAC address   - 6 bytes
102 	 * src IP address    - 4 bytes
103 	 * target MAC addess - 6 bytes
104 	 */
105 	target_ip = (const void *)((const u8 *)(arp + 1) +
106 				   ETH_ALEN + sizeof(__be32) + ETH_ALEN);
107 
108 	/*
109 	 * ARP request is forwarded to ME only if IP address match in the
110 	 * ARP request's target ip field.
111 	 */
112 	if (arp->ar_op == htons(ARPOP_REQUEST) &&
113 	    (filt->flags & cpu_to_le32(SAP_IPV4_FILTER_ARP_REQ_PASS)) &&
114 	    (filt->ipv4_addr == 0 || filt->ipv4_addr == *target_ip)) {
115 		if (flags & SAP_IPV4_FILTER_ARP_REQ_COPY)
116 			*rx_handler_res = RX_HANDLER_PASS;
117 		else
118 			*rx_handler_res = RX_HANDLER_CONSUMED;
119 
120 		return true;
121 	}
122 
123 	/* ARP reply is always forwarded to ME regardless of the IP */
124 	if (flags & SAP_IPV4_FILTER_ARP_RESP_PASS &&
125 	    arp->ar_op == htons(ARPOP_REPLY)) {
126 		if (flags & SAP_IPV4_FILTER_ARP_RESP_COPY)
127 			*rx_handler_res = RX_HANDLER_PASS;
128 		else
129 			*rx_handler_res = RX_HANDLER_CONSUMED;
130 
131 		return true;
132 	}
133 
134 	return false;
135 }
136 
137 static bool
138 iwl_mei_rx_filter_tcp_udp(struct sk_buff *skb, bool  ip_match,
139 			  const struct iwl_sap_oob_filters *filters,
140 			  rx_handler_result_t *rx_handler_res)
141 {
142 	const struct iwl_sap_flex_filter *filt;
143 
144 	for (filt = &filters->flex_filters[0];
145 	     filt < &filters->flex_filters[0] + ARRAY_SIZE(filters->flex_filters);
146 	     filt++) {
147 		if (!(filt->flags & SAP_FLEX_FILTER_ENABLED))
148 			break;
149 
150 		/*
151 		 * We are required to have a match on the IP level and we didn't
152 		 * have such match.
153 		 */
154 		if ((filt->flags &
155 		     (SAP_FLEX_FILTER_IPV4 | SAP_FLEX_FILTER_IPV6)) &&
156 		    !ip_match)
157 			continue;
158 
159 		if ((filt->flags & SAP_FLEX_FILTER_UDP) &&
160 		    ip_hdr(skb)->protocol != IPPROTO_UDP)
161 			continue;
162 
163 		if ((filt->flags & SAP_FLEX_FILTER_TCP) &&
164 		    ip_hdr(skb)->protocol != IPPROTO_TCP)
165 			continue;
166 
167 		/*
168 		 * We must have either a TCP header or a UDP header, both
169 		 * starts with a source port and then a destination port.
170 		 * Both are big endian words.
171 		 * Use a UDP header and that will work for TCP as well.
172 		 */
173 		if ((filt->src_port && filt->src_port != udp_hdr(skb)->source) ||
174 		    (filt->dst_port && filt->dst_port != udp_hdr(skb)->dest))
175 			continue;
176 
177 		if (filt->flags & SAP_FLEX_FILTER_COPY)
178 			*rx_handler_res = RX_HANDLER_PASS;
179 		else
180 			*rx_handler_res = RX_HANDLER_CONSUMED;
181 
182 		return true;
183 	}
184 
185 	return false;
186 }
187 
188 static bool iwl_mei_rx_filter_ipv4(struct sk_buff *skb,
189 				   const struct iwl_sap_oob_filters *filters,
190 				   rx_handler_result_t *rx_handler_res)
191 {
192 	const struct iwl_sap_ipv4_filter *filt = &filters->ipv4_filter;
193 	const struct iphdr *iphdr;
194 	unsigned int iphdrlen;
195 	bool match;
196 
197 	if (!pskb_may_pull(skb, skb_network_offset(skb) + sizeof(*iphdr)) ||
198 	    !pskb_may_pull(skb, skb_network_offset(skb) + ip_hdrlen(skb)))
199 		return false;
200 
201 	iphdrlen = ip_hdrlen(skb);
202 	iphdr = ip_hdr(skb);
203 	match = !filters->ipv4_filter.ipv4_addr ||
204 		filters->ipv4_filter.ipv4_addr == iphdr->daddr;
205 
206 	skb_set_transport_header(skb, skb_network_offset(skb) + iphdrlen);
207 
208 	switch (ip_hdr(skb)->protocol) {
209 	case IPPROTO_UDP:
210 	case IPPROTO_TCP:
211 		/*
212 		 * UDP header is shorter than TCP header and we look at the first bytes
213 		 * of the header anyway (see below).
214 		 * If we have a truncated TCP packet, let CSME handle this.
215 		 */
216 		if (!pskb_may_pull(skb, skb_transport_offset(skb) +
217 				   sizeof(struct udphdr)))
218 			return false;
219 
220 		return iwl_mei_rx_filter_tcp_udp(skb, match,
221 						 filters, rx_handler_res);
222 
223 	case IPPROTO_ICMP: {
224 		struct icmphdr *icmp;
225 
226 		if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(*icmp)))
227 			return false;
228 
229 		icmp = icmp_hdr(skb);
230 
231 		/*
232 		 * Don't pass echo requests to ME even if it wants it as we
233 		 * want the host to answer.
234 		 */
235 		if ((filt->flags & cpu_to_le32(SAP_IPV4_FILTER_ICMP_PASS)) &&
236 		    match && (icmp->type != ICMP_ECHO || icmp->code != 0)) {
237 			if (filt->flags & cpu_to_le32(SAP_IPV4_FILTER_ICMP_COPY))
238 				*rx_handler_res = RX_HANDLER_PASS;
239 			else
240 				*rx_handler_res = RX_HANDLER_CONSUMED;
241 
242 			return true;
243 		}
244 		break;
245 		}
246 	case IPPROTO_ICMPV6:
247 		/* TODO: Should we have the same ICMP request logic here too? */
248 		if ((filters->icmpv6_flags & cpu_to_le32(SAP_ICMPV6_FILTER_ENABLED) &&
249 		     match)) {
250 			if (filters->icmpv6_flags &
251 			    cpu_to_le32(SAP_ICMPV6_FILTER_COPY))
252 				*rx_handler_res = RX_HANDLER_PASS;
253 			else
254 				*rx_handler_res = RX_HANDLER_CONSUMED;
255 
256 			return true;
257 		}
258 		break;
259 	default:
260 		return false;
261 	}
262 
263 	return false;
264 }
265 
266 static bool iwl_mei_rx_filter_ipv6(struct sk_buff *skb,
267 				   const struct iwl_sap_oob_filters *filters,
268 				   rx_handler_result_t *rx_handler_res)
269 {
270 	*rx_handler_res = RX_HANDLER_PASS;
271 
272 	/* TODO */
273 
274 	return false;
275 }
276 
277 static rx_handler_result_t
278 iwl_mei_rx_pass_to_csme(struct sk_buff *skb,
279 			const struct iwl_sap_oob_filters *filters,
280 			bool *pass_to_csme)
281 {
282 	const struct ethhdr *ethhdr = (void *)skb_mac_header(skb);
283 	rx_handler_result_t rx_handler_res = RX_HANDLER_PASS;
284 	bool (*filt_handler)(struct sk_buff *skb,
285 			     const struct iwl_sap_oob_filters *filters,
286 			     rx_handler_result_t *rx_handler_res);
287 
288 	/*
289 	 * skb->data points the IP header / ARP header and the ETH header
290 	 * is in the headroom.
291 	 */
292 	skb_reset_network_header(skb);
293 
294 	/*
295 	 * MCAST IP packets sent by us are received again here without
296 	 * an ETH header. Drop them here.
297 	 */
298 	if (!skb_mac_offset(skb))
299 		return RX_HANDLER_PASS;
300 
301 	if (skb_headroom(skb) < sizeof(*ethhdr))
302 		return RX_HANDLER_PASS;
303 
304 	if (iwl_mei_rx_filter_eth(ethhdr, filters,
305 				  pass_to_csme, &rx_handler_res))
306 		return rx_handler_res;
307 
308 	switch (skb->protocol) {
309 	case htons(ETH_P_IP):
310 		filt_handler = iwl_mei_rx_filter_ipv4;
311 		break;
312 	case htons(ETH_P_ARP):
313 		filt_handler = iwl_mei_rx_filter_arp;
314 		break;
315 	case htons(ETH_P_IPV6):
316 		filt_handler = iwl_mei_rx_filter_ipv6;
317 		break;
318 	default:
319 		*pass_to_csme = false;
320 		return rx_handler_res;
321 	}
322 
323 	*pass_to_csme = filt_handler(skb, filters, &rx_handler_res);
324 
325 	return rx_handler_res;
326 }
327 
328 rx_handler_result_t iwl_mei_rx_filter(struct sk_buff *orig_skb,
329 				      const struct iwl_sap_oob_filters *filters,
330 				      bool *pass_to_csme)
331 {
332 	rx_handler_result_t ret;
333 	struct sk_buff *skb;
334 
335 	ret = iwl_mei_rx_pass_to_csme(orig_skb, filters, pass_to_csme);
336 
337 	if (!*pass_to_csme)
338 		return RX_HANDLER_PASS;
339 
340 	if (ret == RX_HANDLER_PASS)
341 		skb = skb_copy(orig_skb, GFP_ATOMIC);
342 	else
343 		skb = orig_skb;
344 
345 	/* CSME wants the MAC header as well, push it back */
346 	skb_push(skb, skb->data - skb_mac_header(skb));
347 
348 	/*
349 	 * Add the packet that CSME wants to get to the ring. Don't send the
350 	 * Check Shared Area HECI message since this is not possible from the
351 	 * Rx context. The caller will schedule a worker to do just that.
352 	 */
353 	iwl_mei_add_data_to_ring(skb, false);
354 
355 	/*
356 	 * In case we drop the packet, don't free it, the caller will do that
357 	 * for us
358 	 */
359 	if (ret == RX_HANDLER_PASS)
360 		dev_kfree_skb(skb);
361 
362 	return ret;
363 }
364 
365 #define DHCP_SERVER_PORT 67
366 #define DHCP_CLIENT_PORT 68
367 void iwl_mei_tx_copy_to_csme(struct sk_buff *origskb, unsigned int ivlen)
368 {
369 	struct ieee80211_hdr *hdr;
370 	struct sk_buff *skb;
371 	struct ethhdr ethhdr;
372 	struct ethhdr *eth;
373 
374 	/* Catch DHCP packets */
375 	if (origskb->protocol != htons(ETH_P_IP) ||
376 	    ip_hdr(origskb)->protocol != IPPROTO_UDP ||
377 	    udp_hdr(origskb)->source != htons(DHCP_CLIENT_PORT) ||
378 	    udp_hdr(origskb)->dest != htons(DHCP_SERVER_PORT))
379 		return;
380 
381 	/*
382 	 * We could be a bit less aggressive here and not copy everything, but
383 	 * this is very rare anyway, do don't bother much.
384 	 */
385 	skb = skb_copy(origskb, GFP_ATOMIC);
386 	if (!skb)
387 		return;
388 
389 	skb->protocol = origskb->protocol;
390 
391 	hdr = (void *)skb->data;
392 
393 	memcpy(ethhdr.h_dest, ieee80211_get_DA(hdr), ETH_ALEN);
394 	memcpy(ethhdr.h_source, ieee80211_get_SA(hdr), ETH_ALEN);
395 
396 	/*
397 	 * Remove the ieee80211 header + IV + SNAP but leave the ethertype
398 	 * We still have enough headroom for the sap header.
399 	 */
400 	pskb_pull(skb, ieee80211_hdrlen(hdr->frame_control) + ivlen + 6);
401 	eth = skb_push(skb, sizeof(ethhdr.h_dest) + sizeof(ethhdr.h_source));
402 	memcpy(eth, &ethhdr, sizeof(ethhdr.h_dest) + sizeof(ethhdr.h_source));
403 
404 	iwl_mei_add_data_to_ring(skb, true);
405 
406 	dev_kfree_skb(skb);
407 }
408 EXPORT_SYMBOL_GPL(iwl_mei_tx_copy_to_csme);
409