brintos

brintos / linux-shallow public Read only

0
0
Text · 10.5 KiB · eac46d1 Raw
413 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2021-2022 Intel Corporation4 */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 case30 * pass_to_csme and rx_handler_res are set. Otherwise, next level of filters31 * should be checked.32 */33static 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		else59			*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 case78 * rx_handler_res is set.79 */80static 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 bytes102	 * src IP address    - 4 bytes103	 * target MAC addess - 6 bytes104	 */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 the110	 * 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		else118			*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		else129			*rx_handler_res = RX_HANDLER_CONSUMED;130 131		return true;132	}133 134	return false;135}136 137static bool138iwl_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't152		 * 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, both169		 * 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		else180			*rx_handler_res = RX_HANDLER_CONSUMED;181 182		return true;183	}184 185	return false;186}187 188static 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 bytes213		 * 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 we233		 * 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			else240				*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			else254				*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 266static 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 277static rx_handler_result_t278iwl_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 header290	 * is in the headroom.291	 */292	skb_reset_network_header(skb);293 294	/*295	 * MCAST IP packets sent by us are received again here without296	 * 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 328rx_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 343		if (!skb)344			return RX_HANDLER_PASS;345	} else {346		skb = orig_skb;347	}348 349	/* CSME wants the MAC header as well, push it back */350	skb_push(skb, skb->data - skb_mac_header(skb));351 352	/*353	 * Add the packet that CSME wants to get to the ring. Don't send the354	 * Check Shared Area HECI message since this is not possible from the355	 * Rx context. The caller will schedule a worker to do just that.356	 */357	iwl_mei_add_data_to_ring(skb, false);358 359	/*360	 * In case we drop the packet, don't free it, the caller will do that361	 * for us362	 */363	if (ret == RX_HANDLER_PASS)364		dev_kfree_skb(skb);365 366	return ret;367}368 369#define DHCP_SERVER_PORT 67370#define DHCP_CLIENT_PORT 68371void iwl_mei_tx_copy_to_csme(struct sk_buff *origskb, unsigned int ivlen)372{373	struct ieee80211_hdr *hdr;374	struct sk_buff *skb;375	struct ethhdr ethhdr;376	struct ethhdr *eth;377 378	/* Catch DHCP packets */379	if (origskb->protocol != htons(ETH_P_IP) ||380	    ip_hdr(origskb)->protocol != IPPROTO_UDP ||381	    udp_hdr(origskb)->source != htons(DHCP_CLIENT_PORT) ||382	    udp_hdr(origskb)->dest != htons(DHCP_SERVER_PORT))383		return;384 385	/*386	 * We could be a bit less aggressive here and not copy everything, but387	 * this is very rare anyway, do don't bother much.388	 */389	skb = skb_copy(origskb, GFP_ATOMIC);390	if (!skb)391		return;392 393	skb->protocol = origskb->protocol;394 395	hdr = (void *)skb->data;396 397	memcpy(ethhdr.h_dest, ieee80211_get_DA(hdr), ETH_ALEN);398	memcpy(ethhdr.h_source, ieee80211_get_SA(hdr), ETH_ALEN);399 400	/*401	 * Remove the ieee80211 header + IV + SNAP but leave the ethertype402	 * We still have enough headroom for the sap header.403	 */404	pskb_pull(skb, ieee80211_hdrlen(hdr->frame_control) + ivlen + 6);405	eth = skb_push(skb, sizeof(ethhdr.h_dest) + sizeof(ethhdr.h_source));406	memcpy(eth, &ethhdr, sizeof(ethhdr.h_dest) + sizeof(ethhdr.h_source));407 408	iwl_mei_add_data_to_ring(skb, true);409 410	dev_kfree_skb(skb);411}412EXPORT_SYMBOL_GPL(iwl_mei_tx_copy_to_csme);413