12da4366fSEmmanuel Grumbach // SPDX-License-Identifier: GPL-2.0-only
22da4366fSEmmanuel Grumbach /*
3*d3df49ddSJohannes Berg  * Copyright (C) 2021-2022 Intel Corporation
42da4366fSEmmanuel Grumbach  */
52da4366fSEmmanuel Grumbach 
62da4366fSEmmanuel Grumbach #include <uapi/linux/if_ether.h>
72da4366fSEmmanuel Grumbach #include <uapi/linux/if_arp.h>
82da4366fSEmmanuel Grumbach #include <uapi/linux/icmp.h>
92da4366fSEmmanuel Grumbach 
102da4366fSEmmanuel Grumbach #include <linux/etherdevice.h>
112da4366fSEmmanuel Grumbach #include <linux/netdevice.h>
122da4366fSEmmanuel Grumbach #include <linux/skbuff.h>
132da4366fSEmmanuel Grumbach #include <linux/ieee80211.h>
142da4366fSEmmanuel Grumbach 
152da4366fSEmmanuel Grumbach #include <net/cfg80211.h>
162da4366fSEmmanuel Grumbach #include <net/ip.h>
172da4366fSEmmanuel Grumbach 
182da4366fSEmmanuel Grumbach #include <linux/if_arp.h>
192da4366fSEmmanuel Grumbach #include <linux/icmp.h>
202da4366fSEmmanuel Grumbach #include <linux/udp.h>
212da4366fSEmmanuel Grumbach #include <linux/ip.h>
222da4366fSEmmanuel Grumbach #include <linux/mm.h>
232da4366fSEmmanuel Grumbach 
242da4366fSEmmanuel Grumbach #include "internal.h"
252da4366fSEmmanuel Grumbach #include "sap.h"
262da4366fSEmmanuel Grumbach #include "iwl-mei.h"
272da4366fSEmmanuel Grumbach 
282da4366fSEmmanuel Grumbach /*
292da4366fSEmmanuel Grumbach  * Returns true if further filtering should be stopped. Only in that case
302da4366fSEmmanuel Grumbach  * pass_to_csme and rx_handler_res are set. Otherwise, next level of filters
312da4366fSEmmanuel Grumbach  * should be checked.
322da4366fSEmmanuel Grumbach  */
iwl_mei_rx_filter_eth(const struct ethhdr * ethhdr,const struct iwl_sap_oob_filters * filters,bool * pass_to_csme,rx_handler_result_t * rx_handler_res)332da4366fSEmmanuel Grumbach static bool iwl_mei_rx_filter_eth(const struct ethhdr *ethhdr,
342da4366fSEmmanuel Grumbach 				  const struct iwl_sap_oob_filters *filters,
352da4366fSEmmanuel Grumbach 				  bool *pass_to_csme,
362da4366fSEmmanuel Grumbach 				  rx_handler_result_t *rx_handler_res)
372da4366fSEmmanuel Grumbach {
382da4366fSEmmanuel Grumbach 	const struct iwl_sap_eth_filter *filt;
392da4366fSEmmanuel Grumbach 
402da4366fSEmmanuel Grumbach 	/* This filter is not relevant for UCAST packet */
412da4366fSEmmanuel Grumbach 	if (!is_multicast_ether_addr(ethhdr->h_dest) ||
422da4366fSEmmanuel Grumbach 	    is_broadcast_ether_addr(ethhdr->h_dest))
432da4366fSEmmanuel Grumbach 		return false;
442da4366fSEmmanuel Grumbach 
452da4366fSEmmanuel Grumbach 	for (filt = &filters->eth_filters[0];
462da4366fSEmmanuel Grumbach 	     filt < &filters->eth_filters[0] + ARRAY_SIZE(filters->eth_filters);
472da4366fSEmmanuel Grumbach 	     filt++) {
482da4366fSEmmanuel Grumbach 		/* Assume there are no enabled filter after a disabled one */
492da4366fSEmmanuel Grumbach 		if (!(filt->flags & SAP_ETH_FILTER_ENABLED))
502da4366fSEmmanuel Grumbach 			break;
512da4366fSEmmanuel Grumbach 
522da4366fSEmmanuel Grumbach 		if (compare_ether_header(filt->mac_address, ethhdr->h_dest))
532da4366fSEmmanuel Grumbach 			continue;
542da4366fSEmmanuel Grumbach 
552da4366fSEmmanuel Grumbach 		/* Packet needs to reach the host's stack */
562da4366fSEmmanuel Grumbach 		if (filt->flags & SAP_ETH_FILTER_COPY)
572da4366fSEmmanuel Grumbach 			*rx_handler_res = RX_HANDLER_PASS;
582da4366fSEmmanuel Grumbach 		else
592da4366fSEmmanuel Grumbach 			*rx_handler_res = RX_HANDLER_CONSUMED;
602da4366fSEmmanuel Grumbach 
612da4366fSEmmanuel Grumbach 		/* We have an authoritative answer, stop filtering */
622da4366fSEmmanuel Grumbach 		if (filt->flags & SAP_ETH_FILTER_STOP) {
632da4366fSEmmanuel Grumbach 			*pass_to_csme = true;
642da4366fSEmmanuel Grumbach 			return true;
652da4366fSEmmanuel Grumbach 		}
662da4366fSEmmanuel Grumbach 
672da4366fSEmmanuel Grumbach 		return false;
682da4366fSEmmanuel Grumbach 	}
692da4366fSEmmanuel Grumbach 
702da4366fSEmmanuel Grumbach 	 /* MCAST frames that don't match layer 2 filters are not sent to ME */
712da4366fSEmmanuel Grumbach 	*pass_to_csme  = false;
722da4366fSEmmanuel Grumbach 
732da4366fSEmmanuel Grumbach 	return true;
742da4366fSEmmanuel Grumbach }
752da4366fSEmmanuel Grumbach 
762da4366fSEmmanuel Grumbach /*
772da4366fSEmmanuel Grumbach  * Returns true iff the frame should be passed to CSME in which case
782da4366fSEmmanuel Grumbach  * rx_handler_res is set.
792da4366fSEmmanuel Grumbach  */
iwl_mei_rx_filter_arp(struct sk_buff * skb,const struct iwl_sap_oob_filters * filters,rx_handler_result_t * rx_handler_res)802da4366fSEmmanuel Grumbach static bool iwl_mei_rx_filter_arp(struct sk_buff *skb,
812da4366fSEmmanuel Grumbach 				  const struct iwl_sap_oob_filters *filters,
822da4366fSEmmanuel Grumbach 				  rx_handler_result_t *rx_handler_res)
832da4366fSEmmanuel Grumbach {
842da4366fSEmmanuel Grumbach 	const struct iwl_sap_ipv4_filter *filt = &filters->ipv4_filter;
852da4366fSEmmanuel Grumbach 	const struct arphdr *arp;
862da4366fSEmmanuel Grumbach 	const __be32 *target_ip;
872da4366fSEmmanuel Grumbach 	u32 flags = le32_to_cpu(filt->flags);
882da4366fSEmmanuel Grumbach 
892da4366fSEmmanuel Grumbach 	if (!pskb_may_pull(skb, arp_hdr_len(skb->dev)))
902da4366fSEmmanuel Grumbach 		return false;
912da4366fSEmmanuel Grumbach 
922da4366fSEmmanuel Grumbach 	arp = arp_hdr(skb);
932da4366fSEmmanuel Grumbach 
942da4366fSEmmanuel Grumbach 	/* Handle only IPv4 over ethernet ARP frames */
952da4366fSEmmanuel Grumbach 	if (arp->ar_hrd != htons(ARPHRD_ETHER) ||
962da4366fSEmmanuel Grumbach 	    arp->ar_pro != htons(ETH_P_IP))
972da4366fSEmmanuel Grumbach 		return false;
982da4366fSEmmanuel Grumbach 
992da4366fSEmmanuel Grumbach 	/*
1002da4366fSEmmanuel Grumbach 	 * After the ARP header, we have:
1012da4366fSEmmanuel Grumbach 	 * src MAC address   - 6 bytes
1022da4366fSEmmanuel Grumbach 	 * src IP address    - 4 bytes
1032da4366fSEmmanuel Grumbach 	 * target MAC addess - 6 bytes
1042da4366fSEmmanuel Grumbach 	 */
105a31ec5faSJohannes Berg 	target_ip = (const void *)((const u8 *)(arp + 1) +
1062da4366fSEmmanuel Grumbach 				   ETH_ALEN + sizeof(__be32) + ETH_ALEN);
1072da4366fSEmmanuel Grumbach 
1082da4366fSEmmanuel Grumbach 	/*
1092da4366fSEmmanuel Grumbach 	 * ARP request is forwarded to ME only if IP address match in the
1102da4366fSEmmanuel Grumbach 	 * ARP request's target ip field.
1112da4366fSEmmanuel Grumbach 	 */
1122da4366fSEmmanuel Grumbach 	if (arp->ar_op == htons(ARPOP_REQUEST) &&
1132da4366fSEmmanuel Grumbach 	    (filt->flags & cpu_to_le32(SAP_IPV4_FILTER_ARP_REQ_PASS)) &&
1142da4366fSEmmanuel Grumbach 	    (filt->ipv4_addr == 0 || filt->ipv4_addr == *target_ip)) {
1152da4366fSEmmanuel Grumbach 		if (flags & SAP_IPV4_FILTER_ARP_REQ_COPY)
1162da4366fSEmmanuel Grumbach 			*rx_handler_res = RX_HANDLER_PASS;
1172da4366fSEmmanuel Grumbach 		else
1182da4366fSEmmanuel Grumbach 			*rx_handler_res = RX_HANDLER_CONSUMED;
1192da4366fSEmmanuel Grumbach 
1202da4366fSEmmanuel Grumbach 		return true;
1212da4366fSEmmanuel Grumbach 	}
1222da4366fSEmmanuel Grumbach 
1232da4366fSEmmanuel Grumbach 	/* ARP reply is always forwarded to ME regardless of the IP */
1242da4366fSEmmanuel Grumbach 	if (flags & SAP_IPV4_FILTER_ARP_RESP_PASS &&
1252da4366fSEmmanuel Grumbach 	    arp->ar_op == htons(ARPOP_REPLY)) {
1262da4366fSEmmanuel Grumbach 		if (flags & SAP_IPV4_FILTER_ARP_RESP_COPY)
1272da4366fSEmmanuel Grumbach 			*rx_handler_res = RX_HANDLER_PASS;
1282da4366fSEmmanuel Grumbach 		else
1292da4366fSEmmanuel Grumbach 			*rx_handler_res = RX_HANDLER_CONSUMED;
1302da4366fSEmmanuel Grumbach 
1312da4366fSEmmanuel Grumbach 		return true;
1322da4366fSEmmanuel Grumbach 	}
1332da4366fSEmmanuel Grumbach 
1342da4366fSEmmanuel Grumbach 	return false;
1352da4366fSEmmanuel Grumbach }
1362da4366fSEmmanuel Grumbach 
1372da4366fSEmmanuel Grumbach static bool
iwl_mei_rx_filter_tcp_udp(struct sk_buff * skb,bool ip_match,const struct iwl_sap_oob_filters * filters,rx_handler_result_t * rx_handler_res)1382da4366fSEmmanuel Grumbach iwl_mei_rx_filter_tcp_udp(struct sk_buff *skb, bool  ip_match,
1392da4366fSEmmanuel Grumbach 			  const struct iwl_sap_oob_filters *filters,
1402da4366fSEmmanuel Grumbach 			  rx_handler_result_t *rx_handler_res)
1412da4366fSEmmanuel Grumbach {
1422da4366fSEmmanuel Grumbach 	const struct iwl_sap_flex_filter *filt;
1432da4366fSEmmanuel Grumbach 
1442da4366fSEmmanuel Grumbach 	for (filt = &filters->flex_filters[0];
1452da4366fSEmmanuel Grumbach 	     filt < &filters->flex_filters[0] + ARRAY_SIZE(filters->flex_filters);
1462da4366fSEmmanuel Grumbach 	     filt++) {
1472da4366fSEmmanuel Grumbach 		if (!(filt->flags & SAP_FLEX_FILTER_ENABLED))
1482da4366fSEmmanuel Grumbach 			break;
1492da4366fSEmmanuel Grumbach 
1502da4366fSEmmanuel Grumbach 		/*
1512da4366fSEmmanuel Grumbach 		 * We are required to have a match on the IP level and we didn't
1522da4366fSEmmanuel Grumbach 		 * have such match.
1532da4366fSEmmanuel Grumbach 		 */
1542da4366fSEmmanuel Grumbach 		if ((filt->flags &
1552da4366fSEmmanuel Grumbach 		     (SAP_FLEX_FILTER_IPV4 | SAP_FLEX_FILTER_IPV6)) &&
1562da4366fSEmmanuel Grumbach 		    !ip_match)
1572da4366fSEmmanuel Grumbach 			continue;
1582da4366fSEmmanuel Grumbach 
1592da4366fSEmmanuel Grumbach 		if ((filt->flags & SAP_FLEX_FILTER_UDP) &&
1602da4366fSEmmanuel Grumbach 		    ip_hdr(skb)->protocol != IPPROTO_UDP)
1612da4366fSEmmanuel Grumbach 			continue;
1622da4366fSEmmanuel Grumbach 
1632da4366fSEmmanuel Grumbach 		if ((filt->flags & SAP_FLEX_FILTER_TCP) &&
1642da4366fSEmmanuel Grumbach 		    ip_hdr(skb)->protocol != IPPROTO_TCP)
1652da4366fSEmmanuel Grumbach 			continue;
1662da4366fSEmmanuel Grumbach 
1672da4366fSEmmanuel Grumbach 		/*
1682da4366fSEmmanuel Grumbach 		 * We must have either a TCP header or a UDP header, both
1692da4366fSEmmanuel Grumbach 		 * starts with a source port and then a destination port.
1702da4366fSEmmanuel Grumbach 		 * Both are big endian words.
1712da4366fSEmmanuel Grumbach 		 * Use a UDP header and that will work for TCP as well.
1722da4366fSEmmanuel Grumbach 		 */
1732da4366fSEmmanuel Grumbach 		if ((filt->src_port && filt->src_port != udp_hdr(skb)->source) ||
1742da4366fSEmmanuel Grumbach 		    (filt->dst_port && filt->dst_port != udp_hdr(skb)->dest))
1752da4366fSEmmanuel Grumbach 			continue;
1762da4366fSEmmanuel Grumbach 
1772da4366fSEmmanuel Grumbach 		if (filt->flags & SAP_FLEX_FILTER_COPY)
1782da4366fSEmmanuel Grumbach 			*rx_handler_res = RX_HANDLER_PASS;
1792da4366fSEmmanuel Grumbach 		else
1802da4366fSEmmanuel Grumbach 			*rx_handler_res = RX_HANDLER_CONSUMED;
1812da4366fSEmmanuel Grumbach 
1822da4366fSEmmanuel Grumbach 		return true;
1832da4366fSEmmanuel Grumbach 	}
1842da4366fSEmmanuel Grumbach 
1852da4366fSEmmanuel Grumbach 	return false;
1862da4366fSEmmanuel Grumbach }
1872da4366fSEmmanuel Grumbach 
iwl_mei_rx_filter_ipv4(struct sk_buff * skb,const struct iwl_sap_oob_filters * filters,rx_handler_result_t * rx_handler_res)1882da4366fSEmmanuel Grumbach static bool iwl_mei_rx_filter_ipv4(struct sk_buff *skb,
1892da4366fSEmmanuel Grumbach 				   const struct iwl_sap_oob_filters *filters,
1902da4366fSEmmanuel Grumbach 				   rx_handler_result_t *rx_handler_res)
1912da4366fSEmmanuel Grumbach {
1922da4366fSEmmanuel Grumbach 	const struct iwl_sap_ipv4_filter *filt = &filters->ipv4_filter;
1932da4366fSEmmanuel Grumbach 	const struct iphdr *iphdr;
1942da4366fSEmmanuel Grumbach 	unsigned int iphdrlen;
1952da4366fSEmmanuel Grumbach 	bool match;
1962da4366fSEmmanuel Grumbach 
1972da4366fSEmmanuel Grumbach 	if (!pskb_may_pull(skb, skb_network_offset(skb) + sizeof(*iphdr)) ||
198e1849784SEmmanuel Grumbach 	    !pskb_may_pull(skb, skb_network_offset(skb) + ip_hdrlen(skb)))
1992da4366fSEmmanuel Grumbach 		return false;
2002da4366fSEmmanuel Grumbach 
2012da4366fSEmmanuel Grumbach 	iphdrlen = ip_hdrlen(skb);
2022da4366fSEmmanuel Grumbach 	iphdr = ip_hdr(skb);
2032da4366fSEmmanuel Grumbach 	match = !filters->ipv4_filter.ipv4_addr ||
2042da4366fSEmmanuel Grumbach 		filters->ipv4_filter.ipv4_addr == iphdr->daddr;
2052da4366fSEmmanuel Grumbach 
2062da4366fSEmmanuel Grumbach 	skb_set_transport_header(skb, skb_network_offset(skb) + iphdrlen);
2072da4366fSEmmanuel Grumbach 
2082da4366fSEmmanuel Grumbach 	switch (ip_hdr(skb)->protocol) {
2092da4366fSEmmanuel Grumbach 	case IPPROTO_UDP:
2102da4366fSEmmanuel Grumbach 	case IPPROTO_TCP:
2112da4366fSEmmanuel Grumbach 		/*
2122da4366fSEmmanuel Grumbach 		 * UDP header is shorter than TCP header and we look at the first bytes
2132da4366fSEmmanuel Grumbach 		 * of the header anyway (see below).
2142da4366fSEmmanuel Grumbach 		 * If we have a truncated TCP packet, let CSME handle this.
2152da4366fSEmmanuel Grumbach 		 */
2162da4366fSEmmanuel Grumbach 		if (!pskb_may_pull(skb, skb_transport_offset(skb) +
2172da4366fSEmmanuel Grumbach 				   sizeof(struct udphdr)))
2182da4366fSEmmanuel Grumbach 			return false;
2192da4366fSEmmanuel Grumbach 
2202da4366fSEmmanuel Grumbach 		return iwl_mei_rx_filter_tcp_udp(skb, match,
2212da4366fSEmmanuel Grumbach 						 filters, rx_handler_res);
2222da4366fSEmmanuel Grumbach 
2232da4366fSEmmanuel Grumbach 	case IPPROTO_ICMP: {
2242da4366fSEmmanuel Grumbach 		struct icmphdr *icmp;
2252da4366fSEmmanuel Grumbach 
2262da4366fSEmmanuel Grumbach 		if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(*icmp)))
2272da4366fSEmmanuel Grumbach 			return false;
2282da4366fSEmmanuel Grumbach 
2292da4366fSEmmanuel Grumbach 		icmp = icmp_hdr(skb);
2302da4366fSEmmanuel Grumbach 
2312da4366fSEmmanuel Grumbach 		/*
2322da4366fSEmmanuel Grumbach 		 * Don't pass echo requests to ME even if it wants it as we
2332da4366fSEmmanuel Grumbach 		 * want the host to answer.
2342da4366fSEmmanuel Grumbach 		 */
2352da4366fSEmmanuel Grumbach 		if ((filt->flags & cpu_to_le32(SAP_IPV4_FILTER_ICMP_PASS)) &&
2362da4366fSEmmanuel Grumbach 		    match && (icmp->type != ICMP_ECHO || icmp->code != 0)) {
2372da4366fSEmmanuel Grumbach 			if (filt->flags & cpu_to_le32(SAP_IPV4_FILTER_ICMP_COPY))
2382da4366fSEmmanuel Grumbach 				*rx_handler_res = RX_HANDLER_PASS;
2392da4366fSEmmanuel Grumbach 			else
2402da4366fSEmmanuel Grumbach 				*rx_handler_res = RX_HANDLER_CONSUMED;
2412da4366fSEmmanuel Grumbach 
2422da4366fSEmmanuel Grumbach 			return true;
2432da4366fSEmmanuel Grumbach 		}
2442da4366fSEmmanuel Grumbach 		break;
2452da4366fSEmmanuel Grumbach 		}
2462da4366fSEmmanuel Grumbach 	case IPPROTO_ICMPV6:
2472da4366fSEmmanuel Grumbach 		/* TODO: Should we have the same ICMP request logic here too? */
2482da4366fSEmmanuel Grumbach 		if ((filters->icmpv6_flags & cpu_to_le32(SAP_ICMPV6_FILTER_ENABLED) &&
2492da4366fSEmmanuel Grumbach 		     match)) {
2502da4366fSEmmanuel Grumbach 			if (filters->icmpv6_flags &
2512da4366fSEmmanuel Grumbach 			    cpu_to_le32(SAP_ICMPV6_FILTER_COPY))
2522da4366fSEmmanuel Grumbach 				*rx_handler_res = RX_HANDLER_PASS;
2532da4366fSEmmanuel Grumbach 			else
2542da4366fSEmmanuel Grumbach 				*rx_handler_res = RX_HANDLER_CONSUMED;
2552da4366fSEmmanuel Grumbach 
2562da4366fSEmmanuel Grumbach 			return true;
2572da4366fSEmmanuel Grumbach 		}
2582da4366fSEmmanuel Grumbach 		break;
2592da4366fSEmmanuel Grumbach 	default:
2602da4366fSEmmanuel Grumbach 		return false;
2612da4366fSEmmanuel Grumbach 	}
2622da4366fSEmmanuel Grumbach 
2632da4366fSEmmanuel Grumbach 	return false;
2642da4366fSEmmanuel Grumbach }
2652da4366fSEmmanuel Grumbach 
iwl_mei_rx_filter_ipv6(struct sk_buff * skb,const struct iwl_sap_oob_filters * filters,rx_handler_result_t * rx_handler_res)2662da4366fSEmmanuel Grumbach static bool iwl_mei_rx_filter_ipv6(struct sk_buff *skb,
2672da4366fSEmmanuel Grumbach 				   const struct iwl_sap_oob_filters *filters,
2682da4366fSEmmanuel Grumbach 				   rx_handler_result_t *rx_handler_res)
2692da4366fSEmmanuel Grumbach {
2702da4366fSEmmanuel Grumbach 	*rx_handler_res = RX_HANDLER_PASS;
2712da4366fSEmmanuel Grumbach 
2722da4366fSEmmanuel Grumbach 	/* TODO */
2732da4366fSEmmanuel Grumbach 
2742da4366fSEmmanuel Grumbach 	return false;
2752da4366fSEmmanuel Grumbach }
2762da4366fSEmmanuel Grumbach 
2772da4366fSEmmanuel Grumbach static rx_handler_result_t
iwl_mei_rx_pass_to_csme(struct sk_buff * skb,const struct iwl_sap_oob_filters * filters,bool * pass_to_csme)2782da4366fSEmmanuel Grumbach iwl_mei_rx_pass_to_csme(struct sk_buff *skb,
2792da4366fSEmmanuel Grumbach 			const struct iwl_sap_oob_filters *filters,
2802da4366fSEmmanuel Grumbach 			bool *pass_to_csme)
2812da4366fSEmmanuel Grumbach {
2822da4366fSEmmanuel Grumbach 	const struct ethhdr *ethhdr = (void *)skb_mac_header(skb);
2832da4366fSEmmanuel Grumbach 	rx_handler_result_t rx_handler_res = RX_HANDLER_PASS;
2842da4366fSEmmanuel Grumbach 	bool (*filt_handler)(struct sk_buff *skb,
2852da4366fSEmmanuel Grumbach 			     const struct iwl_sap_oob_filters *filters,
2862da4366fSEmmanuel Grumbach 			     rx_handler_result_t *rx_handler_res);
2872da4366fSEmmanuel Grumbach 
2882da4366fSEmmanuel Grumbach 	/*
2892da4366fSEmmanuel Grumbach 	 * skb->data points the IP header / ARP header and the ETH header
2902da4366fSEmmanuel Grumbach 	 * is in the headroom.
2912da4366fSEmmanuel Grumbach 	 */
2922da4366fSEmmanuel Grumbach 	skb_reset_network_header(skb);
2932da4366fSEmmanuel Grumbach 
2942da4366fSEmmanuel Grumbach 	/*
2952da4366fSEmmanuel Grumbach 	 * MCAST IP packets sent by us are received again here without
2962da4366fSEmmanuel Grumbach 	 * an ETH header. Drop them here.
2972da4366fSEmmanuel Grumbach 	 */
2982da4366fSEmmanuel Grumbach 	if (!skb_mac_offset(skb))
2992da4366fSEmmanuel Grumbach 		return RX_HANDLER_PASS;
3002da4366fSEmmanuel Grumbach 
3012da4366fSEmmanuel Grumbach 	if (skb_headroom(skb) < sizeof(*ethhdr))
3022da4366fSEmmanuel Grumbach 		return RX_HANDLER_PASS;
3032da4366fSEmmanuel Grumbach 
3042da4366fSEmmanuel Grumbach 	if (iwl_mei_rx_filter_eth(ethhdr, filters,
3052da4366fSEmmanuel Grumbach 				  pass_to_csme, &rx_handler_res))
3062da4366fSEmmanuel Grumbach 		return rx_handler_res;
3072da4366fSEmmanuel Grumbach 
3082da4366fSEmmanuel Grumbach 	switch (skb->protocol) {
3092da4366fSEmmanuel Grumbach 	case htons(ETH_P_IP):
3102da4366fSEmmanuel Grumbach 		filt_handler = iwl_mei_rx_filter_ipv4;
3112da4366fSEmmanuel Grumbach 		break;
3122da4366fSEmmanuel Grumbach 	case htons(ETH_P_ARP):
3132da4366fSEmmanuel Grumbach 		filt_handler = iwl_mei_rx_filter_arp;
3142da4366fSEmmanuel Grumbach 		break;
3152da4366fSEmmanuel Grumbach 	case htons(ETH_P_IPV6):
3162da4366fSEmmanuel Grumbach 		filt_handler = iwl_mei_rx_filter_ipv6;
3172da4366fSEmmanuel Grumbach 		break;
3182da4366fSEmmanuel Grumbach 	default:
3192da4366fSEmmanuel Grumbach 		*pass_to_csme = false;
3202da4366fSEmmanuel Grumbach 		return rx_handler_res;
3212da4366fSEmmanuel Grumbach 	}
3222da4366fSEmmanuel Grumbach 
3232da4366fSEmmanuel Grumbach 	*pass_to_csme = filt_handler(skb, filters, &rx_handler_res);
3242da4366fSEmmanuel Grumbach 
3252da4366fSEmmanuel Grumbach 	return rx_handler_res;
3262da4366fSEmmanuel Grumbach }
3272da4366fSEmmanuel Grumbach 
iwl_mei_rx_filter(struct sk_buff * orig_skb,const struct iwl_sap_oob_filters * filters,bool * pass_to_csme)3282da4366fSEmmanuel Grumbach rx_handler_result_t iwl_mei_rx_filter(struct sk_buff *orig_skb,
3292da4366fSEmmanuel Grumbach 				      const struct iwl_sap_oob_filters *filters,
3302da4366fSEmmanuel Grumbach 				      bool *pass_to_csme)
3312da4366fSEmmanuel Grumbach {
3322da4366fSEmmanuel Grumbach 	rx_handler_result_t ret;
3332da4366fSEmmanuel Grumbach 	struct sk_buff *skb;
3342da4366fSEmmanuel Grumbach 
3352da4366fSEmmanuel Grumbach 	ret = iwl_mei_rx_pass_to_csme(orig_skb, filters, pass_to_csme);
3362da4366fSEmmanuel Grumbach 
3372da4366fSEmmanuel Grumbach 	if (!*pass_to_csme)
3382da4366fSEmmanuel Grumbach 		return RX_HANDLER_PASS;
3392da4366fSEmmanuel Grumbach 
340*d3df49ddSJohannes Berg 	if (ret == RX_HANDLER_PASS) {
3412da4366fSEmmanuel Grumbach 		skb = skb_copy(orig_skb, GFP_ATOMIC);
342*d3df49ddSJohannes Berg 
343*d3df49ddSJohannes Berg 		if (!skb)
344*d3df49ddSJohannes Berg 			return RX_HANDLER_PASS;
345*d3df49ddSJohannes Berg 	} else {
3462da4366fSEmmanuel Grumbach 		skb = orig_skb;
347*d3df49ddSJohannes Berg 	}
3482da4366fSEmmanuel Grumbach 
3492da4366fSEmmanuel Grumbach 	/* CSME wants the MAC header as well, push it back */
3502da4366fSEmmanuel Grumbach 	skb_push(skb, skb->data - skb_mac_header(skb));
3512da4366fSEmmanuel Grumbach 
3522da4366fSEmmanuel Grumbach 	/*
3532da4366fSEmmanuel Grumbach 	 * Add the packet that CSME wants to get to the ring. Don't send the
3542da4366fSEmmanuel Grumbach 	 * Check Shared Area HECI message since this is not possible from the
3552da4366fSEmmanuel Grumbach 	 * Rx context. The caller will schedule a worker to do just that.
3562da4366fSEmmanuel Grumbach 	 */
3572da4366fSEmmanuel Grumbach 	iwl_mei_add_data_to_ring(skb, false);
3582da4366fSEmmanuel Grumbach 
3592da4366fSEmmanuel Grumbach 	/*
3602da4366fSEmmanuel Grumbach 	 * In case we drop the packet, don't free it, the caller will do that
3612da4366fSEmmanuel Grumbach 	 * for us
3622da4366fSEmmanuel Grumbach 	 */
3632da4366fSEmmanuel Grumbach 	if (ret == RX_HANDLER_PASS)
3642da4366fSEmmanuel Grumbach 		dev_kfree_skb(skb);
3652da4366fSEmmanuel Grumbach 
3662da4366fSEmmanuel Grumbach 	return ret;
3672da4366fSEmmanuel Grumbach }
3682da4366fSEmmanuel Grumbach 
3692da4366fSEmmanuel Grumbach #define DHCP_SERVER_PORT 67
3702da4366fSEmmanuel Grumbach #define DHCP_CLIENT_PORT 68
iwl_mei_tx_copy_to_csme(struct sk_buff * origskb,unsigned int ivlen)3712da4366fSEmmanuel Grumbach void iwl_mei_tx_copy_to_csme(struct sk_buff *origskb, unsigned int ivlen)
3722da4366fSEmmanuel Grumbach {
3732da4366fSEmmanuel Grumbach 	struct ieee80211_hdr *hdr;
3742da4366fSEmmanuel Grumbach 	struct sk_buff *skb;
3752da4366fSEmmanuel Grumbach 	struct ethhdr ethhdr;
3762da4366fSEmmanuel Grumbach 	struct ethhdr *eth;
3772da4366fSEmmanuel Grumbach 
3782da4366fSEmmanuel Grumbach 	/* Catch DHCP packets */
3792da4366fSEmmanuel Grumbach 	if (origskb->protocol != htons(ETH_P_IP) ||
3802da4366fSEmmanuel Grumbach 	    ip_hdr(origskb)->protocol != IPPROTO_UDP ||
3812da4366fSEmmanuel Grumbach 	    udp_hdr(origskb)->source != htons(DHCP_CLIENT_PORT) ||
3822da4366fSEmmanuel Grumbach 	    udp_hdr(origskb)->dest != htons(DHCP_SERVER_PORT))
3832da4366fSEmmanuel Grumbach 		return;
3842da4366fSEmmanuel Grumbach 
3852da4366fSEmmanuel Grumbach 	/*
3862da4366fSEmmanuel Grumbach 	 * We could be a bit less aggressive here and not copy everything, but
3872da4366fSEmmanuel Grumbach 	 * this is very rare anyway, do don't bother much.
3882da4366fSEmmanuel Grumbach 	 */
3892da4366fSEmmanuel Grumbach 	skb = skb_copy(origskb, GFP_ATOMIC);
3902da4366fSEmmanuel Grumbach 	if (!skb)
3912da4366fSEmmanuel Grumbach 		return;
3922da4366fSEmmanuel Grumbach 
3932da4366fSEmmanuel Grumbach 	skb->protocol = origskb->protocol;
3942da4366fSEmmanuel Grumbach 
3952da4366fSEmmanuel Grumbach 	hdr = (void *)skb->data;
3962da4366fSEmmanuel Grumbach 
3972da4366fSEmmanuel Grumbach 	memcpy(ethhdr.h_dest, ieee80211_get_DA(hdr), ETH_ALEN);
3982da4366fSEmmanuel Grumbach 	memcpy(ethhdr.h_source, ieee80211_get_SA(hdr), ETH_ALEN);
3992da4366fSEmmanuel Grumbach 
4002da4366fSEmmanuel Grumbach 	/*
4012da4366fSEmmanuel Grumbach 	 * Remove the ieee80211 header + IV + SNAP but leave the ethertype
4022da4366fSEmmanuel Grumbach 	 * We still have enough headroom for the sap header.
4032da4366fSEmmanuel Grumbach 	 */
4042da4366fSEmmanuel Grumbach 	pskb_pull(skb, ieee80211_hdrlen(hdr->frame_control) + ivlen + 6);
4052da4366fSEmmanuel Grumbach 	eth = skb_push(skb, sizeof(ethhdr.h_dest) + sizeof(ethhdr.h_source));
4062da4366fSEmmanuel Grumbach 	memcpy(eth, &ethhdr, sizeof(ethhdr.h_dest) + sizeof(ethhdr.h_source));
4072da4366fSEmmanuel Grumbach 
4082da4366fSEmmanuel Grumbach 	iwl_mei_add_data_to_ring(skb, true);
4092da4366fSEmmanuel Grumbach 
4102da4366fSEmmanuel Grumbach 	dev_kfree_skb(skb);
4112da4366fSEmmanuel Grumbach }
4122da4366fSEmmanuel Grumbach EXPORT_SYMBOL_GPL(iwl_mei_tx_copy_to_csme);
413